Files
skills/docs/testkit/testing/testing-integration-surrealdb/src/fixture/surrealdb-fixture.md
2026-05-29 08:20:56 +01:00

5.4 KiB

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<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

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

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.