@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
@@ -19,7 +19,7 @@ import type { Db, DbType } from '../db/db.js';
19
19
  import type { PasswordHashDeps } from '../auth/password.js';
20
20
  import { type SessionOptions } from '../auth/session_cookie.js';
21
21
  import { type AppBackend, type AuditFactory } from '../server/app_backend.js';
22
- import { type AppServerOptions, type AppServerContext } from '../server/app_server.js';
22
+ import { type AppServerOptions, type AppServerContext, type BootstrapServerOptions, type BootstrapLiveOptions } from '../server/app_server.js';
23
23
  import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
24
24
  import type { RouteSpec } from '../http/route_spec.js';
25
25
  import type { RpcEndpointsSuiteOption } from './rpc_helpers.js';
@@ -33,9 +33,12 @@ 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
35
  /**
36
- * Options for `bootstrap_test_account`.
36
+ * Options for `bootstrap_test_keeper` and `create_test_account_with_credentials`.
37
+ *
38
+ * Same shape for both — the data inserted is identical; the only behavioral
39
+ * difference is the lock flip on the keeper path.
37
40
  */
38
- export interface BootstrapTestAccountOptions {
41
+ export interface CreateTestAccountWithCredentialsOptions {
39
42
  db: Db;
40
43
  keyring: Keyring;
41
44
  session_options: SessionOptions<string>;
@@ -44,17 +47,48 @@ export interface BootstrapTestAccountOptions {
44
47
  password_value?: string;
45
48
  roles?: Array<string>;
46
49
  }
50
+ /** Alias for the keeper-flavored call site. Same shape. */
51
+ export type BootstrapTestKeeperOptions = CreateTestAccountWithCredentialsOptions;
47
52
  /**
48
- * Bootstrap a test account with credentials.
53
+ * Create a test account with credentials. Use for additional accounts
54
+ * minted alongside the keeper (e.g. `TestApp.create_account` for
55
+ * cross-account / multi-user tests). Does NOT flip `bootstrap_lock` —
56
+ * non-keeper accounts should not appear to the system as bootstrap
57
+ * having happened.
49
58
  *
50
59
  * Creates an account with actor, grants roles, creates an API token,
51
- * creates a session, and signs a session cookie. Shared by
52
- * `create_test_app_server` and `TestApp.create_account`.
60
+ * creates a session, and signs a session cookie.
53
61
  *
54
62
  * @mutates the underlying `options.db` — inserts rows into `account`, `actor`,
55
63
  * `role_grant` (one per role), `api_token`, and `auth_session`.
56
64
  */
57
- export declare const bootstrap_test_account: (options: BootstrapTestAccountOptions) => Promise<{
65
+ export declare const create_test_account_with_credentials: (options: CreateTestAccountWithCredentialsOptions) => Promise<{
66
+ account: {
67
+ id: Uuid;
68
+ username: string;
69
+ };
70
+ actor: {
71
+ id: Uuid;
72
+ };
73
+ api_token: string;
74
+ session_cookie: string;
75
+ }>;
76
+ /**
77
+ * Bootstrap the test-DB keeper. Direct-query shortcut for the default
78
+ * `create_test_app` path — bootstrap is not what most tests exercise, so
79
+ * we skip the real `bootstrap_account` flow (no audit row, no
80
+ * `on_bootstrap` callback). Tests that need the full success-path flow
81
+ * use `create_test_app_for_bootstrap` instead.
82
+ *
83
+ * Flips `bootstrap_lock.bootstrapped = true` so the post-insert DB state
84
+ * matches a real bootstrap completion — production code can trust the
85
+ * lock as the single signal without a belt-and-suspenders
86
+ * `query_account_has_any` defense.
87
+ *
88
+ * @mutates the underlying `options.db` — inserts the account/actor/roles/
89
+ * API token/session_cookie rows AND flips `bootstrap_lock.bootstrapped`.
90
+ */
91
+ export declare const bootstrap_test_keeper: (options: BootstrapTestKeeperOptions) => Promise<{
58
92
  account: {
59
93
  id: Uuid;
60
94
  username: string;
@@ -122,26 +156,6 @@ export interface TestAppServerOptions {
122
156
  */
123
157
  audit_factory?: AuditFactory;
124
158
  }
125
- /**
126
- * Create an app server with a bootstrapped account for testing.
127
- *
128
- * Sets up:
129
- * - Auth tables (via cached PGlite factory, or reuses existing `db`)
130
- * - A keeper account with hashed password
131
- * - Role role_grants for each role in `options.roles`
132
- * - An API token for Bearer auth
133
- * - A session with a signed cookie value
134
- *
135
- * Uses `stub_password_deps` by default — deterministic hashing that works
136
- * correctly for login/logout tests without Argon2 overhead.
137
- *
138
- * @param options - session options and optional overrides
139
- * @returns a `TestAppServer` ready for HTTP testing
140
- * @mutates the underlying database — when `db` is supplied, resets singleton
141
- * state (`bootstrap_lock.bootstrapped`, `app_settings.open_signup`) before
142
- * bootstrapping; in either branch inserts an account, actor, role role_grants,
143
- * API token, and session row.
144
- */
145
159
  export declare const create_test_app_server: (options: TestAppServerOptions) => Promise<TestAppServer>;
146
160
  /**
147
161
  * Configuration for `create_test_app`.
@@ -159,23 +173,35 @@ export interface CreateTestAppOptions extends TestAppServerOptions {
159
173
  */
160
174
  rpc_endpoints?: RpcEndpointsSuiteOption;
161
175
  /**
162
- * Optional overrides for `AppServerOptions`. The four fields
163
- * `create_test_app` manages are excluded: `backend`, `session_options`,
164
- * `create_route_specs`, and `rpc_endpoints` (see top-level slot above).
176
+ * Bootstrap config symmetric with `AppServerOptions.bootstrap`. Same
177
+ * single-source-of-truth precedent as `rpc_endpoints`: setup-time surface
178
+ * generation and runtime dispatch both read this slot, so the equivalent
179
+ * field under `app_options` is `Omit`'d. Discriminated union over
180
+ * `{mode: 'disabled' | 'surface_only' | 'live'}`. Omit (or pass
181
+ * `{mode: 'disabled'}`) for the default — no bootstrap route mounted.
182
+ *
183
+ * For tests that exercise the bootstrap success path against a real
184
+ * token + empty DB, use `create_test_app_for_bootstrap` instead — it
185
+ * skips the keeper pre-creation that blocks the success branch.
186
+ */
187
+ bootstrap?: BootstrapServerOptions;
188
+ /**
189
+ * Optional overrides for `AppServerOptions`. Excludes fields
190
+ * `create_test_app` manages directly: `backend`, `session_options`,
191
+ * `create_route_specs`, `rpc_endpoints`, `bootstrap` (top-level slots
192
+ * above).
165
193
  */
166
194
  app_options?: SuiteAppOptions;
167
195
  }
168
196
  /**
169
197
  * `app_options` shape accepted by `create_test_app` and the DB-backed suite
170
- * helpers (`describe_standard_integration_tests`,
171
- * `describe_audit_completeness_tests`, etc.). Excludes the four fields the
172
- * helpers manage directly — `backend` / `session_options` /
173
- * `create_route_specs` are constructed by the helper itself; `rpc_endpoints`
174
- * lives on the top-level option (hard-failed by `require_rpc_endpoint_path`
175
- * in the suites) so setup-time path lookup and runtime dispatch read from
176
- * one source of truth.
198
+ * helpers. Excludes fields the helpers manage directly — `backend` /
199
+ * `session_options` / `create_route_specs` are constructed by the helper
200
+ * itself; `rpc_endpoints` and `bootstrap` live on top-level options so
201
+ * setup-time surface lookup and runtime dispatch read from one source of
202
+ * truth.
177
203
  */
178
- export type SuiteAppOptions = Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs' | 'rpc_endpoints'>>;
204
+ export type SuiteAppOptions = Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs' | 'rpc_endpoints' | 'bootstrap'>>;
179
205
  /**
180
206
  * A bootstrapped test account with credentials.
181
207
  */
@@ -234,4 +260,65 @@ export interface TestApp {
234
260
  * @returns a `TestApp` ready for HTTP testing
235
261
  */
236
262
  export declare const create_test_app: (options: CreateTestAppOptions) => Promise<TestApp>;
263
+ /**
264
+ * Configuration for `create_test_app_for_bootstrap`. Like
265
+ * `CreateTestAppOptions` but the keeper-related fields drop (no
266
+ * pre-bootstrap keeper) and `bootstrap` is required + narrowed to
267
+ * `live` mode (the helper exists specifically to drive the success
268
+ * path).
269
+ */
270
+ export interface CreateTestAppForBootstrapOptions {
271
+ session_options: SessionOptions<string>;
272
+ create_route_specs: (context: AppServerContext) => Array<RouteSpec>;
273
+ rpc_endpoints?: RpcEndpointsSuiteOption;
274
+ app_options?: SuiteAppOptions;
275
+ /** Live bootstrap config — the test drives `POST /bootstrap` against this. */
276
+ bootstrap: BootstrapLiveOptions;
277
+ /**
278
+ * Token contents the stub fs returns when reading `bootstrap.token_path`.
279
+ * The test posts a body containing this same value as `token` to satisfy
280
+ * the timing-safe equality check inside `bootstrap_account`.
281
+ */
282
+ bootstrap_token: string;
283
+ db?: Db;
284
+ db_type?: DbType;
285
+ password?: PasswordHashDeps;
286
+ audit_factory?: AuditFactory;
287
+ }
288
+ /**
289
+ * A fully assembled test app in the pre-bootstrap state — empty DB,
290
+ * `bootstrap_lock.bootstrapped = false`, no keeper account. Test drives
291
+ * `POST /bootstrap` itself.
292
+ */
293
+ export interface TestAppForBootstrap {
294
+ app: Hono;
295
+ backend: AppBackend;
296
+ surface_spec: AppSurfaceSpec;
297
+ surface: AppSurface;
298
+ route_specs: Array<RouteSpec>;
299
+ /** Build host/origin request headers for the anonymous bootstrap POST. */
300
+ create_request_headers: (extra?: Record<string, string>) => Record<string, string>;
301
+ /** Release test resources (no-op when DB is injected or factory-cached). */
302
+ cleanup: () => Promise<void>;
303
+ }
304
+ /**
305
+ * Create a test app in the pre-bootstrap state for exercising the
306
+ * bootstrap success path end-to-end.
307
+ *
308
+ * Skips the keeper pre-creation `create_test_app` does by default —
309
+ * `bootstrap_lock.bootstrapped` stays at `false` and the DB has no
310
+ * accounts. The fs stubs return `options.bootstrap_token` when the
311
+ * bootstrap handler reads `bootstrap.token_path`, so a `POST /bootstrap`
312
+ * with `{token: bootstrap_token, username, password}` reaches the
313
+ * success branch.
314
+ *
315
+ * Pair with `describe_bootstrap_success_tests` for the consumer-runnable
316
+ * suite that drives the full happy path + adjacent assertions on
317
+ * observable state (account exists, audit row emitted, on_bootstrap
318
+ * callback fired).
319
+ *
320
+ * @param options - bootstrap config + factory inputs
321
+ * @returns a `TestAppForBootstrap` ready for the test to drive bootstrap
322
+ */
323
+ export declare const create_test_app_for_bootstrap: (options: CreateTestAppForBootstrapOptions) => Promise<TestAppForBootstrap>;
237
324
  //# sourceMappingURL=app_server.d.ts.map
@@ -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,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;AAI9D;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,EAAE,gBAIhC,CAAC;AAEF,gFAAgF;AAChF,eAAO,MAAM,kBAAkB,QAAiB,CAAC;AASjD;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC3C,EAAE,EAAE,EAAE,CAAC;IACP,OAAO,EAAE,OAAO,CAAC;IACjB,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACtB;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,sBAAsB,GAClC,SAAS,2BAA2B,KAClC,OAAO,CAAC;IACV,OAAO,EAAE;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC,CAAC;IACtC,KAAK,EAAE;QAAC,EAAE,EAAE,IAAI,CAAA;KAAC,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,cAAc,EAAE,MAAM,CAAC;CACvB,CAyCA,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,aAAc,SAAQ,UAAU;IAChD,gCAAgC;IAChC,OAAO,EAAE;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC,CAAC;IACtC,uCAAuC;IACvC,KAAK,EAAE;QAAC,EAAE,EAAE,IAAI,CAAA;KAAC,CAAC;IAClB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,mDAAmD;IACnD,cAAc,EAAE,MAAM,CAAC;IACvB,+FAA+F;IAC/F,OAAO,EAAE,OAAO,CAAC;IACjB,4EAA4E;IAC5E,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,oBAAoB;IACpC,mDAAmD;IACnD,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,kGAAkG;IAClG,EAAE,CAAC,EAAE,EAAE,CAAC;IACR,0FAA0F;IAC1F,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,yHAAyH;IACzH,QAAQ,CAAC,EAAE,gBAAgB,CAAC;IAC5B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,6EAA6E;IAC7E,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,gDAAgD;IAChD,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtB;;;;;;;;;;;;;;OAcG;IACH,aAAa,CAAC,EAAE,YAAY,CAAC;CAC7B;AAKD;;;;;;;;;;;;;;;;;;;GAmBG;AACH,eAAO,MAAM,sBAAsB,GAClC,SAAS,oBAAoB,KAC3B,OAAO,CAAC,aAAa,CAyFvB,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,oBAAqB,SAAQ,oBAAoB;IACjE,yEAAyE;IACzE,kBAAkB,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IACpE;;;;;;;OAOG;IACH,aAAa,CAAC,EAAE,uBAAuB,CAAC;IACxC;;;;OAIG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,MAAM,MAAM,eAAe,GAAG,OAAO,CACpC,IAAI,CAAC,gBAAgB,EAAE,SAAS,GAAG,iBAAiB,GAAG,oBAAoB,GAAG,eAAe,CAAC,CAC9F,CAAC;AAEF;;GAEG;AACH,MAAM,WAAW,WAAW;IAC3B,OAAO,EAAE;QAAC,EAAE,EAAE,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC,CAAC;IACtC,KAAK,EAAE;QAAC,EAAE,EAAE,IAAI,CAAA;KAAC,CAAC;IAClB,mCAAmC;IACnC,cAAc,EAAE,MAAM,CAAC;IACvB,qCAAqC;IACrC,SAAS,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,sBAAsB,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnF,8DAA8D;IAC9D,qBAAqB,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAClF;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACvB,GAAG,EAAE,IAAI,CAAC;IACV,OAAO,EAAE,aAAa,CAAC;IACvB,YAAY,EAAE,cAAc,CAAC;IAC7B,OAAO,EAAE,UAAU,CAAC;IACpB,WAAW,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC9B,kEAAkE;IAClE,sBAAsB,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACnF,gEAAgE;IAChE,qBAAqB,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAClF,iEAAiE;IACjE,2BAA2B,EAAE,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACxF,qDAAqD;IACrD,cAAc,EAAE,CAAC,OAAO,CAAC,EAAE;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,cAAc,CAAC,EAAE,MAAM,CAAC;QACxB,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;KACtB,KAAK,OAAO,CAAC,WAAW,CAAC,CAAC;IAC3B,8DAA8D;IAC9D,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7B;AAED;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,eAAe,GAAU,SAAS,oBAAoB,KAAG,OAAO,CAAC,OAAO,CAmGpF,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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"}
@@ -15,7 +15,6 @@ import { default_audit_factory } from '../server/app_backend.js';
15
15
  import { create_app_server, } from '../server/app_server.js';
16
16
  import { generate_daemon_token, DAEMON_TOKEN_HEADER, } from '../auth/daemon_token.js';
17
17
  import { create_pglite_factory } from './db.js';
18
- /* eslint-disable @typescript-eslint/require-await */
19
18
  /**
20
19
  * Fast password stub for tests that don't exercise login/password flows.
21
20
  *
@@ -36,16 +35,19 @@ const fallback_pglite_factory = create_pglite_factory(async (db) => {
36
35
  await run_migrations(db, [auth_migration_ns]);
37
36
  });
38
37
  /**
39
- * Bootstrap a test account with credentials.
38
+ * Create a test account with credentials. Use for additional accounts
39
+ * minted alongside the keeper (e.g. `TestApp.create_account` for
40
+ * cross-account / multi-user tests). Does NOT flip `bootstrap_lock` —
41
+ * non-keeper accounts should not appear to the system as bootstrap
42
+ * having happened.
40
43
  *
41
44
  * Creates an account with actor, grants roles, creates an API token,
42
- * creates a session, and signs a session cookie. Shared by
43
- * `create_test_app_server` and `TestApp.create_account`.
45
+ * creates a session, and signs a session cookie.
44
46
  *
45
47
  * @mutates the underlying `options.db` — inserts rows into `account`, `actor`,
46
48
  * `role_grant` (one per role), `api_token`, and `auth_session`.
47
49
  */
48
- export const bootstrap_test_account = async (options) => {
50
+ export const create_test_account_with_credentials = async (options) => {
49
51
  const { db, keyring, session_options, password, username = 'keeper', password_value = 'test-password-123', roles = [], } = options;
50
52
  const deps = { db };
51
53
  const password_hash = await password.hash_password(password_value);
@@ -73,54 +75,63 @@ export const bootstrap_test_account = async (options) => {
73
75
  session_cookie,
74
76
  };
75
77
  };
76
- /** Silent logger for tests — suppresses all output. */
77
- const test_log = new Logger('test', { level: 'off' });
78
78
  /**
79
- * Create an app server with a bootstrapped account for testing.
79
+ * Bootstrap the test-DB keeper. Direct-query shortcut for the default
80
+ * `create_test_app` path — bootstrap is not what most tests exercise, so
81
+ * we skip the real `bootstrap_account` flow (no audit row, no
82
+ * `on_bootstrap` callback). Tests that need the full success-path flow
83
+ * use `create_test_app_for_bootstrap` instead.
80
84
  *
81
- * Sets up:
82
- * - Auth tables (via cached PGlite factory, or reuses existing `db`)
83
- * - A keeper account with hashed password
84
- * - Role role_grants for each role in `options.roles`
85
- * - An API token for Bearer auth
86
- * - A session with a signed cookie value
85
+ * Flips `bootstrap_lock.bootstrapped = true` so the post-insert DB state
86
+ * matches a real bootstrap completion production code can trust the
87
+ * lock as the single signal without a belt-and-suspenders
88
+ * `query_account_has_any` defense.
87
89
  *
88
- * Uses `stub_password_deps` by default deterministic hashing that works
89
- * correctly for login/logout tests without Argon2 overhead.
90
+ * @mutates the underlying `options.db` — inserts the account/actor/roles/
91
+ * API token/session_cookie rows AND flips `bootstrap_lock.bootstrapped`.
92
+ */
93
+ export const bootstrap_test_keeper = async (options) => {
94
+ const result = await create_test_account_with_credentials(options);
95
+ // Lock flip — mirrors production `bootstrap_account` so test/prod write
96
+ // semantics stay in parity.
97
+ await options.db.query('UPDATE bootstrap_lock SET bootstrapped = true WHERE id = 1 AND bootstrapped = false');
98
+ return result;
99
+ };
100
+ /** Silent logger for tests — suppresses all output. */
101
+ const test_log = new Logger('test', { level: 'off' });
102
+ const default_test_fs_stubs = {
103
+ stat: async () => null,
104
+ read_text_file: async () => '',
105
+ delete_file: async () => { },
106
+ };
107
+ /**
108
+ * Shared backend-assembly path for `create_test_app_server` and
109
+ * `create_test_app_for_bootstrap`. Returns the raw `AppBackend` + the
110
+ * keyring used to sign session cookies; callers wrap with their own
111
+ * concerns (keeper pre-creation vs. pre-bootstrap state).
90
112
  *
91
- * @param options - session options and optional overrides
92
- * @returns a `TestAppServer` ready for HTTP testing
93
- * @mutates the underlying database — when `db` is supplied, resets singleton
94
- * state (`bootstrap_lock.bootstrapped`, `app_settings.open_signup`) before
95
- * bootstrapping; in either branch inserts an account, actor, role role_grants,
96
- * API token, and session row.
113
+ * Resets `app_settings` singleton row for caller-supplied DBs so prior
114
+ * tests don't leak `open_signup`. Does NOT reset `bootstrap_lock` —
115
+ * callers own that policy (`create_test_app_server` lets
116
+ * `bootstrap_test_keeper` flip it; `create_test_app_for_bootstrap`
117
+ * resets it to false before this runs).
97
118
  */
98
- export const create_test_app_server = async (options) => {
99
- const { session_options, db: existing_db, db_type = 'pglite-memory', password = stub_password_deps, username = 'keeper', password_value = 'test-password-123', roles = [ROLE_KEEPER], audit_factory = default_audit_factory, } = options;
100
- // Keyring from test secret
119
+ const _build_test_backend = async (options) => {
120
+ const { db: existing_db, db_type = 'pglite-memory', password = stub_password_deps, audit_factory = default_audit_factory, fs_stubs = default_test_fs_stubs, } = options;
101
121
  const keyring_result = create_validated_keyring(TEST_COOKIE_SECRET);
102
122
  if (!keyring_result.ok) {
103
123
  throw new Error(`Test keyring failed: ${keyring_result.errors.join(', ')}`);
104
124
  }
105
- const fs_stubs = {
106
- stat: async () => null,
107
- read_text_file: async () => '',
108
- delete_file: async (_path) => { }, // eslint-disable-line @typescript-eslint/no-empty-function
109
- };
110
125
  let backend;
111
126
  if (existing_db) {
112
- // Reset singleton config rows that may retain state from a previous test.
113
- // Harmless for fresh pglite (these are already at defaults).
114
- await existing_db.query('UPDATE bootstrap_lock SET bootstrapped = false WHERE bootstrapped = true');
127
+ // Reset singleton config row from a previous test (harmless on fresh pglite).
115
128
  await existing_db.query('UPDATE app_settings SET open_signup = false, updated_at = NULL, updated_by = NULL WHERE open_signup = true OR updated_at IS NOT NULL');
116
- // Use the caller's database — tables already created by the factory's init_schema.
117
- // Caller owns the DB lifecycle — close is a no-op.
118
129
  const audit = audit_factory({ db: existing_db, log: test_log });
119
130
  backend = {
120
131
  db_type,
121
132
  db_name: 'test',
122
- migration_results: [], // migrations ran in the factory's init_schema, results not captured
123
- close: async () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
133
+ migration_results: [], // migrations ran in the factory's init_schema
134
+ close: async () => { },
124
135
  deps: {
125
136
  keyring: keyring_result.keyring,
126
137
  password,
@@ -141,7 +152,7 @@ export const create_test_app_server = async (options) => {
141
152
  db_type: 'pglite-memory',
142
153
  db_name: '(memory)',
143
154
  migration_results: [],
144
- close: async () => { }, // eslint-disable-line @typescript-eslint/no-empty-function
155
+ close: async () => { },
145
156
  deps: {
146
157
  keyring: keyring_result.keyring,
147
158
  password,
@@ -152,9 +163,14 @@ export const create_test_app_server = async (options) => {
152
163
  },
153
164
  };
154
165
  }
155
- const bootstrapped = await bootstrap_test_account({
166
+ return { backend, keyring: keyring_result.keyring };
167
+ };
168
+ export const create_test_app_server = async (options) => {
169
+ const { session_options, password = stub_password_deps, username = 'keeper', password_value = 'test-password-123', roles = [ROLE_KEEPER], } = options;
170
+ const { backend, keyring } = await _build_test_backend(options);
171
+ const bootstrapped = await bootstrap_test_keeper({
156
172
  db: backend.deps.db,
157
- keyring: keyring_result.keyring,
173
+ keyring,
158
174
  session_options,
159
175
  password,
160
176
  username,
@@ -164,7 +180,7 @@ export const create_test_app_server = async (options) => {
164
180
  return {
165
181
  ...backend,
166
182
  ...bootstrapped,
167
- keyring: keyring_result.keyring,
183
+ keyring,
168
184
  cleanup: () => backend.close(),
169
185
  };
170
186
  };
@@ -205,6 +221,7 @@ export const create_test_app = async (options) => {
205
221
  await_pending_effects: true,
206
222
  daemon_token_state,
207
223
  rpc_endpoints: options.rpc_endpoints,
224
+ bootstrap: options.bootstrap,
208
225
  ...options.app_options,
209
226
  create_route_specs: options.create_route_specs,
210
227
  });
@@ -230,7 +247,7 @@ export const create_test_app = async (options) => {
230
247
  let account_counter = 0;
231
248
  const create_account = async (account_options) => {
232
249
  account_counter++;
233
- const bootstrapped = await bootstrap_test_account({
250
+ const bootstrapped = await create_test_account_with_credentials({
234
251
  db: test_server.deps.db,
235
252
  keyring: test_server.keyring,
236
253
  session_options: options.session_options,
@@ -267,3 +284,84 @@ export const create_test_app = async (options) => {
267
284
  cleanup: () => test_server.cleanup(),
268
285
  };
269
286
  };
287
+ /**
288
+ * Create a test app in the pre-bootstrap state for exercising the
289
+ * bootstrap success path end-to-end.
290
+ *
291
+ * Skips the keeper pre-creation `create_test_app` does by default —
292
+ * `bootstrap_lock.bootstrapped` stays at `false` and the DB has no
293
+ * accounts. The fs stubs return `options.bootstrap_token` when the
294
+ * bootstrap handler reads `bootstrap.token_path`, so a `POST /bootstrap`
295
+ * with `{token: bootstrap_token, username, password}` reaches the
296
+ * success branch.
297
+ *
298
+ * Pair with `describe_bootstrap_success_tests` for the consumer-runnable
299
+ * suite that drives the full happy path + adjacent assertions on
300
+ * observable state (account exists, audit row emitted, on_bootstrap
301
+ * callback fired).
302
+ *
303
+ * @param options - bootstrap config + factory inputs
304
+ * @returns a `TestAppForBootstrap` ready for the test to drive bootstrap
305
+ */
306
+ export const create_test_app_for_bootstrap = async (options) => {
307
+ const { session_options, bootstrap, bootstrap_token } = options;
308
+ // Caller-supplied DB may carry lock state from a prior test — reset to false
309
+ // before `_build_test_backend` runs (which doesn't touch the lock itself).
310
+ // Fresh pglite already starts at false (factory init).
311
+ if (options.db) {
312
+ await options.db.query('UPDATE bootstrap_lock SET bootstrapped = false WHERE bootstrapped = true');
313
+ }
314
+ // Token-aware fs stubs: the bootstrap route's filesystem operations resolve
315
+ // against the configured token_path; everything else returns no-op defaults.
316
+ let token_file_deleted = false;
317
+ const fs_stubs = {
318
+ stat: async (path) => path === bootstrap.token_path && !token_file_deleted
319
+ ? { is_file: true, is_directory: false }
320
+ : null,
321
+ read_text_file: async (path) => path === bootstrap.token_path && !token_file_deleted ? bootstrap_token : '',
322
+ delete_file: async (path) => {
323
+ if (path === bootstrap.token_path)
324
+ token_file_deleted = true;
325
+ },
326
+ };
327
+ const { backend } = await _build_test_backend({ ...options, fs_stubs });
328
+ // Daemon token state isn't reachable pre-bootstrap (no keeper account)
329
+ // but the field is required by AppServerOptions; pass a placeholder.
330
+ const daemon_token_state = {
331
+ current_token: generate_daemon_token(),
332
+ previous_token: null,
333
+ rotated_at: new Date(),
334
+ keeper_account_id: null,
335
+ };
336
+ const result = await create_app_server({
337
+ backend,
338
+ session_options,
339
+ allowed_origins: [/^http:\/\/localhost/],
340
+ proxy: { trusted_proxies: ['127.0.0.1'], get_connection_ip: () => '127.0.0.1' },
341
+ env_schema: z.object({}),
342
+ ip_rate_limiter: null,
343
+ login_account_rate_limiter: null,
344
+ signup_account_rate_limiter: null,
345
+ bearer_ip_rate_limiter: null,
346
+ await_pending_effects: true,
347
+ daemon_token_state,
348
+ rpc_endpoints: options.rpc_endpoints,
349
+ bootstrap,
350
+ ...options.app_options,
351
+ create_route_specs: options.create_route_specs,
352
+ });
353
+ const create_request_headers = (extra) => ({
354
+ host: 'localhost',
355
+ origin: 'http://localhost:5173',
356
+ ...extra,
357
+ });
358
+ return {
359
+ app: result.app,
360
+ backend,
361
+ surface_spec: result.surface_spec,
362
+ surface: result.surface_spec.surface,
363
+ route_specs: result.surface_spec.route_specs,
364
+ create_request_headers,
365
+ cleanup: () => backend.close(),
366
+ };
367
+ };
@@ -1,42 +1,43 @@
1
1
  import './assert_dev_env.js';
2
2
  import type { SessionOptions } from '../auth/session_cookie.js';
3
- import type { AppServerContext } from '../server/app_server.js';
4
- import type { RouteSpec } from '../http/route_spec.js';
5
- import { type SuiteAppOptions } from './app_server.js';
6
- import { type DbFactory } from './db.js';
7
3
  import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
4
+ import type { BackendCapabilities } from './cross_backend/capabilities.js';
5
+ import type { SetupTest } from './cross_backend/setup.js';
6
+ import type { SurfaceSource } from './transports/surface_source.js';
8
7
  /**
9
8
  * Configuration for `describe_audit_completeness_tests`.
10
9
  */
11
10
  export interface AuditCompletenessTestOptions {
12
- /** Session config for cookie-based auth. */
11
+ /**
12
+ * Per-test fixture-producing function. The audit suite calls this
13
+ * in every `test()` body — `auth_integration_truncate_tables` clears
14
+ * the audit log between tests, so each test re-bootstraps the
15
+ * keeper and the observer admin against a fresh table.
16
+ */
17
+ setup_test: SetupTest;
18
+ /**
19
+ * Source of the app surface. Currently requires `kind: 'inline'` —
20
+ * the cross-process snapshot variant lands alongside the spawned-backend
21
+ * transport plumbing.
22
+ */
23
+ surface_source: SurfaceSource;
24
+ /** Backend capability declarations. */
25
+ capabilities: BackendCapabilities;
26
+ /** Session config — needed for factory-form rpc_endpoints resolution. */
13
27
  session_options: SessionOptions<string>;
14
- /** Route spec factory — same one used in production. */
15
- create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
16
28
  /**
17
- * RPC endpoint specs — the source `RpcAction` arrays. Required; the
18
- * admin role_grant flow is RPC-only and the suite hard-fails without it.
19
- *
20
- * Accepts either an array (eager) or a factory
21
- * `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
22
- * is required when action handlers must close over the per-test
23
- * `ctx.app_settings` / `ctx.deps` (e.g. exercising `app_settings_update`).
24
- * The factory must return the same endpoint `path` regardless of ctx —
25
- * it is invoked once at setup with a stub ctx for path lookup and again
26
- * per-test by `create_app_server` for live dispatch.
29
+ * RPC endpoint specs — required. The admin role_grant flow is RPC-only
30
+ * and the suite hard-fails without it.
27
31
  */
28
32
  rpc_endpoints: RpcEndpointsSuiteOption;
29
- /** Optional overrides for `AppServerOptions`. */
30
- app_options?: SuiteAppOptions;
31
- /** Database factories to run tests against. Default: pglite only. */
32
- db_factories?: Array<DbFactory>;
33
33
  }
34
34
  /**
35
35
  * Composable audit log completeness test suite.
36
36
  *
37
37
  * Verifies that every auth mutation route produces the correct audit log
38
38
  * event type. Exercises routes via HTTP requests against a real PGlite
39
- * database, then queries the `audit_log` table to verify events.
39
+ * database, then reads events back through the `audit_log_list` RPC
40
+ * (the production observation path the admin UI consumes).
40
41
  *
41
42
  * @throws Error at setup time when `options.rpc_endpoints` is empty — the
42
43
  * mutation-audit tests drive role_grant flow, session/token revoke-all, and
@@ -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;AAkB7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAIrD,OAAO,EAGN,KAAK,eAAe,EAEpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAIN,KAAK,SAAS,EACd,MAAM,SAAS,CAAC;AAKjB,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAqB1B;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;;;;;;;OAWG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;CAChC;AAyED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAihBzF,CAAC"}
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,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gCAAgC,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,aAAa,CAAC;IAC9B,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,IA6hBzF,CAAC"}