494beadf94
Co-Authored-By: Claude Code <noreply@anthropic.com>
71 lines
3.2 KiB
TypeScript
71 lines
3.2 KiB
TypeScript
// 集成测试公共夹具(Task 13 收敛):Task 6–12 七个域测试文件的内联重复件抽此共享。
|
||
// 收敛纪律:只抽"每个文件逐字重复"的实现——随机后缀、建 site、不可变表清场开关、
|
||
// 拒绝断言解包、连接生命周期;各文件的断言、对照组、清场范围与顺序保持不变。
|
||
// 注:各 withXFixture 的 setup 阶段中途失败不清理已插半的行——该盲区已被 ADR-015 临时库整库 DROP 实质中和,属已接受设计。
|
||
import { getDb } from '../../apps/api/src/db/client.js'
|
||
|
||
export type Db = ReturnType<typeof getDb>
|
||
|
||
/** 随机后缀:毫秒时间戳 + 6 位随机,同毫秒并发行也可区分;所有 fixture 的唯一性来源。 */
|
||
export function rand(): string {
|
||
return `${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
|
||
}
|
||
|
||
// drizzle 会把 PostgresError 包成 DrizzleQueryError(message 只有 "Failed query: ..."),
|
||
// 服务端原始错误文本在 cause 里——断言触发器/约束/枚举身份时必须看 cause。
|
||
export async function rejectionMessage(db: Db, query: string): Promise<string> {
|
||
try {
|
||
await db.execute(query)
|
||
} catch (e: any) {
|
||
return String(e?.cause?.message ?? e?.message ?? e)
|
||
}
|
||
throw new Error(`expected query to be rejected: ${query}`)
|
||
}
|
||
|
||
/**
|
||
* 站点夹具:随机 key 建 site,返回 id + 清理句柄。
|
||
* 清理句柄只删 site 行——调用方必须先删完该站点的子表行再调用(FK 依赖顺序由各域自查)。
|
||
* name/origin 是不参与任何断言的装饰列,统一按 prefix 派生。
|
||
*/
|
||
export type SiteFixture = { siteId: string; key: string; rand: string; cleanup: () => Promise<void> }
|
||
|
||
export async function createSiteFixture(db: Db, prefix: string): Promise<SiteFixture> {
|
||
const r = rand()
|
||
const key = `${prefix}-${r}`
|
||
const site = (await db.execute(`insert into sites (id, key, name, default_locale, production_origin)
|
||
values (gen_random_uuid(), '${key}', '${prefix} fixture', 'en', 'https://${prefix}.example') returning id`)) as any
|
||
const siteId = site[0].id
|
||
return {
|
||
siteId,
|
||
key,
|
||
rand: r,
|
||
cleanup: async () => {
|
||
await db.execute(`delete from sites where id = '${siteId}'`)
|
||
},
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 不可变表清场封装:DISABLE 触发器 → 删行 → ENABLE(嵌套 finally 保证触发器必然恢复)。
|
||
* 不可变表的生产语义是永不删;测试清场是唯一例外,删除范围由调用方谓词圈定。
|
||
* @param where 完整 WHERE 谓词(域测试按 site_id 圈;audit_logs 组织级行按 command_id 圈)
|
||
*/
|
||
export async function withImmutableCleanup(db: Db, table: string, trigger: string, where: string): Promise<void> {
|
||
await db.execute(`alter table ${table} disable trigger ${trigger}`)
|
||
try {
|
||
await db.execute(`delete from ${table} where ${where}`)
|
||
} finally {
|
||
await db.execute(`alter table ${table} enable trigger ${trigger}`)
|
||
}
|
||
}
|
||
|
||
/** 连接生命周期封装:每个测试持有独立 getDb 池,结束必关(清场先于关池执行)。 */
|
||
export async function withTestDb(fn: (db: Db) => Promise<void>): Promise<void> {
|
||
const db = getDb()
|
||
try {
|
||
await fn(db)
|
||
} finally {
|
||
await (db as any).$client.end()
|
||
}
|
||
}
|