// @ts-nocheck /** * 在**进程内**用真实 Prisma 客户端把 schema 应用到一次性临时 SQLite 文件。 * * 由 config/test/itest/global-setup.js 通过 `npx tsx` 调用(与项目既有约定一致: * package.json 的 db:seed / check:contrast 同样用 `npx tsx` 跑 TS 脚本)。 * * 为什么不用 `prisma db push`:db push 会自行解析数据源并连接,无法在「连接之前」证明它 * 打开的是哪个文件。本脚本显式传入 `datasourceUrl`,并在建表后用 * `PRAGMA database_list` 让 **SQLite 自己回答**它打开了哪个文件,再断言该路径位于 * realpath(os.tmpdir()) 之下 —— 这是比「事后检查」更强的保证。 */ import { createRequire } from 'node:module'; import path from 'node:path'; import fs from 'node:fs'; const require = createRequire(import.meta.url); const guard = require('./env.js'); // 真实(非 mock)Prisma 客户端:与 src/lib/db.ts 走同一个生成产物 const { PrismaClient } = require(path.join(guard.REPO_ROOT, 'src', 'generated', 'prisma', 'client.ts')); const url = process.env.DATABASE_URL; const ddlFile = process.env.ITEST_DDL_FILE; const resultFile = process.env.ITEST_RESULT_FILE; /** 与 src/lib/db.ts 完全一致的构造方式(无参构造 → 读 env DATABASE_URL), * 唯一区别是这里额外显式传 datasourceUrl,让「指向哪里」在代码里可读可审。 */ const client = new PrismaClient({ datasourceUrl: url }); const resolvedUrl = guard.assertSafeDatasourceUrl(url, 'apply-schema'); const statements = guard.splitStatements(fs.readFileSync(ddlFile, 'utf8')); const applied = []; for (const statement of statements) { await client.$executeRawUnsafe(statement); applied.push(statement.split('\n')[0].slice(0, 60)); } // SQLite 自报打开的文件 —— 断言它确实是临时库,而不是 prisma/dev.db。 const dbList = await client.$queryRawUnsafe('PRAGMA database_list'); const openedFile = dbList?.[0]?.file; if (typeof openedFile !== 'string') { throw new Error(`[itest-guard] PRAGMA database_list 未返回文件路径:${JSON.stringify(dbList)}`); } const verifiedOpen = guard.assertSafeDatasourceUrl(`file:${openedFile}`, 'database_list'); const tables = await client.$queryRawUnsafe( `SELECT name FROM sqlite_master WHERE type='table' ORDER BY name`, ); // 建表后复查项目库指纹:db push/diff 期间不得改动 prisma/dev.db。 const after = guard.devDbBaselines(); const baseline = JSON.parse(fs.readFileSync(process.env.ITEST_BASELINE_FILE, 'utf8')); const drift = Object.keys(baseline).filter((key) => !guard.sameFingerprint(baseline[key], after[key])); if (drift.length > 0) { throw new Error(`[itest-guard] 项目数据源被改动:${drift.join(', ')}`); } await client.$disconnect(); fs.writeFileSync( resultFile, JSON.stringify({ ok: true, datasourceUrl: url, resolvedUrl, openedFile: verifiedOpen, statementCount: statements.length, applied, tables: tables.map((t) => t.name), devDb: after, }, null, 2), ); console.log('[itest] schema applied'); console.log('[itest] DATABASE_URL =', url); console.log('[itest] sqlite opened', openedFile); console.log('[itest] tables =', tables.map((t) => t.name).join(','));