@fuzdev/fuz_app 0.68.0 → 0.70.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 (105) hide show
  1. package/dist/actions/perform_action.d.ts.map +1 -1
  2. package/dist/actions/perform_action.js +10 -3
  3. package/dist/auth/admin_action_specs.d.ts +2 -3
  4. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  5. package/dist/auth/admin_action_specs.js +2 -3
  6. package/dist/auth/admin_actions.d.ts +4 -14
  7. package/dist/auth/admin_actions.d.ts.map +1 -1
  8. package/dist/auth/admin_actions.js +28 -36
  9. package/dist/auth/signup_routes.d.ts +0 -3
  10. package/dist/auth/signup_routes.d.ts.map +1 -1
  11. package/dist/auth/signup_routes.js +9 -3
  12. package/dist/auth/standard_rpc_actions.d.ts +5 -5
  13. package/dist/auth/standard_rpc_actions.js +4 -4
  14. package/dist/server/app_server.d.ts +1 -7
  15. package/dist/server/app_server.d.ts.map +1 -1
  16. package/dist/server/app_server.js +1 -5
  17. package/dist/testing/CLAUDE.md +98 -10
  18. package/dist/testing/app_server.d.ts +34 -0
  19. package/dist/testing/app_server.d.ts.map +1 -1
  20. package/dist/testing/app_server.js +31 -6
  21. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  22. package/dist/testing/cross_backend/account_lifecycle.js +69 -1
  23. package/dist/testing/cross_backend/actor_lookup.d.ts +10 -0
  24. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -0
  25. package/dist/testing/cross_backend/actor_lookup.js +83 -0
  26. package/dist/testing/cross_backend/actor_search.d.ts +6 -0
  27. package/dist/testing/cross_backend/actor_search.d.ts.map +1 -0
  28. package/dist/testing/cross_backend/actor_search.js +92 -0
  29. package/dist/testing/cross_backend/app_settings.d.ts +6 -0
  30. package/dist/testing/cross_backend/app_settings.d.ts.map +1 -0
  31. package/dist/testing/cross_backend/app_settings.js +95 -0
  32. package/dist/testing/cross_backend/backend_config.d.ts +1 -1
  33. package/dist/testing/cross_backend/capabilities.d.ts +0 -9
  34. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  35. package/dist/testing/cross_backend/capabilities.js +0 -1
  36. package/dist/testing/cross_backend/cell_grant_role.d.ts +8 -0
  37. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -0
  38. package/dist/testing/cross_backend/cell_grant_role.js +102 -0
  39. package/dist/testing/cross_backend/conformance_case.d.ts +144 -0
  40. package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -0
  41. package/dist/testing/cross_backend/conformance_case.js +132 -0
  42. package/dist/testing/cross_backend/conformance_table.d.ts +46 -0
  43. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -0
  44. package/dist/testing/cross_backend/conformance_table.js +199 -0
  45. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts +57 -0
  46. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts.map +1 -0
  47. package/dist/testing/cross_backend/create_cross_backend_global_setup.js +31 -0
  48. package/dist/testing/cross_backend/default_backend_configs.d.ts +13 -0
  49. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  50. package/dist/testing/cross_backend/default_backend_configs.js +4 -6
  51. package/dist/testing/cross_backend/default_spine_surface.d.ts +17 -9
  52. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  53. package/dist/testing/cross_backend/default_spine_surface.js +20 -12
  54. package/dist/testing/cross_backend/make_cross_backend_project.d.ts +72 -0
  55. package/dist/testing/cross_backend/make_cross_backend_project.d.ts.map +1 -0
  56. package/dist/testing/cross_backend/make_cross_backend_project.js +51 -0
  57. package/dist/testing/cross_backend/origin.d.ts +10 -0
  58. package/dist/testing/cross_backend/origin.d.ts.map +1 -0
  59. package/dist/testing/cross_backend/origin.js +73 -0
  60. package/dist/testing/cross_backend/setup.d.ts +22 -40
  61. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  62. package/dist/testing/cross_backend/setup.js +34 -5
  63. package/dist/testing/cross_backend/standard.d.ts +8 -0
  64. package/dist/testing/cross_backend/standard.d.ts.map +1 -1
  65. package/dist/testing/cross_backend/standard.js +1 -0
  66. package/dist/testing/cross_backend/testing_reset_actions.d.ts +102 -10
  67. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  68. package/dist/testing/cross_backend/testing_reset_actions.js +96 -5
  69. package/dist/testing/cross_backend/xfail.d.ts +15 -0
  70. package/dist/testing/cross_backend/xfail.d.ts.map +1 -0
  71. package/dist/testing/cross_backend/xfail.js +37 -0
  72. package/dist/testing/integration.d.ts +2 -3
  73. package/dist/testing/integration.d.ts.map +1 -1
  74. package/dist/testing/integration.js +40 -88
  75. package/dist/testing/rate_limiting.d.ts +1 -1
  76. package/dist/testing/rpc_helpers.d.ts +3 -3
  77. package/dist/testing/sse_round_trip.d.ts +1 -1
  78. package/dist/testing/stubs.d.ts.map +1 -1
  79. package/dist/testing/stubs.js +0 -1
  80. package/dist/ui/AdminAccounts.svelte +74 -83
  81. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  82. package/dist/ui/AdminSessions.svelte +21 -23
  83. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  84. package/dist/ui/CLAUDE.md +17 -26
  85. package/dist/ui/OpenSignupToggle.svelte +2 -5
  86. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
  87. package/dist/ui/account_sessions_state.svelte.d.ts +9 -10
  88. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  89. package/dist/ui/account_sessions_state.svelte.js +7 -17
  90. package/dist/ui/admin_accounts_state.svelte.d.ts +12 -19
  91. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  92. package/dist/ui/admin_accounts_state.svelte.js +10 -24
  93. package/dist/ui/admin_invites_state.svelte.d.ts +8 -11
  94. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
  95. package/dist/ui/admin_invites_state.svelte.js +7 -16
  96. package/dist/ui/admin_sessions_state.svelte.d.ts +6 -10
  97. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
  98. package/dist/ui/admin_sessions_state.svelte.js +4 -14
  99. package/dist/ui/app_settings_state.svelte.d.ts +8 -12
  100. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
  101. package/dist/ui/app_settings_state.svelte.js +6 -16
  102. package/dist/ui/audit_log_state.svelte.d.ts +9 -8
  103. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  104. package/dist/ui/audit_log_state.svelte.js +8 -20
  105. package/package.json +1 -1
