diff --git a/tests/global-setup.ts b/tests/global-setup.ts index c4d428e..d779515 100644 --- a/tests/global-setup.ts +++ b/tests/global-setup.ts @@ -1,57 +1,10 @@ import { PostgreSqlContainer, type StartedPostgreSqlContainer } from "@testcontainers/postgresql"; -import { drizzle } from "drizzle-orm/node-postgres"; -import { migrate } from "drizzle-orm/node-postgres/migrator"; -import { Pool } from "pg"; -import path from "node:path"; let container: StartedPostgreSqlContainer; -// Every test file clones this database instead of replaying the migration -// chain itself. Shared with tests/integration/setup.ts via TEST_TEMPLATE_DB. -const TEMPLATE_DB = "ledgr_test_template"; - export async function setup() { - container = await new PostgreSqlContainer("postgres:17-alpine") - .withCommand([ - "postgres", - // One throwaway database per test file, all on this single server. The - // default max_connections (100) sits below what the fork pool can demand - // — 14 workers holding a pool each exhausts it, and Postgres starts - // terminating connections mid-run (57P01). - "-c", - "max_connections=300", - // Durability buys nothing for a server destroyed at teardown. - "-c", - "fsync=off", - "-c", - "synchronous_commit=off", - "-c", - "full_page_writes=off", - ]) - .start(); - - const connectionString = container.getConnectionUri(); - process.env.DATABASE_URL = connectionString; - process.env.TEST_TEMPLATE_DB = TEMPLATE_DB; - - // Migrate once, here, into a template database. Cloning that template per - // test file replaces one full migration run per file with a file copy. - const admin = new Pool({ connectionString, max: 1 }); - await admin.query(`CREATE DATABASE "${TEMPLATE_DB}"`); - await admin.end(); - - const templateUrl = new URL(connectionString); - templateUrl.pathname = `/${TEMPLATE_DB}`; - const pool = new Pool({ connectionString: templateUrl.toString(), max: 1 }); - try { - await migrate(drizzle({ client: pool }), { - migrationsFolder: path.join(process.cwd(), "src/db/migrations"), - }); - } finally { - // The template must have no open connections, or CREATE DATABASE ... - // TEMPLATE fails with 55006 for every test file that follows. - await pool.end(); - } + container = await new PostgreSqlContainer("postgres:17-alpine").start(); + process.env.DATABASE_URL = container.getConnectionUri(); } export async function teardown() { diff --git a/tests/integration/setup.ts b/tests/integration/setup.ts index 10f1c7f..8b3483b 100644 --- a/tests/integration/setup.ts +++ b/tests/integration/setup.ts @@ -5,35 +5,11 @@ import { randomUUID } from "crypto"; import * as schema from "../../src/db/schema"; import path from "node:path"; -// Each worker holds a pool for the life of its test file. Left at pg's default -// of 10 these overrun the server's connection limit once vitest scales forks to -// the core count; the suite needs only a couple of concurrent queries per file. -const POOL_MAX = 4; - -// CREATE DATABASE ... TEMPLATE briefly conflicts when several workers clone the -// same template at once (55006). It clears on its own, so retry rather than -// serialize every worker behind a lock. -const CLONE_RETRIES = 10; - -async function cloneTemplate(admin: Pool, dbName: string, template: string) { - for (let attempt = 0; ; attempt++) { - try { - await admin.query(`CREATE DATABASE "${dbName}" TEMPLATE "${template}"`); - return; - } catch (error) { - const code = (error as { code?: string }).code; - if (code !== "55006" || attempt >= CLONE_RETRIES) throw error; - await new Promise((resolve) => setTimeout(resolve, 50 * (attempt + 1))); - } - } -} - export async function createTestDb() { const connectionString = process.env.DATABASE_URL || "postgresql://ledgr:ledgr@localhost:5432/ledgr_test"; const dbName = `test_${randomUUID().replace(/-/g, "")}`; - const template = process.env.TEST_TEMPLATE_DB; // Isolate each test file in its own *database* (not a schema). Migrations // reference tables as `"public"."