Files
cms-website/tests/integration/helpers.ts
T

71 lines
3.2 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.
// 集成测试公共夹具(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()
}
}