@@ -0,0 +1,73 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Cross-backend parity suite for Origin verification.
4
+ *
5
+ * Origin checking is middleware that runs *before* the RPC dispatcher and
6
+ * returns a flat REST `{error}` body — not a JSON-RPC envelope — so it
7
+ * doesn't fit the envelope-shaped conformance-table runner. This dedicated
8
+ * imperative suite drives raw transport calls instead, mirroring how the
9
+ * in-process origin tests were already hand-rolled. Two cases:
10
+ *
11
+ * - **disallowed `Origin` → 403** `forbidden_origin`, refused before any
12
+ * handler runs (the allowlist rejects the cross-origin request even with
13
+ * a valid session cookie attached).
14
+ * - **absent `Origin` → request passes** — non-browser / direct-access
15
+ * clients (curl, CLI, server-to-server) carry no `Origin` and must not be
16
+ * blocked; token auth is the control for those callers.
17
+ *
18
+ * Runs both legs via the shared `{setup_test, capabilities}` protocol: the
19
+ * in-process leg (`auth/origin_parity.db.test.ts`, plain `gro test`) and the
20
+ * cross-process leg (`cross_backend/origin.cross.test.ts`, the TS spine
21
+ * binaries + Rust `testing_spine_stub` over real HTTP). Origin middleware is
22
+ * on every spine, so the suite is ungated.
23
+ *
24
+ * `$lib`-free by contract (relative specifiers only), like the sibling
25
+ * cross-backend suites.
26
+ *
27
+ * @module
28
+ */
29
+ import { describe, test, assert } from 'vitest';
30
+ import { account_verify_action_spec } from '../../auth/account_action_specs.js';
31
+ import { ERROR_FORBIDDEN_ORIGIN } from '../../http/error_schemas.js';
32
+ import { SPINE_RPC_PATH } from './default_spine_surface.js';
33
+ /** Build the JSON-RPC envelope body for a nullary `account_verify` call. */
34
+ const verify_envelope = (id) => JSON.stringify({ jsonrpc: '2.0', method: account_verify_action_spec.method, id });
35
+ export const describe_origin_cross_tests = (options) => {
36
+ const { setup_test } = options;
37
+ const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
38
+ describe('origin verification parity', () => {
39
+ test('disallowed Origin → 403 forbidden_origin (refused before dispatch)', async () => {
40
+ const fixture = await setup_test();
41
+ // Keeper session cookie attached + a rogue Origin header (overrides
42
+ // the transport's default allowed Origin). The allowlist must reject
43
+ // before the dispatcher, returning a flat REST error body.
44
+ const res = await fixture.transport(rpc_path, {
45
+ method: 'POST',
46
+ headers: {
47
+ ...fixture.create_session_headers(),
48
+ origin: 'http://evil.com',
49
+ 'content-type': 'application/json',
50
+ },
51
+ body: verify_envelope('evil-origin'),
52
+ });
53
+ assert.strictEqual(res.status, 403, 'disallowed Origin must be rejected with 403');
54
+ const body = (await res.json().catch(() => undefined));
55
+ assert.strictEqual(body?.error, ERROR_FORBIDDEN_ORIGIN);
56
+ });
57
+ test('absent Origin → request passes (non-browser direct access)', async () => {
58
+ const fixture = await setup_test();
59
+ // `origin: null` so no Origin header is sent at all (a header omission
60
+ // alone wouldn't suffice cross-process — the jar auto-adds the default
61
+ // allowed Origin). The keeper cookie rides via an explicit header.
62
+ const res = await fixture.fresh_transport({ origin: null })(rpc_path, {
63
+ method: 'POST',
64
+ headers: {
65
+ ...fixture.create_session_headers(),
66
+ 'content-type': 'application/json',
67
+ },
68
+ body: verify_envelope('no-origin'),
69
+ });
70
+ assert.strictEqual(res.status, 200, 'absent Origin with a valid session must pass');
71
+ });
72
+ });
73
+ };
@@ -1,10 +1,9 @@
1
1
  import '../assert_dev_env.js';
