// A Postgres of its own for the server's tests: FILIUS_TEST_DATABASE_URL // when it is set, else a throwaway container of the postgres image (Docker), // stopped and gone after the tests. Never a database of the dashboard. import { execFileSync } from 'node:child_process'; import pg from 'pg'; export interface TestDatabase { readonly url: string; stop(): Promise; } export async function testDatabase(): Promise { const given = process.env.FILIUS_TEST_DATABASE_URL; if (given !== undefined) return { url: given, stop: async () => {} }; const name = `filius-test-pg-${process.pid}-${Date.now()}`; execFileSync('docker', ['run', '-d', '--rm', '--name', name, '-e', 'POSTGRES_PASSWORD=filius', '-e', 'POSTGRES_DB=filius', '-p', '127.0.0.1::5432', 'postgres:16-alpine'], { stdio: 'pipe' }); const stop = async () => { execFileSync('docker', ['stop', name], { stdio: 'pipe' }); }; try { const port = execFileSync('docker', ['port', name, '5432/tcp']).toString().trim().split('\n')[0]!.split(':').pop(); const url = `postgres://postgres:filius@127.0.0.1:${port}/filius`; // The image's first start runs a server on its socket only; the TCP port answers once the real one is up. for (let tries = 0; ; tries++) { const client = new pg.Client({ connectionString: url }); try { await client.connect(); await client.query('SELECT 1'); await client.end(); return { url, stop }; } catch (error) { await client.end().catch(() => {}); if (tries > 120) throw error; await new Promise((resolve) => setTimeout(resolve, 250)); } } } catch (error) { await stop(); throw error; } }