# PostgresFixture _(no submodules)_ ## Purpose `PostgresFixture` is an integration test fixture that manages the full lifecycle of a PostgreSQL 16 container using testcontainers. It implements the `ServiceFixture` interface from `@suaveplan/testing-integration`, providing a ready-to-use PostgreSQL instance for tests with env-bridge passthrough, in-container health checking via `pg_isready`, and full configuration control over database name, username, and password. The fixture intentionally excludes the `pg` client from its dependencies — it returns a plain `PostgresConfig` object containing a connection URL and parsed components. Consumers provide their own PostgreSQL client, keeping the fixture lightweight and client-agnostic. ## Features - **Env-bridge passthrough**: when `POSTGRES_URL` is set and non-empty, the fixture parses the URL into a `PostgresConfig` and returns immediately without starting Docker. - **`pg_isready` health check**: runs `pg_isready -U postgres` inside the container — no external pg client or TCP connection required. - **Injectable exec function**: the `execFn` option allows unit tests to stub the pg_isready call without a real PostgreSQL container. - **Injectable container factory**: the `containerFactory` option allows unit tests to swap in a fake container without Docker. - **Configurable db/user/password**: override defaults via options; values flow into container environment variables and the returned connection URL. - **URL parsing for passthrough**: `new URL(url)` extracts host, port, database, username, and password from the `POSTGRES_URL` env var. ## Usage: Basic Example ```typescript import { PostgresFixture } from "@suaveplan/testing-integration-postgres"; const fixture = new PostgresFixture(); const config = await fixture.start(); // config.url → "postgresql://test:test@localhost:/test" // config.host → "localhost" // config.port → random ephemeral port // config.database → "test" // config.username → "test" // config.password → "test" await fixture.stop(); ``` ## Usage: Advanced Example ```typescript import { PostgresFixture } from "@suaveplan/testing-integration-postgres"; import postgres from "postgres"; const fixture = new PostgresFixture({ database: "myapp", username: "appuser", password: "s3cr3t", }); const config = await fixture.start(); const sql = postgres(config.url); // Run migrations, seed data, run tests... await sql`CREATE TABLE users (id serial PRIMARY KEY, name text)`; await sql.end(); await fixture.stop(); ``` ## API Reference ### `class PostgresFixture` Implements `ServiceFixture`. #### `constructor(options?: PostgresFixtureOptions)` Creates a new fixture instance. All options are optional. #### `name: string` Always `"postgres"`. Used by `TestEnvironment` as the registry key. #### `start(): Promise` Starts the service. Returns the runtime configuration. In passthrough mode (when `POSTGRES_URL` env var is set), returns immediately with parsed config. Otherwise starts a testcontainers container with the configured environment variables. #### `stop(): Promise` Stops the container. No-op in passthrough mode or when not started. #### `healthCheck(): Promise` Runs `pg_isready -U postgres` inside the container and returns `true` when exit code is `0`. Returns `false` if no container is active. ### `interface PostgresConfig` | Field | Type | Description | |-------|------|-------------| | `url` | `string` | Full connection URL: `postgresql://user:pass@localhost:/db` | | `host` | `string` | Hostname | | `port` | `number` | Port number | | `database` | `string` | Database name | | `username` | `string` | Username | | `password` | `string` | Password | ### `interface PostgresFixtureOptions` | Field | Type | Description | |-------|------|-------------| | `image?` | `string` | Docker image override. Defaults to `postgres:16-alpine`. | | `database?` | `string` | Database name override. Defaults to `"test"`. | | `username?` | `string` | Username override. Defaults to `"test"`. | | `password?` | `string` | Password override. Defaults to `"test"`. | | `containerFactory?` | `(image: string) => GenericContainer` | Injectable factory for unit testing. | | `execFn?` | `(cmd: string[]) => Promise<{ exitCode: number }>` | Injectable executor for health check unit testing. | ## Implementation Notes - **Why `pg_isready` over TCP**: `pg_isready` is the canonical PostgreSQL health check tool, shipped in the official `postgres:16-alpine` image. It validates that PostgreSQL is accepting connections at the protocol level, which is more reliable than a bare TCP connect. It also avoids requiring a pg client in this fixture. - **Dynamic import of testcontainers**: `GenericContainer` is deferred to `start()` time so unit tests that provide a `containerFactory` do not trigger testcontainers initialization. - **URL construction**: the connection URL uses `postgresql://` (the PostgreSQL standard scheme) rather than `postgres://` to ensure compatibility with clients like `postgres` (node-postgres) and Prisma. - **Passthrough URL parsing**: `pathname.slice(1)` strips the leading `/` from the URL path to extract the database name correctly.