Cross-backend parity suite for the per-account WebSocket connection cap
over a real upgrade.
Both spines bound an account's concurrent WebSocket connections
(BackendWebsocketTransport's max_connections_per_account on the TS side,
the Rust fuz_realtime ConnectionRegistry on the other; both default to
DEFAULT_MAX_CONNECTIONS_PER_ACCOUNT) with the same policy: evict-oldest
— a connection past the cap is always admitted, and the account's oldest is
closed with WS_CLOSE_CONNECTION_LIMIT and the reason connection limit.
A client reads that close as "superseded": closed until the user acts, not
revoked and not reconnecting (FrontendWebsocketClient.superseded). The
code, the reason, and which socket closes are therefore wire contract, and
this suite is the pin that runs on both. Two cases:
- the oldest is superseded, and it is not a revocation — on one session,
opening one socket past the cap closes exactly the first with the
connection-limit code and reason; every other socket, the newest included,
stays open and still dispatches. The session then upgrades again, which a
revoked session could not — and that upgrade supersedes the next-oldest
in turn.
- a closed connection frees its slot — one socket held open while a
cap's worth of others open and close in sequence is never evicted. A
backend that leaks closed connections in its registry counts them toward
the cap and closes the held socket, which is what this catches.
Sockets open in sequence, each answering a heartbeat before the next
opens (create_admitted_ws_transport), so the backend registered them in
that order and "oldest" names the same socket on both spines (an upgrade
answers its handshake before the connection is registered and admitted, so
the handshake alone doesn't order them).
Consumer-agnostic, like describe_cross_process_ws_tests: it drives
only the heartbeat protocol action, present on every WS endpoint. Gated on
capabilities.ws; cross-process only (create_ws_transport needs a real
bound socket). Each case opens more than a cap's worth of real sockets,
which is why it's a separate call rather than more cases on the WS
round-trip suite.