Files
浅念 f1c181de02 test(db): 空库一键迁移验收 + globalSetup 临时库 + 公共夹具收敛(阶段1验收)
- vitest globalSetup:每次运行 CREATE sf_test_<ts> 临时库 → drizzle-orm 程序化迁移器
  全量迁移(apps/api/src/db/migrate.ts,drizzle-kit 无法指向运行时建库)→ 设
  DATABASE_URL_TEST 让 getDb() 自动回退到临时库;teardown DROP(WITH FORCE)。
  有 default 导出时 vitest 不捕获具名 teardown,故用双具名导出。
- db.full-migrate.test.ts:21 表齐 + 四张不可变触发器在库 + sf_test_* 护栏断言
  (current_database 不符即红,杜绝静默连开发库)。
- helpers.ts 收敛:rand/rejectionMessage/createSiteFixture/withImmutableCleanup/
  withTestDb,Task 6–12 七文件内联版替换为 import(断言、对照组、清场顺序不变)。
- scripts/migrate-test.sh:docker compose up --wait + pnpm test 一键验收。
- 实证:75 测试全绿(72+3);开发库 21 表行数总和 0(零写入);无 sf_test_* 残留。

Co-Authored-By: Claude Code <noreply@anthropic.com>
2026-10-20 20:43:13 +08:00

48 lines
2.4 KiB
TypeScript
Raw Permalink 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.
// 空库全量迁移验收(Task 13 / 阶段 1 收官):globalSetup 对 sf_test_<ts> 临时库跑
// apps/api/drizzle 全量迁移(0000–0006),本文件在空迁移后的临时库上断言:
// 1. 21 张域表全部存在(数量恰为 21——既防缺表也防多表);
// 2. 四张不可变表的触发器在库;
// 3. 护栏:current_database() 必须是 sf_test_*(globalSetup 生效证明 + 绝不静默连开发库)。
// 全文件零写入——验收的是迁移产物本身。
import { describe, it, expect } from 'vitest'
import { getDb } from '../../apps/api/src/db/client.js'
const EXPECTED_TABLES = [
'sites', 'site_config_revisions',
'source_connections', 'source_snapshots', 'import_jobs', 'source_records',
'content_entries', 'entry_locales', 'content_revisions',
'taxonomies', 'terms', 'content_terms',
'media_assets', 'media_usages',
'route_registry',
'build_jobs', 'build_input_snapshots', 'build_artifacts', 'validation_runs', 'validation_issues',
'audit_logs',
]
describe('空库全量迁移', () => {
it('一键迁移后 21 张核心表全部存在', async () => {
const db = getDb()
const r = await db.execute(`select table_name from information_schema.tables where table_schema='public'`)
const tables = (r as any[]).map((x) => (x as any).table_name)
for (const t of EXPECTED_TABLES) expect(tables).toContain(t)
expect(tables.filter((t: string) => EXPECTED_TABLES.includes(t)).length).toBe(21)
})
it('四张不可变表的触发器在库', async () => {
const db = getDb()
const r = await db.execute(`select trigger_name from information_schema.triggers where trigger_schema='public'`)
const names = new Set((r as any[]).map((x) => (x as any).trigger_name))
for (const t of ['source_snapshots_immutable', 'content_revisions_immutable', 'build_input_snapshots_immutable', 'audit_logs_immutable']) {
expect(names.has(t)).toBe(true)
}
})
it('护栏:套件运行在 globalSetup 创建的 sf_test_* 临时库上,绝不连开发库', async () => {
const db = getDb()
const r = (await db.execute(`select current_database() as db`)) as any
// globalSetup 未生效时此断言失败(current_database 是开发库 siteforge)——
// 宁可红也不让集成测试静默读写开发库。
expect(r[0].db).toMatch(/^sf_test_\d+$/)
expect(process.env.DATABASE_URL_TEST ?? '').toMatch(/^postgres:\/\/.*sf_test_\d+$/)
})
})