2
2
  import { Uuid } from '@fuzdev/fuz_util/id.js';
3
- import type { Keyring } from '../../auth/keyring.js';
4
3
  import type { RouteSpec } from '../../http/route_spec.js';
5
4
  import type { AppServerContext, BootstrapServerOptions } from '../../server/app_server.js';
6
5
  import type { SessionOptions } from '../../auth/session_cookie.js';
7
- import { type CreateTestAppOptions, type SuiteAppOptions, type TestAccount, type TestAppServer } from '../app_server.js';
6
+ import { type CreateTestAppOptions, type SuiteAppOptions, type TestAccount } from '../app_server.js';
8
7
  import { type RpcEndpointsSuiteOption } from '../rpc_helpers.js';
9
8
  import { type BackendCapabilities } from './capabilities.js';
10
9
  import type { AppSurfaceSpec } from '../../http/surface.js';
@@ -140,49 +139,32 @@ export interface TestFixtureBase {
140
139
  * for suites that don't declare any.
141
140
  */
142
141
  readonly extra_accounts: Readonly<Record<string, ExtraAccountFixture>>;
142
+ /**
143
+ * Forge an *expired server-side session* for the keeper account and
144
+ * return the ready-to-send `Cookie` header value (`name=value`). The
145
+ * minted `auth_session` row is backdated while the signed cookie payload
146
+ * stays valid — so resolution clears the cookie-payload gate
147
+ * (`parse_session`) and is refused at the authoritative DB-row gate
148
+ * (`query_session_get_valid` — `WHERE expires_at > NOW()`). Backs the
149
+ * `expired_session` conformance principal. In-process mints directly via
150
+ * `mint_test_session`; cross-process drives the `_testing_mint_session`
151
+ * RPC over the keeper's daemon-token channel (the driver has no keyring).
152
+ */
153
+ readonly mint_expired_session: () => Promise<string>;
143
154
  }
144
155
  /**
145
156
  * The per-test bundle returned by `SetupTest`. Every Tier 1 suite body
146
- * reads exclusively from this shape — no `test_app.backend.*` reads
147
- * remain in the suite bodies.
157
+ * reads exclusively from this shape — no `test_app.backend.*` reads remain
158
+ * in the suite bodies.
148
159
  *
149
- * Discriminated by `in_process`: when `true`, `keyring` and
150
- * `backend_internals` are present (compile-time narrowed); when `false`,
151
- * they're absent and the suite body must either run via the public wire
152
- * (cross-process) or gate the test with
153
- * `test_if(capabilities.in_process_only, ...)`. The discriminant value
154
- * mirrors `BackendCapabilities.in_process_only` — both source from the
155
- * same producer (`default_in_process_setup` vs. cross-process variant).
156
- *
157
- * Suite bodies narrow with `assert(fixture.in_process)` after
158
- * `setup_test()`; sites that reach for `keyring` or `backend_internals`
159
- * are in-process-only by structure and the assertion surfaces a clear
160
- * failure if a future cross-process consumer reaches them without a
161
- * `test_if` gate.
160
+ * Transport-agnostic: in-process and cross-process producers return the
161
+ * same shape. Behaviors that once needed raw backend access (keyring for
162
+ * forging cookies) are reached through wire-shaped seams instead
163
+ * `mint_expired_session()` mints over the `_testing_mint_session` channel
164
+ * cross-process and directly in-process, so suite bodies never branch on
165
+ * the transport.
162
166
  */
