@fuzdev/fuz_app 0.64.0 → 0.65.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.
Files changed (111) hide show
  1. package/dist/actions/CLAUDE.md +513 -928
  2. package/dist/actions/broadcast_api.d.ts +1 -1
  3. package/dist/actions/broadcast_api.js +1 -1
  4. package/dist/actions/cancel.d.ts +2 -2
  5. package/dist/actions/cancel.js +3 -3
  6. package/dist/actions/connection_closer.d.ts +1 -4
  7. package/dist/actions/connection_closer.d.ts.map +1 -1
  8. package/dist/actions/connection_closer.js +1 -4
  9. package/dist/actions/register_action_ws.d.ts +2 -2
  10. package/dist/actions/register_ws_endpoint.d.ts +1 -1
  11. package/dist/actions/transports_ws_auth_guard.d.ts +1 -2
  12. package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
  13. package/dist/actions/transports_ws_auth_guard.js +1 -2
  14. package/dist/auth/CLAUDE.md +591 -1871
  15. package/dist/auth/account_schema.d.ts +1 -1
  16. package/dist/auth/account_schema.d.ts.map +1 -1
  17. package/dist/auth/api_token_queries.js +1 -1
  18. package/dist/auth/audit_log_ddl.d.ts +1 -1
  19. package/dist/auth/audit_log_ddl.d.ts.map +1 -1
  20. package/dist/auth/audit_log_ddl.js +1 -1
  21. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  22. package/dist/auth/bootstrap_account.js +1 -5
  23. package/dist/auth/bootstrap_routes.d.ts +7 -1
  24. package/dist/auth/bootstrap_routes.d.ts.map +1 -1
  25. package/dist/auth/bootstrap_routes.js +15 -11
  26. package/dist/auth/keyring.d.ts +6 -6
  27. package/dist/auth/keyring.js +8 -8
  28. package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
  29. package/dist/auth/role_grant_offer_actions.js +4 -2
  30. package/dist/db/create_db.d.ts.map +1 -1
  31. package/dist/db/create_db.js +13 -0
  32. package/dist/dev/setup.d.ts +2 -2
  33. package/dist/dev/setup.js +3 -3
  34. package/dist/http/CLAUDE.md +224 -498
  35. package/dist/http/error_schemas.d.ts +0 -4
  36. package/dist/http/error_schemas.d.ts.map +1 -1
  37. package/dist/http/error_schemas.js +0 -4
  38. package/dist/http/ip_canonical.d.ts +5 -4
  39. package/dist/http/ip_canonical.d.ts.map +1 -1
  40. package/dist/http/ip_canonical.js +8 -4
  41. package/dist/http/origin.d.ts +1 -1
  42. package/dist/http/origin.js +1 -1
  43. package/dist/runtime/mock.js +1 -1
  44. package/dist/server/app_server.d.ts +41 -10
  45. package/dist/server/app_server.d.ts.map +1 -1
  46. package/dist/server/app_server.js +10 -4
  47. package/dist/server/env.d.ts +7 -7
  48. package/dist/server/env.d.ts.map +1 -1
  49. package/dist/server/env.js +14 -14
  50. package/dist/server/static.d.ts +4 -4
  51. package/dist/server/static.js +7 -7
  52. package/dist/testing/CLAUDE.md +220 -46
  53. package/dist/testing/admin_integration.d.ts +18 -23
  54. package/dist/testing/admin_integration.d.ts.map +1 -1
  55. package/dist/testing/admin_integration.js +159 -201
  56. package/dist/testing/app_server.d.ts +125 -38
  57. package/dist/testing/app_server.d.ts.map +1 -1
  58. package/dist/testing/app_server.js +140 -42
  59. package/dist/testing/audit_completeness.d.ts +23 -22
  60. package/dist/testing/audit_completeness.d.ts.map +1 -1
  61. package/dist/testing/audit_completeness.js +199 -156
  62. package/dist/testing/bootstrap_success.d.ts +28 -0
  63. package/dist/testing/bootstrap_success.d.ts.map +1 -0
  64. package/dist/testing/bootstrap_success.js +144 -0
  65. package/dist/testing/cross_backend/capabilities.d.ts +64 -0
  66. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -0
  67. package/dist/testing/cross_backend/capabilities.js +47 -0
  68. package/dist/testing/cross_backend/setup.d.ts +215 -0
  69. package/dist/testing/cross_backend/setup.d.ts.map +1 -0
  70. package/dist/testing/cross_backend/setup.js +101 -0
  71. package/dist/testing/data_exposure.d.ts +14 -15
  72. package/dist/testing/data_exposure.d.ts.map +1 -1
  73. package/dist/testing/data_exposure.js +127 -146
  74. package/dist/testing/db_entities.d.ts +11 -1
  75. package/dist/testing/db_entities.d.ts.map +1 -1
  76. package/dist/testing/db_entities.js +13 -1
  77. package/dist/testing/integration.d.ts +35 -21
  78. package/dist/testing/integration.d.ts.map +1 -1
  79. package/dist/testing/integration.js +231 -291
  80. package/dist/testing/integration_helpers.d.ts +16 -6
  81. package/dist/testing/integration_helpers.d.ts.map +1 -1
  82. package/dist/testing/integration_helpers.js +7 -7
  83. package/dist/testing/mock_fs.d.ts.map +1 -1
  84. package/dist/testing/mock_fs.js +0 -2
  85. package/dist/testing/rate_limiting.d.ts.map +1 -1
  86. package/dist/testing/rate_limiting.js +9 -0
  87. package/dist/testing/role_grant_helpers.d.ts +31 -0
  88. package/dist/testing/role_grant_helpers.d.ts.map +1 -0
  89. package/dist/testing/role_grant_helpers.js +46 -0
  90. package/dist/testing/round_trip.d.ts +21 -16
  91. package/dist/testing/round_trip.d.ts.map +1 -1
  92. package/dist/testing/round_trip.js +65 -86
  93. package/dist/testing/rpc_round_trip.d.ts +24 -21
  94. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  95. package/dist/testing/rpc_round_trip.js +91 -104
  96. package/dist/testing/schema_introspect.d.ts +106 -0
  97. package/dist/testing/schema_introspect.d.ts.map +1 -0
  98. package/dist/testing/schema_introspect.js +123 -0
  99. package/dist/testing/schema_parity.d.ts +144 -0
  100. package/dist/testing/schema_parity.d.ts.map +1 -0
  101. package/dist/testing/schema_parity.js +233 -0
  102. package/dist/testing/standard.d.ts +57 -25
  103. package/dist/testing/standard.d.ts.map +1 -1
  104. package/dist/testing/standard.js +62 -5
  105. package/dist/testing/stubs.d.ts +11 -3
  106. package/dist/testing/stubs.d.ts.map +1 -1
  107. package/dist/testing/stubs.js +24 -21
  108. package/dist/testing/transports/surface_source.d.ts +51 -0
  109. package/dist/testing/transports/surface_source.d.ts.map +1 -0
  110. package/dist/testing/transports/surface_source.js +19 -0
  111. package/package.json +4 -4
