@fuzdev/fuz_app 0.65.0 → 0.67.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 +65 -86
- package/dist/actions/action_codegen.d.ts +1 -1
- package/dist/actions/action_codegen.js +1 -1
- package/dist/actions/action_event_data.d.ts +1 -1
- package/dist/auth/CLAUDE.md +83 -104
- package/dist/auth/audit_log_schema.js +2 -2
- package/dist/auth/daemon_token_middleware.d.ts +15 -5
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +24 -15
- package/dist/auth/invite_queries.d.ts +17 -7
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +19 -8
- package/dist/auth/signup_routes.d.ts +47 -1
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +103 -52
- package/dist/env/resolve.d.ts +44 -7
- package/dist/env/resolve.d.ts.map +1 -1
- package/dist/env/resolve.js +94 -27
- package/dist/http/CLAUDE.md +47 -52
- package/dist/http/jsonrpc.d.ts +23 -7
- package/dist/http/jsonrpc.d.ts.map +1 -1
- package/dist/http/jsonrpc.js +19 -3
- package/dist/http/surface.d.ts +9 -2
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/runtime/mock.d.ts +1 -1
- package/dist/runtime/mock.js +1 -1
- package/dist/testing/CLAUDE.md +659 -511
- package/dist/testing/admin_integration.d.ts +5 -5
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +95 -39
- package/dist/testing/app_server.d.ts +16 -1
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +18 -3
- package/dist/testing/audit_completeness.d.ts +7 -5
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +5 -9
- package/dist/testing/bootstrap_success.js +2 -2
- package/dist/testing/cross_backend/backend_config.d.ts +113 -0
- package/dist/testing/cross_backend/backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/backend_config.js +1 -0
- package/dist/testing/cross_backend/bench/bench_report.d.ts +46 -0
- package/dist/testing/cross_backend/bench/bench_report.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/bench_report.js +83 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts +44 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.js +38 -0
- package/dist/testing/cross_backend/bench/scenario.d.ts +57 -0
- package/dist/testing/cross_backend/bench/scenario.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/scenario.js +28 -0
- package/dist/testing/cross_backend/bootstrap_backend.d.ts +41 -0
- package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -0
- package/dist/testing/cross_backend/bootstrap_backend.js +34 -0
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts +24 -0
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -0
- package/dist/testing/cross_backend/build_test_backend_paths.js +33 -0
- package/dist/testing/cross_backend/capabilities.d.ts +3 -2
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.d.ts +122 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_backend_configs.js +111 -0
- package/dist/testing/cross_backend/default_secrets.d.ts +40 -0
- package/dist/testing/cross_backend/default_secrets.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_secrets.js +39 -0
- package/dist/testing/cross_backend/default_spine_surface.d.ts +64 -0
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_spine_surface.js +121 -0
- package/dist/testing/cross_backend/setup.d.ts +270 -34
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +495 -15
- package/dist/testing/cross_backend/spawn_backend.d.ts +58 -0
- package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -0
- package/dist/testing/cross_backend/spawn_backend.js +229 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.d.ts +66 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.js +49 -0
- package/dist/testing/cross_backend/sse_round_trip.d.ts +37 -0
- package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -0
- package/dist/testing/cross_backend/sse_round_trip.js +137 -0
- package/dist/testing/cross_backend/standard.d.ts +96 -0
- package/dist/testing/cross_backend/standard.d.ts.map +1 -0
- package/dist/testing/cross_backend/standard.js +49 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +171 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_reset_actions.js +213 -0
- package/dist/testing/cross_backend/testing_server_bun.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_bun.js +59 -0
- package/dist/testing/cross_backend/testing_server_core.d.ts +140 -0
- package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_core.js +68 -0
- package/dist/testing/cross_backend/testing_server_deno.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_deno.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_deno.js +37 -0
- package/dist/testing/cross_backend/testing_server_node.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_node.js +50 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +72 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.js +112 -0
- package/dist/testing/cross_backend/ws_round_trip.d.ts +35 -0
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_round_trip.js +113 -0
- package/dist/testing/data_exposure.d.ts +4 -6
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +1 -5
- package/dist/testing/db_entities.d.ts +18 -7
- package/dist/testing/db_entities.d.ts.map +1 -1
- package/dist/testing/db_entities.js +18 -7
- package/dist/testing/integration.d.ts +27 -6
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +93 -58
- package/dist/testing/round_trip.d.ts +4 -5
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +1 -5
- package/dist/testing/rpc_helpers.d.ts +10 -4
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +1 -1
- package/dist/testing/rpc_round_trip.d.ts +5 -5
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +1 -5
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +1 -68
- package/dist/testing/standard.d.ts +4 -5
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/stubs.d.ts +10 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +9 -2
- package/dist/testing/testing_rate_limiter.d.ts +59 -0
- package/dist/testing/testing_rate_limiter.d.ts.map +1 -0
- package/dist/testing/testing_rate_limiter.js +74 -0
- package/dist/testing/transports/bootstrap.d.ts +52 -0
- package/dist/testing/transports/bootstrap.d.ts.map +1 -0
- package/dist/testing/transports/bootstrap.js +70 -0
- package/dist/testing/transports/fetch_transport.d.ts +81 -0
- package/dist/testing/transports/fetch_transport.d.ts.map +1 -0
- package/dist/testing/transports/fetch_transport.js +74 -0
- package/dist/testing/transports/sse_frame_reader.d.ts +41 -0
- package/dist/testing/transports/sse_frame_reader.d.ts.map +1 -0
- package/dist/testing/transports/sse_frame_reader.js +84 -0
- package/dist/testing/transports/sse_transport.d.ts +54 -0
- package/dist/testing/transports/sse_transport.d.ts.map +1 -0
- package/dist/testing/transports/sse_transport.js +51 -0
- package/dist/testing/transports/ws_client.d.ts +108 -0
- package/dist/testing/transports/ws_client.d.ts.map +1 -0
- package/dist/testing/transports/ws_client.js +56 -0
- package/dist/testing/transports/ws_transport.d.ts +43 -0
- package/dist/testing/transports/ws_transport.d.ts.map +1 -0
- package/dist/testing/transports/ws_transport.js +169 -0
- package/dist/testing/ws_round_trip.d.ts +21 -103
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +42 -40
- package/dist/ui/CLAUDE.md +5 -3
- package/dist/ui/MenuLink.svelte +16 -16
- package/dist/ui/MenuLink.svelte.d.ts +13 -4
- package/dist/ui/MenuLink.svelte.d.ts.map +1 -1
- package/package.json +20 -3
- package/dist/testing/transports/surface_source.d.ts +0 -51
- package/dist/testing/transports/surface_source.d.ts.map +0 -1
- package/dist/testing/transports/surface_source.js +0 -19
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
3
|
+
import { write_daemon_info, read_daemon_info, is_daemon_running } from '../../cli/daemon.js';
|
|
4
|
+
/** Hosts that expose the test binary beyond loopback — refused at startup. */
|
|
5
|
+
const OPEN_HOSTS = new Set(['0.0.0.0', '::', '[::]']);
|
|
6
|
+
/**
|
|
7
|
+
* Boot a test-mode server using the supplied runtime adapter.
|
|
8
|
+
*
|
|
9
|
+
* Mirrors a production `start_server` at the surface level — stale-daemon
|
|
10
|
+
* check, daemon-info write, bind, graceful drain — but the app is the
|
|
11
|
+
* caller's no-domain (or domain) {@link StartTestingServerOptions.build_app}
|
|
12
|
+
* and the runtime boundary is the {@link TestingServerAdapter}. Refuses to
|
|
13
|
+
* bind an open host (the test binary must stay on loopback).
|
|
14
|
+
*/
|
|
15
|
+
export const start_testing_server = async (options) => {
|
|
16
|
+
const { adapter, daemon_name, host, port, app_version, build_app } = options;
|
|
17
|
+
const log = options.log ?? new Logger(`[${daemon_name}]`);
|
|
18
|
+
const { runtime } = adapter;
|
|
19
|
+
if (OPEN_HOSTS.has(host)) {
|
|
20
|
+
log.error(`FATAL: binding to '${host}' exposes the test binary to your entire network. ` +
|
|
21
|
+
`Use --host localhost (default) or 127.0.0.1 instead.`);
|
|
22
|
+
adapter.exit(1);
|
|
23
|
+
}
|
|
24
|
+
const stale = await read_daemon_info(runtime, daemon_name);
|
|
25
|
+
if (stale) {
|
|
26
|
+
if (await is_daemon_running(runtime, stale.pid)) {
|
|
27
|
+
log.warn('found running server', stale);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
log.warn(`stale daemon.json (pid ${stale.pid} not running), replacing`);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
const built = await build_app();
|
|
34
|
+
let ws;
|
|
35
|
+
if (built.mount_websocket) {
|
|
36
|
+
ws = adapter.prepare_websocket(built.app);
|
|
37
|
+
built.mount_websocket(ws.upgrade_websocket);
|
|
38
|
+
}
|
|
39
|
+
await write_daemon_info(runtime, daemon_name, {
|
|
40
|
+
version: 1,
|
|
41
|
+
pid: adapter.pid,
|
|
42
|
+
port,
|
|
43
|
+
started: new Date().toISOString(),
|
|
44
|
+
app_version: app_version ?? '0.0.0-test',
|
|
45
|
+
});
|
|
46
|
+
log.info(`Listening on http://${host}:${port} (${adapter.runtime_label}, test mode)`);
|
|
47
|
+
const server = adapter.serve({ fetch: built.app.fetch, port, hostname: host });
|
|
48
|
+
ws?.attach_to_server?.(server);
|
|
49
|
+
let shutting_down = false;
|
|
50
|
+
const shutdown = async () => {
|
|
51
|
+
if (shutting_down)
|
|
52
|
+
adapter.exit(1);
|
|
53
|
+
shutting_down = true;
|
|
54
|
+
log.info('shutting down...');
|
|
55
|
+
try {
|
|
56
|
+
// Drain HTTP first (stop accepting + let in-flight requests finish)
|
|
57
|
+
// before tearing down the backend, so a request still draining
|
|
58
|
+
// never hits a closed DB.
|
|
59
|
+
await server.shutdown();
|
|
60
|
+
await built.close();
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
log.error('shutdown error:', error);
|
|
64
|
+
}
|
|
65
|
+
adapter.exit(0);
|
|
66
|
+
};
|
|
67
|
+
adapter.register_shutdown_signals(shutdown);
|
|
68
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { TestingServerAdapter } from './testing_server_core.js';
|
|
3
|
+
/** Build the Deno {@link TestingServerAdapter}. */
|
|
4
|
+
export declare const create_deno_testing_adapter: () => TestingServerAdapter;
|
|
5
|
+
//# sourceMappingURL=testing_server_deno.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing_server_deno.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_deno.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAqB9B,OAAO,KAAK,EAAc,oBAAoB,EAAC,MAAM,0BAA0B,CAAC;AAehF,mDAAmD;AACnD,eAAO,MAAM,2BAA2B,QAAO,oBAkB7C,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Deno runtime adapter for spawnable cross-process test server binaries.
|
|
4
|
+
*
|
|
5
|
+
* Binds `Deno.serve` and `hono/deno`'s module-level `upgradeWebSocket`. The
|
|
6
|
+
* shared `testing_server_core.ts` owns the rest. Counterpart to
|
|
7
|
+
* `testing_server_node.ts` — together they isolate the JS-runtime axis (Deno
|
|
8
|
+
* vs Node V8) on identical TS surfaces, and the Rust spine binary covers the
|
|
9
|
+
* cross-language axis.
|
|
10
|
+
*
|
|
11
|
+
* `Deno` globals are declared locally (mirroring `runtime/deno.ts`) so this
|
|
12
|
+
* module typechecks under fuz_app's Node-based config without a `deno.json`
|
|
13
|
+
* or `@types/deno`. It is only ever *run* under Deno.
|
|
14
|
+
*
|
|
15
|
+
* @module
|
|
16
|
+
*/
|
|
17
|
+
import { upgradeWebSocket } from 'hono/deno';
|
|
18
|
+
import { create_deno_runtime } from '../../runtime/deno.js';
|
|
19
|
+
/** Build the Deno {@link TestingServerAdapter}. */
|
|
20
|
+
export const create_deno_testing_adapter = () => ({
|
|
21
|
+
runtime_label: 'Deno',
|
|
22
|
+
runtime: create_deno_runtime([]),
|
|
23
|
+
get_connection_ip: (c) => c.env?.remoteAddr?.hostname,
|
|
24
|
+
// Deno's WS upgrade is module-level and stateless — no post-serve attach.
|
|
25
|
+
prepare_websocket: () => ({ upgrade_websocket: upgradeWebSocket }),
|
|
26
|
+
serve: ({ fetch, port, hostname }) => {
|
|
27
|
+
const server = Deno.serve({ port, hostname }, fetch);
|
|
28
|
+
const handle = { shutdown: () => server.shutdown(), native: server };
|
|
29
|
+
return handle;
|
|
30
|
+
},
|
|
31
|
+
pid: Deno.pid,
|
|
32
|
+
register_shutdown_signals: (handler) => {
|
|
33
|
+
Deno.addSignalListener('SIGINT', () => void handler());
|
|
34
|
+
Deno.addSignalListener('SIGTERM', () => void handler());
|
|
35
|
+
},
|
|
36
|
+
exit: (code) => Deno.exit(code),
|
|
37
|
+
});
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { TestingServerAdapter } from './testing_server_core.js';
|
|
3
|
+
/** Build the Node {@link TestingServerAdapter}. */
|
|
4
|
+
export declare const create_node_testing_adapter: () => TestingServerAdapter;
|
|
5
|
+
//# sourceMappingURL=testing_server_node.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing_server_node.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_node.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAuB9B,OAAO,KAAK,EAAc,oBAAoB,EAAC,MAAM,0BAA0B,CAAC;AAUhF,mDAAmD;AACnD,eAAO,MAAM,2BAA2B,QAAO,oBAsB7C,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Node runtime adapter for spawnable cross-process test server binaries.
|
|
4
|
+
*
|
|
5
|
+
* Binds `@hono/node-server`'s `serve()` and `@hono/node-ws`'s two-phase
|
|
6
|
+
* `createNodeWebSocket(app)` / `injectWebSocket(server)`. The shared
|
|
7
|
+
* `testing_server_core.ts` owns the rest. A test binary builds this adapter
|
|
8
|
+
* and hands it to `start_testing_server` alongside its `build_app` seam.
|
|
9
|
+
*
|
|
10
|
+
* `@hono/node-server` + `@hono/node-ws` are **optional** peer deps (same
|
|
11
|
+
* posture as `ws`) — only test binaries import them; production bundles
|
|
12
|
+
* never reach this module (the `assert_dev_env` guard throws on prod load).
|
|
13
|
+
*
|
|
14
|
+
* @module
|
|
15
|
+
*/
|
|
16
|
+
import process from 'node:process';
|
|
17
|
+
import { serve } from '@hono/node-server';
|
|
18
|
+
import { getConnInfo } from '@hono/node-server/conninfo';
|
|
19
|
+
import { createNodeWebSocket } from '@hono/node-ws';
|
|
20
|
+
import { create_node_runtime } from '../../runtime/node.js';
|
|
21
|
+
const node_serve_handle = (server) => ({
|
|
22
|
+
shutdown: () => new Promise((resolve, reject) => {
|
|
23
|
+
server.close((err) => (err ? reject(err) : resolve()));
|
|
24
|
+
}),
|
|
25
|
+
native: server,
|
|
26
|
+
});
|
|
27
|
+
/** Build the Node {@link TestingServerAdapter}. */
|
|
28
|
+
export const create_node_testing_adapter = () => ({
|
|
29
|
+
runtime_label: 'Node',
|
|
30
|
+
runtime: create_node_runtime(),
|
|
31
|
+
get_connection_ip: (c) => getConnInfo(c).remote.address,
|
|
32
|
+
prepare_websocket: (app) => {
|
|
33
|
+
const { upgradeWebSocket, injectWebSocket } = createNodeWebSocket({ app });
|
|
34
|
+
return {
|
|
35
|
+
upgrade_websocket: upgradeWebSocket,
|
|
36
|
+
attach_to_server: (handle) => {
|
|
37
|
+
// `handle.native` is the `ServerType` from `serve()` —
|
|
38
|
+
// type-erased at the {@link ServeHandle} seam, so it downcasts here.
|
|
39
|
+
injectWebSocket(handle.native);
|
|
40
|
+
},
|
|
41
|
+
};
|
|
42
|
+
},
|
|
43
|
+
serve: ({ fetch, port, hostname }) => node_serve_handle(serve({ fetch, port, hostname })),
|
|
44
|
+
pid: process.pid,
|
|
45
|
+
register_shutdown_signals: (handler) => {
|
|
46
|
+
process.on('SIGINT', () => void handler());
|
|
47
|
+
process.on('SIGTERM', () => void handler());
|
|
48
|
+
},
|
|
49
|
+
exit: (code) => process.exit(code),
|
|
50
|
+
});
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-process `BackendConfig` presets for fuz_app's domain-free **TS**
|
|
4
|
+
* spine test binary (`src/test/cross_backend/testing_spine_server_{node,deno,bun}.ts`).
|
|
5
|
+
*
|
|
6
|
+
* The TS analog of `spine_stub_backend_config` (which spawns the Rust spine):
|
|
7
|
+
* these spawn fuz_app's own TS impl over real HTTP with no domain layer, so
|
|
8
|
+
* the `cross_backend_ts_node` / `cross_backend_ts_deno` / `cross_backend_ts_bun`
|
|
9
|
+
* self-test projects verify fuz_app's wire path in its own repo across all
|
|
10
|
+
* three JS runtimes. All run against in-memory PGlite (`memory://`) — no
|
|
11
|
+
* external Postgres, unlike the Rust path.
|
|
12
|
+
*
|
|
13
|
+
* The binary writes its daemon token to `{FUZ_TESTING_TS_SPINE_DIR}/run/daemon_token`;
|
|
14
|
+
* anchoring the dir to `paths.root` makes that equal `paths.daemon_token_path`,
|
|
15
|
+
* which `spawn_backend` reads after the health probe.
|
|
16
|
+
*
|
|
17
|
+
* @module
|
|
18
|
+
*/
|
|
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
|
+
/**
|
|
23
|
+
* Audit-log SSE stream path the TS spine binary serves (it wires
|
|
24
|
+
* `audit_log_sse`). Matches `SPINE_SSE_PATH` in `default_spine_surface.ts`
|
|
25
|
+
* and the cross-process SSE suite's default. Scoped to the TS configs
|
|
26
|
+
* (which advertise `capabilities.sse: true`) — the Rust spine doesn't serve
|
|
27
|
+
* the stream, so the shared `ts_default_capabilities` stays `sse: false`.
|
|
28
|
+
*/
|
|
29
|
+
export declare const TS_SPINE_SSE_PATH = "/api/admin/audit/stream";
|
|
30
|
+
/** Default port for the Node TS spine binary — slots beside the Rust `spine_stub` (1177). */
|
|
31
|
+
export declare const TS_SPINE_NODE_DEFAULT_PORT = 1178;
|
|
32
|
+
/** Default port for the Deno TS spine binary. */
|
|
33
|
+
export declare const TS_SPINE_DENO_DEFAULT_PORT = 1179;
|
|
34
|
+
/** Default port for the Bun TS spine binary. */
|
|
35
|
+
export declare const TS_SPINE_BUN_DEFAULT_PORT = 1180;
|
|
36
|
+
/** Entry module spawned for the Node TS spine binary. */
|
|
37
|
+
export declare const TS_SPINE_NODE_ENTRY = "src/test/cross_backend/testing_spine_server_node.ts";
|
|
38
|
+
/** Entry module spawned for the Deno TS spine binary. */
|
|
39
|
+
export declare const TS_SPINE_DENO_ENTRY = "src/test/cross_backend/testing_spine_server_deno.ts";
|
|
40
|
+
/** Entry module spawned for the Bun TS spine binary. */
|
|
41
|
+
export declare const TS_SPINE_BUN_ENTRY = "src/test/cross_backend/testing_spine_server_bun.ts";
|
|
42
|
+
export interface TsSpineBackendConfigOptions {
|
|
43
|
+
/** Listening port. Defaults per runtime (`1178` Node, `1179` Deno, `1180` Bun). */
|
|
44
|
+
readonly port?: number;
|
|
45
|
+
/** Database URL. Default `'memory://'` (in-memory PGlite). */
|
|
46
|
+
readonly database_url?: string;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* `BackendConfig` for the Node TS spine binary — spawned via `gro run`
|
|
50
|
+
* (Gro's TS loader resolves the `$lib`-free entry's relative imports).
|
|
51
|
+
*/
|
|
52
|
+
export declare const ts_spine_node_backend_config: (options?: TsSpineBackendConfigOptions) => BackendConfig;
|
|
53
|
+
/**
|
|
54
|
+
* `BackendConfig` for the Bun TS spine binary — spawned via `bun run`. Bun
|
|
55
|
+
* resolves the entry's relative `.js`→`.ts` source specifiers natively (no
|
|
56
|
+
* flag needed — unlike Deno's `--sloppy-imports`, and like Gro's loader on
|
|
57
|
+
* the Node path), and `Bun.serve` + `hono/bun` need no extra deps.
|
|
58
|
+
*/
|
|
59
|
+
export declare const ts_spine_bun_backend_config: (options?: TsSpineBackendConfigOptions) => BackendConfig;
|
|
60
|
+
/**
|
|
61
|
+
* `BackendConfig` for the Deno TS spine binary. The `--allow-*` set mirrors
|
|
62
|
+
* the cross-process needs (net + read/write for the daemon-token file + env
|
|
63
|
+
* + sys); `--unstable-detect-cjs` matches the ecosystem's Deno test entries.
|
|
64
|
+
*
|
|
65
|
+
* `--sloppy-imports` is required because the binary imports fuz_app **source**
|
|
66
|
+
* via relative `.js` specifiers (the `src/lib` convention) — Deno resolves
|
|
67
|
+
* `.js`→`.ts` only under this flag, whereas Gro's loader (the Node path)
|
|
68
|
+
* does so natively. (zzz's Deno entry sidesteps it by importing fuz_app as a
|
|
69
|
+
* built package; this binary tests live source instead.)
|
|
70
|
+
*/
|
|
71
|
+
export declare const ts_spine_deno_backend_config: (options?: TsSpineBackendConfigOptions) => BackendConfig;
|
|
72
|
+
//# sourceMappingURL=ts_spine_backend_config.d.ts.map
|
|
@@ -0,0 +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,EAAC,aAAa,EAAC,MAAM,qBAAqB,CAAC;AAOvD,8GAA8G;AAC9G,eAAO,MAAM,gBAAgB,6BAA6B,CAAC;AAE3D;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,4BAA4B,CAAC;AAK3D,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;CAC/B;AAED;;;GAGG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aAgBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,2BAA2B,GACvC,UAAS,2BAAgC,KACvC,aAgBF,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,UAAS,2BAAgC,KACvC,aA6BF,CAAC"}
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { build_test_backend_paths } from './build_test_backend_paths.js';
|
|
3
|
+
import { make_default_ts_backend_config, ts_default_capabilities, } from './default_backend_configs.js';
|
|
4
|
+
/** Env var naming the backend root dir; `{dir}/run/daemon_token` must match `bootstrap.daemon_token_path`. */
|
|
5
|
+
export const TS_SPINE_DIR_ENV = 'FUZ_TESTING_TS_SPINE_DIR';
|
|
6
|
+
/**
|
|
7
|
+
* Audit-log SSE stream path the TS spine binary serves (it wires
|
|
8
|
+
* `audit_log_sse`). Matches `SPINE_SSE_PATH` in `default_spine_surface.ts`
|
|
9
|
+
* and the cross-process SSE suite's default. Scoped to the TS configs
|
|
10
|
+
* (which advertise `capabilities.sse: true`) — the Rust spine doesn't serve
|
|
11
|
+
* the stream, so the shared `ts_default_capabilities` stays `sse: false`.
|
|
12
|
+
*/
|
|
13
|
+
export const TS_SPINE_SSE_PATH = '/api/admin/audit/stream';
|
|
14
|
+
/** Capabilities for the TS spine binary — `ts_default_capabilities` plus `sse` (the binary wires `audit_log_sse`). */
|
|
15
|
+
const ts_spine_capabilities = Object.freeze({ ...ts_default_capabilities, sse: true });
|
|
16
|
+
/** Default port for the Node TS spine binary — slots beside the Rust `spine_stub` (1177). */
|
|
17
|
+
export const TS_SPINE_NODE_DEFAULT_PORT = 1178;
|
|
18
|
+
/** Default port for the Deno TS spine binary. */
|
|
19
|
+
export const TS_SPINE_DENO_DEFAULT_PORT = 1179;
|
|
20
|
+
/** Default port for the Bun TS spine binary. */
|
|
21
|
+
export const TS_SPINE_BUN_DEFAULT_PORT = 1180;
|
|
22
|
+
/** Entry module spawned for the Node TS spine binary. */
|
|
23
|
+
export const TS_SPINE_NODE_ENTRY = 'src/test/cross_backend/testing_spine_server_node.ts';
|
|
24
|
+
/** Entry module spawned for the Deno TS spine binary. */
|
|
25
|
+
export const TS_SPINE_DENO_ENTRY = 'src/test/cross_backend/testing_spine_server_deno.ts';
|
|
26
|
+
/** Entry module spawned for the Bun TS spine binary. */
|
|
27
|
+
export const TS_SPINE_BUN_ENTRY = 'src/test/cross_backend/testing_spine_server_bun.ts';
|
|
28
|
+
/**
|
|
29
|
+
* `BackendConfig` for the Node TS spine binary — spawned via `gro run`
|
|
30
|
+
* (Gro's TS loader resolves the `$lib`-free entry's relative imports).
|
|
31
|
+
*/
|
|
32
|
+
export const ts_spine_node_backend_config = (options = {}) => {
|
|
33
|
+
const { port = TS_SPINE_NODE_DEFAULT_PORT, database_url } = options;
|
|
34
|
+
const name = 'ts_spine_node';
|
|
35
|
+
const paths = build_test_backend_paths(name);
|
|
36
|
+
return {
|
|
37
|
+
...make_default_ts_backend_config({
|
|
38
|
+
name,
|
|
39
|
+
port,
|
|
40
|
+
start_command: ['gro', 'run', TS_SPINE_NODE_ENTRY],
|
|
41
|
+
database_url,
|
|
42
|
+
paths,
|
|
43
|
+
extra_env: { [TS_SPINE_DIR_ENV]: paths.root },
|
|
44
|
+
capabilities: ts_spine_capabilities,
|
|
45
|
+
}),
|
|
46
|
+
sse_path: TS_SPINE_SSE_PATH,
|
|
47
|
+
};
|
|
48
|
+
};
|
|
49
|
+
/**
|
|
50
|
+
* `BackendConfig` for the Bun TS spine binary — spawned via `bun run`. Bun
|
|
51
|
+
* resolves the entry's relative `.js`→`.ts` source specifiers natively (no
|
|
52
|
+
* flag needed — unlike Deno's `--sloppy-imports`, and like Gro's loader on
|
|
53
|
+
* the Node path), and `Bun.serve` + `hono/bun` need no extra deps.
|
|
54
|
+
*/
|
|
55
|
+
export const ts_spine_bun_backend_config = (options = {}) => {
|
|
56
|
+
const { port = TS_SPINE_BUN_DEFAULT_PORT, database_url } = options;
|
|
57
|
+
const name = 'ts_spine_bun';
|
|
58
|
+
const paths = build_test_backend_paths(name);
|
|
59
|
+
return {
|
|
60
|
+
...make_default_ts_backend_config({
|
|
61
|
+
name,
|
|
62
|
+
port,
|
|
63
|
+
start_command: ['bun', 'run', TS_SPINE_BUN_ENTRY],
|
|
64
|
+
database_url,
|
|
65
|
+
paths,
|
|
66
|
+
extra_env: { [TS_SPINE_DIR_ENV]: paths.root },
|
|
67
|
+
capabilities: ts_spine_capabilities,
|
|
68
|
+
}),
|
|
69
|
+
sse_path: TS_SPINE_SSE_PATH,
|
|
70
|
+
};
|
|
71
|
+
};
|
|
72
|
+
/**
|
|
73
|
+
* `BackendConfig` for the Deno TS spine binary. The `--allow-*` set mirrors
|
|
74
|
+
* the cross-process needs (net + read/write for the daemon-token file + env
|
|
75
|
+
* + sys); `--unstable-detect-cjs` matches the ecosystem's Deno test entries.
|
|
76
|
+
*
|
|
77
|
+
* `--sloppy-imports` is required because the binary imports fuz_app **source**
|
|
78
|
+
* via relative `.js` specifiers (the `src/lib` convention) — Deno resolves
|
|
79
|
+
* `.js`→`.ts` only under this flag, whereas Gro's loader (the Node path)
|
|
80
|
+
* does so natively. (zzz's Deno entry sidesteps it by importing fuz_app as a
|
|
81
|
+
* built package; this binary tests live source instead.)
|
|
82
|
+
*/
|
|
83
|
+
export const ts_spine_deno_backend_config = (options = {}) => {
|
|
84
|
+
const { port = TS_SPINE_DENO_DEFAULT_PORT, database_url } = options;
|
|
85
|
+
const name = 'ts_spine_deno';
|
|
86
|
+
const paths = build_test_backend_paths(name);
|
|
87
|
+
return {
|
|
88
|
+
...make_default_ts_backend_config({
|
|
89
|
+
name,
|
|
90
|
+
port,
|
|
91
|
+
start_command: [
|
|
92
|
+
'deno',
|
|
93
|
+
'run',
|
|
94
|
+
'--allow-net',
|
|
95
|
+
'--allow-read',
|
|
96
|
+
'--allow-env',
|
|
97
|
+
'--allow-write',
|
|
98
|
+
'--allow-sys',
|
|
99
|
+
'--allow-ffi',
|
|
100
|
+
'--allow-run',
|
|
101
|
+
'--unstable-detect-cjs',
|
|
102
|
+
'--sloppy-imports',
|
|
103
|
+
TS_SPINE_DENO_ENTRY,
|
|
104
|
+
],
|
|
105
|
+
database_url,
|
|
106
|
+
paths,
|
|
107
|
+
extra_env: { [TS_SPINE_DIR_ENV]: paths.root },
|
|
108
|
+
capabilities: ts_spine_capabilities,
|
|
109
|
+
}),
|
|
110
|
+
sse_path: TS_SPINE_SSE_PATH,
|
|
111
|
+
};
|
|
112
|
+
};
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { type BackendCapabilities } from './capabilities.js';
|
|
3
|
+
import type { SetupTest } from './setup.js';
|
|
4
|
+
/** Configuration for {@link describe_cross_process_ws_tests}. */
|
|
5
|
+
export interface CrossProcessWsTestOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Per-test fixture producer (`default_cross_process_setup(handle)`).
|
|
8
|
+
* The authenticated case reads the fresh-per-test keeper's session
|
|
9
|
+
* cookies from `fixture.transport.cookies()` to thread onto the upgrade.
|
|
10
|
+
*/
|
|
11
|
+
readonly setup_test: SetupTest;
|
|
12
|
+
/** Backend capability flags; every case gates on `capabilities.ws`. */
|
|
13
|
+
readonly capabilities: BackendCapabilities;
|
|
14
|
+
/** Base URL the backend is reachable at (e.g. `http://localhost:1178`). */
|
|
15
|
+
readonly base_url: string;
|
|
16
|
+
/** WebSocket endpoint path on the backend (e.g. `/api/ws`). */
|
|
17
|
+
readonly ws_path: string;
|
|
18
|
+
/** Origin for the authenticated upgrade. Defaults to `base_url`. */
|
|
19
|
+
readonly origin?: string;
|
|
20
|
+
/**
|
|
21
|
+
* RPC endpoint path (e.g. `/api/rpc`) used by the close-on-revoke case to
|
|
22
|
+
* fire `account_session_revoke_all` over the keeper's session channel.
|
|
23
|
+
* When omitted, that case is skipped — it depends on the standard account
|
|
24
|
+
* actions being mounted on the RPC endpoint.
|
|
25
|
+
*/
|
|
26
|
+
readonly rpc_path?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Register the cross-process WS round-trip suite. Up to four cases over a
|
|
30
|
+
* real upgrade: authed `heartbeat` round-trip, anonymous-upgrade refusal,
|
|
31
|
+
* disallowed-origin refusal, and — when `rpc_path` is supplied —
|
|
32
|
+
* session-revocation closing the live socket.
|
|
33
|
+
*/
|
|
34
|
+
export declare const describe_cross_process_ws_tests: (options: CrossProcessWsTestOptions) => void;
|
|
35
|
+
//# sourceMappingURL=ws_round_trip.d.ts.map
|
|
@@ -0,0 +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;AAmD9B,OAAO,EAAC,KAAK,mBAAmB,EAAU,MAAM,mBAAmB,CAAC;AACpE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAK1C,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;CAC3B;AAED;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B,GAAI,SAAS,yBAAyB,KAAG,IAsEpF,CAAC"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-process WebSocket round-trip suite — the cross-process counterpart
|
|
4
|
+
* to the in-process `testing/ws_round_trip.ts` harness.
|
|
5
|
+
*
|
|
6
|
+
* Where the in-process harness drives `register_action_ws` against a fake
|
|
7
|
+
* Hono upgrade (no wire), this suite performs a **real** `WebSocket`
|
|
8
|
+
* upgrade against a spawned backend via `create_ws_transport` (the `ws`
|
|
9
|
+
* npm package), so the actual upgrade handshake + per-connection auth +
|
|
10
|
+
* JSON-RPC dispatch over the socket are exercised end-to-end. It is the
|
|
11
|
+
* only coverage of the spawned binary's live WS path — the standard
|
|
12
|
+
* cross-process bundle (`describe_standard_cross_process_tests`) omits WS
|
|
13
|
+
* by design, so consumers call this alongside it.
|
|
14
|
+
*
|
|
15
|
+
* **Consumer-agnostic.** Every case drives the `heartbeat` protocol action,
|
|
16
|
+
* which `assert_ws_endpoints_include_protocol_actions` guarantees is present
|
|
17
|
+
* on every WS endpoint — so the suite needs no knowledge of a consumer's
|
|
18
|
+
* domain WS methods. It validates the transport, not the domain.
|
|
19
|
+
*
|
|
20
|
+
* The first three cases mirror the upgrade stack `register_ws_endpoint` wires
|
|
21
|
+
* (origin check → `require_auth` → dispatch): an authenticated upgrade
|
|
22
|
+
* round-trips `heartbeat`; an anonymous upgrade is refused; a
|
|
23
|
+
* disallowed-origin upgrade is refused. Per-connection auth is enforced at
|
|
24
|
+
* upgrade time (not per message), so the negative cases assert the upgrade
|
|
25
|
+
* itself rejects rather than a per-message error frame.
|
|
26
|
+
*
|
|
27
|
+
* A fourth case (gated on `rpc_path`) covers server-initiated close: an
|
|
28
|
+
* authenticated socket is dropped when the account's sessions are revoked
|
|
29
|
+
* mid-connection. Per-message dispatch never re-checks credential validity,
|
|
30
|
+
* so the live socket survives on the audit-fed `create_ws_auth_guard` seam —
|
|
31
|
+
* firing `account_session_revoke_all` over the keeper's session channel
|
|
32
|
+
* emits `session_revoke_all`, which closes the socket. Omit `rpc_path` to
|
|
33
|
+
* skip it (consumers without the standard account actions on their RPC
|
|
34
|
+
* endpoint).
|
|
35
|
+
*
|
|
36
|
+
* Gated on `capabilities.ws` — backends without an end-to-end WS transport
|
|
37
|
+
* skip (the cases still surface as `.skip` in the report). Cross-process
|
|
38
|
+
* only: `create_ws_transport` needs a real bound socket, so wire it from a
|
|
39
|
+
* `*.cross.test.ts` file, never an in-process setup.
|
|
40
|
+
*
|
|
41
|
+
* @module
|
|
42
|
+
*/
|
|
43
|
+
import { assert, describe } from 'vitest';
|
|
44
|
+
import { assert_rejects } from '@fuzdev/fuz_util/testing.js';
|
|
45
|
+
import { heartbeat_action_spec } from '../../actions/heartbeat.js';
|
|
46
|
+
import { account_session_revoke_all_action_spec } from '../../auth/account_action_specs.js';
|
|
47
|
+
import { create_ws_transport } from '../transports/ws_transport.js';
|
|
48
|
+
import { create_rpc_post_init } from '../rpc_helpers.js';
|
|
49
|
+
import { test_if } from './capabilities.js';
|
|
50
|
+
/** Origin guaranteed to fail the `http://localhost:*` allowlist the test backends run with. */
|
|
51
|
+
const DISALLOWED_ORIGIN = 'http://disallowed.example';
|
|
52
|
+
/**
|
|
53
|
+
* Register the cross-process WS round-trip suite. Up to four cases over a
|
|
54
|
+
* real upgrade: authed `heartbeat` round-trip, anonymous-upgrade refusal,
|
|
55
|
+
* disallowed-origin refusal, and — when `rpc_path` is supplied —
|
|
56
|
+
* session-revocation closing the live socket.
|
|
57
|
+
*/
|
|
58
|
+
export const describe_cross_process_ws_tests = (options) => {
|
|
59
|
+
const { setup_test, capabilities, base_url, ws_path, origin, rpc_path } = options;
|
|
60
|
+
describe('cross-process websocket', () => {
|
|
61
|
+
test_if(capabilities.ws, 'authenticated upgrade round-trips heartbeat', async () => {
|
|
62
|
+
const fixture = await setup_test();
|
|
63
|
+
const client = await create_ws_transport({
|
|
64
|
+
base_url,
|
|
65
|
+
ws_path,
|
|
66
|
+
cookies: fixture.transport.cookies(),
|
|
67
|
+
origin,
|
|
68
|
+
});
|
|
69
|
+
try {
|
|
70
|
+
const result = await client.request(1, heartbeat_action_spec.method, {});
|
|
71
|
+
assert.deepStrictEqual(result, {}, 'heartbeat returns an empty result over the wire');
|
|
72
|
+
}
|
|
73
|
+
finally {
|
|
74
|
+
await client.close();
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
// Per-connection auth fires at upgrade time (`require_auth`), so an
|
|
78
|
+
// anonymous socket never opens — the upgrade is refused outright.
|
|
79
|
+
test_if(capabilities.ws, 'anonymous upgrade is refused', async () => {
|
|
80
|
+
await assert_rejects(() => create_ws_transport({ base_url, ws_path, cookies: [], origin }));
|
|
81
|
+
});
|
|
82
|
+
// Origin is checked before auth, so cookies are irrelevant here.
|
|
83
|
+
test_if(capabilities.ws, 'disallowed-origin upgrade is refused', async () => {
|
|
84
|
+
await assert_rejects(() => create_ws_transport({ base_url, ws_path, cookies: [], origin: DISALLOWED_ORIGIN }));
|
|
85
|
+
});
|
|
86
|
+
// Per-message dispatch never re-checks credential validity, so a live
|
|
87
|
+
// socket only drops via the audit-fed `create_ws_auth_guard`. Revoke the
|
|
88
|
+
// keeper's sessions over its own session channel → `session_revoke_all`
|
|
89
|
+
// closes the socket. Gated on `rpc_path` (depends on the standard
|
|
90
|
+
// account actions on the RPC endpoint).
|
|
91
|
+
test_if(capabilities.ws && rpc_path !== undefined, 'session revocation closes the live socket', async () => {
|
|
92
|
+
const fixture = await setup_test();
|
|
93
|
+
const client = await create_ws_transport({
|
|
94
|
+
base_url,
|
|
95
|
+
ws_path,
|
|
96
|
+
cookies: fixture.transport.cookies(),
|
|
97
|
+
origin,
|
|
98
|
+
});
|
|
99
|
+
try {
|
|
100
|
+
// Confirm the socket dispatches before revoking, so a failed
|
|
101
|
+
// close assertion can't be confused with a dead connection.
|
|
102
|
+
await client.request(1, heartbeat_action_spec.method, {});
|
|
103
|
+
const res = await fixture.transport(rpc_path, create_rpc_post_init(account_session_revoke_all_action_spec.method));
|
|
104
|
+
assert.strictEqual(res.status, 200, `account_session_revoke_all RPC failed (status=${res.status})`);
|
|
105
|
+
const closed = await client.wait_for_close(2000);
|
|
106
|
+
assert.ok(closed, 'socket did not close within 2s after session_revoke_all');
|
|
107
|
+
}
|
|
108
|
+
finally {
|
|
109
|
+
await client.close();
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
});
|
|
113
|
+
};
|
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import './assert_dev_env.js';
|
|
2
|
-
import type { AppSurface } from '../http/surface.js';
|
|
2
|
+
import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
|
|
3
3
|
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
4
4
|
import type { SetupTest } from './cross_backend/setup.js';
|
|
5
|
-
import type { SurfaceSource } from './transports/surface_source.js';
|
|
6
5
|
/**
|
|
7
6
|
* Recursively collect all property names from a JSON Schema.
|
|
8
7
|
*
|
|
@@ -23,11 +22,10 @@ export interface DataExposureTestOptions {
|
|
|
23
22
|
/** Per-test fixture-producing function (per-describe cadence). */
|
|
24
23
|
setup_test: SetupTest;
|
|
25
24
|
/**
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
* variant lands alongside the spawned-backend transport plumbing.
|
|
25
|
+
* App surface for schema-level + route-iteration checks. Constructed in
|
|
26
|
+
* TS by the consumer; same shape for in-process and cross-process tests.
|
|
29
27
|
*/
|
|
30
|
-
surface_source:
|
|
28
|
+
surface_source: AppSurfaceSpec;
|
|
31
29
|
/** Backend capability declarations. */
|
|
32
30
|
capabilities: BackendCapabilities;
|
|
33
31
|
/** Fields that must never appear in any response. Default: `sensitive_field_blocklist`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"data_exposure.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/data_exposure.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAqB7B,OAAO,KAAK,EAAC,UAAU,EAAC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"data_exposure.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/data_exposure.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAqB7B,OAAO,KAAK,EAAC,UAAU,EAAE,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAYnE,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AAIrE;;;;;GAKG;AACH,eAAO,MAAM,kCAAkC,GAAI,QAAQ,OAAO,KAAG,GAAG,CAAC,MAAM,CAuB9E,CAAC;AAIF;;GAEG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,UAAU,EACnB,mBAAkB,aAAa,CAAC,MAAM,CAA6B,KACjE,IAWF,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,wCAAwC,GACpD,SAAS,UAAU,EACnB,oBAAmB,aAAa,CAAC,MAAM,CAA8B,KACnE,IAcF,CAAC;AAIF,kDAAkD;AAClD,MAAM,WAAW,uBAAuB;IACvC,kEAAkE;IAClE,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,2FAA2F;IAC3F,gBAAgB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IACzC,iGAAiG;IACjG,iBAAiB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC1C,kDAAkD;IAClD,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,GAAI,SAAS,uBAAuB,KAAG,IA2L/E,CAAC"}
|
|
@@ -93,11 +93,7 @@ export const assert_non_admin_schemas_no_admin_fields = (surface, admin_only_fie
|
|
|
93
93
|
* contain no sensitive fields
|
|
94
94
|
*/
|
|
95
95
|
export const describe_data_exposure_tests = (options) => {
|
|
96
|
-
|
|
97
|
-
throw new Error("describe_data_exposure_tests requires surface_source.kind === 'inline' — " +
|
|
98
|
-
'the cross-process snapshot variant lands with the spawned-backend transport');
|
|
99
|
-
}
|
|
100
|
-
const { surface, route_specs } = options.surface_source.spec;
|
|
96
|
+
const { surface, route_specs } = options.surface_source;
|
|
101
97
|
const { sensitive_fields = sensitive_field_blocklist, admin_only_fields = admin_only_field_blocklist, } = options;
|
|
102
98
|
const skip_set = new Set(options.skip_routes);
|
|
103
99
|
void options.capabilities;
|
|
@@ -20,13 +20,24 @@ export declare const create_test_account_with_actor: (db: Db, options: {
|
|
|
20
20
|
password_hash?: string;
|
|
21
21
|
}) => Promise<TestAccountWithActor>;
|
|
22
22
|
/**
|
|
23
|
-
* Materialize a role_grant directly via `query_create_role_grant`,
|
|
24
|
-
* the production offer/accept consent flow.
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
23
|
+
* Materialize a `role_grant` directly via `query_create_role_grant`,
|
|
24
|
+
* bypassing the production offer/accept consent flow.
|
|
25
|
+
*
|
|
26
|
+
* **In-process only.** This helper takes a raw `Db` handle and seeds
|
|
27
|
+
* rows without firing audit fan-out, WebSocket broadcasts, or the
|
|
28
|
+
* `_supersede` notification chain a real grant emits. Cross-process
|
|
29
|
+
* suites must instead drive `role_grant_offer_create_action_spec` +
|
|
30
|
+
* `role_grant_offer_accept_action_spec` via
|
|
31
|
+
* `role_grant_helpers.ts`'s `role_grant_offer_and_accept` so the
|
|
32
|
+
* fixture observes the full post-commit fan-out the way production
|
|
33
|
+
* does — otherwise tests would mask real divergence between the TS
|
|
34
|
+
* and Rust spines.
|
|
35
|
+
*
|
|
36
|
+
* Use this helper for query-level (`*.db.test.ts`) tests that
|
|
37
|
+
* exercise revoke or isolation semantics — not the consent path
|
|
38
|
+
* itself. The schema's `source_offer_id = null` shape is an
|
|
39
|
+
* intentional admin-direct escape; this helper exposes it so
|
|
40
|
+
* suites don't reimplement the same direct-seed wrapper.
|
|
30
41
|
*/
|
|
31
42
|
export declare const create_test_role_grant_direct: (db: Db, input: CreateRoleGrantInput) => Promise<RoleGrant>;
|
|
32
43
|
//# sourceMappingURL=db_entities.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"db_entities.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db_entities.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAsB7B,OAAO,KAAK,EAAC,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAC,MAAM,2BAA2B,CAAC;AAC/F,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,oFAAoF;AACpF,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,GAC1C,IAAI,EAAE,EACN,SAAS;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAC,KACjD,OAAO,CAAC,oBAAoB,CAI7B,CAAC;AAEH
|
|
1
|
+
{"version":3,"file":"db_entities.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/db_entities.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAsB7B,OAAO,KAAK,EAAC,OAAO,EAAE,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAC,MAAM,2BAA2B,CAAC;AAC/F,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAEpC,oFAAoF;AACpF,MAAM,WAAW,oBAAoB;IACpC,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;CACb;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,8BAA8B,GAC1C,IAAI,EAAE,EACN,SAAS;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,aAAa,CAAC,EAAE,MAAM,CAAA;CAAC,KACjD,OAAO,CAAC,oBAAoB,CAI7B,CAAC;AAEH;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,6BAA6B,GACzC,IAAI,EAAE,EACN,OAAO,oBAAoB,KACzB,OAAO,CAAC,SAAS,CAAyC,CAAC"}
|
|
@@ -29,12 +29,23 @@ import { query_create_role_grant } from '../auth/role_grant_queries.js';
|
|
|
29
29
|
*/
|
|
30
30
|
export const create_test_account_with_actor = async (db, options) => query_create_account_with_actor({ db }, { username: options.username, password_hash: options.password_hash ?? 'hash' });
|
|
31
31
|
/**
|
|
32
|
-
* Materialize a role_grant directly via `query_create_role_grant`,
|
|
33
|
-
* the production offer/accept consent flow.
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
32
|
+
* Materialize a `role_grant` directly via `query_create_role_grant`,
|
|
33
|
+
* bypassing the production offer/accept consent flow.
|
|
34
|
+
*
|
|
35
|
+
* **In-process only.** This helper takes a raw `Db` handle and seeds
|
|
36
|
+
* rows without firing audit fan-out, WebSocket broadcasts, or the
|
|
37
|
+
* `_supersede` notification chain a real grant emits. Cross-process
|
|
38
|
+
* suites must instead drive `role_grant_offer_create_action_spec` +
|
|
39
|
+
* `role_grant_offer_accept_action_spec` via
|
|
40
|
+
* `role_grant_helpers.ts`'s `role_grant_offer_and_accept` so the
|
|
41
|
+
* fixture observes the full post-commit fan-out the way production
|
|
42
|
+
* does — otherwise tests would mask real divergence between the TS
|
|
43
|
+
* and Rust spines.
|
|
44
|
+
*
|
|
45
|
+
* Use this helper for query-level (`*.db.test.ts`) tests that
|
|
46
|
+
* exercise revoke or isolation semantics — not the consent path
|
|
47
|
+
* itself. The schema's `source_offer_id = null` shape is an
|
|
48
|
+
* intentional admin-direct escape; this helper exposes it so
|
|
49
|
+
* suites don't reimplement the same direct-seed wrapper.
|
|
39
50
|
*/
|
|
40
51
|
export const create_test_role_grant_direct = async (db, input) => query_create_role_grant({ db }, input);
|