/** * 集成测试的库夹具(编码规范 §1:集成测试用 Testcontainers(PG))。 * * 取库次序: * ① `JCAI_TEST_DATABASE_URL` / `DATABASE_URL` 指向的库可连 → 直接用(本机 docker-compose 最快) * ② 否则起一个 Testcontainers 的 PG16 * ③ 两者都不可得 → 返回 undefined,集成测试**跳过并告警**(单元测试不受影响) * * 每个套件用**独立 schema 前缀**建表,互不干扰;结束即清理。 */ import { Pool } from 'pg'; export type ItDatabase = { readonly url: string; stop(): Promise; }; const FOUR_SCHEMAS = ['sys', 'ledger', 'mastra', 'queue']; async function reachable(url: string): Promise { const pool = new Pool({ connectionString: url, max: 1, connectionTimeoutMillis: 1500 }); try { await pool.query('select 1'); return true; } catch { return false; } finally { await pool.end().catch(() => undefined); } } async function ensureSchemas(url: string): Promise { const pool = new Pool({ connectionString: url, max: 1 }); try { for (const schema of FOUR_SCHEMAS) await pool.query(`create schema if not exists "${schema}"`); } finally { await pool.end(); } } export async function startItDatabase(): Promise { const configured = process.env['JCAI_TEST_DATABASE_URL'] ?? process.env['DATABASE_URL'] ?? 'postgres://jcai:jcai@localhost:54329/jcai'; if (await reachable(configured)) { await ensureSchemas(configured); return { url: configured, stop: async () => undefined }; } try { const { PostgreSqlContainer } = await import('@testcontainers/postgresql'); const container = await new PostgreSqlContainer('postgres:16-alpine').start(); const url = container.getConnectionUri(); await ensureSchemas(url); return { url, stop: () => container.stop().then(() => undefined) }; } catch (error) { process.stderr.write( `⚠️ 集成测试跳过:既连不上 ${configured},也起不了 Testcontainers(${error instanceof Error ? error.message : String(error)})\n`, ); return undefined; } }