54 lines
3.1 KiB
TypeScript
54 lines
3.1 KiB
TypeScript
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"))
|
|
const steps = [...migrations]
|
|
if (!steps.includes("0075_project_collaboration.sql")) steps.unshift("0075_project_collaboration.sql")
|
|
if (!steps.includes("0081_chat_media_reactions.sql")) steps.push("0081_chat_media_reactions.sql")
|
|
for (const name of steps) {
|
|
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
|
|
}
|
|
}
|