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

Declarations
#

14 declarations

view source

build_broadcast_api
#

testing/ws_round_trip.ts view source

<TApi extends object>(options: { harness: WsTestHarness; specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[]; }): TApi

Wire a typed broadcast API against the harness's transport, matching how a consumer's real backend composes the stack. Returns the typed API so tests can call .zap_run_created(...) / .workspace_changed(...) etc. directly.

const harness = create_ws_test_harness({actions}); const broadcast = build_broadcast_api<MyBackendActionsApi>({ harness, specs: my_broadcast_action_specs, }); const client = await harness.connect(keeper_identity()); await broadcast.zap_run_created({run_id: '...', ...}); await client.wait_for(is_notification('zap_run_created'));

options

type { harness: WsTestHarness; specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[]; }

returns

TApi

create_fake_hono_context
#

testing/ws_round_trip.ts view source

(opts: FakeHonoContextOptions): Context<any, any, {}>

Build a fake Hono Context exposing the auth keys the dispatcher reads via c.get(...). Only .get() is populated — no other Hono context surface is simulated.

opts

returns

Context<any, any, {}>

create_fake_ws
#

testing/ws_round_trip.ts view source

(): FakeWs

Build a real WSContext backed by in-memory send/close capture. Parsing of outgoing frames is left to the caller — sends holds the raw strings as the dispatcher wrote them.

returns

FakeWs

create_stub_upgrade
#

testing/ws_round_trip.ts view source

(): StubUpgrade

Build a fake upgradeWebSocket that captures the createEvents callback. The returned middleware is inert — tests drive createEvents directly.

returns

StubUpgrade

create_ws_test_harness
#

testing/ws_round_trip.ts view source

(options: CreateWsTestHarnessOptions): WsTestHarness

Create a WebSocket test harness for the given specs + handlers.

Registers against a throwaway Hono app with a fake upgradeWebSocket; the captured events factory is invoked per connect() with a synthesized Hono context carrying the requested auth identity. Returned clients drive the real onOpen/onMessage/onClose path against a real WSContext.

options

returns

WsTestHarness

CreateWsTestHarnessOptions
#

testing/ws_round_trip.ts view source

CreateWsTestHarnessOptions

actions

The actions registered on this endpoint — matches the shape register_action_ws accepts. Each entry is a {spec, handler?} tuple; shared fuz_app primitives (like heartbeat_action) can be spread in alongside consumer-specific actions.

type ReadonlyArray<Action>

transport

Pass a pre-created transport to share with a broadcast API.

heartbeat

Threaded through to register_action_ws. Defaults to false in tests — fake timers + receive-silence detection need explicit opt-in and per- test tuning to avoid spurious closes.

type RegisterActionWsOptions['heartbeat']

log

Optional logger. Defaults to a silent [ws-test] logger.

type Logger

on_socket_open

Threaded straight through to register_action_ws.

type RegisterActionWsOptions['on_socket_open']

on_socket_close

Threaded straight through to register_action_ws.

type RegisterActionWsOptions['on_socket_close']

dispatch_ws_message
#

testing/ws_round_trip.ts view source

(on_message: (evt: MessageEvent<WSMessageReceive>, ws: WSContext<unknown>) => void, event: MessageEvent<any>, ws: WSContext<unknown>): Promise<...>

Hono types WSEvents.onMessage as () => void | Promise<void>. Awaits only the Promise branch so tests observe full dispatch (auth, validation, handler, send).

on_message

type (evt: MessageEvent<WSMessageReceive>, ws: WSContext<unknown>) => void

event

type MessageEvent<any>

ws

type WSContext<unknown>

returns

Promise<void>

FakeHonoContextOptions
#

FakeWs
#

testing/ws_round_trip.ts view source

FakeWs

A WSContext paired with capture arrays. Use sends to assert on outgoing frames; use closes to assert on revocation / close.

ws

type WSContext

sends

type Array<string>

closes

type Array<{code?: number; reason?: string}>

keeper_identity
#

MinimalActionEnvironment
#

testing/ws_round_trip.ts view source

Minimal ActionEventEnvironment for tests that instantiate an ActionPeer without pulling in the full runtime. Pre-loads a spec map from the supplied list.

inheritance

executor

type 'frontend' | 'backend'

constructor

type new (specs: readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; })[]): MinimalActionEnvironment

specs
type readonly ({ method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } ...

lookup_action_handler

type (): undefined

returns undefined

lookup_action_spec

type (method: string): { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 7 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; } | undefined

method
type string
returns { method: string; initiator: "frontend" | "backend" | "both"; side_effects: boolean; input: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; output: ZodType<unknown, unknown, $ZodTypeInternals<unknown, unknown>>; ... 6 more ...; rate_limit?: "both" | ... 2 more ... | undefined; } | { ...; } | { ...; }...

StubUpgrade
#

WsConnectIdentity
#

testing/ws_round_trip.ts view source

WsConnectIdentity

Auth identity for a mock connection.

account_id

Account id for the connection. Defaults to a fresh uuid per call.

type Uuid

credential_type

Credential type. Defaults to 'session'. Keeper actions require 'daemon_token'.

session_id

Session id (any string). Defaults to a fresh uuid. Hashed by the dispatcher.

type string

api_token_id

Api token id; set for bearer connections, null otherwise.

type string | null

roles

Roles to grant via active role_grants. Pass [ROLE_KEEPER] for keeper actions.

type Array<string>

WsTestHarness
#

testing/ws_round_trip.ts view source

WsTestHarness

A harness instance — transport handle + connection factory.

transport

connect

Open a mock connection. Resolves after on_socket_open (and the transport's register_ws) completes, so broadcasts issued immediately after the await reach the connection. Earlier revisions returned synchronously and required a settle_open() microtask drain — no longer necessary.

Returns the shared WsClient interface — same surface the cross-process driver in transports/ws_transport.ts implements, so assertion helpers and suite bodies work against either impl.

type (identity?: WsConnectIdentity) => Promise<WsClient>

Depends on
#