163
- export type TestFixture = (TestFixtureBase & {
164
- readonly in_process: true;
165
- /**
166
- * Test-only keyring access — in-process only. Used for
167
- * expired-cookie generation in `describe_standard_integration_tests`.
168
- */
169
- readonly keyring: Keyring;
170
- /**
171
- * Raw backend access (`deps.db`, etc.) — in-process only. Used
172
- * by the origin-verification cookie-composition sites in
173
- * `describe_standard_integration_tests`. Tests that need a
174
- * direct DB role_grant seed at the in-process layer reach for
175
- * `create_test_role_grant_direct` from `db_entities.ts`; that
176
- * helper bypasses the consent flow on purpose for query-level
177
- * (`*.db.test.ts`) tests and has no cross-process analog.
178
- * Cross-process tests grant additional roles via
179
- * `fixture.create_account({roles})` (offer/accept) or
180
- * `extra_accounts` (bootstrap-time bypass).
181
- */
182
- readonly backend_internals: TestAppServer;
183
- }) | (TestFixtureBase & {
184
- readonly in_process: false;
185
- });
167
+ export type TestFixture = TestFixtureBase;
186
168
  /**
187
169
  * Per-test fixture-producing function. Invoked once inside every
188
170
  * `test()` body. The implementation captures factory inputs (in-process)
@@ -1 +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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"}
1
+ {"version":3,"file":"setup.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/setup.ts"],"names":[],"mappings":"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"}
@@ -23,7 +23,7 @@ import { Uuid } from '@fuzdev/fuz_util/id.js';
23
23
  import { ROLE_KEEPER } from '../../auth/role_schema.js';
24
24
  import { DAEMON_TOKEN_HEADER } from '../../auth/daemon_token.js';
25
25
  import { USERNAME_LENGTH_MAX } from '../../primitive_schemas.js';
26
- import { create_test_app, create_test_account_with_credentials, DEFAULT_TEST_PASSWORD, } from '../app_server.js';
26
+ import { create_test_app, create_test_account_with_credentials, mint_test_session, DEFAULT_TEST_PASSWORD, } from '../app_server.js';
27
27
  import { create_test_app_surface_spec } from '../stubs.js';
28
28
  import { http_transport, } from '../rpc_helpers.js';
29
29
  import { in_process_capabilities } from './capabilities.js';
@@ -110,7 +110,6 @@ export const default_in_process_setup = (options) => async () => {
110
110
  extra_accounts[spec.username] = build_extra_account_fixture(seeded, cookie_name);
111
111
  }
112
112
  return {
113
- in_process: true,
114
113
  transport: in_process_fetch_transport(test_app.app),
115
114
  // In-process the wrapper is stateless and never auto-adds Origin —
116
115
  // `options` is accepted for API symmetry with cross-process but
@@ -123,8 +122,18 @@ export const default_in_process_setup = (options) => async () => {
123
122
  create_daemon_token_headers: test_app.create_daemon_token_headers,
124
123
  create_account: test_app.create_account,
125
124
  extra_accounts,
126
- keyring: test_app.backend.keyring,
127
- backend_internals: test_app.backend,
125
+ // Forge directly against the live backend's DB + keyring — no wire
126
+ // hop needed in-process.
127
+ mint_expired_session: async () => {
128
+ const { session_cookie } = await mint_test_session({
129
+ db: test_app.backend.deps.db,
130
+ keyring: test_app.backend.keyring,
131
+ session_options: options.session_options,
132
+ account_id: test_app.backend.account.id,
133
+ expires_in_seconds: EXPIRED_SESSION_OFFSET_SECONDS,
134
+ });
135
+ return `${cookie_name}=${session_cookie}`;
136
+ },
128
137
  };
129
138
  };
130
139
  /**
@@ -224,6 +233,15 @@ const rpc_via_transport = async (transport, rpc_path, method, params, backend_na
224
233
  }
225
234
  return raw.result;
226
235
  };
236
+ /**
237
+ * Backdating offset (seconds) the `mint_expired_session` seam passes to
238
+ * `mint_test_session` / `_testing_mint_session`. A minute in the past is
239
+ * comfortably past `NOW()` for the DB-row expiry gate without depending on
240
+ * clock precision.
241
+ */
242
+ const EXPIRED_SESSION_OFFSET_SECONDS = -60;
243
+ /** Structural subset of `_testing_mint_session`'s output. */
244
+ const MintSessionResponseShape = z.object({ session_cookie: z.string() });
227
245
  /** Structural subset of `_testing_reset`'s output. */
