Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Persist Roles and Permissions between test runs #854

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
1058b3a
run custom db reset in cleanupDb
devneill Sep 14, 2024
0fa8a20
reset test db with prisma reset
devneill Aug 30, 2024
ee9ec4e
run prisma reset via spawn
devneill Sep 23, 2024
d5afca7
use custom db reset
devneill Sep 24, 2024
6bf0771
Update tests/db-utils.ts
kentcdodds Sep 24, 2024
7cc50fd
Update prisma/migrations/20230914194400_init/migration.sql
kentcdodds Sep 24, 2024
ac110c0
remove extra line (whoops)
kentcdodds Sep 24, 2024
99af7cd
whoops
kentcdodds Sep 24, 2024
8d2be4c
improve error logging
devneill Sep 25, 2024
c142a92
Update tests/db-utils.ts
kentcdodds Sep 25, 2024
17686c9
log failed statement as error
devneill Sep 26, 2024
9fae585
trigger CI rerun
devneill Sep 26, 2024
63c021a
throw error again
devneill Sep 26, 2024
c117336
trigger rerun again
devneill Sep 26, 2024
2db0918
one more time
devneill Sep 26, 2024
03edf6c
and again
devneill Sep 26, 2024
55b7950
use console.warn again
devneill Sep 26, 2024
50490e4
retrigger
devneill Sep 26, 2024
0744470
retrigger
devneill Sep 26, 2024
112bd0d
retrigger
devneill Sep 26, 2024
3fbaa78
remove trigger line
devneill Sep 26, 2024
a411a4a
Wrap statement execution in a transaction
devneill Sep 28, 2024
e279227
Try map
devneill Sep 28, 2024
bdfb389
explicitly set isolation level
devneill Sep 28, 2024
8de38de
retrigger
devneill Sep 28, 2024
b3e5377
retrigger
devneill Sep 28, 2024
bfa6b99
retrigger
devneill Sep 28, 2024
eb2dd60
retrigger
devneill Sep 28, 2024
1b0be2b
retrigger
devneill Sep 28, 2024
ccebfbf
remove explicit isolation level config
devneill Sep 29, 2024
39c2702
Remove unused import
devneill Sep 30, 2024
3c975b9
try switching to better-sqlite3
kentcdodds Oct 1, 2024
4d9537b
lint issue
kentcdodds Oct 1, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 48 additions & 1 deletion prisma/migrations/20230914194400_init/migration.sql
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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');
kentcdodds marked this conversation as resolved.
Show resolved Hide resolved
43 changes: 1 addition & 42 deletions prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down
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()
Expand Down Expand Up @@ -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
Expand Up @@ -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 () => {
Expand Down