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 declaredoutput/ error schemas (SSE routes skipped viaContent-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.tsmodule cache); passdbto 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 assertionstesting/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_listRPC — 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 /bootstrapagainst an empty DB (no pre-keeper, lock unflipped) through the real bootstrap_account flow. Asserts on observable state — account exists,bootstrap_lock.bootstrappedis true, audit row emitted, response body shape — rather thanon_bootstrapcallback 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 ontesting_spine_stub— path-based discovery, nopackage.jsoncoupling 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)andbootstrap({transport, config})so a consumer's vitestglobalSetupreduces 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
rootwould mix daemon tokens across concurrently-running backends. Each backend gets its own subtree via theprefixarg (typically theBackendConfig.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 returnedroot.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. Noif (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 fromtokio-postgres), 120s startup window (cargo first-build cost), capabilities including trusted_proxy + login_rate_limit + theFUZ_TESTING_RESET_DB_ON_STARTUP=trueself-wipe gate.Both builders default
port_env_varto'PORT'. Consumers whose binary reads a different name (e.g. zzz'sZZZ_PORT) override.Common across both families:
/api/rpc,/api/ws,/health,/api/account/bootstrap,cookie_name: 'fuz_session', the standard bootstrap block keyed offdefault_test_*constants. Builders callbuild_test_backend_paths(name)internally when the optionalpathsis 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 byassert_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.tsthread these defaults into theBackendConfig.bootstrapblock and theSECRET_FUZ_COOKIE_KEYSenv entry; callers compose thebootstrap_overridesknob 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) toconfig.bootstrap.token_pathso the binary picks it up at startup. 2.child_process.spawn(...)the binary withdetached: true— creates a new process group so aSIGTERMto 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 orstartup_timeout_mselapses. 4. Readconfig.bootstrap.daemon_token_pathto load the binary's deterministic daemon token; thread it onto BackendHandle so_testing_resetand other keeper-credential calls can authenticate.Bootstrapping (
POST /api/account/bootstrap) is a separate concern — the caller composesbootstrap()from../transports/bootstrap.tsagainst a FetchTransport built aroundhandle.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 throughfixture.transportrather than touching the in-processBackend. 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.tsdrivingaudit_log_list/audit_log_role_grant_history. - describe_bootstrap_success_tests — bootstrap is one-shot per backend lifecycle, and the consumer'sglobalSetupalready 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), flipsbootstrap_lockback 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-requestedextra_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
keeperandadminroles are independent. Keeper authorizes daemon-token / bootstrap paths; admin authorizes the user-facing admin RPC surface._testing_resetseeds the keeper account with[ROLE_KEEPER, ROLE_ADMIN]by default — matching the production bootstrap_account flow — plus any roles passed viaextra_keeper_roles. Tests probing the keeper-vs-admin separation (a keeper-only account must 403 on admin RPCs) declare a secondary viaextra_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_grantaction 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 initialKEEPER+ADMINpair. Tests that want a role on a *post-bootstrap* account must route throughrole_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 withimport '../assert_dev_env.js';— production bundles either tree-shake the module out or throw at startup. The Rust mirror (fuz_testingcrate) ships a parallel action; `cargo xtask check-releaseblocksfuz_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 (seeround_trip.tsmodule 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+actorrows 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.tsinstead.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
Uuidfields tostringso tests can pass literal ids without per-call-site casts. The factory brands internally.Uses
create_test_*names to avoid collisions with realcreate_account_with_actorfrom 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
errorcodes) 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_fileandwrite_filecallbacks. 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 passdefault_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 samerole_grant_offer_create+role_grant_offer_acceptspecs the admin UI consumes, so consumer tests pick up post-commit fan-out (audit, SSE broadcasts,_supersedenotifications) 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'ssetup_testfixture-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) callsetup_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_testfixture-producing callback.Cadence: per-describe
setup_test()call (seeround_trip.tsmodule 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_versionrows (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 — distinguishesint4(SERIAL) fromint8(BIGSERIAL)Designed for
pg_catalogintrospection — works against both PostgreSQL and PGlite. The snapshot is fully deterministic: every collection sorts by a stable key and excludes time-varying fields likeapplied_at.Paired with
schema_parity.tsfor 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 TRIGGERis captured via pg_constraint, but standaloneCREATE TRIGGERis 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 ...- theschema_versiontable's own structure (only its rows are captured) - permissions /GRANTsNone 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
: connectedcomment is emitted. 3. Firetrigger()— expect onedata: {...}frame. 4. Validate the payload as{method, params}against declared EventSpecs. 5. Firesession_revoke_allfor the account and assert the stream closes (whenassert_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
rolesisn'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_resetcan clear every bucket between cases. Mirrors theTestingArgon2idHasherpattern 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_allwalks the tracked-keys set and callssuper.resetper 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 sessionSet-Cookieonto 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 fromfixture.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
fetchagainst a base URL, carries cookies across requests in aMap-backed jar so the session cookie set on bootstrap is re-sent on every subsequent call. Satisfies the RpcTestTransport shapeHono.requestalready does — so every suite body that takestransport: RpcTestTransportworks against a cross-process binary unchanged.The
Originheader is threaded onto every request because the backend's allowlist ({ZZZ,ZAP,FUZ}_ALLOWED_ORIGINS) rejects mutations without anOrigin. Cross-process tests run withALLOWED_ORIGINS=http://localhost:*, so defaultingoriginto the configuredbase_urlis safe.The cookie jar is intentionally simple — it does not honour
Domain,Path,Expires, orSameSiteattributes. 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 nativeWebSocketupgrade 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 realWebSocketupgrade 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
wsnpm package — Node's nativeWebSocket(from undici) follows the WHATWG spec strictly and doesn't accept custom request headers on construction, so cookie-on-upgrade requires thewspackage'sheadersoption. fuz_app declareswsas 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
WSContextinstances backed by test-ownedsend/closepairs, fakes the authenticated Hono context (request_context, credential type, session id, api token id), and exposes aconnect()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 afteron_socket_opencompletes, so broadcasts sent immediately afterawait harness.connect()reach the client. Returns a WsClient (shared interface — seetransports/ws_client.ts); the same interface is implemented bytransports/ws_transport.tsfor 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 intransports/ws_client.tsso both in-process and cross-process drivers reference one source.Hono's wire upgrade is skipped — the Node test runtime has no
@hono/node-wsadapter — but the full dispatch path is exercised (per-action auth, input validation,ctx.notifyback to the originating socket, broadcast via BackendWebsocketTransport, and close-on-revoke).