# SurrealDBFixture _(no submodules)_ ## Purpose `SurrealDBFixture` is an integration test fixture that manages the full lifecycle of a SurrealDB v3 container using testcontainers. It implements the `ServiceFixture` 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:" // 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`. #### `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` 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` Stops the container. No-op in passthrough mode or when `start()` was never called. #### `healthCheck(): Promise` Returns `true` when `GET http://localhost:/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:` | | `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.