import assert from 'node:assert/strict' import { existsSync, readFileSync } from 'node:fs' import { createRequire } from 'node:module' import path from 'node:path' import { fileURLToPath } from 'node:url' import test from 'node:test' import { createMigratorLock } from './database-migrator-lock.mjs' const read = relative => JSON.parse(readFileSync(fileURLToPath(new URL(relative, import.meta.url)), 'utf8')) const root = read('../package-lock.json') const manifest = read('../workers/database-migrator/package.json') const lock = read('../workers/database-migrator/package-lock.json') test('committed migrator lock is the exact reproducible main-lock subset', () => { assert.deepEqual(lock, createMigratorLock(root, manifest)) assert.ok(Object.keys(lock.packages).length < 50) for (const [name, record] of Object.entries(lock.packages)) { if (!name) continue for (const key of ['version', 'resolved', 'integrity']) assert.equal(record[key], root.packages[name][key]) assert.equal(record.dev, undefined) assert.equal(record.devOptional, undefined) } }) test('Prisma CLI uses its matching nested engines without app Prisma 6 or UI/media/test packages', () => { assert.equal(lock.packages['node_modules/prisma/node_modules/@prisma/engines'].version, '5.22.0') for (const name of ['@prisma/engines', 'next', 'react', 'canvas', 'sharp', 'jest', 'typescript', '@aws-sdk/client-s3']) { assert.equal(lock.packages[`node_modules/${name}`], undefined, `Unexpected app package ${name}`) } assert.ok(lock.packages['node_modules/@esbuild/linux-x64']) assert.ok(lock.packages['node_modules/get-tsconfig']) }) test('direct dependency version drift and new dependency groups fail closed', () => { const changedRoot = structuredClone(root) changedRoot.packages['node_modules/prisma'].version = '6.18.0' assert.throws(() => createMigratorLock(changedRoot, manifest), /differs from the main lock/) assert.throws(() => createMigratorLock(root, { ...manifest, devDependencies: { next: '14.2.33' } }), /unreviewed dependency groups/) assert.throws(() => createMigratorLock(root, { ...manifest, dependencies: { ...manifest.dependencies, next: '14.2.33' } }), /explicitly reviewed/) }) test('missing transitive package and missing integrity fail closed', () => { const missing = structuredClone(root) delete missing.packages['node_modules/get-tsconfig'] assert.throws(() => createMigratorLock(missing, manifest), /Unresolved dependency/) const unsigned = structuredClone(root) delete unsigned.packages['node_modules/prisma/node_modules/@prisma/engines'].integrity assert.throws(() => createMigratorLock(unsigned, manifest), /integrity/) }) test('optional peer additions require explicit review instead of silently disappearing', () => { const changed = structuredClone(root) changed.packages['node_modules/zod'].peerDependencies = { next: '*' } changed.packages['node_modules/zod'].peerDependenciesMeta = { next: { optional: true } } assert.throws(() => createMigratorLock(changed, manifest), /Unreviewed optional peer/) }) test('slim migrator copies the full seed source closure and no unreviewed runtime dependency', () => { const require = createRequire(import.meta.url) const ts = require('typescript') const rootDir = fileURLToPath(new URL('../', import.meta.url)) const files = new Set() const packages = new Set() const visitFile = relative => { relative = relative.replaceAll('\\', '/') if (files.has(relative)) return files.add(relative) const absolute = path.join(rootDir, relative) const ast = ts.createSourceFile(absolute, readFileSync(absolute, 'utf8'), ts.ScriptTarget.Latest, true) const resolve = specifier => { if (specifier.startsWith('node:')) return if (!specifier.startsWith('.') && !specifier.startsWith('@/')) { packages.add(specifier) return } const base = specifier.startsWith('@/') ? specifier.slice(2) : path.join(path.dirname(relative), specifier) const next = [base, `${base}.ts`, `${base}.tsx`, `${base}/index.ts`].find(candidate => existsSync(path.join(rootDir, candidate))) assert.ok(next, `Unresolved seed import ${specifier} from ${relative}`) visitFile(next) } const visitNode = node => { if ((ts.isImportDeclaration(node) || ts.isExportDeclaration(node)) && node.moduleSpecifier) resolve(node.moduleSpecifier.text) if (ts.isCallExpression(node) && node.expression.getText(ast) === 'require' && ts.isStringLiteral(node.arguments[0])) resolve(node.arguments[0].text) ts.forEachChild(node, visitNode) } visitNode(ast) } visitFile('scripts/seed-team-function-config.ts') assert.deepEqual([...packages].sort(), ['@prisma/client', 'zod']) const dockerfile = readFileSync(path.join(rootDir, 'Dockerfile'), 'utf8') const migrator = dockerfile.slice(dockerfile.search(/^FROM base AS migrator\r?$/m), dockerfile.search(/^FROM base AS runner\r?$/m)) for (const file of files) assert.ok(migrator.includes(`/app/${file}`), `Seed dependency missing from slim image: ${file}`) assert.equal(files.size, 14, 'Review seed dependency closure before changing the image copy list') }) test('Docker locks the isolated install and runs Prisma as nonroot with no network', () => { const dockerfile = readFileSync(fileURLToPath(new URL('../Dockerfile', import.meta.url)), 'utf8') assert.match(dockerfile, /node scripts\/database-migrator-lock\.mjs --check/) const depsStart = dockerfile.search(/^FROM base AS migrator-deps\r?$/m) assert.ok(depsStart >= 0) const deps = dockerfile.slice(depsStart, dockerfile.indexOf('\nFROM ', depsStart + 1)) assert.match(deps, /npm ci --no-audit --no-fund/) assert.match(deps, /PRISMA_SKIP_POSTINSTALL_GENERATE=1 npm ci/) assert.doesNotMatch(deps, /--ignore-scripts/) assert.doesNotMatch(deps, /npm (?:install|update)/) assert.match(deps, /apk add --no-cache openssl/) const start = dockerfile.search(/^FROM base AS migrator\r?$/m) const migrator = dockerfile.slice(start, dockerfile.indexOf('\nFROM ', start + 1)) assert.match(migrator, /USER node[\s\S]*RUN --network=none[^\n]*prisma (?:--version|-v)/) assert.match(migrator, /COPY --from=builder --chown=root:root \/app\/node_modules\/\.prisma/) assert.match(migrator, /ENTRYPOINT \["node", "scripts\/safe-production-migrate\.mjs"\]/) })