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,112 @@
# SurrealDBFixture
<!-- begin:auto-docs -->
_(no submodules)_
<!-- end:auto-docs -->
## Purpose
`SurrealDBFixture` is an integration test fixture that manages the full lifecycle of a SurrealDB v3 container using testcontainers. It implements the `ServiceFixture<SurrealDBConfig>` interface from `@suaveplan/testing-integration`, enabling consistent startup, health checking, and teardown as part of a `TestEnvironment` or standalone usage.
The fixture supports an env-bridge passthrough mode so developer workstations with live SurrealDB instances can skip container startup entirely — just set the three required environment variables and tests run against the real service without Docker overhead.
## Features
- **Env-bridge passthrough**: when `SURREALDB_URL`, `SURREALDB_USERNAME`, and `SURREALDB_PASSWORD` are all set and non-empty, container startup is bypassed and env values are returned directly.
- **HTTP health check**: polls `GET /health` on SurrealDB's HTTP port (same port as WebSocket) to verify the service is ready before returning from `start()`.
- **Random password per run**: generates a new `crypto.randomUUID()` password each time a container starts, preventing credential reuse across test suites.
- **Injectable container factory**: the `containerFactory` option allows unit tests to swap in a fake container without starting Docker, achieving 100% Istanbul coverage without integration dependencies.
- **Configurable namespace and database**: override defaults via options to isolate test suites at the logical database level.
- **Idempotent stop**: calling `stop()` on an already-stopped or passthrough fixture is safe and does not throw.
## Usage: Basic Example
```typescript
import { SurrealDBFixture } from "@suaveplan/testing-integration-surrealdb";
const fixture = new SurrealDBFixture();
const config = await fixture.start();
// config.url → "ws://localhost:<randomPort>"
// config.username → "root"
// config.password → auto-generated UUID
// config.namespace → "test"
// config.database → "test"
await fixture.stop();
```
## Usage: Advanced Example
```typescript
import { SurrealDBFixture } from "@suaveplan/testing-integration-surrealdb";
import Surreal from "surrealdb";
const fixture = new SurrealDBFixture({
image: "surrealdb/surrealdb:v3.0.4",
namespace: "myapp",
database: "integration",
});
const config = await fixture.start();
const db = new Surreal();
await db.connect(config.url);
await db.use({ namespace: config.namespace, database: config.database });
await db.signin({ username: config.username, password: config.password });
// run tests…
await db.close();
await fixture.stop();
```
## API Reference
### `class SurrealDBFixture`
Implements `ServiceFixture<SurrealDBConfig>`.
#### `constructor(options?: SurrealDBFixtureOptions)`
Creates a new fixture instance. All options are optional.
#### `name: string`
Always `"surrealdb"`. Used by `TestEnvironment` as the registry key.
#### `start(): Promise<SurrealDBConfig>`
Starts the service. Returns the runtime configuration on success. If all three env vars are set, returns passthrough config immediately. Otherwise starts a testcontainers container and waits for the HTTP health endpoint.
#### `stop(): Promise<void>`
Stops the container. No-op in passthrough mode or when `start()` was never called.
#### `healthCheck(): Promise<boolean>`
Returns `true` when `GET http://localhost:<port>/health` returns an OK response. Returns `false` if no container is active or the request fails.
### `interface SurrealDBConfig`
| Field | Type | Description |
|-------|------|-------------|
| `url` | `string` | WebSocket URL: `ws://localhost:<port>` |
| `namespace` | `string` | SurrealDB namespace |
| `database` | `string` | SurrealDB database name |
| `username` | `string` | Auth username (always `root` in container mode) |
| `password` | `string` | Auth password (random UUID in container mode) |
### `interface SurrealDBFixtureOptions`
| Field | Type | Description |
|-------|------|-------------|
| `image?` | `string` | Docker image override. Defaults to `surrealdb/surrealdb:v3.0.4`. |
| `namespace?` | `string` | Namespace override. Defaults to `"test"`. |
| `database?` | `string` | Database override. Defaults to `"test"`. |
| `containerFactory?` | `(image: string) => GenericContainer` | Injectable factory for unit testing. When provided, replaces the real `GenericContainer` constructor. |
## Implementation Notes
- **Why HTTP health over WebSocket**: the `/health` HTTP endpoint is simpler to poll and does not require a SurrealDB client package in this fixture. WebSocket handshakes are stateful and require protocol negotiation; plain HTTP is sufficient for readiness signalling.
- **Why random password per run**: reusing a static password across test runs creates a security surface for tests running in CI with shared Docker networks. UUID-generation via `crypto.randomUUID()` (the Web Crypto API, universally available in modern Bun and Node.js) is both fast and cryptographically random.
- **Dynamic import of testcontainers**: `GenericContainer` is dynamically imported inside `start()` to keep the module side-effect-free and to allow the `containerFactory` injection path to bypass the import entirely for unit tests that provide their own factory.
- **Port mapping**: testcontainers allocates an ephemeral host port for each `withExposedPorts()` declaration. `getMappedPort(8000)` retrieves the actual host port after the container starts.