testing/app_server.ts

Bootstrapped app server factory for integration tests.

Creates a keeper account, API token, and signed session cookie on a database. By default uses a cached in-memory pglet (one shared WASM instance per loaded copy of testing/db.ts — once per run under the db project's isolate: false + fileParallelism: false); pass db to use an existing database (any driver) instead.

Also provides create_test_app — a combined helper that creates both a TestAppServer and a fully assembled Hono app with middleware and routes.

view source

Declarations
#

21 declarations

bootstrap_test_keeper
#

testing/app_server.ts view source

(options: CreateTestAccountWithCredentialsOptions): Promise<{ account: { id: string & $brand<"Uuid">; username: string; }; actor: { ...; }; api_token: string; session_cookie: string; }> import {bootstrap_test_keeper} from '@fuzdev/fuz_app/testing/app_server.js';

Bootstrap the test-DB keeper. Direct-query shortcut for the default create_test_app path — bootstrap is not what most tests exercise, so we skip the real bootstrap_account flow (no audit row, no on_bootstrap callback). Tests that need the full success-path flow use create_test_app_for_bootstrap instead.

Flips bootstrap_lock.bootstrapped = true so the post-insert DB state matches a real bootstrap completion — production code can trust the lock as the single signal without a belt-and-suspenders query_account_has_any defense.

options

returns

Promise<{ account: { id: string & $brand<"Uuid">; username: string; }; actor: { id: string & $brand<"Uuid">; }; api_token: string; session_cookie: string; }>

mutates

  • the underlying options.db — inserts the account/actor/roles/ — API token/session_cookie rows AND flips `bootstrap_lock.bootstrapped`.

BootstrapTestKeeperOptions
#

testing/app_server.ts view source

CreateTestAccountWithCredentialsOptions import type {BootstrapTestKeeperOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Alias for the keeper-flavored call site. Same shape.

db

type Db

keyring

type Keyring

session_options

type SessionOptions<string>

password

type PasswordHashDeps

username?

type string

password_value?

type string

roles?

type string[]

email?

Optional email stored on the account row — exercises the username-or-email login lookup.

type string

create_loopback_app_server_options
#

testing/app_server.ts view source

(): Pick<AppServerOptions, "allowed_origins" | "proxy" | "env_schema"> import {create_loopback_app_server_options} from '@fuzdev/fuz_app/testing/app_server.js';

The loopback create_app_server options an in-process test server takes: localhost origins allowed, the loopback proxy trusted (so X-Forwarded-For sets the client IP), and an empty env schema. A fresh object per call — spread it ahead of the options a test varies.

returns

Pick<AppServerOptions, "allowed_origins" | "proxy" | "env_schema">

create_test_account_with_credentials
#

testing/app_server.ts view source

(options: CreateTestAccountWithCredentialsOptions): Promise<{ account: { id: string & $brand<"Uuid">; username: string; }; actor: { ...; }; api_token: string; session_cookie: string; }> import {create_test_account_with_credentials} from '@fuzdev/fuz_app/testing/app_server.js';

Create a test account with credentials. Use for additional accounts minted alongside the keeper (e.g. TestApp.create_account for cross-account / multi-user tests). Does NOT flip bootstrap_lock — non-keeper accounts should not appear to the system as bootstrap having happened.

Creates an account with actor, grants roles, creates an API token, creates a session, and signs a session cookie.

options

returns

Promise<{ account: { id: string & $brand<"Uuid">; username: string; }; actor: { id: string & $brand<"Uuid">; }; api_token: string; session_cookie: string; }>

mutates

  • the underlying options.db — inserts rows into account, actor, — `role_grant` (one per role), `api_token`, and `auth_session`.

create_test_app
#

testing/app_server.ts view source

(options: CreateTestAppOptions): Promise<TestApp> import {create_test_app} from '@fuzdev/fuz_app/testing/app_server.js';

Create a fully assembled test app with a Hono server, middleware, and routes.

Combines create_test_app_server + create_app_server into a single call. Disables every rate limiter (rate_limiters: 'disabled_for_testing') and logging by default (test-friendly). A limiter passed through app_options wins over the mode, so a rate-limit test enables just the one it pins.

A fresh Hono app is created each call — middleware closures bind to the server's deps (db, keyring), so reuse across servers is unsafe. The expensive resource (the WASM database) is cached separately in testing/db.ts.

options

test app configuration

returns

Promise<TestApp>

a TestApp ready for HTTP testing

create_test_app_for_bootstrap
#

testing/app_server.ts view source

(options: CreateTestAppForBootstrapOptions): Promise<TestAppForBootstrap> import {create_test_app_for_bootstrap} from '@fuzdev/fuz_app/testing/app_server.js';

Create a test app in the pre-bootstrap state for exercising the bootstrap success path end-to-end.

Skips the keeper pre-creation create_test_app does by default — bootstrap_lock.bootstrapped stays at false and the DB has no accounts. The fs stubs return options.bootstrap_token when the bootstrap handler reads bootstrap.token_path, so a POST /bootstrap with {token: bootstrap_token, username, password} reaches the success branch. Rate limiters are disabled the way create_test_app disables them — a limiter in app_options wins.

Pair with describe_bootstrap_success_tests for the consumer-runnable suite that drives the full happy path + adjacent assertions on observable state (account exists, audit row emitted, on_bootstrap callback fired).

options

bootstrap config + factory inputs

returns

Promise<TestAppForBootstrap>

a TestAppForBootstrap ready for the test to drive bootstrap

create_test_app_server
#

testing/app_server.ts view source

(options: TestAppServerOptions): Promise<TestAppServer> import {create_test_app_server} from '@fuzdev/fuz_app/testing/app_server.js';

Create an app server with a bootstrapped account for testing.

Sets up:

  • Auth tables (via the cached default factory, or reuses existing db)
  • A keeper account with hashed password
  • Role role_grants for each role in options.roles
  • An API token for Bearer auth
  • A session with a signed cookie value

Uses stub_password_deps by default — deterministic hashing that works correctly for login/logout tests without Argon2 overhead.

options

session options and optional overrides

returns

Promise<TestAppServer>

a TestAppServer ready for HTTP testing

mutates

  • the underlying database — when db is supplied, first resets the — `app_settings` row (`open_signup`, `updated_at`, `updated_by`); in either branch inserts an account, actor, role role_grants, API token, and session row, and flips `bootstrap_lock.bootstrapped` (`bootstrap_test_keeper`).

CreateTestAccountWithCredentialsOptions
#

testing/app_server.ts view source

CreateTestAccountWithCredentialsOptions import type {CreateTestAccountWithCredentialsOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Options for bootstrap_test_keeper and create_test_account_with_credentials.

Same shape for both — the data inserted is identical; the only behavioral difference is the lock flip on the keeper path.

db

type Db

keyring

type Keyring

session_options

type SessionOptions<string>

password

type PasswordHashDeps

username?

type string

password_value?

type string

roles?

type string[]

email?

Optional email stored on the account row — exercises the username-or-email login lookup.

type string

CreateTestAppAccountArgs
#

testing/app_server.ts view source

CreateTestAppAccountArgs import type {CreateTestAppAccountArgs} from '@fuzdev/fuz_app/testing/app_server.js';

Args for TestApp.create_account — mint an additional account alongside the keeper. Mirrors the cross-process CreateTestAccountOptions (the two create_account paths share a shape so fixture call sites read identically), kept local to avoid a cross-module cycle with cross_backend/setup.ts.

username?

type string

password_value?

type string

roles?

type string[]

email?

Optional email stored on the account row — exercises the username-or-email login lookup.

type string

CreateTestAppForBootstrapOptions
#

testing/app_server.ts view source

CreateTestAppForBootstrapOptions import type {CreateTestAppForBootstrapOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Configuration for create_test_app_for_bootstrap. Like CreateTestAppOptions but the keeper-related fields drop (no pre-bootstrap keeper) and bootstrap is required + narrowed to live mode (the helper exists specifically to drive the success path).

session_options

type SessionOptions<string>

create_route_specs

type (context: AppServerContext): RouteSpec[]

context

returns RouteSpec[]

rpc_endpoints?

type RpcEndpointsSuiteOption

app_options?

type Partial<Omit<AppServerOptions, "backend" | "bootstrap" | "session_options" | "create_route_specs" | "rpc_endpoints">>

bootstrap

Live bootstrap config — the test drives POST /bootstrap against this.

type BootstrapLiveOptions

bootstrap_token

Token contents the stub fs returns when reading bootstrap.token_path. The test posts a body containing this same value as token to satisfy the timing-safe equality check inside bootstrap_account.

type string

db?

type Db

db_type?

type DbType

password?

type PasswordHashDeps

audit_factory?

type (params: { db: Db; log: Logger; }): AuditEmitter

params

type { db: Db; log: Logger; }
returns AuditEmitter

CreateTestAppOptions
#

testing/app_server.ts view source

CreateTestAppOptions import type {CreateTestAppOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Configuration for create_test_app.

inheritance

create_route_specs

Route spec factory — called with the assembled AppServerContext.

type (context: AppServerContext): RouteSpec[]

context

returns RouteSpec[]

rpc_endpoints?

RPC endpoints mounted by create_app_server — eager array or (ctx: AppServerContext) => Array<RpcEndpointSpec> factory. Single source of truth; the equivalent slot under app_options is Omit'd so setup-time path lookup and runtime dispatch read from one place. Symmetric with the suite-level rpc_endpoints option on describe_standard_admin_integration_tests etc.

type RpcEndpointsSuiteOption

bootstrap?

Bootstrap config — symmetric with AppServerOptions.bootstrap. Same single-source-of-truth precedent as rpc_endpoints: setup-time surface generation and runtime dispatch both read this slot, so the equivalent field under app_options is Omit'd. Discriminated union over {mode: 'disabled' | 'surface_only' | 'live'}. Omit (or pass {mode: 'disabled'}) for the default — no bootstrap route mounted.

For tests that exercise the bootstrap success path against a real token + empty DB, use create_test_app_for_bootstrap instead — it skips the keeper pre-creation that blocks the success branch.

type BootstrapServerOptions

app_options?

Optional overrides for AppServerOptions. Excludes fields create_test_app manages directly: backend, session_options, create_route_specs, rpc_endpoints, bootstrap (top-level slots above).

type Partial<Omit<AppServerOptions, "backend" | "bootstrap" | "session_options" | "create_route_specs" | "rpc_endpoints">>

mint_test_session
#

testing/app_server.ts view source

(options: MintTestSessionOptions): Promise<{ session_cookie: string; }> import {mint_test_session} from '@fuzdev/fuz_app/testing/app_server.js';

Mint a real auth_session row for an existing account and return a validly-signed session cookie value referencing it. Test-only — the forge behind the cross-backend expiry conformance cases (the expired_session principal): pass a negative expires_in_seconds to produce an *expired server-side session* whose signed cookie envelope is still well-formed. Both the TS _testing_mint_session action and the in-process fixture.mint_expired_session() seam call this so the write semantics match across transports.

options

returns

Promise<{ session_cookie: string; }>

mutates

  • options.db — inserts one auth_session row. —

MintTestSessionOptions
#

testing/app_server.ts view source

MintTestSessionOptions import type {MintTestSessionOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Options for mint_test_session.

db

type Db

keyring

type Keyring

session_options

type SessionOptions<string>

account_id

Account the minted session belongs to.

type string

expires_in_seconds

Session lifetime offset in seconds applied to NOW() for the auth_session.expires_at row. A negative value backdates the row so the authoritative DB-row expiry gate (query_session_get_valid — WHERE expires_at > NOW()) rejects it, while the returned cookie's own signed payload stays valid (future). Resolution therefore passes the cookie-payload check in parse_session and is refused at the DB-row gate — the gate the in-process payload-expiry tests never reach and the one that structurally needs a server-side mint.

type number

stub_password_deps
#

testing/app_server.ts view source

PasswordHashDeps import {stub_password_deps} from '@fuzdev/fuz_app/testing/app_server.js';

Fast password stub for tests that don't exercise login/password flows.

Hashes are deterministic (stub_hash_<password>) and verify correctly, so auth bootstrap and session creation work without Argon2 overhead.

SuiteAppOptions
#

testing/app_server.ts view source

Partial<Omit<AppServerOptions, "backend" | "bootstrap" | "session_options" | "create_route_specs" | "rpc_endpoints">>

type Partial<Omit<AppServerOptions, "backend" | "bootstrap" | "session_options" | "create_route_specs" | "rpc_endpoints">>

import type {SuiteAppOptions} from '@fuzdev/fuz_app/testing/app_server.js';

app_options shape accepted by create_test_app and the DB-backed suite helpers. Excludes fields the helpers manage directly — backend / session_options / create_route_specs are constructed by the helper itself; rpc_endpoints and bootstrap live on top-level options so setup-time surface lookup and runtime dispatch read from one source of truth.

login_ip_rate_limiter?

Per-IP rate limiter for login + password change — the distributed-spray backstop. Omit or undefined to use a default limiter (5 attempts per 15 minutes) under rate_limiters: 'enforced', none under 'disabled_for_testing'. Pass null to explicitly disable rate limiting. Also available on AppServerContext for route factory callbacks.

One instance per auth surface, not one shared across all four. These buckets are monotone within their window — a success never refunds them (see RateLimiter.reset) — so a shared instance let a failure on any surface spend the budget that bounds guessing on every other one, and let one caller's exhaustion deny four routes at once. Pass the same limiter to two of these fields to opt back into a shared budget.

type RateLimiter | null

signup_ip_rate_limiter?

Per-IP rate limiter for signup. Omit or undefined for the mode's default (5 attempts per 15 minutes under rate_limiters: 'enforced'). Pass null to explicitly disable. Separate from login_ip_rate_limiter because an open-signup deployment lets any unauthenticated caller spend it. Also available on AppServerContext for route factory callbacks.

type RateLimiter | null

bootstrap_ip_rate_limiter?

Per-IP rate limiter for bootstrap. Omit or undefined for the mode's default (5 attempts per 15 minutes under rate_limiters: 'enforced'). Pass null to explicitly disable. Separate from login_ip_rate_limiter so a fumbled bootstrap token can't spend the operator's login budget. Wired directly into the factory-managed bootstrap route; also on AppServerContext for symmetry.

type RateLimiter | null

login_account_rate_limiter?

Per-account rate limiter for login attempts. Omit or undefined for the mode's default (10 attempts per 30 minutes under rate_limiters: 'enforced'). Pass null to explicitly disable. Also available on AppServerContext for route factory callbacks.

type RateLimiter | null

signup_account_rate_limiter?

Per-account rate limiter for signup attempts, keyed by submitted username. Omit or undefined for the mode's default (10 attempts per 30 minutes under rate_limiters: 'enforced'). Pass null to explicitly disable. Also available on AppServerContext for route factory callbacks.

type RateLimiter | null

action_ip_rate_limiter?

Per-IP rate limiter for the action dispatchers (HTTP RPC + WebSocket). Consulted for actions whose spec declares rate_limit: 'ip' or 'both'. Same limiter applies across transports — one budget per action. Omit or undefined for the mode's default (600 attempts per 15 minutes — permissive — under rate_limiters: 'enforced'). Pass null to explicitly disable. Threaded into every endpoint mounted from rpc_endpoints and ws_endpoints; also available on AppServerContext.

type RateLimiter | null

action_account_rate_limiter?

Per-account rate limiter for the action dispatchers (HTTP RPC + WebSocket). Consulted for actions whose spec declares rate_limit: 'account' or 'both'. Keyed on the authenticated account's id, so every actor and credential of one account shares a budget. Omit or undefined for the mode's default (1200 attempts per 15 minutes — permissive — under rate_limiters: 'enforced'). Pass null to explicitly disable. Threaded into every endpoint mounted from rpc_endpoints and ws_endpoints; also available on AppServerContext.

type RateLimiter | null

allowed_origins?

Parsed allowed origin patterns.

type RegExp[]

proxy?

Trusted proxy options.

type { trusted_proxies: string[]; get_connection_ip: (c: Context<any, any, {}>) => string | undefined; }

rate_limiters?

What an omitted rate limiter becomes — 'enforced' (the default) builds a default instance for each *_rate_limiter option left out, 'disabled_for_testing' leaves each of them null. An explicit option (an instance or null) always wins over the mode. The disabled mode adds one surface warning, and prints a stderr banner once per module instance (announce_rate_limiters_disabled). Never 'disabled_for_testing' in production — see RateLimiterMode.

type RateLimiterMode

max_body_size?

Maximum allowed request body size in bytes. Omit or undefined to use the default (1 MiB). Pass null to explicitly disable body size limiting.

type number | null

daemon_token_state?

Daemon token state for keeper auth. Omit to disable.

type DaemonTokenState

surface_route?

Mount GET /api/surface, which serves this server's generated AppSurface (every route, its auth, and its schemas) to an admin UI such as AdminSurface.svelte. Off by default — the Rust spine serves no such route, and a map of the API is nothing to publish unasked.

When mounted the route is admin-only (roles: ['admin'], so it takes the ?acting= selector) and a rule-3 token surface (required_scope: 'surface:app_surface'): a narrowed API token is refused it whatever its method list says. See server/surface_route.ts.

type boolean

default false

transform_middleware?

Optional: transform middleware specs before applying.

Receives shallow copies of the trusted-proxy spec followed by the auth stack, in a fresh array — mutating them leaves the originals untouched. Add layers anywhere (before the proxy spec, between it and the auth stack, between the auth layers, or after), but keep each of those specs — its handler, not just its name — mounted once at its original path ('*' for the proxy, AUTH_MIDDLEWARE_PATH for auth) in its original order: the endpoint auth-scope check (assert_endpoint_in_auth_scope) and client-IP rate limiting (get_client_ip) assume it, so assembly throws (assert_middleware_stack_mounted) if the transform moves, drops, duplicates, reorders, wraps, or replaces one. The check pins where those layers sit, not what added layers do — a layer you add runs with full context access.

type (specs: MiddlewareSpec[]): MiddlewareSpec[]

specs

returns MiddlewareSpec[]

audit_log_sse?

Enable factory-managed audit log SSE.

When truthy, creates an AuditLogSse instance internally, registers the SSE listener via backend.deps.audit.add_listener (composing with the consumer's on_audit_event callback rather than rebuilding AppDeps), adds its registry to backend.deps.connection_closer so revocations close its streams, and auto-includes audit_log_event_specs in the surface. The result is exposed on AppServerContext (for route factories) and AppServer (for the caller), always typed as AuditLogSse | null — when this option is set, the field is non-null. Use require_audit_sse(ctx) to assert the invariant in route factories that depend on it.

Pass true for defaults (admin role), or {role: 'custom'} for a custom role. Omit to wire audit SSE manually.

type true | { role?: string | undefined; }

auth_cleanup?

Schedule the auth cleanup beside the server: start_auth_cleanup over backend.deps — a pass as assembly finishes, then one per interval — stopped by AppServer.close. Each pass deletes expired sessions and closes the connections they opened through deps.connection_closer (every WebSocket transport and audit stream this server mounts), and audits each newly expired role_grant offer once.

Off by default; a production server should turn it on. Without it nothing sweeps: expired session rows accumulate, a WebSocket or audit stream outlives the expiry of the session it opened on until it disconnects, and an offer's expiry is never audited. With it, a connection outlives its session's expiry by at most one interval. The Rust spine's servers all schedule the twin (spawn_auth_cleanup).

Pass true for the default interval (DEFAULT_AUTH_CLEANUP_INTERVAL_MS), or {interval_ms} for another. Leave it off in an in-process test harness — a background pass deletes rows and writes audit rows mid-test — and in a process that runs run_auth_cleanup on a schedule of its own.

type boolean | AuthCleanupScheduleOptions

event_specs?

SSE event specs for surface generation. Defaults to [] (no SSE events).

type EventSpec[]

create_upgrade_websocket?

Returns the Hono runtime adapter's upgradeWebSocket helper for the assembled app. create_app_server calls it once, with the app it builds (the same instance as AppServer.app), after creating the app and before mounting ws_endpoints — and only when ws_endpoints resolves non-empty, so it is never called for a server with no WebSocket endpoints. Required in that case: assembly throws without it. One helper services every mounted endpoint.

A factory rather than the helper itself because Node's adapter needs the app: @hono/node-ws has no module-level helper, only createNodeWebSocket({app}), and create_app_server is what builds the app. Deno and Bun ignore the argument:

import {upgradeWebSocket} from '@hono/deno'; // or '@hono/bun' create_upgrade_websocket: () => upgradeWebSocket,

Node keeps the adapter so it can attach to the server after serve():

let node_ws: NodeWebSocket | undefined; const server = await create_app_server({ ..., create_upgrade_websocket: (app) => (node_ws = createNodeWebSocket({app})).upgradeWebSocket, }); node_ws?.injectWebSocket(serve({fetch: server.app.fetch, port}));

Test harnesses pass () => stub.upgradeWebSocket with create_stub_upgrade from testing/ws_round_trip.ts.

type (app: Hono<BlankEnv, BlankSchema, "/">): UpgradeWebSocket<unknown, any, WSEvents<unknown>>

app

type Hono<BlankEnv, BlankSchema, "/">
returns UpgradeWebSocket<unknown, any, WSEvents<unknown>>

ws_endpoints?

WebSocket endpoint specs — single source of truth for both surface generation *and* live dispatch. Each entry is auto-mounted via register_ws_endpoint against the assembled Hono app on every runtime, so consumers don't call register_ws_endpoint themselves.

Accepts either an array (evaluated eagerly) or a factory (ctx: AppServerContext) => ReadonlyArray<WsEndpointSpec> (evaluated after the server context is assembled). Use the factory form when action lists depend on ctx.deps — e.g. when spreading create_standard_rpc_actions({...ctx.deps, notification_sender}) over WS.

When non-empty, create_upgrade_websocket must be supplied (throws otherwise). A factory returning [] does NOT trip the check — feature-flag gated WS surfaces stay safe.

Every mount gets what the HTTP surface gets: the auth middleware (a path outside AUTH_MIDDLEWARE_PATH throws at assembly, like rpc_endpoints), the server's allowed_origins unless the spec narrows them, and the same action_ip_rate_limiter / action_account_rate_limiter instances the RPC endpoints charge — one budget per action across both transports. The upgrade routes register after the route specs and before post_route_middleware and static_serving, so neither runs ahead of an upgrade.

Duplicate path values across two WsEndpointSpecs, and a path a GET RouteSpec already claims, throw at assembly before any endpoint mounts (Hono would silently shadow them otherwise).

Every mounted transport is added to deps.connection_closer, so the revocation handlers' post-commit closes reach its sockets — unconditionally, since those closes are what ends a revoked credential's connections. AppServer.close removes it again.

Each spec's auth_guard? defaults to true — the factory registers create_ws_auth_guard against the mounted transport via deps.audit.add_listener, repeating the close on the audit row. Wiring is deduped by transport reference identity so two specs sharing one BackendWebsocketTransport instance get a single listener; wrapped / proxied transports dedupe as separate entries (set auth_guard: false on duplicates and compose against the underlying transport once).

type readonly WsEndpointSpec[] | ((context: AppServerContext) => readonly WsEndpointSpec[])

env_schema?

Env schema for surface generation. Defaults to BaseServerEnv — pass an extended schema (typically BaseServerEnv.extend({...})) when the consumer adds app-specific env vars.

type ZodObject<$ZodLooseShape, $strip>

post_route_middleware?

Middleware applied after routes, before static serving. Included in surface.

type MiddlewareSpec[]

static_serving?

Static file serving. Omit if not serving static files.

type { serve_static: ServeStaticFactory; root?: string | undefined; spa_fallback?: string | undefined; is_spa_route?: ((path: string) => boolean) | undefined; }

await_pending_effects?

Await all pending fire-and-forget effects before returning the response. Use in tests so audit log assertions don't need polling. Default false (production: true fire-and-forget).

type boolean

on_effect_error?

Called when a pending effect rejects. Use for monitoring, metrics, or alerting in production. Only called when await_pending_effects is false (production mode).

type (error: unknown, context: EffectErrorContext): void

error

type unknown

context

returns void

env_values?

Env values for startup summary logging.

type Record<string, unknown>

TEST_COOKIE_SECRET
#

TestAccount
#

testing/app_server.ts view source

TestAccount import type {TestAccount} from '@fuzdev/fuz_app/testing/app_server.js';

A bootstrapped test account with credentials.

account

type { id: string & $brand<"Uuid">; username: string; }

actor

type { id: string & $brand<"Uuid">; }

session_cookie

Signed session cookie value.

type string

api_token

Raw API token for Bearer auth.

type string

create_session_headers

Build request headers with this account's session cookie.

type (extra?: Record<string, string> | undefined): Record<string, string>

extra?

type Record<string, string>
optional
returns Record<string, string>

create_bearer_headers

Build request headers with this account's Bearer token.

type (extra?: Record<string, string> | undefined): Record<string, string>

extra?

type Record<string, string>
optional
returns Record<string, string>

TestApp
#

testing/app_server.ts view source

TestApp import type {TestApp} from '@fuzdev/fuz_app/testing/app_server.js';

A fully assembled test app — Hono app + backend + helpers.

app

type Hono<BlankEnv, BlankSchema, "/">

backend

type TestAppServer

server

The assembled AppServer — reach for what create_app_server built beyond the fields lifted onto TestApp: audit_sse (with audit_log_sse in app_options), the ws_endpoints transports, bootstrap_status, migration_results.

type AppServer

surface_spec

type AppSurfaceSpec

surface

type AppSurface

route_specs

type RouteSpec[]

create_session_headers

Build request headers with the bootstrapped session cookie.

type (extra?: Record<string, string> | undefined): Record<string, string>

extra?

type Record<string, string>
optional
returns Record<string, string>

create_bearer_headers

Build request headers with the bootstrapped Bearer token.

type (extra?: Record<string, string> | undefined): Record<string, string>

extra?

type Record<string, string>
optional
returns Record<string, string>

create_daemon_token_headers

Build request headers with the daemon token (keeper auth).

type (extra?: Record<string, string> | undefined): Record<string, string>

extra?

type Record<string, string>
optional
returns Record<string, string>

create_account

Create an additional account with credentials.

type (options?: CreateTestAppAccountArgs | undefined): Promise<TestAccount>

options?

optional
returns Promise<TestAccount>

cleanup

Cleanup resources — AppServer.close: stops an opted-in auth cleanup schedule, closes live connections, disposes the limiters the server built and removes the audit listeners and closer members it added, then the backend's close. Idempotent.

type (): Promise<void>

returns Promise<void>

TestAppForBootstrap
#

testing/app_server.ts view source

TestAppForBootstrap import type {TestAppForBootstrap} from '@fuzdev/fuz_app/testing/app_server.js';

A fully assembled test app in the pre-bootstrap state — empty DB, bootstrap_lock.bootstrapped = false, no keeper account. Test drives POST /bootstrap itself.

app

type Hono<BlankEnv, BlankSchema, "/">

backend

type AppBackend

server

The assembled AppServer, as on TestApp — bootstrap_status, audit_sse, the ws_endpoints transports.

type AppServer

surface_spec

type AppSurfaceSpec

surface

type AppSurface

route_specs

type RouteSpec[]

create_request_headers

Build host/origin request headers for the anonymous bootstrap POST.

type (extra?: Record<string, string> | undefined): Record<string, string>

extra?

type Record<string, string>
optional
returns Record<string, string>

cleanup

Cleanup resources — AppServer.close, as on TestApp; the backend's own close leaves the injected or factory-cached database open. Idempotent.

type (): Promise<void>

returns Promise<void>

TestAppServer
#

testing/app_server.ts view source

TestAppServer import type {TestAppServer} from '@fuzdev/fuz_app/testing/app_server.js';

An AppBackend with a bootstrapped account, API token, and session cookie.

inheritance

extends: AppBackend

account

The bootstrapped account.

type { id: string & $brand<"Uuid">; username: string; }

actor

The actor linked to the account.

type { id: string & $brand<"Uuid">; }

api_token

Raw API token for Bearer auth.

type string

session_cookie

Signed session cookie value for cookie auth.

type string

keyring

Keyring used for cookie signing — exposed for forging expired/tampered cookies in tests.

type Keyring

cleanup

Release test resources (no-op when DB is injected or factory-cached).

type (): Promise<void>

returns Promise<void>

TestAppServerOptions
#

testing/app_server.ts view source

TestAppServerOptions import type {TestAppServerOptions} from '@fuzdev/fuz_app/testing/app_server.js';

Configuration for create_test_app_server.

session_options

Session options — needed for cookie signing.

type SessionOptions<string>

db?

Existing database — skips internal DB creation when provided. Caller owns the DB lifecycle.

type Db

db_type?

Database driver type — only used when db is provided. Default: 'pglet-memory'.

type DbType

migration_namespaces?

Extra migration namespaces run after the builtin auth namespace in the auto-created in-memory database, mirroring create_app_backend's migration_namespaces. For suites whose backend needs tables beyond auth — the cell parity suite passes [CELL_MIGRATION_NS]. The harness builds + caches a fresh-per-test factory migrating [auth_migration_ns, ...migration_namespaces]; the reset-on-create gives the same fresh-db isolation as the auth-only default. Mutually exclusive with db (which assumes the caller already migrated).

type readonly MigrationNamespace[]

password?

Password implementation. Default: stub_password_deps. Pass argon2_password_deps for tests that exercise login.

type PasswordHashDeps

username?

Username for the bootstrapped account. Default: 'keeper'.

type string

password_value?

Password for the bootstrapped account. Default: DEFAULT_TEST_PASSWORD.

type string

roles?

Roles to grant. Default: [ROLE_KEEPER].

type string[]

audit_factory?

Build the bound AuditEmitter used by the test backend. Defaults to default_audit_factory (a no-listener create_audit_emitter over the test backend's {db, log}). Pass a custom factory when a test needs:

  • to capture audit events (compose on_audit_event inside the body)
  • to register consumer event-type schemas (pass audit_log_config)
  • to instrument emit (emit_decorator)
  • to wrap or replace the emitter for some other reason

Matches the production shape — create_app_backend requires an audit_factory and create_test_app_server mirrors that contract end-to-end.

type (params: { db: Db; log: Logger; }): AuditEmitter

params

type { db: Db; log: Logger; }
returns AuditEmitter

Depends on
#

Imported by
#