@fuzdev/fuz_app 0.117.1 → 0.119.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/CLAUDE.md +301 -73
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +16 -1
- package/dist/actions/connection_closer.d.ts +136 -51
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +133 -34
- package/dist/actions/register_action_ws.d.ts +176 -24
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +581 -241
- package/dist/actions/register_ws_endpoint.d.ts +1 -0
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +1 -0
- package/dist/actions/socket.svelte.d.ts +43 -9
- package/dist/actions/socket.svelte.d.ts.map +1 -1
- package/dist/actions/socket.svelte.js +59 -8
- package/dist/actions/transports.d.ts +55 -1
- package/dist/actions/transports.d.ts.map +1 -1
- package/dist/actions/transports.js +55 -1
- package/dist/actions/transports_ws_auth_guard.d.ts +53 -67
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +87 -111
- package/dist/actions/transports_ws_backend.d.ts +212 -30
- package/dist/actions/transports_ws_backend.d.ts.map +1 -1
- package/dist/actions/transports_ws_backend.js +327 -70
- package/dist/actions/ws_endpoint_spec.d.ts +26 -13
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -1
- package/dist/auth/CLAUDE.md +125 -31
- package/dist/auth/account_actions.d.ts +12 -18
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +47 -33
- package/dist/auth/account_routes.d.ts +5 -11
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +40 -36
- package/dist/auth/account_schema.d.ts +27 -0
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +22 -0
- package/dist/auth/admin_actions.d.ts +8 -17
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +33 -21
- package/dist/auth/api_token_queries.d.ts +23 -2
- package/dist/auth/api_token_queries.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +39 -3
- package/dist/auth/audit_emitter.d.ts +72 -44
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +60 -16
- package/dist/auth/audit_log_route_schema.js +7 -7
- package/dist/auth/audit_log_routes.d.ts +31 -6
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +85 -11
- package/dist/auth/audit_log_schema.d.ts +48 -0
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +58 -0
- package/dist/auth/bootstrap_account.d.ts +3 -2
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +3 -3
- package/dist/auth/cleanup.d.ts +219 -30
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +260 -45
- package/dist/auth/deps.d.ts +29 -1
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +27 -0
- package/dist/auth/resolved_auth.d.ts +105 -0
- package/dist/auth/resolved_auth.d.ts.map +1 -0
- package/dist/auth/resolved_auth.js +124 -0
- package/dist/auth/role_grant_offer_ddl.d.ts +4 -2
- package/dist/auth/role_grant_offer_ddl.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_ddl.js +10 -3
- package/dist/auth/role_grant_offer_queries.d.ts +16 -7
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_queries.js +33 -15
- package/dist/auth/role_grant_offer_schema.d.ts +9 -0
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -1
- package/dist/auth/session_middleware.d.ts +8 -1
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +9 -3
- package/dist/auth/session_queries.d.ts +31 -6
- package/dist/auth/session_queries.d.ts.map +1 -1
- package/dist/auth/session_queries.js +31 -6
- package/dist/auth/standard_rpc_actions.d.ts +9 -9
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +3 -3
- package/dist/db/CLAUDE.md +7 -1
- package/dist/db/migrate.d.ts +8 -13
- package/dist/db/migrate.d.ts.map +1 -1
- package/dist/db/migrate.js +9 -38
- package/dist/db/status.d.ts +58 -7
- package/dist/db/status.d.ts.map +1 -1
- package/dist/db/status.js +205 -97
- package/dist/http/CLAUDE.md +7 -3
- package/dist/http/db_routes.d.ts +6 -2
- package/dist/http/db_routes.d.ts.map +1 -1
- package/dist/http/db_routes.js +2 -2
- package/dist/periodic.d.ts +80 -0
- package/dist/periodic.d.ts.map +1 -0
- package/dist/periodic.js +122 -0
- package/dist/realtime/sse_auth_guard.d.ts +79 -49
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +103 -73
- package/dist/realtime/subscriber_registry.d.ts +119 -8
- package/dist/realtime/subscriber_registry.d.ts.map +1 -1
- package/dist/realtime/subscriber_registry.js +184 -25
- package/dist/runtime/deno.d.ts.map +1 -1
- package/dist/runtime/deno.js +9 -6
- package/dist/runtime/deps.d.ts +3 -2
- package/dist/runtime/deps.d.ts.map +1 -1
- package/dist/runtime/secure_file.d.ts +23 -9
- package/dist/runtime/secure_file.d.ts.map +1 -1
- package/dist/runtime/secure_file.js +29 -10
- package/dist/server/app_backend.d.ts +13 -2
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +5 -2
- package/dist/server/app_server.d.ts +55 -15
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +41 -13
- package/dist/testing/CLAUDE.md +140 -48
- package/dist/testing/app_server.d.ts +2 -2
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +11 -2
- package/dist/testing/audit_drift_guard.d.ts +3 -52
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +2 -46
- package/dist/testing/connection_closer_helpers.d.ts +9 -22
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -1
- package/dist/testing/connection_closer_helpers.js +11 -17
- package/dist/testing/cross_backend/capabilities.d.ts +67 -13
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +27 -12
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +21 -0
- package/dist/testing/cross_backend/expected_schema.json +1 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +13 -4
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +4 -1
- package/dist/testing/cross_backend/session_cap.d.ts +13 -0
- package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
- package/dist/testing/cross_backend/session_cap.js +55 -19
- package/dist/testing/cross_backend/sse_round_trip.d.ts +10 -2
- package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/sse_round_trip.js +93 -26
- package/dist/testing/cross_backend/testing_server_bun.js +4 -4
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +10 -2
- package/dist/testing/cross_backend/ws_connection_cap.d.ts +35 -0
- package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_connection_cap.js +131 -0
- package/dist/testing/cross_backend/ws_round_trip.d.ts +2 -1
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.js +156 -19
- package/dist/testing/sse_round_trip.d.ts +6 -4
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/stubs.d.ts +1 -1
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +6 -4
- package/dist/testing/transports/sse_transport.d.ts +1 -1
- package/dist/testing/transports/ws_client.d.ts +7 -0
- package/dist/testing/transports/ws_client.d.ts.map +1 -1
- package/dist/testing/transports/ws_raw_client.d.ts +51 -0
- package/dist/testing/transports/ws_raw_client.d.ts.map +1 -0
- package/dist/testing/transports/ws_raw_client.js +156 -0
- package/dist/testing/transports/ws_transport.d.ts +28 -0
- package/dist/testing/transports/ws_transport.d.ts.map +1 -1
- package/dist/testing/transports/ws_transport.js +45 -1
- package/dist/testing/ws_round_trip.d.ts +8 -3
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +19 -4
- package/dist/ui/AdminAccounts.svelte +4 -3
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminOverview.svelte +3 -1
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +22 -6
- package/dist/ui/admin_accounts_state.svelte.d.ts +15 -1
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +16 -0
- package/dist/ui/auth_state.svelte.d.ts +25 -1
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +28 -1
- package/package.json +2 -2
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-backend parity suite for the **per-account WebSocket connection cap**
|
|
4
|
+
* over a real upgrade.
|
|
5
|
+
*
|
|
6
|
+
* Both spines bound an account's concurrent WebSocket connections
|
|
7
|
+
* (`BackendWebsocketTransport`'s `max_connections_per_account` on the TS side,
|
|
8
|
+
* the Rust `fuz_realtime` `ConnectionRegistry` on the other; both default to
|
|
9
|
+
* `DEFAULT_MAX_CONNECTIONS_PER_ACCOUNT`) with the same policy: **evict-oldest**
|
|
10
|
+
* — a connection past the cap is always admitted, and the account's oldest is
|
|
11
|
+
* closed with `WS_CLOSE_CONNECTION_LIMIT` and the reason `connection limit`.
|
|
12
|
+
* A client reads that close as "superseded": closed until the user acts, not
|
|
13
|
+
* revoked and not reconnecting (`FrontendWebsocketClient.superseded`). The
|
|
14
|
+
* code, the reason, and which socket closes are therefore wire contract, and
|
|
15
|
+
* this suite is the pin that runs on both. Two cases:
|
|
16
|
+
*
|
|
17
|
+
* - **the oldest is superseded, and it is not a revocation** — on one session,
|
|
18
|
+
* opening one socket past the cap closes exactly the first with the
|
|
19
|
+
* connection-limit code and reason; every other socket, the newest included,
|
|
20
|
+
* stays open and still dispatches. The session then upgrades again, which a
|
|
21
|
+
* revoked session could not — and that upgrade supersedes the next-oldest
|
|
22
|
+
* in turn.
|
|
23
|
+
* - **a closed connection frees its slot** — one socket held open while a
|
|
24
|
+
* cap's worth of others open and close in sequence is never evicted. A
|
|
25
|
+
* backend that leaks closed connections in its registry counts them toward
|
|
26
|
+
* the cap and closes the held socket, which is what this catches.
|
|
27
|
+
*
|
|
28
|
+
* Sockets open in sequence, each answering a `heartbeat` before the next
|
|
29
|
+
* opens (`create_admitted_ws_transport`), so the backend registered them in
|
|
30
|
+
* that order and "oldest" names the same socket on both spines (an upgrade
|
|
31
|
+
* answers its handshake before the connection is registered and admitted, so
|
|
32
|
+
* the handshake alone doesn't order them).
|
|
33
|
+
*
|
|
34
|
+
* **Consumer-agnostic**, like `describe_cross_process_ws_tests`: it drives
|
|
35
|
+
* only the `heartbeat` protocol action, present on every WS endpoint. Gated on
|
|
36
|
+
* `capabilities.ws`; cross-process only (`create_ws_transport` needs a real
|
|
37
|
+
* bound socket). Each case opens more than a cap's worth of real sockets,
|
|
38
|
+
* which is why it's a separate call rather than more cases on the WS
|
|
39
|
+
* round-trip suite.
|
|
40
|
+
*
|
|
41
|
+
* `$lib`-free by contract (relative specifiers only), like the sibling
|
|
42
|
+
* cross-backend suites.
|
|
43
|
+
*
|
|
44
|
+
* @module
|
|
45
|
+
*/
|
|
46
|
+
import { assert, describe } from 'vitest';
|
|
47
|
+
import { heartbeat_action_spec } from '../../actions/heartbeat.js';
|
|
48
|
+
import { WS_CLOSE_CONNECTION_LIMIT } from '../../actions/transports.js';
|
|
49
|
+
import { DEFAULT_MAX_CONNECTIONS_PER_ACCOUNT } from '../../actions/transports_ws_backend.js';
|
|
50
|
+
import { create_admitted_ws_transport } from '../transports/ws_transport.js';
|
|
51
|
+
import { test_if } from './capabilities.js';
|
|
52
|
+
/** The close reason both spines send with `WS_CLOSE_CONNECTION_LIMIT`. */
|
|
53
|
+
const CONNECTION_LIMIT_REASON = 'connection limit';
|
|
54
|
+
/**
|
|
55
|
+
* Register the WebSocket connection-cap parity suite over a real upgrade:
|
|
56
|
+
* one connection past the cap closes the account's oldest with
|
|
57
|
+
* `WS_CLOSE_CONNECTION_LIMIT` and nothing else, and a closed connection
|
|
58
|
+
* frees its slot.
|
|
59
|
+
*/
|
|
60
|
+
export const describe_ws_connection_cap_cross_tests = (options) => {
|
|
61
|
+
const { setup_test, capabilities, base_url, ws_path, origin } = options;
|
|
62
|
+
const max = options.max_connections_per_account === undefined
|
|
63
|
+
? DEFAULT_MAX_CONNECTIONS_PER_ACCOUNT
|
|
64
|
+
: options.max_connections_per_account;
|
|
65
|
+
const enabled = capabilities.ws && max !== null;
|
|
66
|
+
/**
|
|
67
|
+
* Open a socket the backend has admitted (a `heartbeat` round trip), so
|
|
68
|
+
* the connections are registered in the order they were opened.
|
|
69
|
+
*/
|
|
70
|
+
const open_registered = (cookies) => create_admitted_ws_transport({ base_url, ws_path, cookies, origin });
|
|
71
|
+
const assert_superseded = async (client, label) => {
|
|
72
|
+
const closed = await client.wait_for_close(2000);
|
|
73
|
+
assert.ok(closed, `${label} did not close within 2s of the cap overflow`);
|
|
74
|
+
assert.strictEqual(client.close_code, WS_CLOSE_CONNECTION_LIMIT, `${label} close code`);
|
|
75
|
+
assert.strictEqual(client.close_reason, CONNECTION_LIMIT_REASON, `${label} close reason`);
|
|
76
|
+
};
|
|
77
|
+
/** Every client still open and answering — a fresh request id per call via `id`. */
|
|
78
|
+
const assert_all_answer = async (clients, id, message) => {
|
|
79
|
+
const results = await Promise.all(clients.map((client) => client.request(id, heartbeat_action_spec.method, {})));
|
|
80
|
+
assert.deepStrictEqual(results, clients.map(() => ({})), message);
|
|
81
|
+
assert.deepStrictEqual(clients.map((client) => client.close_code), clients.map(() => null), message);
|
|
82
|
+
};
|
|
83
|
+
// each case opens more than a cap's worth of real sockets in sequence, so its
|
|
84
|
+
// time scales with the cap and the backend's upgrade cost
|
|
85
|
+
describe('per-account websocket connection cap parity', { timeout: 30_000 }, () => {
|
|
86
|
+
test_if(enabled, 'one connection past the cap closes the oldest with WS_CLOSE_CONNECTION_LIMIT, and only it', async () => {
|
|
87
|
+
const fixture = await setup_test();
|
|
88
|
+
const cookies = fixture.transport.cookies();
|
|
89
|
+
const sockets = [];
|
|
90
|
+
try {
|
|
91
|
+
for (let i = 0; i < max; i++) {
|
|
92
|
+
sockets.push(await open_registered(cookies));
|
|
93
|
+
}
|
|
94
|
+
// at the cap, nothing has closed
|
|
95
|
+
await assert_all_answer(sockets, 'at_cap', 'every socket at the cap stays open');
|
|
96
|
+
// one past it: always admitted, never refused
|
|
97
|
+
sockets.push(await open_registered(cookies));
|
|
98
|
+
const [oldest, ...survivors] = sockets;
|
|
99
|
+
await assert_superseded(oldest, 'the oldest socket');
|
|
100
|
+
await assert_all_answer(survivors, 'past_cap', 'only the oldest may close; every other socket, the newest included, keeps answering');
|
|
101
|
+
// Not a revocation — the session still authenticates an upgrade
|
|
102
|
+
// (a revoked one is refused there). That upgrade is one more past
|
|
103
|
+
// the cap, so it supersedes the next-oldest.
|
|
104
|
+
sockets.push(await open_registered(cookies));
|
|
105
|
+
await assert_superseded(survivors[0], 'the next-oldest socket');
|
|
106
|
+
await assert_all_answer(sockets.slice(2), 'past_cap_again', 'the session keeps its newest sockets after a second overflow');
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
await Promise.all(sockets.map((client) => client.close().catch(() => { })));
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
test_if(enabled, 'a closed connection frees its slot', async () => {
|
|
113
|
+
const fixture = await setup_test();
|
|
114
|
+
const cookies = fixture.transport.cookies();
|
|
115
|
+
const held = await open_registered(cookies);
|
|
116
|
+
try {
|
|
117
|
+
// A cap's worth of connections, one at a time, each closed before
|
|
118
|
+
// the next opens — the account never holds more than a few at once,
|
|
119
|
+
// however late the backend notices each close.
|
|
120
|
+
for (let i = 0; i < max; i++) {
|
|
121
|
+
const client = await open_registered(cookies);
|
|
122
|
+
await client.close();
|
|
123
|
+
}
|
|
124
|
+
await assert_all_answer([held], 'held', 'closed connections must not count toward the cap — the held socket was evicted');
|
|
125
|
+
}
|
|
126
|
+
finally {
|
|
127
|
+
await held.close().catch(() => { });
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
};
|
|
@@ -37,7 +37,8 @@ export interface CrossProcessWsTestOptions {
|
|
|
37
37
|
* `heartbeat` round-trip, `heartbeat`'s parameterless-shape contract, an
|
|
38
38
|
* oversized message closing with `WS_CLOSE_MESSAGE_TOO_BIG`, anonymous-upgrade
|
|
39
39
|
* refusal, disallowed-origin refusal, and — when `rpc_path` is supplied —
|
|
40
|
-
* session
|
|
40
|
+
* session revocation and account deletion closing the live socket, plus a
|
|
41
|
+
* self-revoking socket reading its reply before its close.
|
|
41
42
|
*/
|
|
42
43
|
export declare const describe_cross_process_ws_tests: (options: CrossProcessWsTestOptions) => void;
|
|
43
44
|
//# sourceMappingURL=ws_round_trip.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"ws_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6F9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,iEAAiE;AACjE,MAAM,WAAW,yBAAyB;IACzC;;;;OAIG;IACH,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,oEAAoE;IACpE,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;;OAKG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM,CAAC;CACpC;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,yBAAyB,KAAG,IA+RpF,CAAC"}
|
|
@@ -12,10 +12,12 @@ import '../assert_dev_env.js';
|
|
|
12
12
|
* cross-process bundle (`describe_standard_cross_process_tests`) omits WS
|
|
13
13
|
* by design, so consumers call this alongside it.
|
|
14
14
|
*
|
|
15
|
-
* **Consumer-agnostic
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* **Consumer-agnostic**, apart from the cases `rpc_path` and
|
|
16
|
+
* `capabilities.ws_account_actions` gate. Every other case drives the
|
|
17
|
+
* `heartbeat` protocol action, which
|
|
18
|
+
* `assert_ws_endpoints_include_protocol_actions` guarantees is present on
|
|
19
|
+
* every WS endpoint — so the suite needs no knowledge of a consumer's domain
|
|
20
|
+
* WS methods. It validates the transport, not the domain.
|
|
19
21
|
*
|
|
20
22
|
* The first three cases mirror the upgrade stack `register_ws_endpoint` wires
|
|
21
23
|
* (origin check → `require_auth` → dispatch): an authenticated upgrade
|
|
@@ -24,20 +26,36 @@ import '../assert_dev_env.js';
|
|
|
24
26
|
* upgrade time (not per message), so the negative cases assert the upgrade
|
|
25
27
|
* itself rejects rather than a per-message error frame.
|
|
26
28
|
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
+
* Spine-parity cases follow. Both spines admit a connection only after
|
|
30
|
+
* re-reading its credential, and requests that arrive first wait for that
|
|
31
|
+
* rather than being dropped: requests sent the moment the socket opens are all
|
|
32
|
+
* answered, and — gated on `capabilities.ws_handshake_pipelining`, with the
|
|
33
|
+
* ordering forced — so are frames written in the same TCP write as the upgrade
|
|
34
|
+
* request (driven through `connect_raw_ws`, since a client library sends
|
|
35
|
+
* nothing before its `open` event). `heartbeat` answers the parameterless
|
|
36
|
+
* shapes (absent, `{}`) and refuses a declared param with `invalid_params`, and a
|
|
29
37
|
* message over the backend's cap (`max_message_bytes`, default
|
|
30
38
|
* `DEFAULT_WS_MAX_MESSAGE_BYTES`) closes the socket with
|
|
31
39
|
* `WS_CLOSE_MESSAGE_TOO_BIG`.
|
|
32
40
|
*
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
+
* The final cases (gated on `rpc_path`) cover server-initiated close.
|
|
42
|
+
* Per-message dispatch never re-checks credential validity, so an open socket
|
|
43
|
+
* ends only when a revocation closes it — which every revocation does, once
|
|
44
|
+
* its transaction commits. An authenticated socket is dropped when the
|
|
45
|
+
* account's sessions are revoked over the keeper's session channel
|
|
46
|
+
* (`account_session_revoke_all`), and when its account is deleted
|
|
47
|
+
* (`account_delete`, also gated on `capabilities.account_lifecycle`). Omit
|
|
48
|
+
* `rpc_path` to skip them (consumers without the standard account and admin
|
|
49
|
+
* actions on their RPC endpoint).
|
|
50
|
+
*
|
|
51
|
+
* One more case is gated on `capabilities.ws_account_actions` — the backend's
|
|
52
|
+
* WS endpoint mounts the self-service account actions: a socket that revokes
|
|
53
|
+
* its own sessions *over that socket* is closed with
|
|
54
|
+
* `WS_CLOSE_SESSION_REVOKED`, and — on a backend declaring
|
|
55
|
+
* `capabilities.ws_self_revocation_reply` — reads the reply to its request
|
|
56
|
+
* first. The TS spine queues the close behind the commit and sends the reply
|
|
57
|
+
* ahead of it, so the caller is never left without an answer; the Rust spine
|
|
58
|
+
* does not answer yet, which that flag records.
|
|
41
59
|
*
|
|
42
60
|
* Gated on `capabilities.ws` — backends without an end-to-end WS transport
|
|
43
61
|
* skip (the cases still surface as `.skip` in the report). Cross-process
|
|
@@ -49,11 +67,13 @@ import '../assert_dev_env.js';
|
|
|
49
67
|
import { assert, describe } from 'vitest';
|
|
50
68
|
import { assert_rejects } from '@fuzdev/fuz_util/testing.ts';
|
|
51
69
|
import { heartbeat_action_spec } from '../../actions/heartbeat.js';
|
|
52
|
-
import { DEFAULT_WS_MAX_MESSAGE_BYTES, WS_CLOSE_MESSAGE_TOO_BIG } from '../../actions/transports.js';
|
|
70
|
+
import { DEFAULT_WS_MAX_MESSAGE_BYTES, WS_CLOSE_MESSAGE_TOO_BIG, WS_CLOSE_SESSION_REVOKED } from '../../actions/transports.js';
|
|
53
71
|
import { account_session_revoke_all_action_spec } from '../../auth/account_action_specs.js';
|
|
72
|
+
import { account_delete_action_spec } from '../../auth/admin_action_specs.js';
|
|
54
73
|
import { JSONRPC_ERROR_CODES } from '../../http/jsonrpc_errors.js';
|
|
55
74
|
import { is_response_for } from '../transports/ws_client.js';
|
|
56
75
|
import { create_ws_transport } from '../transports/ws_transport.js';
|
|
76
|
+
import { connect_raw_ws, is_raw_ws_close, RAW_WS_OPCODE_TEXT } from '../transports/ws_raw_client.js';
|
|
57
77
|
import { create_rpc_post_init } from '../rpc_helpers.js';
|
|
58
78
|
import { test_if } from './capabilities.js';
|
|
59
79
|
/** Origin guaranteed to fail the `http://localhost:*` allowlist the test backends run with. */
|
|
@@ -63,7 +83,8 @@ const DISALLOWED_ORIGIN = 'http://disallowed.example';
|
|
|
63
83
|
* `heartbeat` round-trip, `heartbeat`'s parameterless-shape contract, an
|
|
64
84
|
* oversized message closing with `WS_CLOSE_MESSAGE_TOO_BIG`, anonymous-upgrade
|
|
65
85
|
* refusal, disallowed-origin refusal, and — when `rpc_path` is supplied —
|
|
66
|
-
* session
|
|
86
|
+
* session revocation and account deletion closing the live socket, plus a
|
|
87
|
+
* self-revoking socket reading its reply before its close.
|
|
67
88
|
*/
|
|
68
89
|
export const describe_cross_process_ws_tests = (options) => {
|
|
69
90
|
const { setup_test, capabilities, base_url, ws_path, origin, rpc_path, max_message_bytes = DEFAULT_WS_MAX_MESSAGE_BYTES } = options;
|
|
@@ -82,6 +103,61 @@ export const describe_cross_process_ws_tests = (options) => {
|
|
|
82
103
|
await client.close();
|
|
83
104
|
}
|
|
84
105
|
});
|
|
106
|
+
// Both spines answer the handshake before the connection is admitted
|
|
107
|
+
// (the credential is re-read in between) and dispatch nothing until it
|
|
108
|
+
// is. Requests that reach the backend first must wait for that
|
|
109
|
+
// admission — not be dropped by it, and not run ahead of it.
|
|
110
|
+
test_if(capabilities.ws, 'requests sent the moment the socket opens are all answered', async () => {
|
|
111
|
+
const client = await open_authed();
|
|
112
|
+
try {
|
|
113
|
+
const ids = [1, 2, 3];
|
|
114
|
+
// back to back, with no round trip first — these may reach the
|
|
115
|
+
// backend while the connection is still pending admission
|
|
116
|
+
await Promise.all(ids.map((id) => client.send({ jsonrpc: '2.0', id, method: heartbeat_action_spec.method })));
|
|
117
|
+
const frames = await Promise.all(ids.map((id) => client.wait_for(is_response_for(id))));
|
|
118
|
+
for (const frame of frames) {
|
|
119
|
+
assert.ok('result' in frame, JSON.stringify(frame));
|
|
120
|
+
assert.deepStrictEqual(frame.result, {});
|
|
121
|
+
}
|
|
122
|
+
assert.strictEqual(client.close_code, null);
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
await client.close();
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
// The same contract with the ordering forced: frames written in the same
|
|
129
|
+
// TCP write as the upgrade request are in the backend's hands before it
|
|
130
|
+
// can have admitted anything.
|
|
131
|
+
test_if(capabilities.ws && capabilities.ws_handshake_pipelining, 'frames sent with the handshake are answered once the connection is admitted', async () => {
|
|
132
|
+
const fixture = await setup_test();
|
|
133
|
+
const url = new URL(base_url);
|
|
134
|
+
const ids = [1, 2, 3];
|
|
135
|
+
const client = await connect_raw_ws({
|
|
136
|
+
hostname: url.hostname,
|
|
137
|
+
port: url.port ? Number(url.port) : 80,
|
|
138
|
+
path: ws_path,
|
|
139
|
+
headers: {
|
|
140
|
+
Cookie: fixture.transport.cookies().join('; '),
|
|
141
|
+
Origin: origin ?? base_url
|
|
142
|
+
},
|
|
143
|
+
pipelined: ids.map((id) => JSON.stringify({ jsonrpc: '2.0', id, method: heartbeat_action_spec.method }))
|
|
144
|
+
});
|
|
145
|
+
try {
|
|
146
|
+
for (const id of ids) {
|
|
147
|
+
const frame = await client.wait_for((f) => f.opcode === RAW_WS_OPCODE_TEXT &&
|
|
148
|
+
JSON.parse(f.payload.toString('utf-8')).id === id);
|
|
149
|
+
assert.deepStrictEqual(JSON.parse(frame.payload.toString('utf-8')), {
|
|
150
|
+
jsonrpc: '2.0',
|
|
151
|
+
id,
|
|
152
|
+
result: {}
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
assert.ok(!client.frames.some(is_raw_ws_close), 'the socket stays open');
|
|
156
|
+
}
|
|
157
|
+
finally {
|
|
158
|
+
client.destroy();
|
|
159
|
+
}
|
|
160
|
+
});
|
|
85
161
|
// `heartbeat` is parameterless on both spines (`z.void()` /
|
|
86
162
|
// `require_void_params`): the empty shapes are the no-arg call, and a
|
|
87
163
|
// declared param is `invalid_params`, not ignored.
|
|
@@ -146,9 +222,9 @@ export const describe_cross_process_ws_tests = (options) => {
|
|
|
146
222
|
await assert_rejects(() => create_ws_transport({ base_url, ws_path, cookies: [], origin: DISALLOWED_ORIGIN }));
|
|
147
223
|
});
|
|
148
224
|
// Per-message dispatch never re-checks credential validity, so a live
|
|
149
|
-
// socket only
|
|
150
|
-
//
|
|
151
|
-
//
|
|
225
|
+
// socket drops only when a revocation closes it. Revoke the keeper's
|
|
226
|
+
// sessions over its own session channel → the handler's post-commit
|
|
227
|
+
// close ends the socket. Gated on `rpc_path` (depends on the standard
|
|
152
228
|
// account actions on the RPC endpoint).
|
|
153
229
|
test_if(capabilities.ws && rpc_path !== undefined, 'session revocation closes the live socket', async () => {
|
|
154
230
|
const fixture = await setup_test();
|
|
@@ -171,5 +247,66 @@ export const describe_cross_process_ws_tests = (options) => {
|
|
|
171
247
|
await client.close();
|
|
172
248
|
}
|
|
173
249
|
});
|
|
250
|
+
// A soft-deleted account's credentials stop authenticating, and its open
|
|
251
|
+
// socket must end with them: the tombstone revokes every session and
|
|
252
|
+
// token, and the handler closes the account's connections once that has
|
|
253
|
+
// committed. The keeper deletes a second account whose socket is open.
|
|
254
|
+
test_if(capabilities.ws && capabilities.account_lifecycle && rpc_path !== undefined, 'an account delete closes the live socket of the deleted account', async () => {
|
|
255
|
+
const fixture = await setup_test();
|
|
256
|
+
const target = await fixture.create_account({ username: 'ws_delete_target', roles: [] });
|
|
257
|
+
const cookie = target.create_session_headers().cookie;
|
|
258
|
+
assert.ok(cookie, 'expected a session cookie for the target account');
|
|
259
|
+
const client = await create_ws_transport({ base_url, ws_path, cookies: [cookie], origin });
|
|
260
|
+
try {
|
|
261
|
+
// admitted and dispatching before the delete, so a failed close
|
|
262
|
+
// assertion can't be confused with a dead connection
|
|
263
|
+
await client.request(1, heartbeat_action_spec.method, {});
|
|
264
|
+
const res = await fixture.transport(rpc_path, create_rpc_post_init(account_delete_action_spec.method, {
|
|
265
|
+
account_id: target.account.id
|
|
266
|
+
}));
|
|
267
|
+
assert.strictEqual(res.status, 200, `account_delete RPC failed (status=${res.status})`);
|
|
268
|
+
const closed = await client.wait_for_close(2000);
|
|
269
|
+
assert.ok(closed, 'socket did not close within 2s after account_delete');
|
|
270
|
+
assert.strictEqual(client.close_code, WS_CLOSE_SESSION_REVOKED);
|
|
271
|
+
}
|
|
272
|
+
finally {
|
|
273
|
+
await client.close();
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
// A socket that revokes the session it is running on. It must be closed;
|
|
277
|
+
// and where the backend declares `ws_self_revocation_reply`, the request
|
|
278
|
+
// is answered first, the close following from behind the revocation's
|
|
279
|
+
// commit. A caller left with only the close cannot tell a completed
|
|
280
|
+
// revocation from a dropped connection.
|
|
281
|
+
test_if(capabilities.ws && capabilities.ws_account_actions, capabilities.ws_self_revocation_reply
|
|
282
|
+
? 'a socket that revokes its own sessions reads the reply, then the revocation close'
|
|
283
|
+
: 'a socket that revokes its own sessions is closed with the revocation code', async () => {
|
|
284
|
+
const client = await open_authed();
|
|
285
|
+
try {
|
|
286
|
+
await client.request(1, heartbeat_action_spec.method, {});
|
|
287
|
+
const revoke_id = 2;
|
|
288
|
+
await client.send({
|
|
289
|
+
jsonrpc: '2.0',
|
|
290
|
+
id: revoke_id,
|
|
291
|
+
method: account_session_revoke_all_action_spec.method
|
|
292
|
+
});
|
|
293
|
+
const closed = await client.wait_for_close(2000);
|
|
294
|
+
assert.ok(closed, 'socket did not close within 2s of revoking its own sessions');
|
|
295
|
+
assert.strictEqual(client.close_code, WS_CLOSE_SESSION_REVOKED);
|
|
296
|
+
if (capabilities.ws_self_revocation_reply) {
|
|
297
|
+
// frames arrive in wire order and nothing arrives after the
|
|
298
|
+
// close, so a reply in hand was written ahead of it
|
|
299
|
+
const reply = client.messages.find(is_response_for(revoke_id));
|
|
300
|
+
assert.ok(reply, 'the reply must reach the caller before the close');
|
|
301
|
+
assert.ok('result' in reply, JSON.stringify(reply));
|
|
302
|
+
const result = reply.result;
|
|
303
|
+
assert.strictEqual(result.ok, true);
|
|
304
|
+
assert.ok((result.count ?? 0) >= 1, "at least the socket's own session was revoked");
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
finally {
|
|
308
|
+
await client.close();
|
|
309
|
+
}
|
|
310
|
+
});
|
|
174
311
|
});
|
|
175
312
|
};
|
|
@@ -27,8 +27,9 @@ export interface SseRouteTestSpec {
|
|
|
27
27
|
event_specs?: Array<EventSpec>;
|
|
28
28
|
/**
|
|
29
29
|
* Whether to assert the stream closes after `session_revoke_all`.
|
|
30
|
-
* Default `true`. Set `false` for endpoints
|
|
31
|
-
*
|
|
30
|
+
* Default `true`. Set `false` for endpoints whose registry no revocation
|
|
31
|
+
* reaches — neither joined to the backend's closer nor guarded (leaves a
|
|
32
|
+
* TODO to fix, rather than silently passing).
|
|
32
33
|
*/
|
|
33
34
|
assert_closes_on_revoke?: boolean;
|
|
34
35
|
}
|
|
@@ -45,8 +46,9 @@ export interface SseRouteTestOptions {
|
|
|
45
46
|
/**
|
|
46
47
|
* Backend audit event callback — threaded to `create_test_app_server`.
|
|
47
48
|
* Use to wire a close-on-revoke guard for consumer SSE registries
|
|
48
|
-
* (e.g., via `create_sse_auth_guard`) so `session_revoke_all`
|
|
49
|
-
*
|
|
49
|
+
* (e.g., via `create_sse_auth_guard`) so `session_revoke_all` closes the
|
|
50
|
+
* tested streams — not needed for a registry the route joined to
|
|
51
|
+
* `ctx.deps.connection_closer`, which the revoking handler closes itself.
|
|
50
52
|
*/
|
|
51
53
|
on_audit_event?: (event: AuditLogEvent) => void;
|
|
52
54
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/sse_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,oBAAoB,CAAC;AAGrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGjE,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhE,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAO1B,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;;;OAGG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B
|
|
1
|
+
{"version":3,"file":"sse_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/sse_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AACvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,SAAS,EAAmB,MAAM,oBAAoB,CAAC;AAGrE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAGjE,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAyB,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEhE,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAO1B,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7E;;;OAGG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B;;;;;OAKG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,qDAAqD;IACrD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC;;;;;;OAMG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,8BAA8B;IAC9B,MAAM,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;CAChC;AAmDD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,mBAAmB,KAAG,IAkIvE,CAAC"}
|
package/dist/testing/stubs.d.ts
CHANGED
|
@@ -71,7 +71,7 @@ export declare const create_test_audit_emitter: () => AuditEmitter;
|
|
|
71
71
|
* Build a no-op `AuditLogSse` for tests that wire `audit_sse` into the
|
|
72
72
|
* surface helper but don't assert on SSE fan-out or subscriber state.
|
|
73
73
|
*
|
|
74
|
-
* `
|
|
74
|
+
* `on_audit_event` is a no-op, so nothing is broadcast; the
|
|
75
75
|
* `registry` is a fresh `SubscriberRegistry` instance (call sites that
|
|
76
76
|
* inspect `.size` or call `.close_*` see a real registry, so writes are
|
|
77
77
|
* isolated per test). Tests that need real SSE plumbing build it via
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAa7B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAE7B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAC;AAEjE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"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"}
|
package/dist/testing/stubs.js
CHANGED
|
@@ -9,6 +9,7 @@ import './assert_dev_env.js';
|
|
|
9
9
|
*/
|
|
10
10
|
import { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
11
11
|
import { ApiError, RateLimitError } from '../http/error_schemas.js';
|
|
12
|
+
import { create_realtime_closer } from '../actions/connection_closer.js';
|
|
12
13
|
import { Db } from '../db/db.js';
|
|
13
14
|
import { prefix_route_specs } from '../http/route_spec.js';
|
|
14
15
|
import { bootstrap_route_shape } from '../auth/bootstrap_route_schema.js';
|
|
@@ -114,7 +115,7 @@ export const create_test_audit_emitter = () => ({
|
|
|
114
115
|
* Build a no-op `AuditLogSse` for tests that wire `audit_sse` into the
|
|
115
116
|
* surface helper but don't assert on SSE fan-out or subscriber state.
|
|
116
117
|
*
|
|
117
|
-
* `
|
|
118
|
+
* `on_audit_event` is a no-op, so nothing is broadcast; the
|
|
118
119
|
* `registry` is a fresh `SubscriberRegistry` instance (call sites that
|
|
119
120
|
* inspect `.size` or call `.close_*` see a real registry, so writes are
|
|
120
121
|
* isolated per test). Tests that need real SSE plumbing build it via
|
|
@@ -125,7 +126,6 @@ export const create_stub_audit_sse = () => {
|
|
|
125
126
|
max_per_scope: AUDIT_LOG_SSE_MAX_PER_SCOPE
|
|
126
127
|
});
|
|
127
128
|
return {
|
|
128
|
-
subscribe: () => () => { },
|
|
129
129
|
log: new Logger('test:audit_sse', { level: 'off' }),
|
|
130
130
|
on_audit_event: () => { },
|
|
131
131
|
registry
|
|
@@ -139,7 +139,8 @@ export const stub_app_deps = {
|
|
|
139
139
|
password: create_throwing_stub('password'),
|
|
140
140
|
db: create_throwing_stub('db'),
|
|
141
141
|
log: create_throwing_stub('log'),
|
|
142
|
-
audit: create_test_audit_emitter()
|
|
142
|
+
audit: create_test_audit_emitter(),
|
|
143
|
+
connection_closer: create_realtime_closer()
|
|
143
144
|
};
|
|
144
145
|
/**
|
|
145
146
|
* Create no-op `AppDeps` for auth surface testing.
|
|
@@ -153,7 +154,8 @@ export const create_stub_app_deps = () => ({
|
|
|
153
154
|
password: create_noop_stub('password'),
|
|
154
155
|
db: stub_db,
|
|
155
156
|
log: new Logger('test', { level: 'off' }),
|
|
156
|
-
audit: create_test_audit_emitter()
|
|
157
|
+
audit: create_test_audit_emitter(),
|
|
158
|
+
connection_closer: create_realtime_closer()
|
|
157
159
|
});
|
|
158
160
|
/** Create the API middleware stub array matching `create_auth_middleware_specs` output. */
|
|
159
161
|
export const create_stub_api_middleware = (options) => {
|
|
@@ -33,7 +33,7 @@ export interface SseTransport {
|
|
|
33
33
|
/**
|
|
34
34
|
* Drain until the server closes the stream. Resolves `true` if the
|
|
35
35
|
* stream closes within `timeout_ms`, `false` on timeout. The signal for
|
|
36
|
-
*
|
|
36
|
+
* a revocation dropping a live stream — mirrors
|
|
37
37
|
* `WsClient.wait_for_close`.
|
|
38
38
|
*/
|
|
39
39
|
wait_for_close: (timeout_ms?: number) => Promise<boolean>;
|
|
@@ -130,6 +130,13 @@ export interface WsClient {
|
|
|
130
130
|
* connection dropped without a close frame. `null` while open.
|
|
131
131
|
*/
|
|
132
132
|
readonly close_code: number | null;
|
|
133
|
+
/**
|
|
134
|
+
* The close reason of the connection once it has closed — the text sent
|
|
135
|
+
* with `close_code` (e.g. `'connection limit'` with
|
|
136
|
+
* `WS_CLOSE_CONNECTION_LIMIT`), empty when the close carried none. `null`
|
|
137
|
+
* while open.
|
|
138
|
+
*/
|
|
139
|
+
readonly close_reason: string | null;
|
|
133
140
|
/** Every message the server has sent, in arrival order. */
|
|
134
141
|
readonly messages: ReadonlyArray<unknown>;
|
|
135
142
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws_client.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/ws_client.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ws_client.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/ws_client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/** RFC 6455 opcode of a text frame. */
|
|
3
|
+
export declare const RAW_WS_OPCODE_TEXT = 1;
|
|
4
|
+
/** RFC 6455 opcode of a close frame. */
|
|
5
|
+
export declare const RAW_WS_OPCODE_CLOSE = 8;
|
|
6
|
+
/** One frame the server sent. */
|
|
7
|
+
export interface RawWsFrame {
|
|
8
|
+
opcode: number;
|
|
9
|
+
payload: Buffer;
|
|
10
|
+
}
|
|
11
|
+
/** A WebSocket client that sends frames and never answers a close. */
|
|
12
|
+
export interface RawWsClient {
|
|
13
|
+
/** Every frame the server sent, in order. */
|
|
14
|
+
frames: Array<RawWsFrame>;
|
|
15
|
+
/** Send one masked text frame. */
|
|
16
|
+
send_text: (text: string) => void;
|
|
17
|
+
/** Resolves with the first frame, already received or not, matching `predicate`. */
|
|
18
|
+
wait_for: (predicate: (frame: RawWsFrame) => boolean, timeout_ms?: number) => Promise<RawWsFrame>;
|
|
19
|
+
/** Drop the TCP connection, with no close handshake. */
|
|
20
|
+
destroy: () => void;
|
|
21
|
+
}
|
|
22
|
+
/** Options for `connect_raw_ws`. */
|
|
23
|
+
export interface RawWsConnectOptions {
|
|
24
|
+
port: number;
|
|
25
|
+
/** @default '127.0.0.1' */
|
|
26
|
+
hostname?: string;
|
|
27
|
+
/** The WebSocket endpoint path (e.g. `/api/ws`). */
|
|
28
|
+
path: string;
|
|
29
|
+
/** Extra upgrade request headers — `Cookie`, `Origin`. */
|
|
30
|
+
headers?: Record<string, string>;
|
|
31
|
+
/**
|
|
32
|
+
* Text frames to write in the **same TCP write** as the upgrade request, so
|
|
33
|
+
* they are in the server's hands before the handshake is answered — and so
|
|
34
|
+
* before the connection can have been admitted.
|
|
35
|
+
*/
|
|
36
|
+
pipelined?: ReadonlyArray<string>;
|
|
37
|
+
}
|
|
38
|
+
/** Whether a frame is a close frame. */
|
|
39
|
+
export declare const is_raw_ws_close: (frame: RawWsFrame) => boolean;
|
|
40
|
+
/** The close code a close frame carries, or `null` when it carries none. */
|
|
41
|
+
export declare const raw_ws_close_code: (frame: RawWsFrame) => number | null;
|
|
42
|
+
/**
|
|
43
|
+
* Upgrade a raw TCP socket to a WebSocket.
|
|
44
|
+
*
|
|
45
|
+
* Resolves once the server answers `101`; rejects on any other status or a
|
|
46
|
+
* connection error.
|
|
47
|
+
*
|
|
48
|
+
* @throws Error when the upgrade is refused or the connection fails
|
|
49
|
+
*/
|
|
50
|
+
export declare const connect_raw_ws: (options: RawWsConnectOptions) => Promise<RawWsClient>;
|
|
51
|
+
//# sourceMappingURL=ws_raw_client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ws_raw_client.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/transports/ws_raw_client.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuB9B,uCAAuC;AACvC,eAAO,MAAM,kBAAkB,IAAM,CAAC;AACtC,wCAAwC;AACxC,eAAO,MAAM,mBAAmB,IAAM,CAAC;AAEvC,iCAAiC;AACjC,MAAM,WAAW,UAAU;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,sEAAsE;AACtE,MAAM,WAAW,WAAW;IAC3B,6CAA6C;IAC7C,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAC1B,kCAAkC;IAClC,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,oFAAoF;IACpF,QAAQ,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,OAAO,EAAE,UAAU,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAClG,wDAAwD;IACxD,OAAO,EAAE,MAAM,IAAI,CAAC;CACpB;AAED,oCAAoC;AACpC,MAAM,WAAW,mBAAmB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,2BAA2B;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,0DAA0D;IAC1D,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC;;;;OAIG;IACH,SAAS,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAClC;AAED,wCAAwC;AACxC,eAAO,MAAM,eAAe,GAAI,OAAO,UAAU,KAAG,OAA+C,CAAC;AAEpG,4EAA4E;AAC5E,eAAO,MAAM,iBAAiB,GAAI,OAAO,UAAU,KAAG,MAAM,GAAG,IACE,CAAC;AAwClE;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,GAAI,SAAS,mBAAmB,KAAG,OAAO,CAAC,WAAW,CAoF9E,CAAC"}
|