testing/cross_backend/role_grant_offer_notification_ws.ts

Cross-process role-grant-offer lifecycle WS notification suite — the machinery proof for the consentful-role-grants notification fan-out across any spine backend. Covers all seven server-initiated notifications:

  • role_grant_offer_received → recipient (offer created)
  • role_grant_offer_accepted → grantor (recipient accepts)
  • role_grant_offer_declined → grantor (recipient declines)
  • role_grant_offer_retracted → recipient (grantor retracts)
  • role_grant_revoke (flat, omits revoked_by) → revokee (active grant revoked)
  • role_grant_offer_supersede → each superseded sibling's grantor, fired on BOTH the accept-cascade (reason: 'sibling_accepted') and the revoke-cascade (reason: 'role_grant_revoked')

These exercise only spine primitives (accounts, role-grants, offers, WS notifications) — zero consumer domain — so the suite lives here and runs against any backend that wires the standard RPC actions' notification_sender and mounts a registered WS socket: fuz_app's own spine self-tests (testing_spine_server + the Rust testing_spine_stub) and downstream twin-impl consumers (the fuz_forge Deno/Hono + Rust fuz_forge_server backends) alike.

Each case is a *targeted* server-initiated notification (vs the broadcast in a repo_updated-style suite), so it opens the affected counterparty's socket, drives the lifecycle RPC over HTTP, then asserts the frame lands on that socket and strict-parses against the canonical wire schema — the guard against serialization drift (field / null / datetime / the flat revoke shape / the supersede reason + cause_id).

Sends are queued on the post-commit drain (handler-emit, not audit-derived), so a frame may land a beat after the RPC resolves — WsClient.wait_for polls already-received messages then waits, absorbing the fan-out latency without a sleep, and its method+predicate filter ignores unrelated frames (e.g. the received push the recipient also gets). ROLE_ADMIN is the only admin-grantable role; accounts that already hold it can still be offered it again (a fresh pending row — the prior accept is terminal, no already-granted guard), and accept stays idempotent on the role_grant while still superseding pending siblings. Gated on capabilities.ws.

Cross-process only: create_ws_transport needs a real bound socket, so wire it from a *.cross.test.ts file, never an in-process setup. Authed cookies come from the per-account session minted by fixture.create_account / fixture.create_session_headers.

view source

Declarations
#

2 declarations

describe_role_grant_offer_notification_ws_tests
#

testing/cross_backend/role_grant_offer_notification_ws.ts view source

(options: RoleGrantOfferNotificationWsTestOptions): void import {describe_role_grant_offer_notification_ws_tests} from '@fuzdev/fuz_app/testing/cross_backend/role_grant_offer_notification_ws.js';

Register the role-grant-offer WS notification suite — seven cases over a real upgrade, one per server-initiated notification (received / accepted / declined / retracted / revoke + supersede on both the accept and revoke cascades). Each opens the affected counterparty's socket, drives the lifecycle RPC, and strict-parses the delivered frame against its canonical params schema. Gated on capabilities.ws.

options

returns

void

RoleGrantOfferNotificationWsTestOptions
#

testing/cross_backend/role_grant_offer_notification_ws.ts view source

RoleGrantOfferNotificationWsTestOptions import type {RoleGrantOfferNotificationWsTestOptions} from '@fuzdev/fuz_app/testing/cross_backend/role_grant_offer_notification_ws.js';

Configuration for .

setup_test

Per-test fixture producer (default_cross_process_setup(handle, ...)).

type SetupTest

readonly

capabilities

Backend capability flags; every case gates on capabilities.ws.

type BackendCapabilities

readonly

base_url

Base URL the backend is reachable at (e.g. http://localhost:1178).

type string

readonly

ws_path

WebSocket endpoint path on the backend (e.g. /api/ws).

type string

readonly

Depends on
#