manicanldes-backend/test/helpers/test-database.ts

71 lines
2.3 KiB
TypeScript

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<unknown>;
let close: () => Promise<void>;
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;
}
}