first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,43 @@
# @suaveplan/testing-integration-postgres - PostgreSQL 16 testcontainers fixture for @suaveplan/testing-integration
## Summary
PostgreSQL 16 testcontainers fixture for `@suaveplan/testing-integration`.
Implements `ServiceFixture<PostgresConfig>` — manages a PostgreSQL container lifecycle with env-bridge passthrough and `pg_isready` health checks. Returns a plain config; consumers bring their own `pg` client.
<!-- begin:auto-docs -->
## Documentation tree
- [fixture](./src/fixture/postgres-fixture.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
- [postgres-fixture](./src/fixture/postgres-fixture.md) — <!-- begin:auto-docs --> _(no submodules)_ <!-- end:auto-docs -->
<!-- end:auto-docs --> - [postgres-fixture](./src/fixture/postgres-fixture.md) — <!-- begin:auto-docs --> _(no submodules)_- [postgres-fixture](./src/fixture/postgres-fixture.md) — <!-- begin:auto-docs --> _(no submodules)_- [postgres-fixture](./src/fixture/postgres-fixture.md) — <!-- begin:auto-docs --> _(no submodules)_- [postgres-fixture](./src/fixture/postgres-fixture.md) — <!-- begin:auto-docs --> _(no submodules)_
## Install
```bash
bun add -D @suaveplan/testing-integration-postgres
```
## Quick Start
```typescript
import { PostgresFixture } from "@suaveplan/testing-integration-postgres";
const fixture = new PostgresFixture();
const config = await fixture.start();
// config.url, config.host, config.port, config.database, config.username, config.password
await fixture.stop();
```
## Env-bridge passthrough
Set the env var to skip container startup:
```text
POSTGRES_URL=postgresql://test:test@localhost:5432/test
```
## Docs
- [PostgresFixture](src/fixture/postgres-fixture.md)

View File

@@ -0,0 +1,114 @@
# PostgresFixture
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`PostgresFixture` is an integration test fixture that manages the full lifecycle of a PostgreSQL 16 container using testcontainers. It implements the `ServiceFixture<PostgresConfig>` 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:<randomPort>/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<PostgresConfig>`.
#### `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<PostgresConfig>`
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<void>`
Stops the container. No-op in passthrough mode or when not started.
#### `healthCheck(): Promise<boolean>`
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:<port>/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.