@fuzdev/fuz_app 0.126.0 → 0.128.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 +37 -21
- package/dist/actions/connection_closer.d.ts +16 -4
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +20 -4
- package/dist/actions/register_action_ws.d.ts +10 -2
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +6 -3
- package/dist/actions/register_ws_endpoint.d.ts +3 -1
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +3 -1
- package/dist/auth/CLAUDE.md +13 -9
- package/dist/auth/audit_emitter.d.ts +59 -12
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +53 -15
- package/dist/auth/middleware.d.ts +30 -0
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +59 -0
- package/dist/http/CLAUDE.md +3 -3
- package/dist/http/surface.d.ts +12 -11
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +1 -15
- package/dist/rate_limiter.d.ts +19 -1
- package/dist/rate_limiter.d.ts.map +1 -1
- package/dist/rate_limiter.js +23 -0
- package/dist/realtime/sse_auth_guard.d.ts +3 -1
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/server/app_server.d.ts +41 -13
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +105 -50
- package/dist/testing/CLAUDE.md +51 -38
- package/dist/testing/app_server.d.ts +1 -1
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +15 -14
- package/dist/testing/attack_surface.d.ts +1 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.d.ts +4 -2
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +14 -4
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +1 -2
- package/dist/testing/cross_backend/capabilities.d.ts +3 -1
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.d.ts +3 -34
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +1 -31
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +1 -1
- 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 +3 -3
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +43 -3
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +43 -3
- package/dist/testing/cross_backend/testing_backdoor.d.ts +15 -1
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +49 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +27 -18
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +30 -19
- package/dist/testing/cross_backend/testing_server_core.d.ts +11 -11
- package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_core.js +2 -2
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +0 -2
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +2 -4
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
- 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 +40 -4
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +4 -3
- package/dist/testing/stubs.d.ts +26 -6
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +56 -16
- package/dist/testing/ws_round_trip.d.ts +7 -31
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +0 -38
- package/package.json +1 -1
|
@@ -168,16 +168,21 @@ export const testing_reset_action_spec = {
|
|
|
168
168
|
* the cross-backend conformance suite uses in place of a poll/sleep before
|
|
169
169
|
* asserting on audit rows.
|
|
170
170
|
*
|
|
171
|
-
*
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
*
|
|
176
|
-
*
|
|
177
|
-
*
|
|
178
|
-
*
|
|
179
|
-
*
|
|
180
|
-
*
|
|
171
|
+
* The handler awaits `AuditEmitter.drain_inflight`, which waits until no
|
|
172
|
+
* tracked `emit` write is outstanding — so the binary's emitter must be built
|
|
173
|
+
* with `track_inflight: true`, or the drain resolves at once and is no barrier.
|
|
174
|
+
* Tracking is what makes it a barrier whatever the transport: an **HTTP**
|
|
175
|
+
* mutation on a binary running `await_pending_effects: true` has its writes
|
|
176
|
+
* settled by the time it responds, but a **WS** mutation is answered before
|
|
177
|
+
* its effects flush (reply-then-close for a self-revocation), so its audit
|
|
178
|
+
* write can still be in flight when the caller reads the reply. The Rust
|
|
179
|
+
* spine does the same await in `AuditEmitter::drain_inflight`, over an
|
|
180
|
+
* emitter built with `new_with_inflight_tracking`.
|
|
181
|
+
*
|
|
182
|
+
* Covered: every `emit` that ran before the drain was dispatched, and any
|
|
183
|
+
* started while it waits. A write that starts after the drain resolves is
|
|
184
|
+
* not — nor are eager writes outside `emit` (session touch, token usage),
|
|
185
|
+
* which only the dispatching transport's own flush awaits.
|
|
181
186
|
*
|
|
182
187
|
* `auth` gates on the daemon-token credential, matching `_testing_reset`.
|
|
183
188
|
*/
|
|
@@ -246,13 +251,18 @@ export const testing_mint_session_action_spec = {
|
|
|
246
251
|
'signed cookie value (exercises the authoritative server-side DB-row expiry gate).'
|
|
247
252
|
};
|
|
248
253
|
/**
|
|
249
|
-
* Build the standalone `_testing_drain_effects` action
|
|
250
|
-
*
|
|
251
|
-
*
|
|
252
|
-
* suite asserts on audit rows (the spine binary bundles it via
|
|
254
|
+
* Build the standalone `_testing_drain_effects` action over `audit` — the
|
|
255
|
+
* emitter the backend's handlers emit through (`deps.audit`), built with
|
|
256
|
+
* `track_inflight: true` (see the spec doc). Mount it on any test endpoint
|
|
257
|
+
* whose suite asserts on audit rows (the spine binary bundles it via
|
|
253
258
|
* `create_testing_actions`; in-process suites mount it directly).
|
|
259
|
+
*
|
|
260
|
+
* @param audit - the backend's emitter, whose in-flight writes the action awaits
|
|
254
261
|
*/
|
|
255
|
-
export const create_testing_drain_effects_action = () => rpc_action(testing_drain_effects_action_spec, async () =>
|
|
262
|
+
export const create_testing_drain_effects_action = (audit) => rpc_action(testing_drain_effects_action_spec, async () => {
|
|
263
|
+
await audit.drain_inflight();
|
|
264
|
+
return { ok: true };
|
|
265
|
+
});
|
|
256
266
|
/**
|
|
257
267
|
* `_testing_put_fact` — seed an **embedded** fact (`fact.bytes`) for the
|
|
258
268
|
* cross-process fact-serving suite, which drives over real HTTP and has no
|
|
@@ -405,9 +415,10 @@ export const create_testing_action_manifest_action = (mounted) => {
|
|
|
405
415
|
/**
|
|
406
416
|
* Build the testing RPC actions for a test binary's registry.
|
|
407
417
|
*
|
|
408
|
-
* Returns `
|
|
409
|
-
*
|
|
410
|
-
*
|
|
418
|
+
* Returns the bundled `_testing_*` actions (module doc). The test binary
|
|
419
|
+
* calls this at server-assembly time and registers the result on its
|
|
420
|
+
* dispatcher. `_testing_drain_effects` drains `deps.audit`, so the binary's
|
|
421
|
+
* `audit_factory` builds the emitter with `track_inflight: true`.
|
|
411
422
|
*
|
|
412
423
|
* The reset action's table-wipe list mirrors
|
|
413
424
|
* `auth_integration_truncate_tables` from `testing/db.ts` — the
|
|
@@ -533,7 +544,7 @@ export const create_testing_actions = (deps, options) => {
|
|
|
533
544
|
});
|
|
534
545
|
return { hash };
|
|
535
546
|
}),
|
|
536
|
-
create_testing_drain_effects_action(),
|
|
547
|
+
create_testing_drain_effects_action(deps.audit),
|
|
537
548
|
create_testing_schema_snapshot_action(),
|
|
538
549
|
create_testing_migration_tracker_action()
|
|
539
550
|
];
|
|
@@ -9,13 +9,13 @@ import '../assert_dev_env.js';
|
|
|
9
9
|
* orchestration — stale-daemon check, daemon-info write, serve, post-serve
|
|
10
10
|
* WS attach, graceful shutdown — and delegates the runtime-boundary
|
|
11
11
|
* primitives (HTTP serve, WS upgrade construction, signals, pid, exit) to a
|
|
12
|
-
*
|
|
12
|
+
* `TestingServerAdapter`. The shipped adapters are
|
|
13
13
|
* `testing/cross_backend/testing_server_node.ts` (`@hono/node-server` + `@hono/node-ws`),
|
|
14
14
|
* `testing/cross_backend/testing_server_deno.ts` (`Deno.serve` + `@hono/deno`), and
|
|
15
15
|
* `testing/cross_backend/testing_server_bun.ts` (`Bun.serve` + `@hono/bun`).
|
|
16
16
|
*
|
|
17
17
|
* The app itself — routes, RPC, DB, `_testing_reset`, WS endpoints — is the
|
|
18
|
-
* caller's
|
|
18
|
+
* caller's `StartTestingServerOptions.build_app` seam, so this core
|
|
19
19
|
* stays domain-free. WebSocket endpoints mount through `create_app_server`'s
|
|
20
20
|
* `ws_endpoints`: `build_app` asks its context to prepare the runtime's
|
|
21
21
|
* upgrade helper and passes the resulting factory as
|
|
@@ -49,7 +49,7 @@ export interface ServeHandle {
|
|
|
49
49
|
/** Adapter-specific server ref for post-serve hooks. Type-erased at the seam. */
|
|
50
50
|
native?: unknown;
|
|
51
51
|
}
|
|
52
|
-
/** Options for
|
|
52
|
+
/** Options for `TestingServerAdapter.prepare_websocket`. */
|
|
53
53
|
export interface PrepareWebsocketOptions {
|
|
54
54
|
/**
|
|
55
55
|
* Largest inbound WebSocket message, in bytes — pass the largest
|
|
@@ -70,7 +70,7 @@ export interface PrepareWebsocketOptions {
|
|
|
70
70
|
*
|
|
71
71
|
* `create_upgrade_websocket` is the factory a build passes to
|
|
72
72
|
* `create_app_server` as `AppServerOptions.create_upgrade_websocket`.
|
|
73
|
-
* `attach_to_server` runs after `serve()` returns a
|
|
73
|
+
* `attach_to_server` runs after `serve()` returns a `ServeHandle` —
|
|
74
74
|
* Node uses it for `injectWebSocket(server)`, and it does nothing when the
|
|
75
75
|
* factory was never called (no `ws_endpoints`); Deno and Bun leave it
|
|
76
76
|
* undefined.
|
|
@@ -83,7 +83,7 @@ export interface PreparedWebsocket {
|
|
|
83
83
|
* Runtime adapter contract for the test-binary entry. Each adapter
|
|
84
84
|
* (`testing/cross_backend/testing_server_node.ts`, `testing/cross_backend/testing_server_deno.ts`,
|
|
85
85
|
* `testing/cross_backend/testing_server_bun.ts`) implements this and hands the shape to
|
|
86
|
-
*
|
|
86
|
+
* `start_testing_server`.
|
|
87
87
|
*/
|
|
88
88
|
export interface TestingServerAdapter {
|
|
89
89
|
/** Human-readable runtime label for log output (e.g. `"Node"`, `"Deno"`). */
|
|
@@ -94,7 +94,7 @@ export interface TestingServerAdapter {
|
|
|
94
94
|
get_connection_ip: (c: Context) => string | undefined;
|
|
95
95
|
/** Prepare the runtime's WS upgrade helper and its post-serve attach step. */
|
|
96
96
|
prepare_websocket: (options?: PrepareWebsocketOptions) => PreparedWebsocket;
|
|
97
|
-
/** Bind `app.fetch` to `port` on `hostname`; return a
|
|
97
|
+
/** Bind `app.fetch` to `port` on `hostname`; return a `ServeHandle`. */
|
|
98
98
|
serve: (options: {
|
|
99
99
|
fetch: Hono['fetch'];
|
|
100
100
|
port: number;
|
|
@@ -107,7 +107,7 @@ export interface TestingServerAdapter {
|
|
|
107
107
|
/** Forceful exit on graceful-shutdown completion or fatal error. */
|
|
108
108
|
exit: (code: number) => never;
|
|
109
109
|
}
|
|
110
|
-
/** What the core hands a
|
|
110
|
+
/** What the core hands a `StartTestingServerOptions.build_app` seam. */
|
|
111
111
|
export interface BuildTestingAppContext {
|
|
112
112
|
/**
|
|
113
113
|
* Prepare the runtime adapter's WS upgrade helper and return the factory
|
|
@@ -120,7 +120,7 @@ export interface BuildTestingAppContext {
|
|
|
120
120
|
*/
|
|
121
121
|
prepare_websocket: (options?: PrepareWebsocketOptions) => (app: Hono) => UpgradeWebSocket;
|
|
122
122
|
}
|
|
123
|
-
/** The assembled app a
|
|
123
|
+
/** The assembled app a `StartTestingServerOptions.build_app` seam returns. */
|
|
124
124
|
export interface BuiltTestingApp {
|
|
125
125
|
/** The assembled Hono app (HTTP routes, RPC, and WS endpoints already mounted). */
|
|
126
126
|
app: Hono;
|
|
@@ -131,7 +131,7 @@ export interface BuiltTestingApp {
|
|
|
131
131
|
*/
|
|
132
132
|
close: () => Promise<void>;
|
|
133
133
|
}
|
|
134
|
-
/** Options for
|
|
134
|
+
/** Options for `start_testing_server`. */
|
|
135
135
|
export interface StartTestingServerOptions {
|
|
136
136
|
/** Runtime-boundary adapter (Node or Deno). */
|
|
137
137
|
adapter: TestingServerAdapter;
|
|
@@ -173,8 +173,8 @@ export declare const is_loopback_host: (host: string) => boolean;
|
|
|
173
173
|
*
|
|
174
174
|
* Mirrors a production `start_server` at the surface level — stale-daemon
|
|
175
175
|
* check, daemon-info write, bind, graceful shutdown — but the app is the
|
|
176
|
-
* caller's no-domain (or domain)
|
|
177
|
-
* and the runtime boundary is the
|
|
176
|
+
* caller's no-domain (or domain) `StartTestingServerOptions.build_app`
|
|
177
|
+
* and the runtime boundary is the `TestingServerAdapter`. Refuses any
|
|
178
178
|
* non-loopback bind host (the test binary must stay on loopback — see
|
|
179
179
|
* `is_loopback_host`).
|
|
180
180
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"testing_server_core.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_core.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAChD,OAAO,EAAU,KAAK,MAAM,IAAI,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAG5E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC;AAEzD;;;;;;;;GAQG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED,
|
|
1
|
+
{"version":3,"file":"testing_server_core.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_core.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,8 +19,8 @@ export const is_loopback_host = (host) => {
|
|
|
19
19
|
*
|
|
20
20
|
* Mirrors a production `start_server` at the surface level — stale-daemon
|
|
21
21
|
* check, daemon-info write, bind, graceful shutdown — but the app is the
|
|
22
|
-
* caller's no-domain (or domain)
|
|
23
|
-
* and the runtime boundary is the
|
|
22
|
+
* caller's no-domain (or domain) `StartTestingServerOptions.build_app`
|
|
23
|
+
* and the runtime boundary is the `TestingServerAdapter`. Refuses any
|
|
24
24
|
* non-loopback bind host (the test binary must stay on loopback — see
|
|
25
25
|
* `is_loopback_host`).
|
|
26
26
|
*/
|
|
@@ -17,8 +17,6 @@ import '../assert_dev_env.js';
|
|
|
17
17
|
* @module
|
|
18
18
|
*/
|
|
19
19
|
import type { BackendConfig } from './backend_config.js';
|
|
20
|
-
/** Env var naming the backend root dir; `{dir}/run/daemon_token` must match `bootstrap.daemon_token_path`. */
|
|
21
|
-
export declare const TS_SPINE_DIR_ENV = "FUZ_TESTING_TS_SPINE_DIR";
|
|
22
20
|
/** Default port for the Node TS spine binary — slots beside the Rust `spine_stub` (1177). */
|
|
23
21
|
export declare const TS_SPINE_NODE_DEFAULT_PORT = 1178;
|
|
24
22
|
/** Default port for the Deno TS spine binary. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"ts_spine_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ts_spine_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAsDzD,6FAA6F;AAC7F,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,iDAAiD;AACjD,eAAO,MAAM,0BAA0B,OAAO,CAAC;AAE/C,gDAAgD;AAChD,eAAO,MAAM,yBAAyB,OAAO,CAAC;AAE9C,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,yDAAyD;AACzD,eAAO,MAAM,mBAAmB,wDAAwD,CAAC;AAEzF,wDAAwD;AACxD,eAAO,MAAM,kBAAkB,uDAAuD,CAAC;AAEvF,MAAM,WAAW,2BAA2B;IAC3C,mFAAmF;IACnF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,8DAA8D;IAC9D,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;;;;OASG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAWD;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAoBF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAoBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAiCF,CAAC"}
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import { build_test_backend_paths } from './build_test_backend_paths.js';
|
|
3
|
-
import {
|
|
4
|
-
import { SPINE_SSE_PATH } from './spine_surface_constants.js';
|
|
5
|
-
/** Env var naming the backend root dir; `{dir}/run/daemon_token` must match `bootstrap.daemon_token_path`. */
|
|
6
|
-
export const TS_SPINE_DIR_ENV = 'FUZ_TESTING_TS_SPINE_DIR';
|
|
3
|
+
import { action_rate_limit_env, make_default_ts_backend_config, ts_default_capabilities } from './default_backend_configs.js';
|
|
4
|
+
import { LOGIN_RATE_LIMIT_ENABLED_ENV, SPINE_SSE_PATH, TS_SPINE_DIR_ENV } from './spine_surface_constants.js';
|
|
7
5
|
/**
|
|
8
6
|
* Capabilities for the TS spine binary — `ts_default_capabilities` plus `sse`
|
|
9
7
|
* (the binary wires `audit_log_sse`), `ready` (the binary live-mounts the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import type { SetupTest } from './setup.js';
|
|
3
|
-
/** Options for
|
|
3
|
+
/** Options for `describe_ws_action_rate_limit_cross_tests`. */
|
|
4
4
|
export interface WsActionRateLimitCrossTestOptions {
|
|
5
5
|
/** Per-test fixture producer (cross-process only — see the module doc). */
|
|
6
6
|
readonly setup_test: SetupTest;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ws_action_rate_limit.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_action_rate_limit.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmE9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAyC5C
|
|
1
|
+
{"version":3,"file":"ws_action_rate_limit.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/ws_action_rate_limit.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmE9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAyC5C,+DAA+D;AAC/D,MAAM,WAAW,iCAAiC;IACjD,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,gDAAgD;IAChD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,yEAAyE;IACzE,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC9B;AAkBD;;;;GAIG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,iCAAiC,KACxC,IAqRF,CAAC"}
|
|
@@ -38,7 +38,8 @@ export interface CrossProcessWsTestOptions {
|
|
|
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
40
|
* session revocation and account deletion closing the live socket, plus a
|
|
41
|
-
* self-revoking socket reading its reply before its close
|
|
41
|
+
* self-revoking socket reading its reply before its close, and a WS mutation's
|
|
42
|
+
* audit row read back after `_testing_drain_effects`.
|
|
42
43
|
*/
|
|
43
44
|
export declare const describe_cross_process_ws_tests: (options: CrossProcessWsTestOptions) => void;
|
|
44
45
|
//# 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;AAwG9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AAEtE,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;;;;;;;;GAQG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,yBAAyB,KAAG,IAkVpF,CAAC"}
|
|
@@ -55,7 +55,11 @@ import '../assert_dev_env.js';
|
|
|
55
55
|
* `capabilities.ws_self_revocation_reply` — reads the reply to its request
|
|
56
56
|
* first. The TS spine queues the close behind the commit and sends the reply
|
|
57
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.
|
|
58
|
+
* does not answer yet, which that flag records. Under the same gate (and
|
|
59
|
+
* `rpc_path`), a socket mints an API token over WS and the token's
|
|
60
|
+
* `token_create` audit row is read back after `_testing_drain_effects` — the
|
|
61
|
+
* barrier a WS mutation needs, since a WS reply can precede its audit write.
|
|
62
|
+
* The binary's `create_testing_actions` mount provides the drain.
|
|
59
63
|
*
|
|
60
64
|
* Gated on `capabilities.ws` — backends without an end-to-end WS transport
|
|
61
65
|
* skip (the cases still surface as `.skip` in the report). Cross-process
|
|
@@ -68,14 +72,15 @@ import { assert, describe } from 'vitest';
|
|
|
68
72
|
import { assert_rejects } from '@fuzdev/fuz_util/testing.ts';
|
|
69
73
|
import { heartbeat_action_spec } from '../../actions/heartbeat.js';
|
|
70
74
|
import { DEFAULT_WS_MAX_MESSAGE_BYTES, WS_CLOSE_MESSAGE_TOO_BIG, WS_CLOSE_SESSION_REVOKED } from '../../actions/transports.js';
|
|
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';
|
|
75
|
+
import { account_session_revoke_all_action_spec, account_token_create_action_spec } from '../../auth/account_action_specs.js';
|
|
76
|
+
import { account_delete_action_spec, audit_log_list_action_spec, AuditLogListOutput } from '../../auth/admin_action_specs.js';
|
|
73
77
|
import { JSONRPC_ERROR_CODES } from '../../http/jsonrpc_errors.js';
|
|
74
78
|
import { is_response_for } from '../transports/ws_client.js';
|
|
75
79
|
import { create_ws_transport } from '../transports/ws_transport.js';
|
|
76
80
|
import { connect_raw_ws, is_raw_ws_close, RAW_WS_OPCODE_TEXT } from '../transports/ws_raw_client.js';
|
|
77
81
|
import { create_rpc_post_init } from '../rpc_helpers.js';
|
|
78
82
|
import { test_if } from './capabilities.js';
|
|
83
|
+
import { cross_rpc_call, expect_output } from './cell_cross_helpers.js';
|
|
79
84
|
/** Origin guaranteed to fail the `http://localhost:*` allowlist the test backends run with. */
|
|
80
85
|
const DISALLOWED_ORIGIN = 'http://disallowed.example';
|
|
81
86
|
/**
|
|
@@ -84,7 +89,8 @@ const DISALLOWED_ORIGIN = 'http://disallowed.example';
|
|
|
84
89
|
* oversized message closing with `WS_CLOSE_MESSAGE_TOO_BIG`, anonymous-upgrade
|
|
85
90
|
* refusal, disallowed-origin refusal, and — when `rpc_path` is supplied —
|
|
86
91
|
* session revocation and account deletion closing the live socket, plus a
|
|
87
|
-
* self-revoking socket reading its reply before its close
|
|
92
|
+
* self-revoking socket reading its reply before its close, and a WS mutation's
|
|
93
|
+
* audit row read back after `_testing_drain_effects`.
|
|
88
94
|
*/
|
|
89
95
|
export const describe_cross_process_ws_tests = (options) => {
|
|
90
96
|
const { setup_test, capabilities, base_url, ws_path, origin, rpc_path, max_message_bytes = DEFAULT_WS_MAX_MESSAGE_BYTES } = options;
|
|
@@ -308,5 +314,35 @@ export const describe_cross_process_ws_tests = (options) => {
|
|
|
308
314
|
await client.close();
|
|
309
315
|
}
|
|
310
316
|
});
|
|
317
|
+
// A WS mutation is answered before its effects flush, so its audit row
|
|
318
|
+
// can land after the reply. `_testing_drain_effects` is the barrier: once
|
|
319
|
+
// it returns, the row is visible on either spine.
|
|
320
|
+
test_if(capabilities.ws && capabilities.ws_account_actions && rpc_path !== undefined, "a WS mutation's audit row is visible after _testing_drain_effects", async () => {
|
|
321
|
+
const fixture = await setup_test();
|
|
322
|
+
const client = await create_ws_transport({
|
|
323
|
+
base_url,
|
|
324
|
+
ws_path,
|
|
325
|
+
cookies: fixture.transport.cookies(),
|
|
326
|
+
origin
|
|
327
|
+
});
|
|
328
|
+
try {
|
|
329
|
+
await client.request(1, account_token_create_action_spec.method, {
|
|
330
|
+
name: 'ws drain probe',
|
|
331
|
+
scope: { kind: 'full' },
|
|
332
|
+
lifetime: { kind: 'eternal' }
|
|
333
|
+
});
|
|
334
|
+
}
|
|
335
|
+
finally {
|
|
336
|
+
await client.close();
|
|
337
|
+
}
|
|
338
|
+
const drained = await cross_rpc_call(fixture.fresh_transport({ origin: null }), rpc_path, '_testing_drain_effects', undefined, fixture.create_daemon_token_headers());
|
|
339
|
+
assert.ok(drained.ok, `_testing_drain_effects failed: ${JSON.stringify(drained.error)}`);
|
|
340
|
+
// `_testing_reset` wiped audit_log at setup, so the mint is the only
|
|
341
|
+
// token_create event
|
|
342
|
+
const listed = expect_output(await cross_rpc_call(fixture.transport, rpc_path, audit_log_list_action_spec.method, { event_type: 'token_create' }, fixture.create_session_headers()), AuditLogListOutput);
|
|
343
|
+
assert.strictEqual(listed.events.length, 1, JSON.stringify(listed.events));
|
|
344
|
+
assert.strictEqual(listed.events[0].outcome, 'success');
|
|
345
|
+
assert.strictEqual(listed.events[0].account_id, fixture.account.id);
|
|
346
|
+
});
|
|
311
347
|
});
|
|
312
348
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAE,EAAE,EAA0B,MAAM,QAAQ,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,6 +12,7 @@ import { vi, test, assert, describe } from 'vitest';
|
|
|
12
12
|
import { Hono } from 'hono';
|
|
13
13
|
import { Logger } from '@fuzdev/fuz_util/log.ts';
|
|
14
14
|
import { create_bearer_auth_middleware } from '../auth/bearer_auth.js';
|
|
15
|
+
import { AUTH_MIDDLEWARE_PATH } from '../auth/middleware.js';
|
|
15
16
|
import { query_validate_api_token } from '../auth/api_token_queries.js';
|
|
16
17
|
import { query_account_by_id, query_actor_by_id, query_active_actors_by_account } from '../auth/account_queries.js';
|
|
17
18
|
import { query_role_grant_find_active_for_actor } from '../auth/role_grant_queries.js';
|
|
@@ -117,7 +118,7 @@ export const create_bearer_auth_test_app = (tc) => {
|
|
|
117
118
|
c.set('client_ip', TEST_CLIENT_IP);
|
|
118
119
|
await next();
|
|
119
120
|
});
|
|
120
|
-
app.use(
|
|
121
|
+
app.use(AUTH_MIDDLEWARE_PATH, bearer_middleware);
|
|
121
122
|
// route handler echoes the account-grain identity the middleware writes
|
|
122
123
|
// (bearer auth sets `ACCOUNT_ID_KEY` + `CREDENTIAL_TYPE_KEY` +
|
|
123
124
|
// `AUTH_API_TOKEN_ID_KEY`; it never builds the full request context —
|
|
@@ -230,8 +231,8 @@ export const create_test_middleware_stack_app = (options) => {
|
|
|
230
231
|
const bearer_mw = create_bearer_auth_middleware(STUB_DEPS, new Logger('test', { level: 'off' }));
|
|
231
232
|
const app = new Hono();
|
|
232
233
|
app.use('*', proxy_mw);
|
|
233
|
-
app.use(
|
|
234
|
-
app.use(
|
|
234
|
+
app.use(AUTH_MIDDLEWARE_PATH, origin_mw);
|
|
235
|
+
app.use(AUTH_MIDDLEWARE_PATH, bearer_mw);
|
|
235
236
|
// echo route for assertions — exposes the account-grain identity bearer
|
|
236
237
|
// auth writes (`ACCOUNT_ID_KEY`); the full request context is the
|
|
237
238
|
// dispatcher's authorization phase concern, not middleware.
|
package/dist/testing/stubs.d.ts
CHANGED
|
@@ -62,7 +62,8 @@ export declare const stub_mw: (_c: any, next: any) => Promise<void>;
|
|
|
62
62
|
* immediately; `notify` is a no-op; `add_listener` throws, so a test that
|
|
63
63
|
* wires a listener fails loudly instead of silently never firing
|
|
64
64
|
* (`create_recording_audit_emitter` accepts listeners); `listener_count`
|
|
65
|
-
* returns 0
|
|
65
|
+
* returns 0; `drain_inflight` resolves immediately, since nothing is ever
|
|
66
|
+
* written. Tests asserting on real audit-row persistence (or on listener
|
|
66
67
|
* fan-out) build a real emitter via `create_audit_emitter` against a stub or
|
|
67
68
|
* real DB — `create_test_app` already does this on the test backend.
|
|
68
69
|
*/
|
|
@@ -84,7 +85,11 @@ export declare const stub_app_deps: AppDeps;
|
|
|
84
85
|
* Create no-op `AppDeps` for auth surface testing.
|
|
85
86
|
*/
|
|
86
87
|
export declare const create_stub_app_deps: () => AppDeps;
|
|
87
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* Create the API middleware stub array matching `create_auth_middleware_specs`
|
|
90
|
+
* output. Like the real factory's, each spec gets its own pass-through handler,
|
|
91
|
+
* so `assert_middleware_stack_mounted` can tell the layers apart by identity.
|
|
92
|
+
*/
|
|
88
93
|
export declare const create_stub_api_middleware: (options?: {
|
|
89
94
|
/** Include the daemon_token middleware layer. */
|
|
90
95
|
include_daemon_token?: boolean;
|
|
@@ -132,10 +137,19 @@ export interface CreateTestAppSurfaceSpecOptions {
|
|
|
132
137
|
* The server's origin allowlist — `AppServerOptions.allowed_origins`.
|
|
133
138
|
* Read only as the default for a `ws_endpoints` spec that declares no
|
|
134
139
|
* `allowed_origins`, as `create_app_server` does; required when any spec
|
|
135
|
-
* omits its own,
|
|
140
|
+
* omits its own — without it, such a spec throws, since the surface records
|
|
141
|
+
* the exact patterns the upgrade gate matches.
|
|
136
142
|
*/
|
|
137
143
|
allowed_origins?: ReadonlyArray<RegExp>;
|
|
138
|
-
/**
|
|
144
|
+
/**
|
|
145
|
+
* Transform middleware array — `AppServerOptions.transform_middleware`.
|
|
146
|
+
* Receives shallow copies of a stub `trusted_proxy` spec (at `'*'`)
|
|
147
|
+
* followed by the stub auth stack, in a fresh array, as
|
|
148
|
+
* `create_app_server` passes the real ones. Throws, like
|
|
149
|
+
* `create_app_server`, if the result doesn't keep each of those specs
|
|
150
|
+
* mounted once at its original path in its original order
|
|
151
|
+
* (`assert_middleware_stack_mounted`).
|
|
152
|
+
*/
|
|
139
153
|
transform_middleware?: (specs: Array<MiddlewareSpec>) => Array<MiddlewareSpec>;
|
|
140
154
|
/**
|
|
141
155
|
* Bootstrap config — symmetric with `AppServerOptions.bootstrap`. Discriminated
|
|
@@ -161,13 +175,19 @@ export interface CreateTestAppSurfaceSpecOptions {
|
|
|
161
175
|
* Mirrors `create_app_server`'s route assembly: consumer routes +
|
|
162
176
|
* factory-managed bootstrap routes + surface generation — including its
|
|
163
177
|
* refusal of an `rpc_endpoints` or `ws_endpoints` path outside the auth
|
|
164
|
-
* middleware's scope (`assert_endpoint_in_auth_scope`)
|
|
178
|
+
* middleware's scope (`assert_endpoint_in_auth_scope`) and of a
|
|
179
|
+
* `transform_middleware` that doesn't keep the proxy and auth stack mounted
|
|
180
|
+
* (`assert_middleware_stack_mounted`). The middleware list mirrors the real
|
|
181
|
+
* one: a stub `trusted_proxy` spec at `'*'`, then the stub auth stack. If
|
|
165
182
|
* `create_app_server` changes how it wires routes, update this helper
|
|
166
183
|
* to stay in sync (single source of truth for all consumers).
|
|
167
184
|
*
|
|
168
185
|
* @param options - surface spec options
|
|
169
186
|
* @returns the surface spec for the standard suites
|
|
170
|
-
* @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH
|
|
187
|
+
* @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`, a
|
|
188
|
+
* `ws_endpoints` spec declares no `allowed_origins` and no `allowed_origins`
|
|
189
|
+
* option is given, or `transform_middleware` moves, drops, duplicates,
|
|
190
|
+
* reorders, wraps, or replaces the stub proxy spec or a stub auth spec
|
|
171
191
|
*/
|
|
172
192
|
export declare const create_test_app_surface_spec: (options: CreateTestAppSurfaceSpecOptions) => AppSurfaceSpec;
|
|
173
193
|
//# sourceMappingURL=stubs.d.ts.map
|
|
@@ -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;AAE7D,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yBAAyB,CAAC;AACtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,iCAAiC,CAAC;AACxE,OAAO,EAAE,EAAE,EAAE,MAAM,aAAa,CAAC;AACjC,OAAO,EAAsB,KAAK,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAG3E,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,
|
|
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
|
@@ -19,7 +19,7 @@ import { resolve_ws_endpoints } from '../actions/ws_endpoint_spec.js';
|
|
|
19
19
|
import { AUDIT_LOG_SSE_MAX_PER_SCOPE } from '../realtime/sse_auth_guard.js';
|
|
20
20
|
import { SubscriberRegistry } from '../realtime/subscriber_registry.js';
|
|
21
21
|
import { BaseServerEnv } from '../server/env.js';
|
|
22
|
-
import { AUTH_MIDDLEWARE_PATH, assert_endpoint_in_auth_scope } from '../auth/middleware.js';
|
|
22
|
+
import { AUTH_MIDDLEWARE_PATH, assert_endpoint_in_auth_scope, assert_middleware_stack_mounted } from '../auth/middleware.js';
|
|
23
23
|
/**
|
|
24
24
|
* Create a Proxy that throws descriptive errors on any property access or method call.
|
|
25
25
|
*
|
|
@@ -99,7 +99,8 @@ const stub_db = create_noop_stub('stub_db');
|
|
|
99
99
|
* immediately; `notify` is a no-op; `add_listener` throws, so a test that
|
|
100
100
|
* wires a listener fails loudly instead of silently never firing
|
|
101
101
|
* (`create_recording_audit_emitter` accepts listeners); `listener_count`
|
|
102
|
-
* returns 0
|
|
102
|
+
* returns 0; `drain_inflight` resolves immediately, since nothing is ever
|
|
103
|
+
* written. Tests asserting on real audit-row persistence (or on listener
|
|
103
104
|
* fan-out) build a real emitter via `create_audit_emitter` against a stub or
|
|
104
105
|
* real DB — `create_test_app` already does this on the test backend.
|
|
105
106
|
*/
|
|
@@ -111,7 +112,8 @@ export const create_test_audit_emitter = () => ({
|
|
|
111
112
|
add_listener: () => {
|
|
112
113
|
throw new Error('create_test_audit_emitter accepts no listeners — use create_recording_audit_emitter');
|
|
113
114
|
},
|
|
114
|
-
listener_count: () => 0
|
|
115
|
+
listener_count: () => 0,
|
|
116
|
+
drain_inflight: async () => { }
|
|
115
117
|
});
|
|
116
118
|
/**
|
|
117
119
|
* Build a no-op `AuditLogSse` for tests that wire `audit_sse` into the
|
|
@@ -159,16 +161,27 @@ export const create_stub_app_deps = () => ({
|
|
|
159
161
|
audit: create_test_audit_emitter(),
|
|
160
162
|
connection_closer: create_realtime_closer()
|
|
161
163
|
});
|
|
162
|
-
/**
|
|
164
|
+
/** A fresh pass-through middleware — a distinct handler per call, unlike the shared `stub_mw`. */
|
|
165
|
+
const create_stub_mw = () => async (_c, next) => next();
|
|
166
|
+
/**
|
|
167
|
+
* Create the API middleware stub array matching `create_auth_middleware_specs`
|
|
168
|
+
* output. Like the real factory's, each spec gets its own pass-through handler,
|
|
169
|
+
* so `assert_middleware_stack_mounted` can tell the layers apart by identity.
|
|
170
|
+
*/
|
|
163
171
|
export const create_stub_api_middleware = (options) => {
|
|
164
172
|
const specs = [
|
|
165
|
-
{
|
|
166
|
-
|
|
167
|
-
|
|
173
|
+
{
|
|
174
|
+
name: 'origin',
|
|
175
|
+
path: AUTH_MIDDLEWARE_PATH,
|
|
176
|
+
handler: create_stub_mw(),
|
|
177
|
+
errors: { 403: ApiError }
|
|
178
|
+
},
|
|
179
|
+
{ name: 'session', path: AUTH_MIDDLEWARE_PATH, handler: create_stub_mw() },
|
|
180
|
+
{ name: 'request_context', path: AUTH_MIDDLEWARE_PATH, handler: create_stub_mw() },
|
|
168
181
|
{
|
|
169
182
|
name: 'bearer_auth',
|
|
170
183
|
path: AUTH_MIDDLEWARE_PATH,
|
|
171
|
-
handler:
|
|
184
|
+
handler: create_stub_mw(),
|
|
172
185
|
errors: { 401: ApiError, 403: ApiError, 429: RateLimitError }
|
|
173
186
|
}
|
|
174
187
|
];
|
|
@@ -176,7 +189,7 @@ export const create_stub_api_middleware = (options) => {
|
|
|
176
189
|
specs.push({
|
|
177
190
|
name: 'daemon_token',
|
|
178
191
|
path: AUTH_MIDDLEWARE_PATH,
|
|
179
|
-
handler:
|
|
192
|
+
handler: create_stub_mw(),
|
|
180
193
|
errors: { 401: ApiError, 500: ApiError, 503: ApiError }
|
|
181
194
|
});
|
|
182
195
|
}
|
|
@@ -213,6 +226,21 @@ export const create_stub_app_server_context = (session_options) => {
|
|
|
213
226
|
audit_sse: null
|
|
214
227
|
};
|
|
215
228
|
};
|
|
229
|
+
/**
|
|
230
|
+
* Narrow a WS endpoint spec that carries its own `allowed_origins` — the only
|
|
231
|
+
* way it resolves when no server list is given to default from.
|
|
232
|
+
*
|
|
233
|
+
* @throws Error when the spec declares no `allowed_origins`
|
|
234
|
+
*/
|
|
235
|
+
const to_self_resolved_ws_endpoint = (endpoint) => {
|
|
236
|
+
const { allowed_origins } = endpoint;
|
|
237
|
+
if (!allowed_origins) {
|
|
238
|
+
throw new Error(`create_test_app_surface_spec: ws endpoint ${endpoint.path} has no allowed_origins — ` +
|
|
239
|
+
"pass the server's list as create_test_app_surface_spec's allowed_origins option, " +
|
|
240
|
+
'or set allowed_origins on the spec');
|
|
241
|
+
}
|
|
242
|
+
return { ...endpoint, allowed_origins };
|
|
243
|
+
};
|
|
216
244
|
/**
|
|
217
245
|
* Create an `AppSurfaceSpec` for the standard testing suites.
|
|
218
246
|
*
|
|
@@ -226,13 +254,19 @@ export const create_stub_app_server_context = (session_options) => {
|
|
|
226
254
|
* Mirrors `create_app_server`'s route assembly: consumer routes +
|
|
227
255
|
* factory-managed bootstrap routes + surface generation — including its
|
|
228
256
|
* refusal of an `rpc_endpoints` or `ws_endpoints` path outside the auth
|
|
229
|
-
* middleware's scope (`assert_endpoint_in_auth_scope`)
|
|
257
|
+
* middleware's scope (`assert_endpoint_in_auth_scope`) and of a
|
|
258
|
+
* `transform_middleware` that doesn't keep the proxy and auth stack mounted
|
|
259
|
+
* (`assert_middleware_stack_mounted`). The middleware list mirrors the real
|
|
260
|
+
* one: a stub `trusted_proxy` spec at `'*'`, then the stub auth stack. If
|
|
230
261
|
* `create_app_server` changes how it wires routes, update this helper
|
|
231
262
|
* to stay in sync (single source of truth for all consumers).
|
|
232
263
|
*
|
|
233
264
|
* @param options - surface spec options
|
|
234
265
|
* @returns the surface spec for the standard suites
|
|
235
|
-
* @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH
|
|
266
|
+
* @throws Error when an endpoint path is outside `AUTH_MIDDLEWARE_PATH`, a
|
|
267
|
+
* `ws_endpoints` spec declares no `allowed_origins` and no `allowed_origins`
|
|
268
|
+
* option is given, or `transform_middleware` moves, drops, duplicates,
|
|
269
|
+
* reorders, wraps, or replaces the stub proxy spec or a stub auth spec
|
|
236
270
|
*/
|
|
237
271
|
export const create_test_app_surface_spec = (options) => {
|
|
238
272
|
const ctx = create_stub_app_server_context(options.session_options);
|
|
@@ -255,11 +289,9 @@ export const create_test_app_surface_spec = (options) => {
|
|
|
255
289
|
// Resolve ws endpoints (mirrors create_app_server). Surface-only —
|
|
256
290
|
// no `register_ws_endpoint` call here, so no `create_upgrade_websocket` needed.
|
|
257
291
|
const declared_ws_endpoints = typeof options.ws_endpoints === 'function' ? options.ws_endpoints(ctx) : options.ws_endpoints;
|
|
258
|
-
// without the server's list, an origin-less spec reaches surface
|
|
259
|
-
// generation unresolved and throws there
|
|
260
292
|
const resolved_ws_endpoints = options.allowed_origins
|
|
261
293
|
? resolve_ws_endpoints(declared_ws_endpoints, options.allowed_origins)
|
|
262
|
-
: declared_ws_endpoints;
|
|
294
|
+
: declared_ws_endpoints?.map(to_self_resolved_ws_endpoint);
|
|
263
295
|
for (const endpoint of resolved_ws_endpoints ?? []) {
|
|
264
296
|
assert_endpoint_in_auth_scope('ws_endpoints', endpoint.path);
|
|
265
297
|
}
|
|
@@ -277,9 +309,17 @@ export const create_test_app_surface_spec = (options) => {
|
|
|
277
309
|
])
|
|
278
310
|
: [];
|
|
279
311
|
const route_specs = [...consumer_routes, ...rpc_route_specs, ...bootstrap_route_specs];
|
|
280
|
-
|
|
312
|
+
// mirrors `create_app_server`'s `[proxy_spec, ...auth_middleware]`
|
|
313
|
+
const stack_specs = [
|
|
314
|
+
{ name: 'trusted_proxy', path: '*', handler: create_stub_mw() },
|
|
315
|
+
...create_stub_api_middleware()
|
|
316
|
+
];
|
|
317
|
+
let middleware_specs = stack_specs;
|
|
281
318
|
if (options.transform_middleware) {
|
|
282
|
-
|
|
319
|
+
// the transform gets copies in a fresh array, so mutating what it was
|
|
320
|
+
// handed can't rewrite the originals the check matches against
|
|
321
|
+
middleware_specs = options.transform_middleware(stack_specs.map((spec) => ({ ...spec })));
|
|
322
|
+
assert_middleware_stack_mounted(stack_specs, middleware_specs);
|
|
283
323
|
}
|
|
284
324
|
return create_app_surface_spec({
|
|
285
325
|
middleware_specs,
|