Skip to content

Commit

Permalink
Persist Roles and Permissions between test runs (#854)
Browse files Browse the repository at this point in the history
  • Loading branch information
devneill authored Oct 1, 2024

Verified

This commit was signed with the committer’s verified signature.
YOU54F Yousaf Nabi
1 parent ec0c3c4 commit 343dc5a
Showing 4 changed files with 105 additions and 60 deletions.
49 changes: 48 additions & 1 deletion prisma/migrations/20230914194400_init/migration.sql
Original file line number Diff line number Diff line change
@@ -173,6 +173,53 @@ CREATE INDEX "_RoleToUser_B_index" ON "_RoleToUser"("B");
-- Hey there, Kent here! This is how you can reliably seed your database with
-- some data. You edit the migration.sql file and that will handle it for you.

-- The user Roles and Permissions are seeded here.
-- If you'd like to customise roles and permissions, you can edit and add the code below to your `prisma/seed.ts` file.
-- Seed your development database with `npx prisma db seed`
-- Create a sql dump of your database with `sqlite3 prisma/data.db .dump > seed.sql`
-- Replace the SQL below with your new Roles & Permissions related SQL from `seed.sql`

-- console.time('🔑 Created permissions...')
-- const entities = ['user', 'note']
-- const actions = ['create', 'read', 'update', 'delete']
-- const accesses = ['own', 'any'] as const

-- let permissionsToCreate = []
-- for (const entity of entities) {
-- for (const action of actions) {
-- for (const access of accesses) {
-- permissionsToCreate.push({ entity, action, access })
-- }
-- }
-- }
-- await prisma.permission.createMany({ data: permissionsToCreate })
-- console.timeEnd('🔑 Created permissions...')

-- console.time('👑 Created roles...')
-- await prisma.role.create({
-- data: {
-- name: 'admin',
-- permissions: {
-- connect: await prisma.permission.findMany({
-- select: { id: true },
-- where: { access: 'any' },
-- }),
-- },
-- },
-- })
-- await prisma.role.create({
-- data: {
-- name: 'user',
-- permissions: {
-- connect: await prisma.permission.findMany({
-- select: { id: true },
-- where: { access: 'own' },
-- }),
-- },
-- },
-- })
-- console.timeEnd('👑 Created roles...')

INSERT INTO Permission VALUES('clnf2zvli0000pcou3zzzzome','create','user','own','',1696625465526,1696625465526);
INSERT INTO Permission VALUES('clnf2zvll0001pcouly1310ku','create','user','any','',1696625465529,1696625465529);
INSERT INTO Permission VALUES('clnf2zvll0002pcouka7348re','read','user','own','',1696625465530,1696625465530);
@@ -208,4 +255,4 @@ INSERT INTO _PermissionToRole VALUES('clnf2zvlo0006pcouyoptc5jp','clnf2zvlx000hp
INSERT INTO _PermissionToRole VALUES('clnf2zvlp0008pcou9r0fhbm8','clnf2zvlx000hpcou5dfrbegs');
INSERT INTO _PermissionToRole VALUES('clnf2zvlq000apcouxnspejs9','clnf2zvlx000hpcou5dfrbegs');
INSERT INTO _PermissionToRole VALUES('clnf2zvlr000cpcouy1vp6oeg','clnf2zvlx000hpcou5dfrbegs');
INSERT INTO _PermissionToRole VALUES('clnf2zvls000epcou4ts5ui8f','clnf2zvlx000hpcou5dfrbegs');
INSERT INTO _PermissionToRole VALUES('clnf2zvls000epcou4ts5ui8f','clnf2zvlx000hpcou5dfrbegs');
43 changes: 1 addition & 42 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
@@ -17,50 +17,9 @@ async function seed() {
console.time(`🌱 Database has been seeded`)

console.time('🧹 Cleaned up the database...')
await cleanupDb(prisma)
await cleanupDb()
console.timeEnd('🧹 Cleaned up the database...')

console.time('🔑 Created permissions...')
const entities = ['user', 'note']
const actions = ['create', 'read', 'update', 'delete']
const accesses = ['own', 'any'] as const

let permissionsToCreate = []
for (const entity of entities) {
for (const action of actions) {
for (const access of accesses) {
permissionsToCreate.push({ entity, action, access })
}
}
}
await prisma.permission.createMany({ data: permissionsToCreate })
console.timeEnd('🔑 Created permissions...')

console.time('👑 Created roles...')
await prisma.role.create({
data: {
name: 'admin',
permissions: {
connect: await prisma.permission.findMany({
select: { id: true },
where: { access: 'any' },
}),
},
},
})
await prisma.role.create({
data: {
name: 'user',
permissions: {
connect: await prisma.permission.findMany({
select: { id: true },
where: { access: 'own' },
}),
},
},
})
console.timeEnd('👑 Created roles...')

const totalUsers = 5
console.time(`👤 Created ${totalUsers} users...`)
const noteImages = await getNoteImages()
70 changes: 55 additions & 15 deletions tests/db-utils.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import fs from 'node:fs'
import { faker } from '@faker-js/faker'
import { type PrismaClient } from '@prisma/client'
import bcrypt from 'bcryptjs'
import Database from 'better-sqlite3'
import { UniqueEnforcer } from 'enforce-unique'

const uniqueUsernameEnforcer = new UniqueEnforcer()
@@ -115,23 +115,63 @@ export async function img({
}
}

export async function cleanupDb(prisma: PrismaClient) {
const tables = await prisma.$queryRaw<
{ name: string }[]
>`SELECT name FROM sqlite_master WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_prisma_migrations';`
let _migrationSqls: Array<Array<string>> | undefined
async function getMigrationSqls() {
if (_migrationSqls) return _migrationSqls

const migrationSqls: Array<Array<string>> = []
const migrationPaths = (await fs.promises.readdir('prisma/migrations'))
.filter((dir) => dir !== 'migration_lock.toml')
.map((dir) => `prisma/migrations/${dir}/migration.sql`)

for (const path of migrationPaths) {
const sql = await fs.promises.readFile(path, 'utf8')
const statements = sql
.split(';')
.map((statement) => statement.trim())
.filter(Boolean)
migrationSqls.push(statements)
}

_migrationSqls = migrationSqls

return migrationSqls
}

export async function cleanupDb() {
const db = new Database(process.env.DATABASE_URL!.replace('file:', ''))

try {
// Disable FK constraints to avoid relation conflicts during deletion
await prisma.$executeRawUnsafe(`PRAGMA foreign_keys = OFF`)
await prisma.$transaction([
// Delete all rows from each table, preserving table structures
...tables.map(({ name }) =>
prisma.$executeRawUnsafe(`DELETE from "${name}"`),
),
])
} catch (error) {
console.error('Error cleaning up database:', error)
db.exec('PRAGMA foreign_keys = OFF')

// Get all table names
const tables = db
.prepare(
`
SELECT name FROM sqlite_master
WHERE type='table' AND name NOT LIKE 'sqlite_%' AND name NOT LIKE '_prisma_migrations'
`,
)
.all() as { name: string }[]

// Delete tables except the ones that are excluded above
for (const { name } of tables) {
db.exec(`DROP TABLE IF EXISTS "${name}"`)
}

// Get migration SQLs and run each migration
const migrationSqls = await getMigrationSqls()
for (const statements of migrationSqls) {
// Run each sql statement in the migration
db.transaction(() => {
for (const statement of statements) {
db.exec(statement)
}
})()
}
} finally {
await prisma.$executeRawUnsafe(`PRAGMA foreign_keys = ON`)
db.exec('PRAGMA foreign_keys = ON')
db.close()
}
}
3 changes: 1 addition & 2 deletions tests/setup/db-setup.ts
Original file line number Diff line number Diff line change
@@ -15,8 +15,7 @@ beforeAll(async () => {
// we *must* use dynamic imports here so the process.env.DATABASE_URL is set
// before prisma is imported and initialized
afterEach(async () => {
const { prisma } = await import('#app/utils/db.server.ts')
await cleanupDb(prisma)
await cleanupDb()
})

afterAll(async () => {

0 comments on commit 343dc5a

Please sign in to comment.