228
246
  const TestingResetResponseShape = z.object({
229
247
  account: z.object({ id: Uuid, username: z.string() }),
@@ -504,7 +522,6 @@ export const default_cross_process_setup = (handle, options) => {
504
522
  initial_cookies: [`${cookie_name}=${keeper.session_cookie}`],
505
523
  });
506
524
  return {
507
- in_process: false,
508
525
  transport,
509
526
  fresh_transport: (fresh_options) => create_fetch_transport({
510
527
  base_url: handle.config.base_url,
@@ -517,6 +534,18 @@ export const default_cross_process_setup = (handle, options) => {
517
534
  create_daemon_token_headers,
518
535
  create_account,
519
536
  extra_accounts,
537
+ // Forge over the wire — the cross-process driver has no keyring,
538
+ // so `_testing_mint_session` mints the backdated row + signs the
539
+ // cookie server-side over the keeper's daemon-token channel.
540
+ mint_expired_session: async () => {
541
+ const raw = await rpc_via_transport(refreshed_handle.keeper_transport, handle.config.rpc_path, '_testing_mint_session', { account_id: keeper.account.id, expires_in_seconds: EXPIRED_SESSION_OFFSET_SECONDS }, handle.config.name, { [DAEMON_TOKEN_HEADER]: handle.daemon_token });
542
+ const parsed = MintSessionResponseShape.safeParse(raw);
543
+ if (!parsed.success) {
544
+ throw new Error(`_testing_mint_session(${handle.config.name}) returned unexpected result: ` +
545
+ `${JSON.stringify(raw)} (${parsed.error.message})`);
546
+ }
547
+ return `${cookie_name}=${parsed.data.session_cookie}`;
548
+ },
520
549
  };
521
550
  };
522
551
  };
@@ -86,6 +86,14 @@ export interface StandardCrossProcessTestOptions {
86
86
  * `0` to skip the assertion entirely.
87
87
  */
88
88
  error_coverage_min?: number;
89
+ /**
90
+ * Forwarded to `describe_round_trip_validation` as `skip_routes`
91
+ * (`'METHOD /path'` keys). For consumer REST routes whose responses
92
+ * aren't JSON-with-an-output-schema and so can't be round-tripped — e.g.
93
+ * fuz_forge's git smart-HTTP routes (`git-upload-pack` / `git-receive-pack`
94
+ * / `info/refs`) which stream git protocol bytes.
95
+ */
96
+ round_trip_skip_routes?: Array<string>;
89
97
  }
90
98
  /**
91
99
  * Run the cross-process standard test bundle — integration, admin (when
@@ -1 +1 @@
1
- {"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/standard.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAM1D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,WAAW,+BAA+B;IAC/C,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;;;GAIG;AACH,eAAO,MAAM,qCAAqC,GACjD,SAAS,+BAA+B,KACtC,IAqCF,CAAC"}
1
+ {"version":3,"file":"standard.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/standard.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsCG;AAEH,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,2BAA2B,CAAC;AAChE,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,uBAAuB,CAAC;AAM1D,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,mBAAmB,CAAC;AAC/D,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,mBAAmB,CAAC;AAC3D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,YAAY,CAAC;AAE1C;;;;;;GAMG;AACH,MAAM,WAAW,+BAA+B;IAC/C,2CAA2C;IAC3C,UAAU,EAAE,SAAS,CAAC;IACtB;;;OAGG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;;;OAKG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;OAGG;IACH,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B;;;;;;OAMG;IACH,sBAAsB,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACvC;AAED;;;;GAIG;AACH,eAAO,MAAM,qCAAqC,GACjD,SAAS,+BAA+B,KACtC,IAsCF,CAAC"}
@@ -22,6 +22,7 @@ export const describe_standard_cross_process_tests = (options) => {
22
22
  setup_test: options.setup_test,
23
23
  surface_source: options.surface_source,
24
24
  capabilities: options.capabilities,
25
+ skip_routes: options.round_trip_skip_routes,
25
26
  });
26
27
  describe_rpc_round_trip_tests({
27
28
  setup_test: options.setup_test,
@@ -2,8 +2,14 @@ import '../assert_dev_env.js';
2
2
  /**
3
3
  * Test-binary RPC actions for cross-process integration tests.
4
4
  *
5
- * Single daemon-token-authed action: **`_testing_reset`** full DB
6
- * wipe + keeper re-seed + optional secondary-account seeding. The
5
+ * Three daemon-token-authed actions, bundled by `create_testing_actions`:
6
+ * **`_testing_reset`** (DB wipe + keeper re-seed), **`_testing_drain_effects`**
7
+ * (audit barrier), and **`_testing_mint_session`** (forge an
8
+ * expired-by-construction server-side session for the expiry conformance
9
+ * cases).
10
+ *
11
+ * `_testing_reset` — full DB wipe + keeper re-seed + optional
12
+ * secondary-account seeding. The
7
13
  * handler wipes every auth-namespace row (no keeper-preserve filter),
8
14
  * flips `bootstrap_lock` back to its post-bootstrap shape, seeds a
9
15
  * fresh keeper account inline (reusing `create_test_account_with_credentials`
@@ -56,6 +62,7 @@ import { type RpcAction } from '../../actions/action_rpc.js';
56
62
  import type { AppDeps } from '../../auth/deps.js';
57
63
  import type { SessionOptions } from '../../auth/session_cookie.js';
58
64
  import type { DaemonTokenState } from '../../auth/daemon_token.js';
65
+ import type { Db } from '../../db/db.js';
59
66
  /**
60
67
  * The `_testing_reset` action spec.
61
68
  *
@@ -124,6 +131,88 @@ export declare const testing_reset_action_spec: {
124
131
  readonly async: true;
125
132
  readonly description: "Test-binary only — wipe auth tables, re-bootstrap a fresh keeper (+ optional extras), fire the domain-state reset.";
126
133
  };
134
+ /**
135
+ * `_testing_drain_effects` — await in-flight fire-and-forget audit writes so
136
+ * a following `audit_log_list` is authoritative. The deterministic barrier
137
+ * the cross-backend conformance suite uses in place of a poll/sleep before
138
+ * asserting on audit rows.
139
+ *
140
+ * On the TS spine the barrier is **satisfied by construction**: the test
141
+ * binary runs `await_pending_effects: true`, so every mutation's fire-and-
142
+ * forget audit emits are awaited before its response returns — by the time
143
+ * a later drain call runs, prior emits are already durable. The action still
144
+ * exists so the cross-backend test body calls the same method on every
145
+ * backend; the Rust spine (whose audit writes are detached tokio tasks)
146
+ * does the real await in `AuditEmitter::drain_inflight`.
147
+ *
148
+ * `auth` gates on the daemon-token credential, matching `_testing_reset`.
149
+ */
150
+ export declare const testing_drain_effects_action_spec: {
151
+ readonly method: "_testing_drain_effects";
152
+ readonly kind: "request_response";
153
+ readonly initiator: "frontend";
154
+ readonly auth: {
155
+ readonly account: "required";
156
+ readonly actor: "none";
157
+ readonly credential_types: readonly ["daemon_token"];
158
+ };
159
+ readonly side_effects: false;
160
+ readonly input: z.ZodVoid;
161
+ readonly output: z.ZodObject<{
162
+ ok: z.ZodBoolean;
163
+ }, z.core.$strict>;
164
+ readonly async: true;
165
+ readonly description: "Test-binary only — await in-flight fire-and-forget audit writes so a following audit_log_list read is authoritative.";
166
+ };
167
+ /**
168
+ * `_testing_mint_session` — mint an expired-by-construction server-side
169
+ * session for an existing account and return its signed cookie value.
170
+ *
171
+ * The minted `auth_session` row's `expires_at` is backdated (negative
172
+ * `expires_in_seconds`) while the returned cookie's own signed payload
173
+ * stays valid (future). Cross-process auth resolution therefore passes the
174
+ * cookie-payload gate (`parse_session`) and is refused by the authoritative
175
+ * DB-row gate (`query_session_get_valid` — `WHERE expires_at > NOW()`) —
176
+ * the gate the in-process payload-expiry tests never reach and the one that
177
+ * structurally needs a server-side mint (the cross-process driver has no
178
+ * keyring / DB access). The `expired_session` conformance principal drives
179
+ * this.
180
+ *
181
+ * `auth` gates on the daemon-token credential, matching `_testing_reset` —
182
+ * effectively keeper-only. Like its siblings the action is internally
183
+ * privileged (a direct `auth_session` insert the production wire never
184
+ * exposes); daemon-token auth is the structural fence and the module's
185
+ * `assert_dev_env` import (TS) plus the Rust `cargo xtask check-release`
186
+ * dep-graph audit keep the `_testing_` surface out of every shipped build.
187
+ */
188
+ export declare const testing_mint_session_action_spec: {
189
+ readonly method: "_testing_mint_session";
190
+ readonly kind: "request_response";
191
+ readonly initiator: "frontend";
192
+ readonly auth: {
193
+ readonly account: "required";
194
+ readonly actor: "none";
195
+ readonly credential_types: readonly ["daemon_token"];
196
+ };
197
+ readonly side_effects: true;
198
+ readonly input: z.ZodObject<{
199
+ account_id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
200
+ expires_in_seconds: z.ZodNumber;
201
+ }, z.core.$strict>;
202
+ readonly output: z.ZodObject<{
203
+ session_cookie: z.ZodString;
204
+ }, z.core.$strict>;
205
+ readonly async: true;
206
+ readonly description: string;
207
+ };
208
+ /**
209
+ * Build the standalone `_testing_drain_effects` action. No deps — on TS the
210
+ * barrier is satisfied by `await_pending_effects` (see the spec doc), so the
211
+ * handler just returns `{ok: true}`. Mount it on any test endpoint whose
212
+ * suite asserts on audit rows (the spine binary bundles it via
213
+ * `create_testing_actions`; in-process suites mount it directly).
214
+ */
215
+ export declare const create_testing_drain_effects_action: () => RpcAction;
127
216
  /** Options for `create_testing_actions`. */
128
217
  export interface CreateTestingActionsOptions {
129
218
  /**
@@ -141,15 +230,18 @@ export interface CreateTestingActionsOptions {
141
230
  */
142
231
  readonly daemon_token_state: DaemonTokenState;
143
232
  /**
144
- * Consumer-supplied callback invoked after the auth-table reset.
145
- * `testing_zzz_server` clears workspace registry + terminals + the
146
- * scoped FS scratch dir here; `testing_spine_stub` has no domain
147
- * layer and passes a no-op (or omits the option). Runs inside the
148
- * same RPC dispatch as the auth-table writes, so a throw surfaces
149
- * to the caller as a JSON-RPC error and the per-test fixture
150
- * short-circuits.
233
+ * Consumer-supplied callback invoked after the auth-table reset, passed
234
+ * the same transactional `Db` the auth wipes ran on. DB-domain consumers
235
+ * (e.g. fuz_forge truncating its cell / fact / file tables) MUST use this
236
+ * `db` rather than a separately-pooled connection under PGlite's single
237
+ * connection a second connection deadlocks against this still-open
238
+ * transaction. `testing_zzz_server` clears in-memory workspace registry +
239
+ * terminals + scoped-FS scratch (ignores `db`); `testing_spine_stub` has
240
+ * no domain layer and omits the option. Runs inside the same RPC dispatch
241
+ * as the auth-table writes, so a throw surfaces to the caller as a
242
+ * JSON-RPC error and the per-test fixture short-circuits.
151
243
  */
152
- readonly reset_state?: () => Promise<void> | void;
244
+ readonly reset_state?: (db: Db) => Promise<void> | void;
153
245
  }
154
246
  /**
155
247
  * Build the testing RPC actions for a test binary's registry.
@@ -1 +1 @@
1
- {"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AAajE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwBQ,CAAC;AAE/C,4CAA4C;AAC5C,MAAM,WAAW,2BAA2B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;;;;;OAQG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAClD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,2BAA2B,KAClC,KAAK,CAAC,SAAS,CA4FjB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,0BAA0B,UAAmC,CAAC"}
1
+ {"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AACjE,OAAO,KAAK,EAAC,EAAE,EAAC,MAAM,gBAAgB,CAAC;AAiBvC;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwBQ,CAAC;AAE/C;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;CAWA,CAAC;AAE/C;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,gCAAgC;;;;;;;;;;;;;;;;;;;CAeC,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,mCAAmC,QAAO,SACiB,CAAC;AAEzE,4CAA4C;AAC5C,MAAM,WAAW,2BAA2B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;;;;;;;;OAWG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CACxD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,2BAA2B,KAClC,KAAK,CAAC,SAAS,CA0GjB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,0BAA0B,UAAmC,CAAC"}
@@ -2,8 +2,14 @@ import '../assert_dev_env.js';
2
2
  /**
3
3
  * Test-binary RPC actions for cross-process integration tests.
4
4
  *
5
- * Single daemon-token-authed action: **`_testing_reset`** full DB
6
- * wipe + keeper re-seed + optional secondary-account seeding. The
5
+ * Three daemon-token-authed actions, bundled by `create_testing_actions`:
6
+ * **`_testing_reset`** (DB wipe + keeper re-seed), **`_testing_drain_effects`**
7
+ * (audit barrier), and **`_testing_mint_session`** (forge an
8
+ * expired-by-construction server-side session for the expiry conformance
9
+ * cases).
10
+ *
11
+ * `_testing_reset` — full DB wipe + keeper re-seed + optional
12
+ * secondary-account seeding. The
7
13
  * handler wipes every auth-namespace row (no keeper-preserve filter),
8
14
  * flips `bootstrap_lock` back to its post-bootstrap shape, seeds a
9
15
  * fresh keeper account inline (reusing `create_test_account_with_credentials`
@@ -56,7 +62,7 @@ import { Uuid } from '@fuzdev/fuz_util/id.js';
56
62
  import { rpc_action } from '../../actions/action_rpc.js';
57
63
  import { ROLE_ADMIN, ROLE_KEEPER } from '../../auth/role_schema.js';
58
64
  import { auth_integration_truncate_tables } from '../db.js';
59
- import { create_test_account_with_credentials, DEFAULT_TEST_PASSWORD } from '../app_server.js';
65
+ import { create_test_account_with_credentials, mint_test_session, DEFAULT_TEST_PASSWORD, } from '../app_server.js';
60
66
  /** Output shape for an individual seeded account (keeper or extra). */
61
67
  const SeededAccountShape = z.strictObject({
62
68
  account: z.strictObject({ id: Uuid, username: z.string() }),
@@ -111,6 +117,77 @@ export const testing_reset_action_spec = {
111
117
  async: true,
112
118
  description: 'Test-binary only — wipe auth tables, re-bootstrap a fresh keeper (+ optional extras), fire the domain-state reset.',
113
119
  };
120
+ /**
121
+ * `_testing_drain_effects` — await in-flight fire-and-forget audit writes so
122
+ * a following `audit_log_list` is authoritative. The deterministic barrier
123
+ * the cross-backend conformance suite uses in place of a poll/sleep before
124
+ * asserting on audit rows.
125
+ *
126
+ * On the TS spine the barrier is **satisfied by construction**: the test
127
+ * binary runs `await_pending_effects: true`, so every mutation's fire-and-
128
+ * forget audit emits are awaited before its response returns — by the time
129
+ * a later drain call runs, prior emits are already durable. The action still
130
+ * exists so the cross-backend test body calls the same method on every
131
+ * backend; the Rust spine (whose audit writes are detached tokio tasks)
132
+ * does the real await in `AuditEmitter::drain_inflight`.
133
+ *
134
+ * `auth` gates on the daemon-token credential, matching `_testing_reset`.
135
+ */
136
+ export const testing_drain_effects_action_spec = {
137
+ method: '_testing_drain_effects',
138
+ kind: 'request_response',
139
+ initiator: 'frontend',
140
+ auth: { account: 'required', actor: 'none', credential_types: ['daemon_token'] },
141
+ side_effects: false,
142
+ input: z.void(),
143
+ output: z.strictObject({ ok: z.boolean() }),
144
+ async: true,
145
+ description: 'Test-binary only — await in-flight fire-and-forget audit writes so a following audit_log_list read is authoritative.',
146
+ };
147
+ /**
148
+ * `_testing_mint_session` — mint an expired-by-construction server-side
149
+ * session for an existing account and return its signed cookie value.
150
+ *
151
+ * The minted `auth_session` row's `expires_at` is backdated (negative
152
+ * `expires_in_seconds`) while the returned cookie's own signed payload
153
+ * stays valid (future). Cross-process auth resolution therefore passes the
154
+ * cookie-payload gate (`parse_session`) and is refused by the authoritative
155
+ * DB-row gate (`query_session_get_valid` — `WHERE expires_at > NOW()`) —
156
+ * the gate the in-process payload-expiry tests never reach and the one that
157
+ * structurally needs a server-side mint (the cross-process driver has no
158
+ * keyring / DB access). The `expired_session` conformance principal drives
159
+ * this.
160
+ *
161
+ * `auth` gates on the daemon-token credential, matching `_testing_reset` —
162
+ * effectively keeper-only. Like its siblings the action is internally
163
+ * privileged (a direct `auth_session` insert the production wire never
164
+ * exposes); daemon-token auth is the structural fence and the module's
165
+ * `assert_dev_env` import (TS) plus the Rust `cargo xtask check-release`
166
+ * dep-graph audit keep the `_testing_` surface out of every shipped build.
167
+ */
168
+ export const testing_mint_session_action_spec = {
169
+ method: '_testing_mint_session',
170
+ kind: 'request_response',
171
+ initiator: 'frontend',
172
+ auth: { account: 'required', actor: 'none', credential_types: ['daemon_token'] },
173
+ side_effects: true,
174
+ input: z.strictObject({
175
+ account_id: Uuid,
176
+ expires_in_seconds: z.number().int(),
177
+ }),
178
+ output: z.strictObject({ session_cookie: z.string() }),
179
+ async: true,
180
+ description: 'Test-binary only — mint a backdated-expiry auth_session row for an account and return its ' +
181
+ 'signed cookie value (exercises the authoritative server-side DB-row expiry gate).',
182
+ };
183
+ /**
184
+ * Build the standalone `_testing_drain_effects` action. No deps — on TS the
185
+ * barrier is satisfied by `await_pending_effects` (see the spec doc), so the
186
+ * handler just returns `{ok: true}`. Mount it on any test endpoint whose
187
+ * suite asserts on audit rows (the spine binary bundles it via
188
+ * `create_testing_actions`; in-process suites mount it directly).
189
+ */
190
+ export const create_testing_drain_effects_action = () => rpc_action(testing_drain_effects_action_spec, async () => ({ ok: true }));
114
191
  /**
115
192
  * Build the testing RPC actions for a test binary's registry.
116
193
  *
@@ -202,11 +279,25 @@ export const create_testing_actions = (deps, options) => {
202
279
  // of stale-id-then-refresh on the next call.
203
280
  daemon_token_state.keeper_account_id = keeper.account.id;
204
281
  // 7. Fire domain-state reset (zzz workspaces/terminals/scratch,
205
- // or no-op for spine_stub).
282
+ // fuz_forge cell/fact/file truncation, or no-op for spine_stub).
283
+ // Pass the transactional `ctx.db` so DB-domain truncation runs
284
+ // on the same connection — a separate pool connection deadlocks
285
+ // against this open transaction under PGlite.
206
286
  if (reset_state)
207
- await reset_state();
287
+ await reset_state(ctx.db);
208
288
  return { ...keeper, extra_accounts: extras };
209
289
  }),
290
+ rpc_action(testing_mint_session_action_spec, async (input, ctx) => {
291
+ const { session_cookie } = await mint_test_session({
292
+ db: ctx.db,
293
+ keyring: deps.keyring,
294
+ session_options,
295
+ account_id: input.account_id,
296
+ expires_in_seconds: input.expires_in_seconds,
297
+ });
298
+ return { session_cookie };
299
+ }),
300
+ create_testing_drain_effects_action(),
210
301
  ];
211
302
  };
212
303
  /** Set of auth-namespace tables `_testing_reset` wipes. Mirrored by the coverage test. */
@@ -0,0 +1,15 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Register `fn` as an expected-failure test. Passes while `fn` throws /
4
+ * rejects; **fails** once `fn` succeeds (signalling the gap closed and the
5
+ * marker should be removed).
6
+ *
7
+ * @param tracking_id - descriptive id of the tracked gap (e.g.
8
+ * `'audit-log-sse-rust-spine'`) — a feature/behavior name, not a
9
+ * process/milestone label.
10
+ * @param reason - why the case is deferred-by-design.
11
+ * @param name - the assertion / test label.
12
+ * @param fn - the test body (expected to throw/reject until the gap closes).
13
+ */
14
+ export declare const xfail_until: (tracking_id: string, reason: string, name: string, fn: () => void | Promise<void>) => void;
15
+ //# sourceMappingURL=xfail.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"xfail.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/xfail.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAyB9B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,WAAW,GACvB,aAAa,MAAM,EACnB,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,IAAI,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAC5B,IAEF,CAAC"}
@@ -0,0 +1,37 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * `xfail_until` — mark a deferred-by-design gap as an expected failure.
4
+ *
5
+ * A thin wrapper over vitest's `test.fails` that bakes a tracking id +
6
+ * reason into the test label. Two properties make it the right tool for
7
+ * declared gaps (distinct from in-scope gaps, which fail loud as a normal
8
+ * red `test`):
9
+ *
10
+ * - **Visible** — the case shows in the report as a distinct expected
11
+ * failure, not a silent `.skip`, so a deferred gap never disappears from
12
+ * view.
13
+ * - **Self-cleaning** — `test.fails` turns **red** the moment the body
14
+ * stops throwing (i.e. the impl starts passing), forcing whoever closed
15
+ * the gap to delete the marker. A `.skip` would rot silently; this can't.
16
+ *
17
+ * Sibling to `test_if` in `capabilities.ts`. No taxonomy — a one-line
18
+ * marker with a tracking id and a reason, nothing more.
19
+ *
20
+ * @module
21
+ */
22
+ import { test } from 'vitest';
23
+ /**
24
+ * Register `fn` as an expected-failure test. Passes while `fn` throws /
25
+ * rejects; **fails** once `fn` succeeds (signalling the gap closed and the
26
+ * marker should be removed).
27
+ *
28
+ * @param tracking_id - descriptive id of the tracked gap (e.g.
29
+ * `'audit-log-sse-rust-spine'`) — a feature/behavior name, not a
30
+ * process/milestone label.
31
+ * @param reason - why the case is deferred-by-design.
32
+ * @param name - the assertion / test label.
33
+ * @param fn - the test body (expected to throw/reject until the gap closes).
34
+ */
35
+ export const xfail_until = (tracking_id, reason, name, fn) => {
36
+ test.fails(`${name} [xfail_until ${tracking_id}: ${reason}]`, fn);
37
+ };