import { createPool, PgDb, type postgres } from "./pg.ts"; /** * Fase 3b: estrategia de tests con Postgres efímero. * * Los tests ya NO crean un esquema paralelo a mano (como hacía * `new Database(":memory:")` de @db/sqlite): corren contra el MISMO * baseline de Liquibase que producción (ver db/core/changesets), en la * base de desarrollo local. Aislamiento entre tests: cada test corre * dentro de una transacción que SIEMPRE se revierte al terminar (nunca * hace commit), así que nunca contamina la base ni a otros tests, sin * necesidad de TRUNCATE. * * Requiere DATABASE_URL_CORE_OWNER (o DATABASE_URL_CORE) apuntando a una * base con las migraciones ya aplicadas -- ver db/provision/dev-local.sh * y `./db/update.sh core --context-filter=dev`. */ let pool: postgres.Sql | null = null; function getPool(): postgres.Sql { if (!pool) { const url = Deno.env.get("DATABASE_URL_CORE_OWNER") || Deno.env.get("DATABASE_URL_CORE"); if (!url) { throw new Error( "Define DATABASE_URL_CORE_OWNER (o DATABASE_URL_CORE) para correr los tests contra Postgres -- ver db/provision/dev-local.sh", ); } pool = createPool(url, { max: 5 }); } return pool; } const ROLLBACK = Symbol("test-rollback"); /** Corre `fn(db)` dentro de una transacción que SIEMPRE se revierte. */ export async function withTestDb(fn: (db: PgDb) => Promise): Promise { const sql = getPool(); let result: T = undefined as unknown as T; try { await sql.begin(async (tx) => { result = await fn(new PgDb(tx as unknown as postgres.Sql)); throw ROLLBACK; }); } catch (e) { if (e !== ROLLBACK) throw e; } return result; } export async function closeTestPool(): Promise { await pool?.end({ timeout: 1 }); pool = null; }