import { randomUUID } from "node:crypto" import { readFile } from "node:fs/promises" import { Pool } from "pg" import { drizzle } from "drizzle-orm/node-postgres" import { getTableColumns, getTableName } from "drizzle-orm" import * as schema from "../../db/schema" export const testDatabaseUrl = process.env.COLLABORATION_TEST_DATABASE_URL /** Always creates and drops a private schema in the explicitly designated disposable test database. */ export async function collaborationDatabase(migrations: string[] = []) { if (!testDatabaseUrl || new URL(testDatabaseUrl).pathname !== "/collaboration_test") { throw new Error("Tests require COLLABORATION_TEST_DATABASE_URL ending in /collaboration_test") } const namespace = `collaboration_test_${randomUUID().replaceAll("-", "")}` const admin = new Pool({ connectionString: testDatabaseUrl, max: 1 }) await admin.query(`CREATE SCHEMA "${namespace}"`) const pool = new Pool({ connectionString: testDatabaseUrl, options: `-c search_path=${namespace}`, max: 6 }) const db = drizzle(pool, { schema }) try { await pool.query("CREATE TYPE locked_tenant AS ENUM ('none', 'maintenance_tenant')") for (const table of [schema.authUsers, schema.authTenantUsers, schema.authProfiles, schema.authRoles, schema.authUserRoles, schema.authRolePermissions, schema.projects, schema.communicationRooms]) { const columns = Object.values(getTableColumns(table)).map((column: any) => { let suffix = column.primary ? " PRIMARY KEY" : "" if (column.name === "id" && column.getSQLType() === "uuid") suffix += " DEFAULT gen_random_uuid()" if (column.columnType === "PgTimestamp") suffix += " DEFAULT now()" if (column.getSQLType() === "boolean") suffix += ` DEFAULT ${column.default === true ? "true" : "false"}` return `"${column.name}" ${column.getSQLType()}${suffix}` }) await pool.query(`CREATE TABLE "${getTableName(table)}" (${columns.join(",")})`) } await pool.query(`CREATE TABLE tenants (id bigint PRIMARY KEY, name text, locked text, locked_by_export_job_id uuid)`) await pool.query(`CREATE UNIQUE INDEX ON communication_rooms (tenant_id, key)`) await pool.query(await readFile(new URL("../../db/migrations/0070_native_communication_chat.sql", import.meta.url), "utf8")) for (const name of migrations) { await pool.query(await readFile(new URL(`../../db/migrations/${name}`, import.meta.url), "utf8")) } return { db, pool, close: async () => { await pool.end() await admin.query(`DROP SCHEMA "${namespace}" CASCADE`) await admin.end() } } } catch (error) { await pool.end() await admin.query(`DROP SCHEMA "${namespace}" CASCADE`) await admin.end() throw error } }