/** Shared integration setup: points the app at TEST_DATABASE_URL, runs migrations, resets content tables. */ import { config } from "dotenv"; config({ path: ".env", quiet: true } as any); import postgres from "postgres"; import { readdirSync, readFileSync } from "node:fs"; import { createHash } from "node:crypto"; const TEST = process.env.TEST_DATABASE_URL ?? "postgres://localhost:5432/tools_spboucher_test"; process.env.DATABASE_URL_ADMIN = TEST; process.env.DATABASE_URL = TEST; process.env.DATABASE_URL_PUBLIC = TEST.replace(/^postgres:\/\/[^@]*@?/, (m) => m); // same DB; role tests connect explicitly (process.env as Record).NODE_ENV = "test"; process.env.SESSION_SECRET ??= "test-secret"; export const TEST_URL = TEST; export async function migrateTest() { const s = postgres(TEST, { max: 1, onnotice: () => {} }); await s`CREATE TABLE IF NOT EXISTS schema_migrations (name text PRIMARY KEY, checksum text NOT NULL, applied_at timestamptz NOT NULL DEFAULT now())`; const applied = new Set((await s`SELECT name FROM schema_migrations`).map((r) => r.name as string)); for (const f of readdirSync("drizzle").filter((x) => /^\d{4}_.*\.sql$/.test(x)).sort()) { if (applied.has(f)) continue; const body = readFileSync(`drizzle/${f}`, "utf8"); await s.begin(async (tx) => { await tx.unsafe(body); await tx`INSERT INTO schema_migrations VALUES (${f}, ${createHash("sha256").update(body).digest("hex")}, now())`; }); } await s.end(); } export async function resetContent() { const s = postgres(TEST, { max: 1, onnotice: () => {} }); await s.unsafe(`TRUNCATE tools, vendors, tags, sources, collections, projects, jobs, link_checks, error_reports, search_events, settings, users, sessions, login_attempts RESTART IDENTITY CASCADE; DELETE FROM audit_log; DELETE FROM tool_revisions;`).catch(async () => { await s.unsafe(`TRUNCATE tools, vendors, tags, sources, collections, projects, jobs, link_checks, error_reports, search_events, settings, users, sessions, login_attempts RESTART IDENTITY CASCADE`); }); await s.end(); }