- e2e:修死选择器与蜜罐误命中、补断言、GA4/security-headers 用例诚实标注 skip 边界;新增 assert-helpers/fixtures/hydrated/primary-nav/touch-targets 等复用桩。 - 集成层:config/test/jest.integration.config.js + tests-integration/ 真库 一次性 SQLite 用例,teardown 守卫开发库指纹。 - 门禁工具链:jest.setup 归位到根、scripts/accessibility 全站 axe 节点计数、 check-brand-text-token/check-motion-constraints 机械守卫、stryker/lighthouse 配置收敛。
123 lines
4.4 KiB
JavaScript
123 lines
4.4 KiB
JavaScript
// @ts-nocheck
|
||
/**
|
||
* 集成测试 globalSetup:创建 /tmp 一次性 SQLite 库并应用 Prisma schema。
|
||
*
|
||
* 步骤(每一步都可审计):
|
||
* 1. 记录项目数据源(prisma/dev.db、<root>/dev.db、<root>/data.db)指纹基线。
|
||
* 2. 用 `prisma migrate diff --from-empty --to-schema-datamodel prisma/schema.prisma --script`
|
||
* 离线生成最终 DDL —— 该命令只输出 SQL 文本,不连接任何数据库。作为第二道保险,
|
||
* 子进程的 DATABASE_URL 同样被强制指向临时库,并在执行后复验项目库指纹未变。
|
||
* 3. 用 `npx tsx config/test/itest/apply-schema.mts` 在进程内以真实 Prisma 客户端把 DDL
|
||
* 应用到临时库,并由 SQLite 的 PRAGMA database_list 自证它打开的是哪个文件。
|
||
* 4. 复验项目库指纹,写入 manifest(worker 的 setupFiles 与 globalTeardown 都读它)。
|
||
*/
|
||
const fs = require('fs');
|
||
const path = require('path');
|
||
const { execFileSync } = require('child_process');
|
||
|
||
const guard = require('./env.js');
|
||
|
||
function stamp(msg) {
|
||
console.log(`[itest:setup] ${msg}`);
|
||
}
|
||
|
||
function assertProjectDbUntouched(baseline, where) {
|
||
const now = guard.devDbBaselines();
|
||
const drift = Object.keys(baseline).filter((k) => !guard.sameFingerprint(baseline[k], now[k]));
|
||
if (drift.length > 0) {
|
||
throw new Error(`[itest:setup] ${where} 之后项目数据源指纹发生变化:${JSON.stringify(drift)}`);
|
||
}
|
||
return now;
|
||
}
|
||
|
||
module.exports = async function globalSetup() {
|
||
// 0) URL 合法性先于任何文件操作
|
||
guard.assertSafeDatasourceUrl(guard.DATASOURCE_URL, 'globalSetup');
|
||
|
||
const baseline = guard.devDbBaselines();
|
||
stamp(`tmp realpath = ${guard.TMP_REALPATH}`);
|
||
stamp(`integration db = ${guard.DB_PATH}`);
|
||
stamp(`prisma/dev.db fp = ${JSON.stringify(baseline.prismaDevDb)}`);
|
||
|
||
// 1) 干净起步:只删除 /tmp 下属于本次运行的文件
|
||
for (const f of [
|
||
guard.DB_PATH,
|
||
`${guard.DB_PATH}-journal`,
|
||
`${guard.DB_PATH}-wal`,
|
||
`${guard.DB_PATH}-shm`,
|
||
guard.DDL_PATH,
|
||
]) {
|
||
if (!f.startsWith(`${guard.TMP_REALPATH}${path.sep}`)) {
|
||
throw new Error(`[itest:setup] 拒绝删除非临时目录文件 ${f}`);
|
||
}
|
||
try {
|
||
fs.rmSync(f);
|
||
} catch {
|
||
/* 不存在即正常 */
|
||
}
|
||
}
|
||
|
||
// 2) 离线生成 DDL(migrate diff 不连接数据库;DATABASE_URL 仍指向临时库作为第二道保险)
|
||
const diffOut = execFileSync(
|
||
'npx',
|
||
[
|
||
'prisma', 'migrate', 'diff',
|
||
'--from-empty',
|
||
'--to-schema-datamodel', 'prisma/schema.prisma',
|
||
'--script',
|
||
],
|
||
{
|
||
cwd: guard.REPO_ROOT,
|
||
encoding: 'utf8',
|
||
env: { ...process.env, DATABASE_URL: guard.DATASOURCE_URL },
|
||
stdio: ['ignore', 'pipe', 'pipe'],
|
||
},
|
||
);
|
||
const statements = guard.splitStatements(diffOut);
|
||
if (statements.length < 5) {
|
||
throw new Error(`[itest:setup] migrate diff 产出的语句数量异常(${statements.length}),拒绝继续`);
|
||
}
|
||
fs.writeFileSync(guard.DDL_PATH, diffOut);
|
||
stamp(`ddl statements = ${statements.length}`);
|
||
assertProjectDbUntouched(baseline, 'migrate diff');
|
||
|
||
// 3) 进程内应用 schema
|
||
const resultFile = `${guard.MANIFEST_PATH}.apply.json`;
|
||
const baselineFile = `${guard.MANIFEST_PATH}.baseline.json`;
|
||
try { fs.rmSync(resultFile); } catch { /* ignore */ }
|
||
fs.writeFileSync(baselineFile, JSON.stringify(baseline));
|
||
|
||
execFileSync('npx', ['tsx', path.join(__dirname, 'apply-schema.mts')], {
|
||
cwd: guard.REPO_ROOT,
|
||
encoding: 'utf8',
|
||
env: {
|
||
...process.env,
|
||
DATABASE_URL: guard.DATASOURCE_URL,
|
||
ITEST_DDL_FILE: guard.DDL_PATH,
|
||
ITEST_RESULT_FILE: resultFile,
|
||
ITEST_BASELINE_FILE: baselineFile,
|
||
},
|
||
stdio: ['ignore', 'inherit', 'inherit'],
|
||
});
|
||
|
||
const applyResult = JSON.parse(fs.readFileSync(resultFile, 'utf8'));
|
||
if (!applyResult.ok) throw new Error('[itest:setup] apply-schema 未报告成功');
|
||
stamp(`sqlite opened file= ${applyResult.openedFile}`);
|
||
stamp(`tables = ${applyResult.tables.join(',')}`);
|
||
|
||
// 4) 复验 + manifest 落盘
|
||
const afterSetup = assertProjectDbUntouched(baseline, 'apply-schema');
|
||
|
||
fs.writeFileSync(
|
||
guard.MANIFEST_PATH,
|
||
JSON.stringify(
|
||
{ datasourceUrl: guard.DATASOURCE_URL, baseline, afterSetup, applyResult, startedAt: new Date().toISOString() },
|
||
null,
|
||
2,
|
||
),
|
||
);
|
||
|
||
process.env.DATABASE_URL = guard.DATASOURCE_URL;
|
||
process.env.NOVALON_ITEST_MANIFEST = guard.MANIFEST_PATH;
|
||
};
|