import { dirname, resolve } from "node:path"; import { fileURLToPath } from "node:url"; import { sql } from "drizzle-orm"; import { migrate } from "drizzle-orm/node-postgres/migrator"; import { Client } from "pg"; import db from "../../../apps/api/src/database"; const currentDir = dirname(fileURLToPath(import.meta.url)); const migrationsFolder = resolve(currentDir, "../../../apps/api/drizzle"); let migrationPromise: Promise | null = null; function getDatabaseName(connectionString: string) { return new URL(connectionString).pathname.replace(/^\//, ""); } function getAdminDatabaseUrl(connectionString: string) { const url = new URL(connectionString); url.pathname = "/postgres"; return url.toString(); } function quoteIdentifier(identifier: string) { return `"${identifier.replaceAll('"', '""')}"`; } async function ensureTestDatabaseExists() { const connectionString = process.env.DATABASE_URL; if (!connectionString) { throw new Error("DATABASE_URL must be defined for integration tests"); } const databaseName = getDatabaseName(connectionString); if (!databaseName.endsWith("_test")) { throw new Error( `Refusing to manage non-test database "${databaseName}". DATABASE_URL must point to a test database.`, ); } const adminClient = new Client({ connectionString: getAdminDatabaseUrl(connectionString), }); await adminClient.connect(); try { const result = await adminClient.query( "SELECT 1 FROM pg_database WHERE datname = $1", [databaseName], ); if (result.rowCount === 0) { await adminClient.query( `CREATE DATABASE ${quoteIdentifier(databaseName)}`, ); } } finally { await adminClient.end(); } } export async function ensureTestDatabaseMigrated() { if (!migrationPromise) { migrationPromise = (async () => { await ensureTestDatabaseExists(); await migrate(db, { migrationsFolder, }); })(); } try { await migrationPromise; } catch (error) { migrationPromise = null; throw error; } } export async function resetTestDatabase() { await ensureTestDatabaseMigrated(); await db.execute( sql.raw(` TRUNCATE TABLE "activity", "account", "apikey", "asset", "column", "comment", "external_link", "github_integration", "integration", "invitation", "label", "notification", "project", "session", "task", "task_relation", "team", "team_member", "time_entry", "verification", "workflow_rule", "workspace", "workspace_member", "user" RESTART IDENTITY CASCADE `), ); }