@@ -0,0 +1,28 @@
1
+ import './assert_dev_env.js';
2
+ import type { SessionOptions } from '../auth/session_cookie.js';
3
+ import type { AppServerContext, BootstrapLiveOptions } from '../server/app_server.js';
4
+ import type { RouteSpec } from '../http/route_spec.js';
5
+ import type { RpcEndpointsSuiteOption } from './rpc_helpers.js';
6
+ /** Options for `describe_bootstrap_success_tests`. */
7
+ export interface BootstrapSuccessTestOptions {
8
+ session_options: SessionOptions<string>;
9
+ /** Same factory the consumer's production server uses. */
10
+ create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
11
+ /** RPC endpoints — passed through to `create_app_server` for shape parity. */
12
+ rpc_endpoints?: RpcEndpointsSuiteOption;
13
+ /**
14
+ * Live bootstrap config — the suite drives `POST /bootstrap` against
15
+ * `bootstrap.token_path`. The suite does NOT assert on `on_bootstrap`
16
+ * callback invocation (Hono-coupled signature is in-process only);
17
+ * assertions land on observable DB state.
18
+ */
19
+ bootstrap: BootstrapLiveOptions;
20
+ /** Override the synthetic token text. Default deterministic. */
21
+ bootstrap_token?: string;
22
+ }
23
+ /**
24
+ * Run the bootstrap success-path test suite against the consumer's
25
+ * production-shaped wiring.
26
+ */
27
+ export declare const describe_bootstrap_success_tests: (options: BootstrapSuccessTestOptions) => void;
28
+ //# sourceMappingURL=bootstrap_success.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap_success.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/bootstrap_success.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAsB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAE,oBAAoB,EAAC,MAAM,yBAAyB,CAAC;AACpF,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAGrD,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,kBAAkB,CAAC;AAM9D,sDAAsD;AACtD,MAAM,WAAW,2BAA2B;IAC3C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,0DAA0D;IAC1D,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,8EAA8E;IAC9E,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC;;;;;OAKG;IACH,SAAS,EAAE,oBAAoB,CAAC;IAChC,gEAAgE;IAChE,eAAe,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,eAAO,MAAM,gCAAgC,GAAI,SAAS,2BAA2B,KAAG,IAyIvF,CAAC"}
@@ -0,0 +1,144 @@
1
+ import './assert_dev_env.js';
2
+ /**
3
+ * Bootstrap success-path suite for consumer projects.
4
+ *
5
+ * Exercises `POST /bootstrap` against an empty DB (no pre-keeper, lock
6
+ * unflipped) through the real `bootstrap_account` flow. Asserts on
7
+ * observable state — account exists, `bootstrap_lock.bootstrapped` is
8
+ * true, audit row emitted, response body shape — rather than
9
+ * `on_bootstrap` callback invocation, so the suite stays cross-impl
10
+ * friendly when Phase 3 cross-process testing wires it against a
11
+ * spawned Rust backend.
12
+ *
13
+ * Folded into `describe_standard_tests` with a `bootstrap.mode === 'live'`
14
+ * silent-skip gate; consumers wiring live bootstrap pick up success-path
15
+ * coverage by default.
16
+ *
17
+ * @module
18
+ */
19
+ import { describe, test, assert } from 'vitest';
20
+ import { ERROR_ALREADY_BOOTSTRAPPED, ERROR_INVALID_TOKEN } from '../http/error_schemas.js';
21
+ import { create_test_app_for_bootstrap } from './app_server.js';
22
+ const DEFAULT_TEST_TOKEN = 'test-bootstrap-token-value-deterministic';
23
+ const TEST_USERNAME = 'keeper';
24
+ const TEST_PASSWORD = 'test-password-with-min-12-chars';
25
+ /**
26
+ * Run the bootstrap success-path test suite against the consumer's
27
+ * production-shaped wiring.
28
+ */
29
+ export const describe_bootstrap_success_tests = (options) => {
30
+ const token = options.bootstrap_token ?? DEFAULT_TEST_TOKEN;
31
+ const route_prefix = options.bootstrap.route_prefix ?? '/api/account';
32
+ const bootstrap_path = `${route_prefix}/bootstrap`;
33
+ describe('bootstrap success path', () => {
34
+ test('POST /bootstrap with valid token creates the keeper account and flips the lock', async () => {
35
+ const test_app = await create_test_app_for_bootstrap({
36
+ session_options: options.session_options,
37
+ create_route_specs: options.create_route_specs,
38
+ rpc_endpoints: options.rpc_endpoints,
39
+ bootstrap: options.bootstrap,
40
+ bootstrap_token: token,
41
+ });
42
+ try {
43
+ const response = await test_app.app.request(bootstrap_path, {
44
+ method: 'POST',
45
+ headers: test_app.create_request_headers({ 'content-type': 'application/json' }),
46
+ body: JSON.stringify({
47
+ token,
48
+ username: TEST_USERNAME,
49
+ password: TEST_PASSWORD,
50
+ }),
51
+ });
52
+ // Response shape
53
+ assert.strictEqual(response.status, 200);
54
+ const body = (await response.json());
55
+ assert.strictEqual(body.ok, true);
56
+ assert.strictEqual(body.account.username, TEST_USERNAME);
57
+ assert.ok(body.account.id);
58
+ assert.ok(body.actor.id);
59
+ // Observable state: account exists in DB
60
+ const account = await test_app.backend.deps.db.query_one('SELECT username FROM account WHERE username = $1', [TEST_USERNAME]);
61
+ assert.ok(account);
62
+ // Observable state: bootstrap_lock flipped to true
63
+ const lock = await test_app.backend.deps.db.query_one('SELECT bootstrapped FROM bootstrap_lock WHERE id = 1');
64
+ assert.ok(lock);
65
+ assert.strictEqual(lock.bootstrapped, true);
66
+ // Observable state: audit row emitted
67
+ const audit_row = await test_app.backend.deps.db.query_one("SELECT event_type, account_id FROM audit_log WHERE event_type = 'bootstrap' AND outcome = 'success' LIMIT 1");
68
+ assert.ok(audit_row);
69
+ assert.strictEqual(audit_row.account_id, body.account.id);
70
+ }
71
+ finally {
72
+ await test_app.cleanup();
73
+ }
74
+ });
75
+ test('second POST /bootstrap returns 403 ALREADY_BOOTSTRAPPED', async () => {
76
+ const test_app = await create_test_app_for_bootstrap({
77
+ session_options: options.session_options,
78
+ create_route_specs: options.create_route_specs,
79
+ rpc_endpoints: options.rpc_endpoints,
80
+ bootstrap: options.bootstrap,
81
+ bootstrap_token: token,
82
+ });
83
+ try {
84
+ // First bootstrap succeeds
85
+ const first = await test_app.app.request(bootstrap_path, {
86
+ method: 'POST',
87
+ headers: test_app.create_request_headers({ 'content-type': 'application/json' }),
88
+ body: JSON.stringify({
89
+ token,
90
+ username: TEST_USERNAME,
91
+ password: TEST_PASSWORD,
92
+ }),
93
+ });
94
+ assert.strictEqual(first.status, 200);
95
+ // Second attempt blocked by lock
96
+ const second = await test_app.app.request(bootstrap_path, {
97
+ method: 'POST',
98
+ headers: test_app.create_request_headers({ 'content-type': 'application/json' }),
99
+ body: JSON.stringify({
100
+ token,
101
+ username: 'another_user',
102
+ password: TEST_PASSWORD,
103
+ }),
104
+ });
105
+ assert.strictEqual(second.status, 403);
106
+ const body = (await second.json());
107
+ assert.strictEqual(body.error, ERROR_ALREADY_BOOTSTRAPPED);
108
+ }
109
+ finally {
110
+ await test_app.cleanup();
111
+ }
112
+ });
113
+ test('POST /bootstrap with wrong token returns 401 INVALID_TOKEN', async () => {
114
+ const test_app = await create_test_app_for_bootstrap({
115
+ session_options: options.session_options,
116
+ create_route_specs: options.create_route_specs,
117
+ rpc_endpoints: options.rpc_endpoints,
118
+ bootstrap: options.bootstrap,
119
+ bootstrap_token: token,
120
+ });
121
+ try {
122
+ const response = await test_app.app.request(bootstrap_path, {
123
+ method: 'POST',
124
+ headers: test_app.create_request_headers({ 'content-type': 'application/json' }),
125
+ body: JSON.stringify({
126
+ token: 'wrong-token-value-that-does-not-match',
127
+ username: TEST_USERNAME,
128
+ password: TEST_PASSWORD,
129
+ }),
130
+ });
131
+ assert.strictEqual(response.status, 401);
132
+ const body = (await response.json());
133
+ assert.strictEqual(body.error, ERROR_INVALID_TOKEN);
134
+ // Observable state: lock NOT flipped (transaction rolled back on auth failure)
135
+ const lock = await test_app.backend.deps.db.query_one('SELECT bootstrapped FROM bootstrap_lock WHERE id = 1');
136
+ assert.ok(lock);
137
+ assert.strictEqual(lock.bootstrapped, false);
138
+ }
139
+ finally {
140
+ await test_app.cleanup();
141
+ }
142
+ });
143
+ });
144
+ };
@@ -0,0 +1,64 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Optional behaviors a backend may support. Each flag's TSDoc names the
4
+ * tests that gate on it; add a new flag here before referencing it from
5
+ * a suite body, and document the gating tests inline.
6
+ */
7
+ export interface BackendCapabilities {
8
+ /**
9
+ * Bearer token auth (`Authorization: Bearer <token>`) is wired through
10
+ * the backend's middleware stack. Gates the bearer-token cases in
11
+ * `describe_standard_integration_tests` and `describe_rate_limiting_tests`.
12
+ */
13
+ readonly bearer_auth: boolean;
14
+ /**
15
+ * Trusted-proxy XFF parsing is wired (`X-Forwarded-For` etc.). Gates
16
+ * the proxy-resolution cases in `describe_standard_integration_tests`
17
+ * and the future cross-process proxy integration suite.
18
+ */
19
+ readonly trusted_proxy: boolean;
20
+ /**
21
+ * Per-account login rate limiting is wired. Gates the per-account
22
+ * rate-limit cases in `describe_rate_limiting_tests`.
23
+ */
24
+ readonly login_rate_limit: boolean;
25
+ /**
26
+ * WebSocket transport is reachable end-to-end. Gates the cross-process
27
+ * WS round-trip suite; the in-process `describe_ws_round_trip_tests`
28
+ * runs against `register_action_ws` directly and ignores this flag.
29
+ */
30
+ readonly ws: boolean;
31
+ /**
32
+ * SSE transport is reachable end-to-end. Gates the cross-process SSE
33
+ * close-detection cases; in-process SSE uses the
34
+ * `on_audit_event` hook and ignores this flag.
35
+ */
36
+ readonly sse: boolean;
37
+ /**
38
+ * Test has direct access to backend-internal state (keyring for
39
+ * signing cookies, DB pool for FK-structural raw queries). Always
40
+ * `true` for in-process Hono via `default_in_process_setup`; always
41
+ * `false` cross-process. Gates the 3 keyring reads in
42
+ * `describe_standard_integration_tests` (expired-cookie generation)
43
+ * and the FK-structural raw query in `describe_audit_completeness_tests`.
44
+ */
45
+ readonly in_process_only: boolean;
46
+ }
47
+ /**
48
+ * Capability declarations for the in-process Hono transport. Every flag
49
+ * is `true` because in-process testing exercises the full backend with
50
+ * no missing optional behaviors. Cross-process consumers
51
+ * declare each flag explicitly per backend.
52
+ */
53
+ export declare const in_process_capabilities: BackendCapabilities;
54
+ /**
55
+ * Conditional `test()` wrapper — registers a vitest case only when
56
+ * `cond` is true; otherwise registers it as `.skip` so the run still
57
+ * surfaces the gated coverage in the report.
58
+ *
59
+ * Thin wrapper around vitest's `test.skipIf(!cond)` with the argument
60
+ * order flipped to match the more readable `test_if(capabilities.bearer_auth, ...)`
61
+ * call pattern.
62
+ */
63
+ export declare const test_if: (cond: boolean, name: string, fn: () => void | Promise<void>) => void;
64
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/capabilities.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmB9B;;;;GAIG;AACH,MAAM,WAAW,mBAAmB;IACnC;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,OAAO,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,OAAO,CAAC;IAChC;;;OAGG;IACH,QAAQ,CAAC,gBAAgB,EAAE,OAAO,CAAC;IACnC;;;;OAIG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC;IACtB;;;;;;;OAOG;IACH,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;CAClC;AAED;;;;;GAKG;AACH,eAAO,MAAM,uBAAuB,EAAE,mBAOpC,CAAC;AAEH;;;;;;;;GAQG;AACH,eAAO,MAAM,OAAO,GAAI,MAAM,OAAO,EAAE,MAAM,MAAM,EAAE,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAG,IAMrF,CAAC"}
@@ -0,0 +1,47 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Capability vocabulary for cross-backend integration testing.
4
+ *
5
+ * Backends declare which optional behaviors they support; suite bodies
6
+ * call `test_if(capabilities.X, ...)` to skip cases the backend doesn't
7
+ * implement. No `if (config.name === 'rust')` branches anywhere — name-
8
+ * checking is a code smell that says capability vocabulary is missing.
9
+ *
10
+ * In-process Hono via `default_in_process_setup` declares every
11
+ * capability `true` (see `in_process_capabilities`). Cross-process
12
+ * backends opt in per-flag on their `BackendConfig`.
13
+ *
14
+ * @module
15
+ */
16
+ import { test } from 'vitest';
17
+ /**
18
+ * Capability declarations for the in-process Hono transport. Every flag
19
+ * is `true` because in-process testing exercises the full backend with
20
+ * no missing optional behaviors. Cross-process consumers
21
+ * declare each flag explicitly per backend.
22
+ */
23
+ export const in_process_capabilities = Object.freeze({
24
+ bearer_auth: true,
25
+ trusted_proxy: true,
26
+ login_rate_limit: true,
27
+ ws: true,
28
+ sse: true,
29
+ in_process_only: true,
30
+ });
31
+ /**
32
+ * Conditional `test()` wrapper — registers a vitest case only when
33
+ * `cond` is true; otherwise registers it as `.skip` so the run still
34
+ * surfaces the gated coverage in the report.
35
+ *
36
+ * Thin wrapper around vitest's `test.skipIf(!cond)` with the argument
37
+ * order flipped to match the more readable `test_if(capabilities.bearer_auth, ...)`
38
+ * call pattern.
39
+ */
40
+ export const test_if = (cond, name, fn) => {
41
+ if (cond) {
42
+ test(name, fn);
43
+ }
44
+ else {
45
+ test.skip(name, fn);
46
+ }
47
+ };
@@ -0,0 +1,215 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Per-test fixture protocol shared by in-process and cross-process
4
+ * transports.
5
+ *
6
+ * Each standard suite body takes a required
7
+ * `setup_test: () => Promise<TestFixture>` callback and invokes it once
8
+ * per test. The fixture carries everything a test needs to fire requests
9
+ * and assert on a single bootstrapped keeper account — transport,
10
+ * account / actor identity, three header builders, a multi-account mint
11
+ * factory, and (in-process only) the in-memory keyring + raw backend.
12
+ *
13
+ * `default_in_process_setup(options)` wraps `create_test_app` into the
14
+ * `SetupTest` contract. The cross-process sibling
15
+ * (`default_cross_process_setup`) lands alongside the spawn-a-backend
16
+ * transport plumbing — it implements the same contract by spawning a
17
+ * binary and bootstrapping over real HTTP.
18
+ *
19
+ * @module
20
+ */
21
+ import type { Uuid } from '@fuzdev/fuz_util/id.js';
22
+ import type { Keyring } from '../../auth/keyring.js';
23
+ import type { RouteSpec } from '../../http/route_spec.js';
24
+ import type { AppServerContext, BootstrapServerOptions } from '../../server/app_server.js';
25
+ import type { SessionOptions } from '../../auth/session_cookie.js';
26
+ import { type CreateTestAppOptions, type SuiteAppOptions, type TestAccount, type TestAppServer } from '../app_server.js';
27
+ import { type RpcTestTransport, type RpcEndpointsSuiteOption } from '../rpc_helpers.js';
28
+ import { type BackendCapabilities } from './capabilities.js';
29
+ import type { SurfaceSource } from '../transports/surface_source.js';
30
+ /**
31
+ * Options for `TestFixture.create_account` — mints an additional
32
+ * bootstrapped account alongside the keeper. Matches the existing
33
+ * `TestApp.create_account` signature so the migration to fixture-style
34
+ * reads is a one-site call rewrite per use.
35
+ */
36
+ export interface CreateTestAccountOptions {
37
+ readonly username?: string;
38
+ readonly password_value?: string;
39
+ readonly roles?: Array<string>;
40
+ }
41
+ /**
42
+ * Shape returned by `TestFixture.create_account`. Aliased to the
43
+ * existing `TestAccount` interface from `app_server.ts` — same shape,
44
+ * stable name on the cross-backend testing surface so call sites read
45
+ * `fixture.create_account(...)` returning `TestAccountFixture` without
46
+ * crossing module boundaries.
47
+ */
48
+ export type TestAccountFixture = TestAccount;
49
+ /**
50
+ * Fields shared by every `TestFixture` regardless of transport. The
51
+ * discriminated union below adds in-process-only fields conditionally.
52
+ */
53
+ export interface TestFixtureBase {
54
+ /** Transport for this test's HTTP requests (cookie-threaded cross-process). */
55
+ readonly transport: RpcTestTransport;
56
+ /** The freshly-bootstrapped keeper account. */
57
+ readonly account: {
58
+ readonly id: Uuid;
59
+ readonly username: string;
60
+ };
61
+ /** The actor linked to the keeper account. */
62
+ readonly actor: {
63
+ readonly id: Uuid;
64
+ };
65
+ /** Build request headers with the keeper's session cookie. */
66
+ readonly create_session_headers: (extra?: Record<string, string>) => Record<string, string>;
67
+ /** Build request headers with the keeper's bearer token. */
68
+ readonly create_bearer_headers: (extra?: Record<string, string>) => Record<string, string>;
69
+ /** Build request headers with the daemon token (keeper auth). */
70
+ readonly create_daemon_token_headers: (extra?: Record<string, string>) => Record<string, string>;
71
+ /**
72
+ * Mint an additional bootstrapped account for cross-account / multi-user
73
+ * tests. In-process: re-uses `create_test_account_with_credentials` against the same DB;
74
+ * cross-process: goes through the consumer-supplied DB-admin
75
+ * channel.
76
+ */
77
+ readonly create_account: (options?: CreateTestAccountOptions) => Promise<TestAccountFixture>;
78
+ }
79
+ /**
80
+ * The per-test bundle returned by `SetupTest`. Every Tier 1 suite body
81
+ * reads exclusively from this shape — no `test_app.backend.*` reads
82
+ * remain in the suite bodies.
83
+ *
84
+ * Discriminated by `in_process`: when `true`, `keyring` and
85
+ * `backend_internals` are present (compile-time narrowed); when `false`,
86
+ * they're absent and the suite body must either run via the public wire
87
+ * (cross-process) or gate the test with
88
+ * `test_if(capabilities.in_process_only, ...)`. The discriminant value
89
+ * mirrors `BackendCapabilities.in_process_only` — both source from the
90
+ * same producer (`default_in_process_setup` vs. cross-process variant).
91
+ *
92
+ * Suite bodies narrow with `assert(fixture.in_process)` after
93
+ * `setup_test()`; sites that reach for `keyring` or `backend_internals`
94
+ * are in-process-only by structure and the assertion surfaces a clear
95
+ * failure if a future cross-process consumer reaches them without a
96
+ * `test_if` gate.
97
+ */
98
+ export type TestFixture = (TestFixtureBase & {
99
+ readonly in_process: true;
100
+ /**
101
+ * Test-only keyring access — in-process only. Used for
102
+ * expired-cookie generation in `describe_standard_integration_tests`.
103
+ */
104
+ readonly keyring: Keyring;
105
+ /**
106
+ * Raw backend access (`deps.db`, etc.) — in-process only. Used by
107
+ * `create_test_role_grant_direct` seed sites in
108
+ * `describe_standard_admin_integration_tests` and the
109
+ * origin-verification cookie-composition sites in
110
+ * `describe_standard_integration_tests`.
111
+ */
112
+ readonly backend_internals: TestAppServer;
113
+ }) | (TestFixtureBase & {
114
+ readonly in_process: false;
115
+ });
116
+ /**
117
+ * Per-test fixture-producing function. Invoked once inside every
118
+ * `test()` body. The implementation captures factory inputs (in-process)
119
+ * or a long-running backend handle (cross-process) and creates
120
+ * a fresh per-test bundle on each call.
121
+ */
122
+ export type SetupTest = () => Promise<TestFixture>;
123
+ /**
124
+ * Build a `SetupTest` that creates a fresh `TestApp` per call via
125
+ * `create_test_app` and projects it into the `TestFixture` shape.
126
+ *
127
+ * Same factory inputs `create_test_app` already takes — this helper
128
+ * is a projection layer, not a new lifecycle. fuz_app's own `src/test/`
129
+ * and consumer suites pass `default_in_process_setup({...factory_inputs})`
130
+ * in place of the old per-suite factory-input bundle.
131
+ *
132
+ * The describe-level `auth_integration_truncate_tables` / pglite WASM
133
+ * cache lifecycle stays in `create_pglite_factory` / `create_describe_db`
134
+ * (`testing/db.js`) — `default_in_process_setup` doesn't manage db state
135
+ * beyond what `create_test_app` already does.
136
+ */
137
+ export declare const default_in_process_setup: (options: CreateTestAppOptions) => SetupTest;
138
+ /**
139
+ * Consumer-facing options for `default_in_process_suite_options` — the
140
+ * minimal factory inputs both `default_in_process_setup` and
141
+ * `create_test_app_surface_spec` consume to produce the
142
+ * `{setup_test, surface_source, capabilities}` bundle.
143
+ */
144
+ export interface DefaultInProcessSuiteOptions {
145
+ session_options: SessionOptions<string>;
146
+ create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
147
+ rpc_endpoints?: RpcEndpointsSuiteOption;
148
+ /**
149
+ * Bootstrap config — top-level slot, single source of truth for both
150
+ * surface generation and live dispatch. Same precedent as
151
+ * `rpc_endpoints`. Discriminated by `mode`; omit for the default (no
152
+ * bootstrap route mounted).
153
+ */
154
+ bootstrap?: BootstrapServerOptions;
155
+ app_options?: SuiteAppOptions;
156
+ /**
157
+ * Additional roles to grant the bootstrapped keeper alongside
158
+ * `ROLE_KEEPER` — additive, never replaces. The keeper account
159
+ * always holds `ROLE_KEEPER` (otherwise daemon-token auth breaks);
160
+ * pass extras here for suites that need additional role coverage.
161
+ *
162
+ * Admin-suite consumers pass `[ROLE_ADMIN]` so the default keeper
163
+ * can hit admin-gated RPC methods.
164
+ * `describe_standard_admin_integration_tests` and
165
+ * `describe_audit_completeness_tests` need this.
166
+ */
167
+ extra_keeper_roles?: Array<string>;
168
+ /**
169
+ * Pre-built `SurfaceSource` — overrides the default which calls
170
+ * `create_test_app_surface_spec` against the same factory inputs.
171
+ * Pass when surface assembly needs fields outside the shared subset
172
+ * (e.g. `env_schema`, `event_specs`, `ws_endpoints`, `transform_middleware`).
173
+ */
174
+ surface_source?: SurfaceSource;
175
+ }
176
+ /**
177
+ * Build the full in-process suite bundle in a single helper invocation.
178
+ * Output covers `{setup_test, surface_source, capabilities}` plus every
179
+ * factory input the Tier 1 suites read at their top level
180
+ * (`session_options`, `create_route_specs`, `rpc_endpoints`) — so the
181
+ * call site spreads once and adds only suite-specific extras
182
+ * (`roles`, `skip_routes`, `input_overrides`, `db_factories`, ...).
183
+ *
184
+ * ```ts
185
+ * // Suite-extras-free call: helper output is the entire options bag.
186
+ * describe_round_trip_validation(default_in_process_suite_options({
187
+ * session_options,
188
+ * create_route_specs,
189
+ * rpc_endpoints: [rpc_endpoint_spec],
190
+ * }));
191
+ *
192
+ * // With suite-specific extras: spread and add.
193
+ * describe_standard_admin_integration_tests({
194
+ * ...default_in_process_suite_options({
195
+ * session_options, create_route_specs, rpc_endpoints,
196
+ * extra_keeper_roles: [ROLE_ADMIN],
197
+ * }),
198
+ * roles,
199
+ * });
200
+ * ```
201
+ *
202
+ * Suites that don't read `session_options` / `rpc_endpoints` at their
203
+ * top level (`round_trip`, `data_exposure`) accept the spread anyway —
204
+ * excess properties on spread sources aren't checked by TS, and the
205
+ * uniform shape keeps consumer call sites mechanical.
206
+ */
207
+ export declare const default_in_process_suite_options: <const O extends DefaultInProcessSuiteOptions>(options: O) => {
208
+ setup_test: SetupTest;
209
+ surface_source: SurfaceSource;
210
+ capabilities: BackendCapabilities;
211
+ session_options: O["session_options"];
212
+ create_route_specs: O["create_route_specs"];
213
+ rpc_endpoints: O["rpc_endpoints"];
214
+ };
215
+ //# sourceMappingURL=setup.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,101 @@
1
+ import '../assert_dev_env.js';
2
+ import { ROLE_KEEPER } from '../../auth/role_schema.js';
3
+ import { create_test_app, } from '../app_server.js';
4
+ import { create_test_app_surface_spec } from '../stubs.js';
5
+ import { http_transport, } from '../rpc_helpers.js';
6
+ import { in_process_capabilities } from './capabilities.js';
7
+ /**
8
+ * Build a `SetupTest` that creates a fresh `TestApp` per call via
9
+ * `create_test_app` and projects it into the `TestFixture` shape.
10
+ *
11
+ * Same factory inputs `create_test_app` already takes — this helper
12
+ * is a projection layer, not a new lifecycle. fuz_app's own `src/test/`
13
+ * and consumer suites pass `default_in_process_setup({...factory_inputs})`
14
+ * in place of the old per-suite factory-input bundle.
15
+ *
16
+ * The describe-level `auth_integration_truncate_tables` / pglite WASM
17
+ * cache lifecycle stays in `create_pglite_factory` / `create_describe_db`
18
+ * (`testing/db.js`) — `default_in_process_setup` doesn't manage db state
19
+ * beyond what `create_test_app` already does.
20
+ */
21
+ export const default_in_process_setup = (options) => async () => {
22
+ const test_app = await create_test_app(options);
23
+ return {
24
+ in_process: true,
25
+ transport: http_transport(test_app.app),
26
+ account: test_app.backend.account,
27
+ actor: test_app.backend.actor,
28
+ create_session_headers: test_app.create_session_headers,
29
+ create_bearer_headers: test_app.create_bearer_headers,
30
+ create_daemon_token_headers: test_app.create_daemon_token_headers,
31
+ create_account: test_app.create_account,
32
+ keyring: test_app.backend.keyring,
33
+ backend_internals: test_app.backend,
34
+ };
35
+ };
36
+ // NOTE: bootstrap config is read from `options.bootstrap` — top-level slot,
37
+ // single source of truth for both the surface spec (so the route appears in
38
+ // `expected_public_routes` / attack-surface iteration) AND the live
39
+ // `create_test_app` (so the route exists at dispatch time and returns 403
40
+ // `ERROR_ALREADY_BOOTSTRAPPED` matching its declared 403 schema). Same
41
+ // precedent as `rpc_endpoints`. Discriminated union shape (`mode: 'disabled'`
42
+ // | `'surface_only'` | `'live'`) replaces the old `token_path: string | null`
43
+ // overload that conflated three deployment intents on one channel.
44
+ /**
45
+ * Build the full in-process suite bundle in a single helper invocation.
46
+ * Output covers `{setup_test, surface_source, capabilities}` plus every
47
+ * factory input the Tier 1 suites read at their top level
48
+ * (`session_options`, `create_route_specs`, `rpc_endpoints`) — so the
49
+ * call site spreads once and adds only suite-specific extras
50
+ * (`roles`, `skip_routes`, `input_overrides`, `db_factories`, ...).
51
+ *
52
+ * ```ts
53
+ * // Suite-extras-free call: helper output is the entire options bag.
54
+ * describe_round_trip_validation(default_in_process_suite_options({
55
+ * session_options,
56
+ * create_route_specs,
57
+ * rpc_endpoints: [rpc_endpoint_spec],
58
+ * }));
59
+ *
60
+ * // With suite-specific extras: spread and add.
61
+ * describe_standard_admin_integration_tests({
62
+ * ...default_in_process_suite_options({
63
+ * session_options, create_route_specs, rpc_endpoints,
64
+ * extra_keeper_roles: [ROLE_ADMIN],
65
+ * }),
66
+ * roles,
67
+ * });
68
+ * ```
69
+ *
70
+ * Suites that don't read `session_options` / `rpc_endpoints` at their
71
+ * top level (`round_trip`, `data_exposure`) accept the spread anyway —
72
+ * excess properties on spread sources aren't checked by TS, and the
73
+ * uniform shape keeps consumer call sites mechanical.
74
+ */
75
+ export const default_in_process_suite_options = (options) => ({
76
+ setup_test: default_in_process_setup({
77
+ session_options: options.session_options,
78
+ create_route_specs: options.create_route_specs,
79
+ rpc_endpoints: options.rpc_endpoints,
80
+ bootstrap: options.bootstrap,
81
+ app_options: options.app_options,
82
+ roles: [ROLE_KEEPER, ...(options.extra_keeper_roles ?? [])],
83
+ }),
84
+ surface_source: options.surface_source ??
85
+ {
86
+ kind: 'inline',
87
+ spec: create_test_app_surface_spec({
88
+ session_options: options.session_options,
89
+ create_route_specs: options.create_route_specs,
90
+ rpc_endpoints: options.rpc_endpoints,
91
+ // Mirror what `create_test_app` → `create_app_server` will mount.
92
+ // Both helpers read from the top-level `bootstrap` slot so surface
93
+ // and live app stay in sync by construction.
94
+ bootstrap: options.bootstrap,
95
+ }),
96
+ },
97
+ capabilities: in_process_capabilities,
98
+ session_options: options.session_options,
99
+ create_route_specs: options.create_route_specs,
100
+ rpc_endpoints: options.rpc_endpoints,
101
+ });
@@ -1,9 +1,8 @@
1
1
  import './assert_dev_env.js';
