testing

51 modules

  • testing/admin_integration.ts

    Standard admin integration test suite for fuz_app admin routes.

    describe_standard_admin_integration_tests creates a composable test suite that exercises admin account listing, role_grant grant/revoke (via the RPC surface — see role_grant_offer_create / role_grant_revoke), session/token management, and audit log routes against a real PGlite database.

    Consumers call it with their route factory, session config, role schema, and RPC endpoint specs — all admin route tests come for free.

    Scope: admin *semantics* — cross-admin isolation, role_grant grant/revoke flow, session/token revoke-all, audit writes. Output-schema conformance for admin methods is not the concern of this suite; it lives in:

    - describe_rpc_round_trip_tests — every RPC method (admin methods included) is hit with a spec-generated valid body and the 2xx result is validated against spec.output. - describe_round_trip_validation — every REST route is hit and validated against its declared output / error schemas (SSE routes skipped via Content-Type: text/event-stream). - describe_sse_route_tests — SSE frames validated against their declared EventSpec.

  • testing/adversarial_404.ts

    Adversarial 404 testing for routes with params and declared 404 error schemas.

    Creates stub handlers that return 404 with the declared error code, fires requests with valid-format-but-nonexistent params (nil UUIDs), and validates response bodies against the declared 404 Zod schemas.

    No DB needed — tests schema conformance of 404 responses, not real handlers.

  • testing/adversarial_headers.ts

    Adversarial header attack test suite.

    Provides standard header injection test cases and a convenience wrapper for exercising middleware stacks with adversarial headers.

  • testing/adversarial_input.ts

    Adversarial input validation testing for route specs.

    Walks Zod schemas directly to generate payloads that must fail validation. Fires requests against a test app and asserts 400 responses before handlers are reached.

    Tests are focused: one representative wrong-type value per field, one format violation per constrained field, one null and one missing test per required field, plus whole-body structural attacks (non-object body, extra unknown keys).

  • 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 PGlite (shared WASM instance per vitest worker thread via test_db.ts module cache); 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.

  • testing/assert_dev_env.ts

  • testing/assertions.ts

    Assertion helpers for auth attack surface testing.

    Plain functions called inside explicit test() blocks to verify surface snapshots, public routes, and middleware stacks.

  • testing/attack_surface.ts

    Adversarial auth enforcement test runners and the standard attack surface suite.

    The combinatorial test runner (describe_adversarial_auth) generates test suites for routes x auth levels. The standard suite (describe_standard_attack_surface_tests) composes all attack surface test groups into a single call.

    Stubs, app factories, and assertion helpers live in focused submodules: - test_auth_stubs — stub factories and pre-built dep bundles - test_auth_apps — auth-level test app factories - test_auth_assertions — snapshot, public route, and middleware assertions

  • testing/audit_completeness.ts

    Composable audit log completeness test suite.

    Verifies that every auth mutation route produces the expected audit log event. Uses the real middleware stack and database, then reads back through the audit_log_list RPC — the production observation path the admin UI consumes. This is intentional end-to-end coverage: emit → persist → query → wire response, all in one round-trip.

    The trade is a deliberate transport coupling: a regression in audit_log_list_action_spec's auth or response shape can surface here as a secondary failure. describe_rpc_round_trip_tests covers that RPC directly, so primary breakages localize there first. For *unit-level* "did the handler emit?" assertions without the persistence path, use create_recording_audit_emitter from audit_drift_guard.ts — that captures emits before they hit DB or transport.

    Bootstrap is excluded because it requires filesystem token state that create_test_app does not provide. Bootstrap audit logging is tested separately in bootstrap_account.db.test.ts.

  • testing/audit_drift_guard.ts

  • testing/auth_apps.ts

    Auth test app factories for adversarial testing.

    Creates Hono test apps at each auth level (public, authenticated, keeper, per-role) for use in adversarial auth enforcement and input validation tests.

  • testing/bootstrap_success.ts

    Bootstrap success-path suite for consumer projects.

    Exercises POST /bootstrap against an empty DB (no pre-keeper, lock unflipped) through the real bootstrap_account flow. Asserts on observable state — account exists, bootstrap_lock.bootstrapped is true, audit row emitted, response body shape — rather than on_bootstrap callback invocation, so the suite stays cross-impl friendly when cross-process testing wires it against a spawned Rust backend.

    Folded into describe_standard_tests with a bootstrap.mode === 'live' silent-skip gate; consumers wiring live bootstrap pick up success-path coverage by default.

  • testing/connection_closer_helpers.ts

  • testing/cross_backend/backend_config.ts

    Cross-process backend configuration.

    BackendConfig describes a spawnable test binary — argv, mount paths, env vars, bootstrap credentials, daemon-token discovery path, declared capabilities. Consumer projects ship per-backend factories (deno_backend_config(), rust_backend_config(), spine_stub_backend_config()) that produce this shape; spawn_backend consumes it.

    fuz_app ships spine_stub_backend_config() as a convenience preset (operational dep on testing_spine_stub — path-based discovery, no package.json coupling to private_fuz). Otherwise backend-specific knowledge (binary paths, port choices, env vars) is a consumer concern; fuz_app's testing library knows nothing about Deno, Cargo, or any specific runtime beyond that preset.

  • testing/cross_backend/bootstrap_backend.ts

    One-call spawn + bootstrap helper.

    Composes spawn_backend(config) and bootstrap({transport, config}) so a consumer's vitest globalSetup reduces to a single await:

    import {bootstrap_backend} from '@fuzdev/fuz_app/testing/cross_backend/bootstrap_backend.js'; export default async function ({provide}) { const bootstrapped = await bootstrap_backend(deno_backend_config()); provide('backend_handle', bootstrapped); return async () => { await bootstrapped.teardown(); }; }

    If bootstrap() throws — typically a bad token, port collision, or keeper-username mismatch — the spawned binary is torn down before the error propagates so vitest doesn't strand the port.

  • testing/cross_backend/build_test_backend_paths.ts

    Per-backend filesystem layout under os.tmpdir() for cross-process tests.

    Isolation matters because vitest projects can run in parallel — a shared root would mix daemon tokens across concurrently-running backends. Each backend gets its own subtree via the prefix arg (typically the BackendConfig.name).

    Consumers compose: take the generic paths from build_test_backend_paths(name), add domain-specific dirs (e.g. zzz_dir, scoped_dir) under the returned root.

  • testing/cross_backend/capabilities.ts

    Capability vocabulary for cross-backend integration testing.

    Backends declare which optional behaviors they support; suite bodies call test_if(capabilities.X, ...) to skip cases the backend doesn't implement. No if (config.name === 'rust') branches anywhere — name- checking is a code smell that says capability vocabulary is missing.

    In-process Hono via default_in_process_setup declares every capability true (see in_process_capabilities). Cross-process backends opt in per-flag on their BackendConfig.

  • testing/cross_backend/default_backend_configs.ts

    Family-shared BackendConfig builders for cross-process test backends.

    Two consumer-facing factories — {@link make_default_ts_backend_config} and {@link make_default_rust_backend_config} — own the common shape for the JS-runtime (Deno/Node on V8) and Rust families respectively. Per-backend factories in consumer projects compose a small declaration against one of these and add consumer-specific env vars via extra_env.

    Defaults baked in by family:

    - TS — 'memory://' PGlite, 30s startup window, capabilities without trusted_proxy/login_rate_limit. The TS canonical path leaves these limiters null in test mode. - Rust — caller-supplied real Postgres URL (PGlite isn't reachable from tokio-postgres), 120s startup window (cargo first-build cost), capabilities including trusted_proxy + login_rate_limit + the FUZ_TESTING_RESET_DB_ON_STARTUP=true self-wipe gate.

    Both builders default port_env_var to 'PORT'. Consumers whose binary reads a different name (e.g. zzz's ZZZ_PORT) override.

    Common across both families: /api/rpc, /api/ws, /health, /api/account/bootstrap, cookie_name: 'fuz_session', the standard bootstrap block keyed off default_test_* constants. Builders call build_test_backend_paths(name) internally when the optional paths is omitted.

  • testing/cross_backend/default_secrets.ts

    Default test secrets for cross-process backend bootstrap.

    Every cross-backend consumer needs the same shape: a bootstrap token the harness writes to disk before spawn, a keeper username/password the harness POSTs to /api/account/bootstrap, and cookie keys the binary uses to construct its Keyring. The literals here are dev-only and protected by assert_dev_env; the binaries themselves throw on production load.

    Each constant is exported individually so consumers can override one without re-deriving the rest. Builders in default_backend_configs.ts thread these defaults into the BackendConfig.bootstrap block and the SECRET_FUZ_COOKIE_KEYS env entry; callers compose the bootstrap_overrides knob when they need a non-default keeper.

  • testing/cross_backend/setup.ts

    Per-test fixture protocol shared by in-process and cross-process transports.

    Each standard suite body takes a required setup_test: () => Promise<TestFixture> callback and invokes it once per test. The fixture carries everything a test needs to fire requests and assert on a single bootstrapped keeper account — transport, account / actor identity, three header builders, a multi-account mint factory, and (in-process only) the in-memory keyring + raw backend.

    default_in_process_setup(options) wraps create_test_app into the SetupTest contract. The cross-process sibling (default_cross_process_setup) lands alongside the spawn-a-backend transport plumbing — it implements the same contract by spawning a binary and bootstrapping over real HTTP.

  • testing/cross_backend/spawn_backend.ts

    Spawn a test backend binary, wait for it to come up, and return a handle the test harness drives.

    Lifecycle:

    1. Write the bootstrap token (config.bootstrap.token) to config.bootstrap.token_path so the binary picks it up at startup. 2. child_process.spawn(...) the binary with detached: true — creates a new process group so a SIGTERM to the negative PID tears down any descendants the binary spawned (PTYs, child workers). vitest worker death + Ctrl+C handlers also fire the group teardown so ports never strand. 3. Poll {base_url}{health_path} until it returns 2xx or startup_timeout_ms elapses. 4. Read config.bootstrap.daemon_token_path to load the binary's deterministic daemon token; thread it onto BackendHandle so _testing_reset and other keeper-credential calls can authenticate.

    Bootstrapping (POST /api/account/bootstrap) is a separate concern — the caller composes bootstrap() from ../transports/bootstrap.ts against a FetchTransport built around handle.config.base_url. Splitting the two keeps spawn_backend consumer-agnostic — fuz_app knows nothing about specific binary contents.

  • testing/cross_backend/standard.ts

    Cross-process counterpart to describe_standard_tests.

    Wires the cross-process-safe subset of the standard bundle — the five suites whose option shape is {setup_test, surface_source, capabilities, ...} and whose bodies fire requests through fixture.transport rather than touching the in-process Backend. Consumers wire one call against a spawned binary instead of repeating the five sibling calls per file.

    Suites included — always run:

    - describe_standard_integration_tests - describe_round_trip_validation - describe_rpc_round_trip_tests - describe_data_exposure_tests

    Gated on roles — included when the consumer supplies a RoleSchemaResult:

    - describe_standard_admin_integration_tests

    Suites omitted — the three that don't survive a process boundary, documented here so per-consumer files don't have to repeat the bookkeeping:

    - describe_rate_limiting_tests — builds a fresh TestApp per test to inject tight per-test rate-limiter overrides. That path requires in-process construction of Backend + rate limiter; the spawned binary has neither knob nor restart-per-test budget. - describe_audit_completeness_tests — reaches into FK-structural introspection that only the in-process backend exposes. Wire-level audit observability lives in the consumer's own audit .cross.test.ts driving audit_log_list / audit_log_role_grant_history. - describe_bootstrap_success_tests — bootstrap is one-shot per backend lifecycle, and the consumer's globalSetup already consumed it before the suite file loads. Re-running would 409.

  • testing/cross_backend/testing_reset_actions.ts

    Test-binary RPC actions for cross-process integration tests.

    Single daemon-token-authed action: _testing_reset — full DB wipe + keeper re-seed + optional secondary-account seeding. The handler wipes every auth-namespace row (no keeper-preserve filter), flips bootstrap_lock back to its post-bootstrap shape, seeds a fresh keeper account inline (reusing create_test_account_with_credentials so cross-process matches in-process write semantics), seeds any caller-requested extra_accounts (also direct-inserted at this setup step), refreshes the daemon-token cache to point at the new keeper, and fires the consumer-supplied domain-state callback. The new keeper + secondary credentials return as the action output so the per-test fixture closes over them.

    The redesign converges in-process and cross-process keeper lifetimes: both modes now run against a freshly bootstrapped keeper per test. Mutation-cascade tests (password change, revoke-all, hardcoded-username signup uniqueness) and direct keeper-vs-admin probes work uniformly cross-process.

    Keeper ≠ admin. The keeper and admin roles are independent. Keeper authorizes daemon-token / bootstrap paths; admin authorizes the user-facing admin RPC surface. _testing_reset seeds the keeper account with [ROLE_KEEPER, ROLE_ADMIN] by default — matching the production bootstrap_account flow — plus any roles passed via extra_keeper_roles. Tests probing the keeper-vs-admin separation (a keeper-only account must 403 on admin RPCs) declare a secondary via extra_accounts: [{username, roles: [ROLE_KEEPER]}] so the account is seeded at this same bootstrap-equivalent step.

    No free-form runtime bypass. Earlier drafts considered a separate _testing_seed_role_grant action for arbitrary direct grants; that was rejected because a runtime bypass would let tests skip the production consent flow's side-effects (audit emit, WS fan-out) and silently mask bugs in those paths. The bypass that does exist — extra_accounts — is framed as bootstrap-time seeding, the same shape bootstrap_account itself uses to grant the initial KEEPER + ADMIN pair. Tests that want a role on a *post-bootstrap* account must route through role_grant_offer_create + role_grant_offer_accept (the production path); they observe the full event chain.

    Production safety: this module lives under cross_backend/ and starts with import '../assert_dev_env.js'; — production bundles either tree-shake the module out or throw at startup. The Rust mirror (fuz_testing crate) ships a parallel action; `cargo xtask check-release blocks fuz_testing` from entering production dep graphs.

  • testing/data_exposure.ts

    Composable data exposure test suite.

    Verifies that sensitive database fields never leak through HTTP responses: - Schema-level: walks JSON Schema output/error schemas for blocklisted property names - Runtime: fires real requests and checks response bodies against field blocklists - Cross-privilege: verifies admin routes return 403 for non-admin users, and non-admin responses exclude admin-only fields

    Cadence: per-describe setup_test() call (see round_trip.ts module docstring). The runtime body manages its own request ordering (auth-free → cross-privilege → 2xx) to avoid session-invalidation contamination between assertions, so per-test fixture re-creation isn't needed.

  • testing/db_entities.ts

    DB-backed entity factories for tests that need real account + actor rows in the database.

    Companion to entities.ts — that file ships in-memory factories (create_test_account, create_test_actor) for tests that mock the DB; this file ships factories that hit a real Db so query-level tests don't reimplement the same query_create_account_with_actor wrapper in every file.

    For full-fledged test accounts that also need an API token + signed session cookie + role_grants, use bootstrap_test_keeper (keeper) or create_test_account_with_credentials (additional accounts) from app_server.ts instead.

  • testing/db.ts

    Test database fixtures for parameterized testing.

    Provides factory builders for creating test databases with pglite (in-memory) and pg (PostgreSQL) drivers. Consumer projects provide their own schema initialization via run_migrations and compose factories into test suites.

    @example

    import {create_pglite_factory, create_pg_factory} from '@fuzdev/fuz_app/testing/db.js'; import {run_migrations} from '@fuzdev/fuz_app/db/migrate.js'; import {auth_migration_ns} from '@fuzdev/fuz_app/auth/migrations.js'; const init_schema = async (db) => { await run_migrations(db, [auth_migration_ns]); }; const pglite_factory = create_pglite_factory(init_schema); const pg_factory = create_pg_factory(init_schema, process.env.TEST_DATABASE_URL); export const db_factories = [pglite_factory, pg_factory];
  • testing/entities.ts

    Shared test entity factories for Account, Actor, RoleGrant, AuditLogEvent, and RequestContext.

    Override types widen branded Uuid fields to string so tests can pass literal ids without per-call-site casts. The factory brands internally.

    Uses create_test_* names to avoid collisions with real create_account_with_actor from auth/account_queries.ts.

  • testing/error_coverage.ts

    Error reachability coverage tracking.

    Tracks which declared error statuses (and specific error codes) are actually exercised in tests. ErrorCoverageCollector records status codes (optionally with body error codes) observed during test runs, then assert_error_coverage compares against declared error schemas to find uncovered error paths — reporting per-code when the declared schema is a literal or enum, per-status otherwise.

  • testing/integration_helpers.ts

    Integration test helpers — route lookup, response validation, and cookie utilities.

  • testing/integration.ts

    Standard integration test suite for fuz_app auth routes.

    describe_standard_integration_tests creates a composable test suite that exercises the full middleware stack (origin, session, bearer_auth, request_context) against a real PGlite database. Consumers call it with their route factory and session config — all auth route tests come for free.

    Tests use stub_password_deps (deterministic hashing, no Argon2 overhead). Login handlers call verify_password(submitted, stored_hash) which works because both hash and verify use the same stub logic.

    Rate limiters are disabled by default — tests make many login attempts and would trigger limits otherwise.

  • testing/middleware.ts

    Table-driven middleware test helpers.

    Provides mock builders for bearer auth middleware dependencies, a generic test runner that iterates case tables, and a reusable middleware stack factory for integration testing.

  • testing/mock_fs.ts

    In-memory file system mock for tests that use dependency-injected read_file and write_file callbacks. Avoids module-level mocking.

  • testing/rate_limiting.ts

    Rate limiting integration test suite.

    Verifies that sensitive routes (login, bootstrap, token creation) enforce rate limits when rate limiters are enabled. Tests create a tight rate limiter (2 attempts / 1 minute) and fire requests until 429 is returned.

    Consumers call describe_rate_limiting_tests with their route factory and session config — rate limit enforcement tests come for free.

    Each test body constructs its own TestApp with a per-test rate limiter override in app_options, so this suite reads its inputs directly from the options bag instead of going through the per-test fixture protocol — the single-fixture model can't carry three different rate-limiter configurations. Consumers pass default_in_process_suite_options(...) anyway for shape uniformity with the other Tier 1 suites; the extra {setup_test, surface_source, capabilities} fields from the helper spread are ignored by TS and the suite body alike.

  • testing/role_grant_helpers.ts

    RPC-flow helpers for role_grant lifecycle in tests.

    Sibling to db_entities.ts's create_test_role_grant_direct — that one seeds a role_grant directly via query_create_role_grant (bypassing the consent flow) for tests that focus on revoke or isolation semantics. This file ships the RPC-driven complement: role_grant_offer_and_accept exercises the same role_grant_offer_create + role_grant_offer_accept specs the admin UI consumes, so consumer tests pick up post-commit fan-out (audit, SSE broadcasts, _supersede notifications) end-to-end.

  • testing/round_trip.ts

    Schema-driven round-trip validation test suite.

    For every route spec in the supplied surface_source, generates a valid request (auth, params, body) and validates the response against declared output or error schemas. DB-backed via the suite's setup_test fixture-producing callback.

    Cadence: per-describe setup_test() call. Each test in the suite fires one HTTP request against a shared fixture — no test mutates state in a way that contaminates the next, so the per-test cost of re-bootstrapping isn't justified here. Suites with state-isolation requirements (integration, admin_integration, audit_completeness) call setup_test() per test.

  • testing/rpc_attack_surface.ts

    Composable RPC attack surface test suite.

    Three test groups for JSON-RPC 2.0 endpoints: 1. Auth enforcement — per-method auth inside the dispatcher 2. Adversarial envelopes — malformed JSON-RPC requests 3. Adversarial params — schema-invalid params per method

    Uses the same {build, roles} config as describe_adversarial_auth and describe_adversarial_input. No DB needed — uses stub deps.

  • testing/rpc_helpers.ts

    JSON-RPC test helpers — request construction, response assertion, and one-shot call ergonomics.

    Shared by testing/rpc_attack_surface.ts, testing/rpc_round_trip.ts, and consumer-facing admin/audit suites that exercise RPC methods directly.

  • testing/rpc_round_trip.ts

    Schema-driven round-trip validation for RPC endpoints.

    For every RPC method, generates valid params and fires JSON-RPC requests (POST for all methods, GET for reads), validating that responses are well-formed JSON-RPC. Successful responses are validated against the method's declared output schema. DB-backed via the suite's setup_test fixture-producing callback.

    Cadence: per-describe setup_test() call (see round_trip.ts module docstring). RPC round-trip tests fire one JSON-RPC envelope per method-direction and don't mutate state in a way that contaminates the next case.

  • testing/schema_generators.ts

    Schema-driven value generation helpers for testing.

    Walks Zod schemas to generate valid values for route params, request bodies, and URL paths. Used by adversarial input, adversarial 404, and round-trip validation test suites.

  • testing/schema_introspect.ts

    PostgreSQL schema introspection — produces a normalized, JSON-serializable snapshot of a database's structure for cross-impl parity checks.

    The snapshot covers:

    - schema_version rows (namespace, name, sequence) — captures migration set state across impls - Tables with columns (data type, nullability, default, identity) - Indexes with canonical Postgres-rendered definitions - Constraints (CHECK, FOREIGN KEY, PRIMARY KEY, UNIQUE, EXCLUSION) - Sequences with data type — distinguishes int4 (SERIAL) from int8 (BIGSERIAL)

    Designed for pg_catalog introspection — works against both PostgreSQL and PGlite. The snapshot is fully deterministic: every collection sorts by a stable key and excludes time-varying fields like applied_at.

    Paired with schema_parity.ts for comparison + assertion helpers.

  • testing/schema_parity.ts

    Cross-impl schema parity — structural diff + assertion over two SchemaSnapshots captured via query_schema_snapshot.

    Two live impls (TS fuz_app vs Rust spine) are each other's parity reference. After both bootstrap, snapshot each, diff, fail loudly on drift. The diff entries name the specific divergence (column type, missing index, schema_version row absent on one side) so the error message points at the source.

    Consumer pattern (in zzz's integration runner or fuz_app's own cross-backend tests):

    const snapshot_a = await query_schema_snapshot(db_after_deno_bootstrap); const snapshot_b = await query_schema_snapshot(db_after_rust_bootstrap); assert_schema_snapshots_equal(snapshot_a, snapshot_b, {a: 'deno', b: 'rust'});

    Non-coverage — drift the gate does not detect:

    - enum types (CREATE TYPE ... AS ENUM) - regular triggers (pg_trigger); CONSTRAINT TRIGGER is captured via pg_constraint, but standalone CREATE TRIGGER is not - views, materialized views, functions, procedures - table storage parameters (fillfactor, tablespace, autovacuum settings) - column physical order — the snapshot keys columns by name, so two impls with the same columns in different declaration order compare equal (functional parity is preserved; SELECT * ordering is not) - COMMENT ON ... - the schema_version table's own structure (only its rows are captured) - permissions / GRANTs

    None of these are used by the current fuz_app auth schema. Extend query_schema_snapshot + SchemaDiff if a consumer's schema reaches for them; omitting them today keeps the diff surface focused on what fuz_app actually emits.

  • testing/sse_round_trip.ts

    Schema-driven SSE route validation test suite.

    Complements describe_round_trip_validation (which skips SSE routes). For each configured SSE route: 1. Open the stream with matching auth. 2. Assert the : connected comment is emitted. 3. Fire trigger() — expect one data: {...} frame. 4. Validate the payload as {method, params} against declared EventSpecs. 5. Fire session_revoke_all for the account and assert the stream closes (when assert_closes_on_revoke !== false).

  • testing/standard.ts

    Combined standard test suite helper.

    Bundles every DB-backed suite carrying the standard option shape, each gated on its relevant config — silent-skip when the gate isn't met (same precedent as describe_standard_admin_integration_tests skipping when roles isn't provided). Consumers wire the standard surface in one call instead of seven; forgetting a suite no longer silently loses coverage.

    Attack surface suites stay separate — their option shape is {build, snapshot_path, expected_public_routes, ...} rather than {setup_test, surface_source, capabilities}. A peer bundler lives for that side if/when needed.

  • testing/stubs.ts

    Stub factories for auth surface testing.

    Provides throwing stubs (catch unexpected access), no-op stubs (allow access without side effects), and pre-built bundles for AppDeps.

  • testing/surface_invariants.ts

    Surface invariant assertions for AppSurface data.

    Two categories: - Structural — validate internal consistency (error schema presence, descriptions, duplicates, middleware propagation, error schema validity, error code consistency). No options needed. - Policy — enforce security policy (sensitive routes rate-limited, no public mutations, mutation method conventions). Configurable with sensible defaults.

    Structural invariants catch schema/surface generation bugs. Policy invariants catch security misconfigurations. Both propagate automatically to consumers via assert_surface_invariants.

  • testing/testing_rate_limiter.ts

    Test-only RateLimiter subclass with bucket tracking + reset_all.

    Production code never instantiates this; test binaries swap it in for production RateLimiter so _testing_reset can clear every bucket between cases. Mirrors the TestingArgon2idHasher pattern from the Rust spine — same call surface as the production class, plus test-only knobs (reset_all, tracked_keys).

    Constructor + every overridden method preserve production semantics by delegating to super.* after tracking; reset_all walks the tracked-keys set and calls super.reset per entry. Tests that depend on burst behavior get identical results between production and test instantiations.

    Usage in a test binary:

    import {TestingRateLimiter} from '@fuzdev/fuz_app/testing/testing_rate_limiter.js'; const limiter = new TestingRateLimiter(default_login_ip_rate_limit); await create_app_server({backend, ip_rate_limiter: limiter, ...}); // Inside the `_testing_reset` handler's `reset_state`: limiter.reset_all();
  • testing/transports/bootstrap.ts

    Stateless cross-process bootstrap.

    POSTs {bootstrap_path} against the running test binary with the preconfigured token + username + password, parses the BootstrapOutput envelope, captures the session Set-Cookie onto the supplied FetchTransport (which carries it on every subsequent call), and returns the keeper credentials.

    Fires exactly once per backend lifetime — spawn_backend calls it inside vitest's globalSetup. Per-test fixtures re-use the captured keeper credentials; fresh per-test accounts come from fixture.create_account() (signup+login through production RPC), not re-bootstrap. The hybrid reset model in default_cross_process_setup depends on this — re-bootstrap would race the bootstrap lock and in-memory caches.

  • testing/transports/fetch_transport.ts

    Cookie-threading HTTP transport for cross-process tests.

    Wraps the global fetch against a base URL, carries cookies across requests in a Map-backed jar so the session cookie set on bootstrap is re-sent on every subsequent call. Satisfies the RpcTestTransport shape Hono.request already does — so every suite body that takes transport: RpcTestTransport works against a cross-process binary unchanged.

    The Origin header is threaded onto every request because the backend's allowlist ({ZZZ,ZAP,FUZ}_ALLOWED_ORIGINS) rejects mutations without an Origin. Cross-process tests run with ALLOWED_ORIGINS=http://localhost:*, so defaulting origin to the configured base_url is safe.

    The cookie jar is intentionally simple — it does not honour Domain, Path, Expires, or SameSite attributes. Cross-process tests always hit a single host:port, so name-keyed last-write-wins matches the behaviour real browsers exhibit against the same surface.

  • testing/transports/ws_client.ts

    Shared WebSocket client surface for cross-backend tests.

    WsClient is the interface every in-process or cross-process WS test driver implements — send / request / close / messages / wait_for. Two impls today:

    - In-process — create_ws_test_harness in ../ws_round_trip.ts. Drives register_action_ws against a fake Hono upgrade so the dispatcher's full path runs without the wire upgrade. - Cross-process — create_ws_transport in ./ws_transport.ts. Wraps the native WebSocket upgrade against a real running binary, threading the session cookie captured by FetchTransport.

    Wire-frame types + predicates also live here so both impls (and every shared assertion helper) reference one source.

  • testing/transports/ws_transport.ts

    Cross-process WebSocket transport.

    Implements the shared WsClient interface (see ws_client.ts) over a real WebSocket upgrade against a spawned test binary. The cookie captured by the sibling FetchTransport on bootstrap is threaded onto the upgrade request so the WS session authenticates as the same account.

    Uses the ws npm package — Node's native WebSocket (from undici) follows the WHATWG spec strictly and doesn't accept custom request headers on construction, so cookie-on-upgrade requires the ws package's headers option. fuz_app declares ws as an optional peerDependency — consumers wiring cross-process tests install it themselves (npm install --save-dev ws).

  • testing/ws_round_trip.ts

    In-process test helpers for WebSocket JSON-RPC round-trips.

    Drives register_action_ws without an HTTP server. Consumers supply specs + handlers; the harness constructs real WSContext instances backed by test-owned send/close pairs, fakes the authenticated Hono context (request_context, credential type, session id, api token id), and exposes a connect() factory returning a WsClient per connection.

    Three layers are exported:

    - Primitives (create_fake_ws, create_fake_hono_context, create_stub_upgrade, MinimalActionEnvironment, dispatch_ws_message) — used by fuz_app's own dispatcher tests and by consumers wiring tight one-off tests. - Harness (create_ws_test_harness, keeper_identity) — the high-level driver. Give it specs + handlers, get back {transport, connect()}. connect() is async and resolves after on_socket_open completes, so broadcasts sent immediately after await harness.connect() reach the client. Returns a WsClient (shared interface — see transports/ws_client.ts); the same interface is implemented by transports/ws_transport.ts for cross-process tests. - Broadcast wiring — build_broadcast_api for wiring a typed broadcast API against the harness's transport. Wire-frame types + predicates (is_notification, is_response_for, JsonrpcNotificationFrame, ...) live in transports/ws_client.ts so both in-process and cross-process drivers reference one source.

    Hono's wire upgrade is skipped — the Node test runtime has no @hono/node-ws adapter — but the full dispatch path is exercised (per-action auth, input validation, ctx.notify back to the originating socket, broadcast via BackendWebsocketTransport, and close-on-revoke).