import { readFileSync, readdirSync } from 'node:fs'; import { dirname, join } from 'node:path'; import { fileURLToPath } from 'node:url'; import { PGlite } from '@electric-sql/pglite'; import { PGLiteSocketServer } from '@electric-sql/pglite-socket'; const here = dirname(fileURLToPath(import.meta.url)); const MIGRATIONS_DIR = join(here, '..', 'prisma', 'migrations'); /** Alle Migrationen in der Reihenfolge, in der Prisma sie anwenden würde. */ function migrationFiles(): string[] { return readdirSync(MIGRATIONS_DIR) .filter((entry) => /^\d/.test(entry)) .sort() .map((entry) => join(MIGRATIONS_DIR, entry, 'migration.sql')); } export interface TestDatabase { url: string; stop: () => Promise; } /** * Startet eine Postgres-Instanz im Speicher und stellt sie über einen lokalen * Port bereit, damit Prisma sich wie an eine echte Datenbank verbindet. * So laufen die Tests ohne Docker oder installiertes Postgres. */ export async function startTestDatabase(port: number): Promise { const db = await PGlite.create(); for (const file of migrationFiles()) { await db.exec(readFileSync(file, 'utf8')); } // Die Testdateien laufen nebeneinander und bekommen feste Ports. Hängt aus // einem vorherigen Lauf noch einer fest, wäre der Start sonst ohne Grund // fehlgeschlagen - deshalb ein paar Versuche mit dem nächsten Port. let server: PGLiteSocketServer | null = null; let benutzt = port; for (let versuch = 0; versuch < 20; versuch += 1) { const kandidat = new PGLiteSocketServer({ db, port: benutzt, host: '127.0.0.1' }); try { await kandidat.start(); server = kandidat; break; } catch (error) { const code = (error as NodeJS.ErrnoException).code; if (code !== 'EADDRINUSE') throw error; benutzt += 1; } } if (!server) { throw new Error(`Kein freier Port ab ${port} für die Testdatenbank gefunden`); } const belegterPort = benutzt; return { // connection_limit=1, weil die Testinstanz immer nur eine Verbindung // gleichzeitig bedient. Echtes Postgres braucht die Einschränkung nicht. url: `postgresql://postgres:postgres@127.0.0.1:${belegterPort}/postgres?connection_limit=1`, stop: async () => { await server.stop(); await db.close(); }, }; }