@fuzdev/fuz_app 0.65.0 → 0.66.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 +7 -1
- 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
|
@@ -2,9 +2,9 @@ import './assert_dev_env.js';
|
|
|
2
2
|
import type { SessionOptions } from '../auth/session_cookie.js';
|
|
3
3
|
import { type RoleSchemaResult } from '../auth/role_schema.js';
|
|
4
4
|
import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
5
|
+
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
5
6
|
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
6
7
|
import type { SetupTest } from './cross_backend/setup.js';
|
|
7
|
-
import type { SurfaceSource } from './transports/surface_source.js';
|
|
8
8
|
/**
|
|
9
9
|
* Configuration for `describe_standard_admin_integration_tests`.
|
|
10
10
|
*/
|
|
@@ -16,11 +16,11 @@ export interface StandardAdminIntegrationTestOptions {
|
|
|
16
16
|
*/
|
|
17
17
|
setup_test: SetupTest;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
* scoping.
|
|
21
|
-
*
|
|
19
|
+
* App surface (with route specs + middleware specs) for route iteration
|
|
20
|
+
* and error-coverage scoping. Constructed in TS by the consumer (same
|
|
21
|
+
* shape for in-process and cross-process tests).
|
|
22
22
|
*/
|
|
23
|
-
surface_source:
|
|
23
|
+
surface_source: AppSurfaceSpec;
|
|
24
24
|
/** Backend capability declarations. */
|
|
25
25
|
capabilities: BackendCapabilities;
|
|
26
26
|
/** Session config — needed for cookie_name + factory-form rpc_endpoints resolution. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,
|
|
1
|
+
{"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAa,KAAK,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAUzE,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAkB1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAi3BF,CAAC"}
|
|
@@ -26,16 +26,15 @@ import './assert_dev_env.js';
|
|
|
26
26
|
* @module
|
|
27
27
|
*/
|
|
28
28
|
import { describe, test, assert, afterAll } from 'vitest';
|
|
29
|
-
import {
|
|
29
|
+
import { ROLE_ADMIN } from '../auth/role_schema.js';
|
|
30
30
|
import { GRANT_PATH_ADMIN } from '../auth/grant_path_schema.js';
|
|
31
|
-
import {} from './app_server.js';
|
|
32
|
-
import { create_test_role_grant_direct } from './db_entities.js';
|
|
31
|
+
import { DEFAULT_TEST_PASSWORD } from './app_server.js';
|
|
33
32
|
import { role_grant_offer_and_accept } from './role_grant_helpers.js';
|
|
34
33
|
import { find_auth_route } from './integration_helpers.js';
|
|
35
34
|
import { ErrorCoverageCollector, assert_error_coverage, DEFAULT_INTEGRATION_ERROR_COVERAGE, } from './error_coverage.js';
|
|
36
35
|
import { rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
|
|
37
36
|
import { role_grant_offer_create_action_spec, role_grant_revoke_action_spec, } from '../auth/role_grant_offer_action_specs.js';
|
|
38
|
-
import { admin_account_list_action_spec, admin_session_list_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, audit_log_list_action_spec, audit_log_role_grant_history_action_spec, } from '../auth/admin_action_specs.js';
|
|
37
|
+
import { admin_account_list_action_spec, ADMIN_ACCOUNT_LIST_LIMIT_MAX, admin_session_list_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, audit_log_list_action_spec, audit_log_role_grant_history_action_spec, } from '../auth/admin_action_specs.js';
|
|
39
38
|
import { account_token_create_action_spec, account_verify_action_spec, } from '../auth/account_action_specs.js';
|
|
40
39
|
/**
|
|
41
40
|
* Pick a role for admin-grant testing, preferring a non-admin app-defined
|
|
@@ -65,11 +64,7 @@ const pick_grantable_role = (role_specs) => {
|
|
|
65
64
|
* see a clear setup error rather than `method not found` mid-suite.
|
|
66
65
|
*/
|
|
67
66
|
export const describe_standard_admin_integration_tests = (options) => {
|
|
68
|
-
|
|
69
|
-
throw new Error("describe_standard_admin_integration_tests requires surface_source.kind === 'inline' — " +
|
|
70
|
-
'the cross-process snapshot variant lands with the spawned-backend transport');
|
|
71
|
-
}
|
|
72
|
-
const route_specs = options.surface_source.spec.route_specs;
|
|
67
|
+
const route_specs = options.surface_source.route_specs;
|
|
73
68
|
// Hard-fail early so consumers see a clear setup error instead of a
|
|
74
69
|
// confusing test failure when `rpc_endpoints` is missing.
|
|
75
70
|
const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
|
|
@@ -122,11 +117,19 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
122
117
|
test('admin can list all accounts', async () => {
|
|
123
118
|
const fixture = await options.setup_test();
|
|
124
119
|
const user_two = await fixture.create_account({ username: 'user_two' });
|
|
120
|
+
// Request the max page size — cross-process the backend persists
|
|
121
|
+
// accounts across tests (`globalSetup` bootstraps once), so a
|
|
122
|
+
// long-running suite accumulates well past the default 50 and
|
|
123
|
+
// `user_two` (newest, `ORDER BY created_at ASC`) falls off the
|
|
124
|
+
// first page. The MAX (200) carries this suite as written; once
|
|
125
|
+
// the suite consistently grows past it, swap to an
|
|
126
|
+
// account-scoped admin RPC (search-by-id, ORDER BY DESC, etc.)
|
|
127
|
+
// rather than chase the limit upward.
|
|
125
128
|
const res = await rpc_call_for_spec({
|
|
126
129
|
app: { request: fixture.transport },
|
|
127
130
|
path: rpc_path,
|
|
128
131
|
spec: admin_account_list_action_spec,
|
|
129
|
-
params: {},
|
|
132
|
+
params: { limit: ADMIN_ACCOUNT_LIST_LIMIT_MAX },
|
|
130
133
|
headers: fixture.create_session_headers(),
|
|
131
134
|
});
|
|
132
135
|
assert.ok(res.ok, `admin_account_list failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
@@ -139,22 +142,47 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
139
142
|
});
|
|
140
143
|
test('non-admin cannot list accounts', async () => {
|
|
141
144
|
const fixture = await options.setup_test();
|
|
142
|
-
//
|
|
143
|
-
//
|
|
144
|
-
const
|
|
145
|
-
username: 'keeper_only',
|
|
146
|
-
roles: [ROLE_KEEPER],
|
|
147
|
-
});
|
|
145
|
+
// Mint a fresh no-roles account — fresh signups default to no
|
|
146
|
+
// roles, so the per-test account is non-admin by construction.
|
|
147
|
+
const non_admin = await fixture.create_account({ username: 'non_admin_user' });
|
|
148
148
|
const res = await rpc_call_for_spec({
|
|
149
149
|
app: { request: fixture.transport },
|
|
150
150
|
path: rpc_path,
|
|
151
151
|
spec: admin_account_list_action_spec,
|
|
152
152
|
params: {},
|
|
153
|
-
headers:
|
|
153
|
+
headers: non_admin.create_session_headers(),
|
|
154
154
|
});
|
|
155
155
|
assert.ok(!res.ok, 'Expected admin_account_list to fail for non-admin');
|
|
156
156
|
assert.strictEqual(res.status, 403);
|
|
157
157
|
});
|
|
158
|
+
// Probe the keeper-vs-admin separation specifically: a keeper-only
|
|
159
|
+
// account (no admin role) must still 403 on admin RPCs. ROLE_KEEPER
|
|
160
|
+
// is bootstrap-only-grantable, so the only way to seed this account
|
|
161
|
+
// is via `extra_accounts` at the test-binary bootstrap-equivalent
|
|
162
|
+
// step. Consumers that want this probe wire
|
|
163
|
+
// `extra_accounts: [{username: 'non_admin_keeper', roles: [ROLE_KEEPER]}]`
|
|
164
|
+
// into their `default_*_suite_options(...)` call; otherwise the
|
|
165
|
+
// test runtime-skips and the looser no-roles probe above carries
|
|
166
|
+
// the suite's "non-admin gets 403" assertion alone.
|
|
167
|
+
test('keeper-only account cannot list accounts (keeper ≠ admin)', async (ctx) => {
|
|
168
|
+
const fixture = await options.setup_test();
|
|
169
|
+
const non_admin_keeper = fixture.extra_accounts.non_admin_keeper;
|
|
170
|
+
if (!non_admin_keeper) {
|
|
171
|
+
ctx.skip(`no \`extra_accounts['non_admin_keeper']\` declared on the fixture — wire ` +
|
|
172
|
+
`\`extra_accounts: [{username: 'non_admin_keeper', roles: [ROLE_KEEPER]}]\` ` +
|
|
173
|
+
`in the consumer's suite options to enable the keeper-vs-admin probe.`);
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
const res = await rpc_call_for_spec({
|
|
177
|
+
app: { request: fixture.transport },
|
|
178
|
+
path: rpc_path,
|
|
179
|
+
spec: admin_account_list_action_spec,
|
|
180
|
+
params: {},
|
|
181
|
+
headers: non_admin_keeper.create_session_headers(),
|
|
182
|
+
});
|
|
183
|
+
assert.ok(!res.ok, 'Expected admin_account_list to fail for keeper-only account');
|
|
184
|
+
assert.strictEqual(res.status, 403);
|
|
185
|
+
});
|
|
158
186
|
});
|
|
159
187
|
// --- 2. Role grant create/revoke lifecycle ---
|
|
160
188
|
// Role grant create/revoke are RPC-only (see `role_grant_offer_create` /
|
|
@@ -242,9 +270,14 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
242
270
|
test('admin can revoke all tokens for another account', async () => {
|
|
243
271
|
const fixture = await options.setup_test();
|
|
244
272
|
const user_two = await fixture.create_account({ username: 'user_two' });
|
|
245
|
-
// Verify user_two's bearer token works via `account_verify` RPC
|
|
273
|
+
// Verify user_two's bearer token works via `account_verify` RPC.
|
|
274
|
+
// Fresh transport — admin's session cookie in `fixture.transport`'s
|
|
275
|
+
// jar would otherwise give a false 200 here, masking whether the
|
|
276
|
+
// bearer credential is the one being validated. `origin: null` so
|
|
277
|
+
// the transport doesn't auto-add a default Origin (which would
|
|
278
|
+
// discard the bearer as browser-context cross-process).
|
|
246
279
|
const before = await rpc_call_for_spec({
|
|
247
|
-
app: { request: fixture.
|
|
280
|
+
app: { request: fixture.fresh_transport({ origin: null }) },
|
|
248
281
|
path: rpc_path,
|
|
249
282
|
spec: account_verify_action_spec,
|
|
250
283
|
params: undefined,
|
|
@@ -263,9 +296,10 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
263
296
|
assert.ok(res.ok, `admin_token_revoke_all failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
|
|
264
297
|
assert.strictEqual(res.result.ok, true);
|
|
265
298
|
assert.ok(res.result.count >= 1, 'Expected at least 1 revoked token');
|
|
266
|
-
// Verify user_two's bearer token no longer works
|
|
299
|
+
// Verify user_two's bearer token no longer works — fresh transport
|
|
300
|
+
// same reason as the `before` call above.
|
|
267
301
|
const after = await rpc_call_for_spec({
|
|
268
|
-
app: { request: fixture.
|
|
302
|
+
app: { request: fixture.fresh_transport({ origin: null }) },
|
|
269
303
|
path: rpc_path,
|
|
270
304
|
spec: account_verify_action_spec,
|
|
271
305
|
params: undefined,
|
|
@@ -341,19 +375,26 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
341
375
|
describe('admin audit trail', () => {
|
|
342
376
|
test('role_grant revoke creates audit event', async () => {
|
|
343
377
|
const fixture = await options.setup_test();
|
|
344
|
-
assert(fixture.in_process, 'direct role_grant seed requires in-process db');
|
|
345
378
|
const user_two = await fixture.create_account({ username: 'user_two' });
|
|
346
|
-
|
|
347
|
-
|
|
379
|
+
// Drive the production consent flow so the role_grant exists
|
|
380
|
+
// via the same code path real users go through. The
|
|
381
|
+
// audit_log_list filter below pulls only `role_grant_revoke`
|
|
382
|
+
// events so the extra `role_grant_offer_create` /
|
|
383
|
+
// `role_grant_offer_accepted` audits emitted upstream don't
|
|
384
|
+
// affect the assertion.
|
|
385
|
+
const { role_grant_id } = await role_grant_offer_and_accept({
|
|
386
|
+
app: { request: fixture.transport },
|
|
387
|
+
rpc_path,
|
|
388
|
+
grantor: fixture,
|
|
389
|
+
recipient: user_two,
|
|
348
390
|
role: grantable_role,
|
|
349
|
-
granted_by: fixture.actor.id,
|
|
350
391
|
});
|
|
351
392
|
// Revoke via RPC
|
|
352
393
|
const revoke_res = await rpc_call_for_spec({
|
|
353
394
|
app: { request: fixture.transport },
|
|
354
395
|
path: rpc_path,
|
|
355
396
|
spec: role_grant_revoke_action_spec,
|
|
356
|
-
params: { actor_id: user_two.actor.id, role_grant_id
|
|
397
|
+
params: { actor_id: user_two.actor.id, role_grant_id },
|
|
357
398
|
headers: fixture.create_session_headers(),
|
|
358
399
|
});
|
|
359
400
|
assert.ok(revoke_res.ok, `role_grant_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
|
|
@@ -492,24 +533,36 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
492
533
|
if (!login_route || !logout_route || !password_route)
|
|
493
534
|
return;
|
|
494
535
|
const user_two = await fixture.create_account({ username: 'audit_user' });
|
|
495
|
-
// 1. login (user_two logs in)
|
|
496
|
-
|
|
536
|
+
// 1. login (user_two logs in) — fresh transport because the per-test
|
|
537
|
+
// session cookie in `fixture.transport`'s jar would otherwise be sent
|
|
538
|
+
// alongside this unauthenticated login attempt, and the route can
|
|
539
|
+
// reject the "already-authed" cookie-collision case with 401.
|
|
540
|
+
// Read the actual username off the returned account — the fresh-DB
|
|
541
|
+
// contract makes the supplied literal `'audit_user'` survive
|
|
542
|
+
// unchanged, but reading from `.account.username` is structurally
|
|
543
|
+
// robust against any future username-mangling at the mint layer.
|
|
544
|
+
const login_res = await fixture.fresh_transport()(login_route.path, {
|
|
497
545
|
method: 'POST',
|
|
498
546
|
headers: {
|
|
499
547
|
host: 'localhost',
|
|
500
548
|
origin: 'http://localhost:5173',
|
|
501
549
|
'content-type': 'application/json',
|
|
502
550
|
},
|
|
503
|
-
body: JSON.stringify({
|
|
551
|
+
body: JSON.stringify({
|
|
552
|
+
username: user_two.account.username,
|
|
553
|
+
password: DEFAULT_TEST_PASSWORD,
|
|
554
|
+
}),
|
|
504
555
|
});
|
|
505
556
|
assert.strictEqual(login_res.status, 200);
|
|
506
557
|
// extract user_two session cookie for logout
|
|
507
558
|
const set_cookie = login_res.headers.get('set-cookie');
|
|
508
559
|
const cookie_match = new RegExp(`${cookie_name}=([^;]+)`).exec(set_cookie ?? '');
|
|
509
560
|
const user_two_cookie = cookie_match?.[1];
|
|
510
|
-
// 2. logout (user_two logs out)
|
|
561
|
+
// 2. logout (user_two logs out) — fresh transport with the explicit
|
|
562
|
+
// user_two cookie, so the per-test session cookie can't interfere with
|
|
563
|
+
// the logout target.
|
|
511
564
|
if (user_two_cookie) {
|
|
512
|
-
await fixture.
|
|
565
|
+
await fixture.fresh_transport()(logout_route.path, {
|
|
513
566
|
method: 'POST',
|
|
514
567
|
headers: {
|
|
515
568
|
host: 'localhost',
|
|
@@ -552,7 +605,7 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
552
605
|
'content-type': 'application/json',
|
|
553
606
|
}),
|
|
554
607
|
body: JSON.stringify({
|
|
555
|
-
current_password:
|
|
608
|
+
current_password: DEFAULT_TEST_PASSWORD,
|
|
556
609
|
new_password: 'new-audit-password-789',
|
|
557
610
|
}),
|
|
558
611
|
});
|
|
@@ -617,20 +670,23 @@ export const describe_standard_admin_integration_tests = (options) => {
|
|
|
617
670
|
username: 'admin_b_iso',
|
|
618
671
|
roles: ['admin'],
|
|
619
672
|
});
|
|
620
|
-
// Seed an active role_grant
|
|
621
|
-
//
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
673
|
+
// Seed an active role_grant for admin B via the production
|
|
674
|
+
// consent flow. The revoke IDOR check is the subject of this
|
|
675
|
+
// test; the grant path is precondition setup, exercised
|
|
676
|
+
// via the same RPCs production drives.
|
|
677
|
+
const { role_grant_id } = await role_grant_offer_and_accept({
|
|
678
|
+
app: { request: fixture.transport },
|
|
679
|
+
rpc_path,
|
|
680
|
+
grantor: fixture,
|
|
681
|
+
recipient: admin_b,
|
|
625
682
|
role: grantable_role,
|
|
626
|
-
granted_by: fixture.actor.id,
|
|
627
683
|
});
|
|
628
684
|
// Admin B revokes their own role_grant via RPC — should succeed
|
|
629
685
|
const revoke_res = await rpc_call_for_spec({
|
|
630
686
|
app: { request: fixture.transport },
|
|
631
687
|
path: rpc_path,
|
|
632
688
|
spec: role_grant_revoke_action_spec,
|
|
633
|
-
params: { actor_id: admin_b.actor.id, role_grant_id
|
|
689
|
+
params: { actor_id: admin_b.actor.id, role_grant_id },
|
|
634
690
|
headers: create_headers(admin_b.session_cookie),
|
|
635
691
|
});
|
|
636
692
|
assert.ok(revoke_res.ok, `role_grant_revoke failed: ${revoke_res.ok ? '' : JSON.stringify(revoke_res.error)}`);
|
|
@@ -32,6 +32,21 @@ import type { RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
|
32
32
|
export declare const stub_password_deps: PasswordHashDeps;
|
|
33
33
|
/** 64-hex-char test cookie secret — deterministic, never used in production. */
|
|
34
34
|
export declare const TEST_COOKIE_SECRET: string;
|
|
35
|
+
/**
|
|
36
|
+
* Default password for bootstrapped test accounts. Shared between the
|
|
37
|
+
* in-process keeper bootstrap (`bootstrap_test_keeper`,
|
|
38
|
+
* `create_test_account_with_credentials`, `create_test_app_server`,
|
|
39
|
+
* `TestApp.create_account`) and the cross-process bootstrap
|
|
40
|
+
* (`cross_backend/setup.ts`). The two paths MUST agree — when they
|
|
41
|
+
* diverged during the 3d cross-process lift, ~20 login tests 401'd
|
|
42
|
+
* silently against the cross-process backend because the per-test
|
|
43
|
+
* fixture minted accounts under a different default than the
|
|
44
|
+
* integration suite's hardcoded login bodies expected. Consumers
|
|
45
|
+
* hardcoding the literal string in test bodies should import this
|
|
46
|
+
* constant instead so a future divergence becomes a typecheck miss
|
|
47
|
+
* rather than a runtime password mismatch.
|
|
48
|
+
*/
|
|
49
|
+
export declare const DEFAULT_TEST_PASSWORD = "test-password-123";
|
|
35
50
|
/**
|
|
36
51
|
* Options for `bootstrap_test_keeper` and `create_test_account_with_credentials`.
|
|
37
52
|
*
|
|
@@ -135,7 +150,7 @@ export interface TestAppServerOptions {
|
|
|
135
150
|
password?: PasswordHashDeps;
|
|
136
151
|
/** Username for the bootstrapped account. Default: `'keeper'`. */
|
|
137
152
|
username?: string;
|
|
138
|
-
/** Password for the bootstrapped account. Default: `
|
|
153
|
+
/** Password for the bootstrapped account. Default: `DEFAULT_TEST_PASSWORD`. */
|
|
139
154
|
password_value?: string;
|
|
140
155
|
/** Roles to grant. Default: `[ROLE_KEEPER]`. */
|
|
141
156
|
roles?: Array<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAG/B,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAGjD,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAE1E,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AAU1D,OAAO,EAA8B,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAG3F,OAAO,EAAwB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAC,MAAM,0BAA0B,CAAC;AACnG,OAAO,EAEN,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,sBAAsB,EAC3B,KAAK,oBAAoB,EACzB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAC,UAAU,EAAE,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAOrD,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,kBAAkB,CAAC;AAE9D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,gBAIhC,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,QAAiB,CAAC;
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -28,6 +28,21 @@ export const stub_password_deps = {
|
|
|
28
28
|
};
|
|
29
29
|
/** 64-hex-char test cookie secret — deterministic, never used in production. */
|
|
30
30
|
export const TEST_COOKIE_SECRET = 'a'.repeat(64);
|
|
31
|
+
/**
|
|
32
|
+
* Default password for bootstrapped test accounts. Shared between the
|
|
33
|
+
* in-process keeper bootstrap (`bootstrap_test_keeper`,
|
|
34
|
+
* `create_test_account_with_credentials`, `create_test_app_server`,
|
|
35
|
+
* `TestApp.create_account`) and the cross-process bootstrap
|
|
36
|
+
* (`cross_backend/setup.ts`). The two paths MUST agree — when they
|
|
37
|
+
* diverged during the 3d cross-process lift, ~20 login tests 401'd
|
|
38
|
+
* silently against the cross-process backend because the per-test
|
|
39
|
+
* fixture minted accounts under a different default than the
|
|
40
|
+
* integration suite's hardcoded login bodies expected. Consumers
|
|
41
|
+
* hardcoding the literal string in test bodies should import this
|
|
42
|
+
* constant instead so a future divergence becomes a typecheck miss
|
|
43
|
+
* rather than a runtime password mismatch.
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_TEST_PASSWORD = 'test-password-123';
|
|
31
46
|
// Module-level PGlite factory for create_test_app_server when no db is provided.
|
|
32
47
|
// Shares the WASM instance cache from test_db.ts, avoiding redundant cold starts
|
|
33
48
|
// within the same vitest worker thread. Schema is reset on each create() call.
|
|
@@ -48,7 +63,7 @@ const fallback_pglite_factory = create_pglite_factory(async (db) => {
|
|
|
48
63
|
* `role_grant` (one per role), `api_token`, and `auth_session`.
|
|
49
64
|
*/
|
|
50
65
|
export const create_test_account_with_credentials = async (options) => {
|
|
51
|
-
const { db, keyring, session_options, password, username = 'keeper', password_value =
|
|
66
|
+
const { db, keyring, session_options, password, username = 'keeper', password_value = DEFAULT_TEST_PASSWORD, roles = [], } = options;
|
|
52
67
|
const deps = { db };
|
|
53
68
|
const password_hash = await password.hash_password(password_value);
|
|
54
69
|
const { account, actor } = await query_create_account_with_actor(deps, {
|
|
@@ -166,7 +181,7 @@ const _build_test_backend = async (options) => {
|
|
|
166
181
|
return { backend, keyring: keyring_result.keyring };
|
|
167
182
|
};
|
|
168
183
|
export const create_test_app_server = async (options) => {
|
|
169
|
-
const { session_options, password = stub_password_deps, username = 'keeper', password_value =
|
|
184
|
+
const { session_options, password = stub_password_deps, username = 'keeper', password_value = DEFAULT_TEST_PASSWORD, roles = [ROLE_KEEPER], } = options;
|
|
170
185
|
const { backend, keyring } = await _build_test_backend(options);
|
|
171
186
|
const bootstrapped = await bootstrap_test_keeper({
|
|
172
187
|
db: backend.deps.db,
|
|
@@ -253,7 +268,7 @@ export const create_test_app = async (options) => {
|
|
|
253
268
|
session_options: options.session_options,
|
|
254
269
|
password,
|
|
255
270
|
username: account_options?.username ?? `test_user_${account_counter}`,
|
|
256
|
-
password_value: account_options?.password_value ??
|
|
271
|
+
password_value: account_options?.password_value ?? DEFAULT_TEST_PASSWORD,
|
|
257
272
|
roles: account_options?.roles ?? [],
|
|
258
273
|
});
|
|
259
274
|
return {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import './assert_dev_env.js';
|
|
2
2
|
import type { SessionOptions } from '../auth/session_cookie.js';
|
|
3
3
|
import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
4
|
+
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
4
5
|
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
5
6
|
import type { SetupTest } from './cross_backend/setup.js';
|
|
6
|
-
import type { SurfaceSource } from './transports/surface_source.js';
|
|
7
7
|
/**
|
|
8
8
|
* Configuration for `describe_audit_completeness_tests`.
|
|
9
9
|
*/
|
|
@@ -16,11 +16,13 @@ export interface AuditCompletenessTestOptions {
|
|
|
16
16
|
*/
|
|
17
17
|
setup_test: SetupTest;
|
|
18
18
|
/**
|
|
19
|
-
*
|
|
20
|
-
*
|
|
21
|
-
*
|
|
19
|
+
* App surface (with route specs). Constructed in TS by the consumer;
|
|
20
|
+
* same shape for in-process and cross-process tests. The audit suite is
|
|
21
|
+
* Tier 2 today (stays in-process per the cross-backend-integration
|
|
22
|
+
* design), but takes the same surface shape as Tier 1 suites for
|
|
23
|
+
* options uniformity.
|
|
22
24
|
*/
|
|
23
|
-
surface_source:
|
|
25
|
+
surface_source: AppSurfaceSpec;
|
|
24
26
|
/** Backend capability declarations. */
|
|
25
27
|
capabilities: BackendCapabilities;
|
|
26
28
|
/** Session config — needed for factory-form rpc_endpoints resolution. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAS9D,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAwB1B,OAAO,KAAK,EAAC,
|
|
1
|
+
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAS9D,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAwB1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AA8FD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAuhBzF,CAAC"}
|
|
@@ -25,7 +25,7 @@ import './assert_dev_env.js';
|
|
|
25
25
|
import { describe, test, assert } from 'vitest';
|
|
26
26
|
import { ROLE_ADMIN } from '../auth/role_schema.js';
|
|
27
27
|
import { AUDIT_EVENT_TYPES, } from '../auth/audit_log_schema.js';
|
|
28
|
-
import {} from './app_server.js';
|
|
28
|
+
import { DEFAULT_TEST_PASSWORD } from './app_server.js';
|
|
29
29
|
import { find_auth_route } from './integration_helpers.js';
|
|
30
30
|
import { rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
|
|
31
31
|
import { role_grant_offer_and_accept } from './role_grant_helpers.js';
|
|
@@ -106,11 +106,7 @@ const json_session_headers = (fixture, extra) => fixture.create_session_headers(
|
|
|
106
106
|
* `require_rpc_endpoint_path`.
|
|
107
107
|
*/
|
|
108
108
|
export const describe_audit_completeness_tests = (options) => {
|
|
109
|
-
|
|
110
|
-
throw new Error("describe_audit_completeness_tests requires surface_source.kind === 'inline' — " +
|
|
111
|
-
'the cross-process snapshot variant lands with the spawned-backend transport');
|
|
112
|
-
}
|
|
113
|
-
const route_specs = options.surface_source.spec.route_specs;
|
|
109
|
+
const route_specs = options.surface_source.route_specs;
|
|
114
110
|
// Hard-fail early so consumers see a clear setup error instead of a
|
|
115
111
|
// confusing test failure when `rpc_endpoints` is missing.
|
|
116
112
|
const rpc_endpoints_for_setup = resolve_rpc_endpoints_for_setup(options.rpc_endpoints, options.session_options);
|
|
@@ -129,7 +125,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
129
125
|
headers: UNAUTHENTICATED_JSON_HEADERS,
|
|
130
126
|
body: JSON.stringify({
|
|
131
127
|
username: fixture.account.username,
|
|
132
|
-
password:
|
|
128
|
+
password: DEFAULT_TEST_PASSWORD,
|
|
133
129
|
}),
|
|
134
130
|
});
|
|
135
131
|
assert.strictEqual(res.status, 200);
|
|
@@ -218,7 +214,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
218
214
|
headers: UNAUTHENTICATED_JSON_HEADERS,
|
|
219
215
|
body: JSON.stringify({
|
|
220
216
|
username: fixture.account.username,
|
|
221
|
-
password:
|
|
217
|
+
password: DEFAULT_TEST_PASSWORD,
|
|
222
218
|
}),
|
|
223
219
|
});
|
|
224
220
|
// get session IDs (newest first — `account_session_list` orders DESC
|
|
@@ -271,7 +267,7 @@ export const describe_audit_completeness_tests = (options) => {
|
|
|
271
267
|
method: 'POST',
|
|
272
268
|
headers: json_session_headers(fixture),
|
|
273
269
|
body: JSON.stringify({
|
|
274
|
-
current_password:
|
|
270
|
+
current_password: DEFAULT_TEST_PASSWORD,
|
|
275
271
|
new_password: 'new-password-456',
|
|
276
272
|
}),
|
|
277
273
|
});
|
|
@@ -7,8 +7,8 @@ import './assert_dev_env.js';
|
|
|
7
7
|
* observable state — account exists, `bootstrap_lock.bootstrapped` is
|
|
8
8
|
* true, audit row emitted, response body shape — rather than
|
|
9
9
|
* `on_bootstrap` callback invocation, so the suite stays cross-impl
|
|
10
|
-
* friendly when
|
|
11
|
-
*
|
|
10
|
+
* friendly when cross-process testing wires it against a spawned
|
|
11
|
+
* Rust backend.
|
|
12
12
|
*
|
|
13
13
|
* Folded into `describe_standard_tests` with a `bootstrap.mode === 'live'`
|
|
14
14
|
* silent-skip gate; consumers wiring live bootstrap pick up success-path
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-process backend configuration.
|
|
4
|
+
*
|
|
5
|
+
* `BackendConfig` describes a spawnable test binary — argv, mount paths,
|
|
6
|
+
* env vars, bootstrap credentials, daemon-token discovery path, declared
|
|
7
|
+
* capabilities. Consumer projects ship per-backend factories
|
|
8
|
+
* (`deno_backend_config()`, `rust_backend_config()`,
|
|
9
|
+
* `spine_stub_backend_config()`) that produce this shape; `spawn_backend`
|
|
10
|
+
* consumes it.
|
|
11
|
+
*
|
|
12
|
+
* fuz_app ships `spine_stub_backend_config()` as a convenience preset
|
|
13
|
+
* (operational dep on `testing_spine_stub` — path-based discovery, no
|
|
14
|
+
* `package.json` coupling to the stub's source package). Otherwise backend-specific
|
|
15
|
+
* knowledge (binary paths, port choices, env vars) is a consumer
|
|
16
|
+
* concern; fuz_app's testing library knows nothing about Deno, Cargo, or
|
|
17
|
+
* any specific runtime beyond that preset.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import type { BackendCapabilities } from './capabilities.js';
|
|
22
|
+
/**
|
|
23
|
+
* Auth-bootstrap configuration for a spawnable test binary. The runner
|
|
24
|
+
* writes `token` to `token_path` before launching the child, then POSTs
|
|
25
|
+
* `bootstrap_path` (default `/api/account/bootstrap`) with the token plus
|
|
26
|
+
* the `username` / `password` to mint the keeper account and capture the
|
|
27
|
+
* session cookie. After health-probe, the runner reads
|
|
28
|
+
* `daemon_token_path` to load the binary's deterministic daemon token,
|
|
29
|
+
* which `default_cross_process_setup` threads onto the per-test
|
|
30
|
+
* `TestFixture` for `_testing_reset` calls and other keeper-credential
|
|
31
|
+
* operations.
|
|
32
|
+
*/
|
|
33
|
+
export interface BackendBootstrapConfig {
|
|
34
|
+
/** Path the binary reads for the bootstrap token (env: `*_BOOTSTRAP_TOKEN_PATH`). */
|
|
35
|
+
readonly token_path: string;
|
|
36
|
+
/** Token text written to `token_path` before spawn. */
|
|
37
|
+
readonly token: string;
|
|
38
|
+
/** Username for the bootstrapped keeper. */
|
|
39
|
+
readonly username: string;
|
|
40
|
+
/** Password for the bootstrapped keeper. */
|
|
41
|
+
readonly password: string;
|
|
42
|
+
/**
|
|
43
|
+
* Path the test binary writes its daemon-token JSON to on boot
|
|
44
|
+
* (env: `*_DAEMON_TOKEN_PATH`). `spawn_backend` reads this file once
|
|
45
|
+
* after the health probe succeeds and threads the token onto
|
|
46
|
+
* `BackendHandle.daemon_token` for `_testing_reset` calls plus any
|
|
47
|
+
* other admin/keeper-gated cross-process tests.
|
|
48
|
+
*/
|
|
49
|
+
readonly daemon_token_path: string;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Configuration for one spawnable test backend. Consumer factories
|
|
53
|
+
* (`deno_backend_config()`, `rust_backend_config()`) produce these and
|
|
54
|
+
* the runner consumes them through `spawn_backend`.
|
|
55
|
+
*
|
|
56
|
+
* Path defaults match the standard fuz_app surface — Deno + Rust spine
|
|
57
|
+
* (`zzz_server`, `fuz_forge_server`, `testing_spine_stub`) all converge on
|
|
58
|
+
* `/api/account/{bootstrap,login,logout,password}`,
|
|
59
|
+
* `/api/rpc`, `/api/ws`, `/health`. Override only when a backend
|
|
60
|
+
* deliberately diverges (which it shouldn't, per the contract).
|
|
61
|
+
*/
|
|
62
|
+
export interface BackendConfig {
|
|
63
|
+
/** Diagnostic label (`"deno"`, `"rust"`, `"spine_stub"`). Surfaces in test output. */
|
|
64
|
+
readonly name: string;
|
|
65
|
+
/** argv passed to the spawn. The first entry is the binary path. */
|
|
66
|
+
readonly start_command: ReadonlyArray<string>;
|
|
67
|
+
/** Base URL for HTTP requests, including port (e.g. `http://localhost:8788`). */
|
|
68
|
+
readonly base_url: string;
|
|
69
|
+
/** JSON-RPC endpoint mount point. Default `/api/rpc`. */
|
|
70
|
+
readonly rpc_path: string;
|
|
71
|
+
/** WebSocket endpoint mount point. Default `/api/ws`. */
|
|
72
|
+
readonly ws_path: string;
|
|
73
|
+
/**
|
|
74
|
+
* SSE stream mount point — drives the cross-process SSE suite's stream
|
|
75
|
+
* path. Optional: only backends advertising `capabilities.sse` serve a
|
|
76
|
+
* stream, and the suite defaults to `/api/admin/audit/stream` (the
|
|
77
|
+
* standard fuz_app audit-log stream) when omitted. Set it only when a
|
|
78
|
+
* backend mounts its stream elsewhere.
|
|
79
|
+
*/
|
|
80
|
+
readonly sse_path?: string;
|
|
81
|
+
/** Readiness probe path. Default `/health`. */
|
|
82
|
+
readonly health_path: string;
|
|
83
|
+
/** Bootstrap POST path. Default `/api/account/bootstrap`. */
|
|
84
|
+
readonly bootstrap_path: string;
|
|
85
|
+
/**
|
|
86
|
+
* Session cookie name the backend issues. Default `fuz_session` per
|
|
87
|
+
* the ecosystem convergence; consumers using a custom session name
|
|
88
|
+
* (legacy `zzz_session`, etc.) override. `default_cross_process_setup`
|
|
89
|
+
* extracts the per-account session value from the transport jar by
|
|
90
|
+
* this name so the cross-process `TestAccount.session_cookie` matches
|
|
91
|
+
* the in-process shape.
|
|
92
|
+
*/
|
|
93
|
+
readonly cookie_name: string;
|
|
94
|
+
/** How long to wait for the health probe (ms) before giving up. */
|
|
95
|
+
readonly startup_timeout_ms: number;
|
|
96
|
+
/**
|
|
97
|
+
* Env vars merged into the child process. Must include the binary's
|
|
98
|
+
* `*_BOOTSTRAP_TOKEN_PATH` + `*_DAEMON_TOKEN_PATH` env var names so
|
|
99
|
+
* the binary reads/writes the right files. Also must include the
|
|
100
|
+
* binary's `*_ALLOWED_ORIGINS` (typically
|
|
101
|
+
* `'http://localhost:*'` for cross-process tests).
|
|
102
|
+
*/
|
|
103
|
+
readonly env: Readonly<Record<string, string>>;
|
|
104
|
+
/** Auth bootstrap details — see `BackendBootstrapConfig`. */
|
|
105
|
+
readonly bootstrap: BackendBootstrapConfig;
|
|
106
|
+
/**
|
|
107
|
+
* Capabilities this backend supports — drives `test_if(capabilities.X, ...)`
|
|
108
|
+
* gating in suite bodies. See `capabilities.ts` for the vocabulary and
|
|
109
|
+
* existing flags. Cross-process backends set `in_process_only: false`.
|
|
110
|
+
*/
|
|
111
|
+
readonly capabilities: BackendCapabilities;
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=backend_config.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAE3D;;;;;;;;;;GAUG;AACH,MAAM,WAAW,sBAAsB;IACtC,qFAAqF;IACrF,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,uDAAuD;IACvD,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B;;;;;;OAMG;IACH,QAAQ,CAAC,iBAAiB,EAAE,MAAM,CAAC;CACnC;AAED;;;;;;;;;;GAUG;AACH,MAAM,WAAW,aAAa;IAC7B,sFAAsF;IACtF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,oEAAoE;IACpE,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;IAC9C,iFAAiF;IACjF,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,yDAAyD;IACzD,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAC3B,+CAA+C;IAC/C,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,6DAA6D;IAC7D,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC;;;;;;;OAOG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,mEAAmE;IACnE,QAAQ,CAAC,kBAAkB,EAAE,MAAM,CAAC;IACpC;;;;;;OAMG;IACH,QAAQ,CAAC,GAAG,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAC/C,6DAA6D;IAC7D,QAAQ,CAAC,SAAS,EAAE,sBAAsB,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;CAC3C"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|