Files
novalon-website/tests-integration/00-security-boundary.itest.ts
T
zhangxiang 6bb7c557ee test(qa): e2e 装配修正 + 集成/门禁测试与工具链重构
- 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
  配置收敛。
2026-09-28 10:48:08 +08:00

93 lines
3.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { afterAll, beforeAll, describe, expect, it } from '@jest/globals';
import fs from 'node:fs';
import path from 'node:path';
import { prisma, resetDb, disconnect, unique, seedModel } from './helpers/harness';
const DEV_DB = path.resolve(__dirname, '..', 'prisma', 'dev.db');
interface DbFingerprint {
exists: boolean;
ino: number | null;
size: number | null;
mtimeMs: number | null;
}
interface Manifest {
datasourceUrl: string;
baseline: Record<string, DbFingerprint>;
}
/** globalSetup 落盘的运行期清单(与临时库同目录,见 config/test/itest/env.js) */
function readManifest(): Manifest {
const dbFile = process.env.NOVALON_ITEST_DB;
if (!dbFile) throw new Error('NOVALON_ITEST_DB 未设置:worker 未经过 config/test/itest/setup-files.js');
const file = path.join(path.dirname(dbFile), 'novalon-itest-manifest.json');
return JSON.parse(fs.readFileSync(file, 'utf8')) as Manifest;
}
/**
* 安全边界规格:这个文件不测业务,它测的是「其余所有 itest 到底连在哪个库上」。
* 它是 A-8 修复的前置条件 —— 集成层一旦误连 prisma/dev.db,这里必须变红。
*/
describe('集成层数据源边界', () => {
beforeAll(async () => {
await resetDb();
});
afterAll(async () => {
await disconnect();
});
it('打开的是临时目录里的一次性 SQLite 文件,项目数据源指纹未变', async () => {
// 1) 引擎自报打开的文件 —— 不是「我们以为的路径」,而是 SQLite 实际 attach 的路径
const attached = await prisma.$queryRawUnsafe<Array<{ file: string }>>('PRAGMA database_list');
const file = attached[0]?.file ?? '';
expect(file).toMatch(/novalon-itest\.db$/);
expect(file).not.toMatch(/(^|[/\\])(dev|data|test|prod)\.db$/);
// 2) 真的在跟 SQLite 说话:引擎版本号只有真实引擎给得出,任何 jest mock 都造不出来
const version = await prisma.$queryRawUnsafe<Array<{ v: string }>>('SELECT sqlite_version() AS v');
expect(version[0]?.v).toMatch(/^\d+\.\d+\.\d+$/);
// 3) worker 的 DATABASE_URL 与 globalSetup 落盘的 manifest 一致,且是绝对 file: 路径
const envUrl = process.env.DATABASE_URL ?? '';
const manifest = readManifest();
expect(envUrl.startsWith('file:/')).toBe(true);
expect(manifest.datasourceUrl).toBe(envUrl);
expect(envUrl).toBe(`file:${process.env.NOVALON_ITEST_DB}`);
// 4) 项目数据源在集成测试运行前后是同一个文件且完全未写(inode + size + mtime 三重比对)
const now = fs.statSync(DEV_DB);
const before = manifest.baseline.prismaDevDb;
if (!before) throw new Error('manifest 缺少 prismaDevDb 基线,守卫无法比对');
expect([now.ino, now.size, Math.round(now.mtimeMs)]).toEqual([
before.ino,
before.size,
before.mtimeMs === null ? null : Math.round(before.mtimeMs),
]);
});
it('应用的 schema 带真实约束(唯一索引与外键都生效,不是空表壳)', async () => {
const code = unique('it-model');
const { id } = await seedModel(code, []);
await prisma.contentItem.create({
data: { modelId: id, modelCode: code, title: '第一版', slug: 'dup', data: '{}' },
});
// 同一 (modelCode, slug, locale) 的第二次写入必须被真实唯一索引拒绝
await expect(
prisma.contentItem.create({
data: { modelId: id, modelCode: code, title: '第二版', slug: 'dup', data: '{}' },
}),
).rejects.toMatchObject({ code: 'P2002' });
// modelId 指向不存在的模型必须被真实外键(ContentItem_modelId_fkey)拒绝
await expect(
prisma.contentItem.create({
data: { modelId: 'no-such-model', modelCode: code, title: '孤儿', slug: unique('orphan'), data: '{}' },
}),
).rejects.toThrow();
});
});