Test Harness

Isolated-boot helper for framework-consumer test suites. Replaces the per-project pattern of:

- mkdtemp a fresh data directory - set MYAPP_DATA_DIR / MYAPP_DB_PATH env vars so the app's boot path reads the test paths instead of production - init b.vault in plaintext mode against the test dataDir so primitives that compose vault don't try to read a real key - tear down: close vault, remove the temp directory, restore env vars

That pattern lands as ~50-100 lines in every framework consumer's tests/helpers/test-server.js. This primitive owns it once.

## Lifecycle

var h = await b.testHarness.start({
  envPrefix:   "MYAPP",            // optional — env vars prefixed with this
  env:         { LOG_LEVEL: "error" },  // optional — additional env vars to set
  initVault:   true,               // optional — init b.vault in plaintext mode
  resetCaches: true,               // optional — call framework _resetForTest() hooks
});
// h.dataDir   — operator-supplied or framework-generated mkdtemp path
// h.dbPath    — `<dataDir>/db.sqlite` unless operator overrides
// h.vaultDir  — `<dataDir>/vault`

// ... operator's app boot reads process.env.MYAPP_DATA_DIR etc.

await h.stop();   // teardown: close vault, remove dataDir, restore env

## Concurrent test isolation

Tests using SMOKE_PARALLEL=N against the framework boot N processes in parallel — each one running this primitive gets its own mkdtemp-generated dataDir (collision-free) and its own env-var override scope (process-local). The harness does NOT use shared state; multiple start() calls in the same process create parallel handles.

## What the harness does NOT own

- **The operator's HTTP server**. Consumers boot their own app.listen(port). The harness only sets up paths + env + vault + cache-reset. The pattern in HS's tests/helpers/test-server.js mounts an Express app onto the harness's prepared paths. - **Per-request authentication state**. The harness doesn't mint session cookies / JWTs; tests that need that compose b.session.create({ store: ... }) against the harness's paths. - **Audit replay tracking**. The harness emits no audit; the framework primitives the operator boots emit their own.

## When to use this vs the existing _resetForTest() hooks

Framework primitives (vault, audit, db, …) expose _resetForTest() so a single test can scrub in-memory state without process-restart. The harness composes those resets + adds filesystem isolation. Use the harness when your test needs WRITE access to a fresh dataDir (file uploads, sealed db, audit-chain on disk); use the bare _resetForTest() hooks when in-memory state is enough.

b.testHarness.start(opts?) #

stable0.9.43
{
  dataDir:     string,    // optional — pre-existing dir to use; harness mkdtemps if absent
  dbPath:      string,    // optional — defaults to `/db.sqlite`
  vaultDir:    string,    // optional — defaults to `/vault`
  envPrefix:   string,    // optional — env vars `_DATA_DIR` / `_DB_PATH` / `_VAULT_DIR`; default no prefix
  env:         object,    // optional — additional env-var overrides; restored on stop()
  initVault:   boolean,   // optional — boot b.vault in plaintext mode against vaultDir; default true
  keepOnStop:  boolean,   // optional — leave dataDir in place after stop(); default false (rm -rf)
}

Boot an isolated test harness. Returns a promise resolving to a handle exposing dataDir, dbPath, vaultDir, env (the env-var overrides set), and an async stop() that tears the harness down (releases vault, removes the temp directory, restores env). Always await the call — vault.init is async, and unawaited failures become unhandled rejections.

Concurrent harnesses with initVault: true share the process-global vault state via reference counting; stopping one harness leaves vault initialized for the remaining peers. The last stop() releases vault.

var h = await b.testHarness.start({ envPrefix: "MYAPP", initVault: true });
try {
  // ... operator's app boot reads process.env.MYAPP_DATA_DIR etc.
  // ... run tests ...
} finally {
  await h.stop();
}

Last updated 2026-08-08T16:39:49.652Z by seeder.