// vitest globalSetup(Task 13):每次测试运行用完好的空临时库,结束即删—— // 彻底消除"测试碰开发库"与"清场残留"两类问题。 // 机制:用开发库凭据连维护库(postgres)CREATE DATABASE sf_test_ → // 用 drizzle-orm 程序化迁移器对临时库跑 apps/api/drizzle 全量迁移 → // 设 process.env.DATABASE_URL_TEST(getDb() 的 DATABASE_URL_TEST ?? DATABASE_URL // 回退链让所有测试自动连临时库;worker 由主进程在 setup 之后派生,env 继承生效)。 // 导出形式注意:vitest 的 globalSetup 加载器在有 default 导出时不捕获具名 teardown // (loadGlobalSetupFile 的 if/default 分支只取 default 作 setup),故这里必须 // 用具名 setup + teardown 双导出、不写 default——teardown 才会被注册并在收尾执行。 // teardown DROP DATABASE 用 WITH (FORCE) 先断开残余业务连接再删;setup 中途失败也自清。 import { requireDatabaseUrl } from '../../apps/api/src/db/env.js' import { createDatabase, dropDatabase, migrateDatabase } from '../../apps/api/src/db/migrate.js' let created: { adminUrl: string; name: string } | undefined export async function setup(): Promise { // 只认 DATABASE_URL(开发库凭据)——即便环境里残留 DATABASE_URL_TEST 也不受影响 const devUrl = requireDatabaseUrl({ allowTestUrl: false }) const adminUrl = new URL(devUrl) adminUrl.pathname = '/postgres' // CREATE/DROP DATABASE 须在维护库会话里执行 const name = `sf_test_${Date.now()}` const testUrl = new URL(devUrl) testUrl.pathname = `/${name}` await createDatabase(adminUrl.toString(), name) try { await migrateDatabase(testUrl.toString()) } catch (e) { // 迁移半途而废:立即清掉本次临时库再抛出,失败运行也不留残留 await dropDatabase(adminUrl.toString(), name) throw e } process.env.DATABASE_URL_TEST = testUrl.toString() created = { adminUrl: adminUrl.toString(), name } } export async function teardown(): Promise { if (!created) return const { adminUrl, name } = created created = undefined await dropDatabase(adminUrl, name) }