import { randomBytes } from 'node:crypto'; import { readFile, readdir } from 'node:fs/promises'; import { join } from 'node:path'; import { Client } from 'pg'; import { PGlite } from '@electric-sql/pglite'; import { PGLiteSocketServer } from '@electric-sql/pglite-socket'; export const legacyOrganizationId = '00000000-0000-4000-8000-000000000001'; export async function testDatabase() { let connectionUrl: string; let execute: (sql: string) => Promise; let close: () => Promise; if (process.env.TEST_DATABASE_URL) { // CI supplies a disposable PostgreSQL administrator URL, never a production URL. const administrator = new Client({ connectionString: process.env.TEST_DATABASE_URL, }); await administrator.connect(); const name = 'mani_test_' + randomBytes(8).toString('hex'); await administrator.query(`CREATE DATABASE "${name}"`); const url = new URL(process.env.TEST_DATABASE_URL); url.pathname = '/' + name; connectionUrl = url.toString(); const client = new Client({ connectionString: connectionUrl }); await client.connect(); execute = (sql) => client.query(sql); close = async () => { await client.end(); await administrator.query(`DROP DATABASE "${name}" WITH (FORCE)`); await administrator.end(); }; } else { const pg = await PGlite.create(); const server = new PGLiteSocketServer({ db: pg, port: 0, host: '127.0.0.1', maxConnections: 1, }); await server.start(); connectionUrl = `postgresql://postgres:postgres@${server.getServerConn()}/postgres`; execute = (sql) => pg.exec(sql); close = async () => { await server.stop(); await pg.close(); }; } try { const migrations = (await readdir('prisma/migrations')) .filter((path) => /^\d/.test(path)) .sort(); for (const path of migrations) { await execute( await readFile( join('prisma/migrations', path, 'migration.sql'), 'utf8', ), ); if (path === '202609080001_create_organizations') { await execute( `INSERT INTO organizations (id, name, updated_at) VALUES ('${legacyOrganizationId}', 'Existing organization', NOW())`, ); } } return { connectionUrl, close, execute }; } catch (error) { await close(); throw error; } }