| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495 |
- import { execFileSync } from 'node:child_process'
- import { existsSync, readFileSync } from 'node:fs'
- import { resolve } from 'node:path'
- /**
- * Puts a known database in front of the suite: every migration applied to an
- * empty schema, then the demo seed.
- *
- * The seed is the reason this is cheap. It pins its user and organisation ids
- * and writes a password hash better-auth can verify, so the tests get a
- * populated workshop and a working login without a mail server in the loop.
- *
- * Run from the web server's own command line, ahead of `next start`, because
- * Playwright brings the server up before global setup runs and a server on an
- * empty schema answers every page with an error. When the suite is pointed at
- * a server somebody else started, global setup calls this instead.
- */
- const TEST_DB_MARKERS = ['e2e', 'test']
- function devDatabaseUrl(): string | null {
- const envFile = resolve(process.cwd(), '.env')
- if (!existsSync(envFile)) return null
- for (const line of readFileSync(envFile, 'utf8').split('\n')) {
- const match = line.match(/^\s*DATABASE_URL\s*=\s*"?([^"\n]+)"?/)
- if (match) return match[1].trim()
- }
- return null
- }
- /**
- * Refuses to point the reset at anything that looks like real data. Resetting
- * drops every table, so a copy-pasted connection string is the one mistake
- * worth being rude about.
- */
- function assertSafeToDestroy(url: string): void {
- if (process.env.E2E_ALLOW_ANY_DB === '1') return
- const dev = devDatabaseUrl()
- if (dev && dev === url) {
- throw new Error(
- 'E2E_DATABASE_URL is the same database as .env DATABASE_URL. ' +
- 'The suite resets the database it is given; point it at a throwaway one.'
- )
- }
- const database = url.split('/').pop()?.split('?')[0]?.toLowerCase() ?? ''
- if (!TEST_DB_MARKERS.some((marker) => database.includes(marker))) {
- throw new Error(
- `Refusing to reset database "${database}": the name contains neither "e2e" nor "test". ` +
- 'Rename it, or set E2E_ALLOW_ANY_DB=1 if you are certain.'
- )
- }
- }
- function run(command: string, args: string[], env: NodeJS.ProcessEnv): void {
- execFileSync(command, args, { stdio: 'inherit', env })
- }
- export function prepareDatabase(): void {
- const url = process.env.E2E_DATABASE_URL
- if (!url) {
- throw new Error('E2E_DATABASE_URL is not set. See e2e/README.md.')
- }
- if (process.env.E2E_SKIP_SEED === '1') {
- console.log('[e2e] E2E_SKIP_SEED=1, leaving the database alone.')
- return
- }
- assertSafeToDestroy(url)
- const env: NodeJS.ProcessEnv = {
- ...process.env,
- DATABASE_URL: url,
- // The seed writes vehicle photos next to the app's uploads. Kept out of the
- // real data directory so a test run cannot disturb a running instance.
- DATA_ROOT: process.env.E2E_DATA_ROOT ?? resolve(process.cwd(), 'e2e/.data'),
- // The seed's own default is four characters, which the reset-password
- // page refuses; the suite seeds the owner with the password it signs in
- // with, long enough to be set back after the reset flow has changed it.
- DEMO_USER_EMAIL: process.env.E2E_USER_EMAIL ?? 'demo@torqvoice.com',
- DEMO_USER_PASSWORD: process.env.E2E_USER_PASSWORD ?? 'demo-e2e-pass',
- }
- console.log('[e2e] resetting the test database')
- // Prisma 7 has no seed hook in this config and no skip flags: reset applies
- // every migration and nothing else, and the seed is the step after.
- run('npx', ['prisma', 'migrate', 'reset', '--force'], env)
- console.log('[e2e] seeding')
- run('npx', ['tsx', 'prisma/seed_dummy_data.ts'], env)
- }
- // `tsx e2e/prepare-db.ts` from the web server command.
- if (process.argv[1]?.endsWith('prepare-db.ts')) prepareDatabase()
|