2
- import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
3
- import type { RouteSpec } from '../http/route_spec.js';
4
- import type { AppServerContext, AppServerOptions } from '../server/app_server.js';
5
- import type { SessionOptions } from '../auth/session_cookie.js';
6
- import { type DbFactory } from './db.js';
2
+ import type { AppSurface } from '../http/surface.js';
3
+ import type { BackendCapabilities } from './cross_backend/capabilities.js';
4
+ import type { SetupTest } from './cross_backend/setup.js';
5
+ import type { SurfaceSource } from './transports/surface_source.js';
7
6
  /**
8
7
  * Recursively collect all property names from a JSON Schema.
9
8
  *
@@ -21,20 +20,20 @@ export declare const assert_output_schemas_no_sensitive_fields: (surface: AppSur
21
20
  export declare const assert_non_admin_schemas_no_admin_fields: (surface: AppSurface, admin_only_fields?: ReadonlyArray<string>) => void;
22
21
  /** Options for `describe_data_exposure_tests`. */
23
22
  export interface DataExposureTestOptions {
24
- /** Build the app surface spec (for schema-level checks). */
25
- build: () => AppSurfaceSpec;
26
- /** Session config for runtime tests. */
27
- session_options: SessionOptions<string>;
28
- /** Route spec factory for runtime tests. */
29
- create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
23
+ /** Per-test fixture-producing function (per-describe cadence). */
24
+ setup_test: SetupTest;
25
+ /**
26
+ * Source of the app surface for schema-level + route-iteration checks.
27
+ * Currently requires `kind: 'inline'` the cross-process snapshot
28
+ * variant lands alongside the spawned-backend transport plumbing.
29
+ */
30
+ surface_source: SurfaceSource;
31
+ /** Backend capability declarations. */
32
+ capabilities: BackendCapabilities;
30
33
  /** Fields that must never appear in any response. Default: `sensitive_field_blocklist`. */
31
34
  sensitive_fields?: ReadonlyArray<string>;
32
35
  /** Fields that must not appear in non-admin responses. Default: `admin_only_field_blocklist`. */
33
36
  admin_only_fields?: ReadonlyArray<string>;
34
- /** Optional overrides for `AppServerOptions`. */
35
- app_options?: Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs'>>;
36
- /** Database factories to run tests against. Default: pglite only. */
37
- db_factories?: Array<DbFactory>;
38
37
  /** Routes to skip, in `'METHOD /path'` format. */
39
38
  skip_routes?: Array<string>;
40
39
  }