@fuzdev/fuz_app 0.67.1 → 0.69.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 (235) 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/CLAUDE.md +99 -5
  4. package/dist/auth/account_queries.d.ts +87 -4
  5. package/dist/auth/account_queries.d.ts.map +1 -1
  6. package/dist/auth/account_queries.js +107 -17
  7. package/dist/auth/account_schema.d.ts +19 -0
  8. package/dist/auth/account_schema.d.ts.map +1 -1
  9. package/dist/auth/account_schema.js +8 -0
  10. package/dist/auth/admin_action_specs.d.ts +170 -3
  11. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  12. package/dist/auth/admin_action_specs.js +148 -4
  13. package/dist/auth/admin_actions.d.ts +4 -14
  14. package/dist/auth/admin_actions.d.ts.map +1 -1
  15. package/dist/auth/admin_actions.js +246 -40
  16. package/dist/auth/audit_log_ddl.d.ts +10 -1
  17. package/dist/auth/audit_log_ddl.d.ts.map +1 -1
  18. package/dist/auth/audit_log_ddl.js +13 -4
  19. package/dist/auth/audit_log_schema.d.ts +34 -1
  20. package/dist/auth/audit_log_schema.d.ts.map +1 -1
  21. package/dist/auth/audit_log_schema.js +73 -0
  22. package/dist/auth/auth_ddl.d.ts +2 -2
  23. package/dist/auth/auth_ddl.d.ts.map +1 -1
  24. package/dist/auth/auth_ddl.js +10 -2
  25. package/dist/auth/cell_action_specs.d.ts +1295 -0
  26. package/dist/auth/cell_action_specs.d.ts.map +1 -0
  27. package/dist/auth/cell_action_specs.js +397 -0
  28. package/dist/auth/cell_actions.d.ts +63 -0
  29. package/dist/auth/cell_actions.d.ts.map +1 -0
  30. package/dist/auth/cell_actions.js +546 -0
  31. package/dist/auth/cell_audit_action_specs.d.ts +131 -0
  32. package/dist/auth/cell_audit_action_specs.d.ts.map +1 -0
  33. package/dist/auth/cell_audit_action_specs.js +70 -0
  34. package/dist/auth/cell_audit_actions.d.ts +18 -0
  35. package/dist/auth/cell_audit_actions.d.ts.map +1 -0
  36. package/dist/auth/cell_audit_actions.js +59 -0
  37. package/dist/auth/cell_audit_events.d.ts +28 -0
  38. package/dist/auth/cell_audit_events.d.ts.map +1 -0
  39. package/dist/auth/cell_audit_events.js +42 -0
  40. package/dist/auth/cell_audit_metadata.d.ts +48 -0
  41. package/dist/auth/cell_audit_metadata.d.ts.map +1 -0
  42. package/dist/auth/cell_audit_metadata.js +46 -0
  43. package/dist/auth/cell_authorize.d.ts +88 -0
  44. package/dist/auth/cell_authorize.d.ts.map +1 -0
  45. package/dist/auth/cell_authorize.js +172 -0
  46. package/dist/auth/cell_data_schema.d.ts +44 -0
  47. package/dist/auth/cell_data_schema.d.ts.map +1 -0
  48. package/dist/auth/cell_data_schema.js +42 -0
  49. package/dist/auth/cell_field_action_specs.d.ts +244 -0
  50. package/dist/auth/cell_field_action_specs.d.ts.map +1 -0
  51. package/dist/auth/cell_field_action_specs.js +136 -0
  52. package/dist/auth/cell_field_actions.d.ts +34 -0
  53. package/dist/auth/cell_field_actions.d.ts.map +1 -0
  54. package/dist/auth/cell_field_actions.js +153 -0
  55. package/dist/auth/cell_field_audit_metadata.d.ts +30 -0
  56. package/dist/auth/cell_field_audit_metadata.d.ts.map +1 -0
  57. package/dist/auth/cell_field_audit_metadata.js +28 -0
  58. package/dist/auth/cell_grant_action_specs.d.ts +333 -0
  59. package/dist/auth/cell_grant_action_specs.d.ts.map +1 -0
  60. package/dist/auth/cell_grant_action_specs.js +148 -0
  61. package/dist/auth/cell_grant_actions.d.ts +50 -0
  62. package/dist/auth/cell_grant_actions.d.ts.map +1 -0
  63. package/dist/auth/cell_grant_actions.js +208 -0
  64. package/dist/auth/cell_grant_audit_metadata.d.ts +75 -0
  65. package/dist/auth/cell_grant_audit_metadata.d.ts.map +1 -0
  66. package/dist/auth/cell_grant_audit_metadata.js +54 -0
  67. package/dist/auth/cell_item_action_specs.d.ts +331 -0
  68. package/dist/auth/cell_item_action_specs.d.ts.map +1 -0
  69. package/dist/auth/cell_item_action_specs.js +182 -0
  70. package/dist/auth/cell_item_actions.d.ts +37 -0
  71. package/dist/auth/cell_item_actions.d.ts.map +1 -0
  72. package/dist/auth/cell_item_actions.js +204 -0
  73. package/dist/auth/cell_item_audit_metadata.d.ts +35 -0
  74. package/dist/auth/cell_item_audit_metadata.d.ts.map +1 -0
  75. package/dist/auth/cell_item_audit_metadata.js +32 -0
  76. package/dist/auth/cell_relation_visibility.d.ts +32 -0
  77. package/dist/auth/cell_relation_visibility.d.ts.map +1 -0
  78. package/dist/auth/cell_relation_visibility.js +57 -0
  79. package/dist/auth/deps.d.ts +9 -0
  80. package/dist/auth/deps.d.ts.map +1 -1
  81. package/dist/auth/role_grant_queries.d.ts +30 -0
  82. package/dist/auth/role_grant_queries.d.ts.map +1 -1
  83. package/dist/auth/role_grant_queries.js +54 -0
  84. package/dist/auth/signup_routes.d.ts +0 -3
  85. package/dist/auth/signup_routes.d.ts.map +1 -1
  86. package/dist/auth/signup_routes.js +9 -3
  87. package/dist/auth/standard_rpc_actions.d.ts +5 -5
  88. package/dist/auth/standard_rpc_actions.js +4 -4
  89. package/dist/db/CLAUDE.md +118 -0
  90. package/dist/db/cell_audit_queries.d.ts +26 -0
  91. package/dist/db/cell_audit_queries.d.ts.map +1 -0
  92. package/dist/db/cell_audit_queries.js +53 -0
  93. package/dist/db/cell_ddl.d.ts +151 -0
  94. package/dist/db/cell_ddl.d.ts.map +1 -0
  95. package/dist/db/cell_ddl.js +247 -0
  96. package/dist/db/cell_field_queries.d.ts +105 -0
  97. package/dist/db/cell_field_queries.d.ts.map +1 -0
  98. package/dist/db/cell_field_queries.js +113 -0
  99. package/dist/db/cell_grant_queries.d.ts +132 -0
  100. package/dist/db/cell_grant_queries.d.ts.map +1 -0
  101. package/dist/db/cell_grant_queries.js +145 -0
  102. package/dist/db/cell_history_ddl.d.ts +38 -0
  103. package/dist/db/cell_history_ddl.d.ts.map +1 -0
  104. package/dist/db/cell_history_ddl.js +61 -0
  105. package/dist/db/cell_item_queries.d.ts +107 -0
  106. package/dist/db/cell_item_queries.d.ts.map +1 -0
  107. package/dist/db/cell_item_queries.js +119 -0
  108. package/dist/db/cell_queries.d.ts +327 -0
  109. package/dist/db/cell_queries.d.ts.map +1 -0
  110. package/dist/db/cell_queries.js +431 -0
  111. package/dist/db/fact_ddl.d.ts +38 -0
  112. package/dist/db/fact_ddl.d.ts.map +1 -0
  113. package/dist/db/fact_ddl.js +71 -0
  114. package/dist/db/fact_queries.d.ts +140 -0
  115. package/dist/db/fact_queries.d.ts.map +1 -0
  116. package/dist/db/fact_queries.js +161 -0
  117. package/dist/db/fact_store.d.ts +112 -0
  118. package/dist/db/fact_store.d.ts.map +1 -0
  119. package/dist/db/fact_store.js +225 -0
  120. package/dist/server/app_server.d.ts +1 -7
  121. package/dist/server/app_server.d.ts.map +1 -1
  122. package/dist/server/app_server.js +1 -5
  123. package/dist/server/env.d.ts +2 -0
  124. package/dist/server/env.d.ts.map +1 -1
  125. package/dist/server/env.js +6 -0
  126. package/dist/server/fact_write.d.ts +32 -0
  127. package/dist/server/fact_write.d.ts.map +1 -0
  128. package/dist/server/fact_write.js +56 -0
  129. package/dist/server/file_fact_fetcher.d.ts +42 -0
  130. package/dist/server/file_fact_fetcher.d.ts.map +1 -0
  131. package/dist/server/file_fact_fetcher.js +60 -0
  132. package/dist/server/file_fact_url.d.ts +53 -0
  133. package/dist/server/file_fact_url.d.ts.map +1 -0
  134. package/dist/server/file_fact_url.js +52 -0
  135. package/dist/server/serve_fact_route.d.ts +78 -0
  136. package/dist/server/serve_fact_route.d.ts.map +1 -0
  137. package/dist/server/serve_fact_route.js +205 -0
  138. package/dist/testing/CLAUDE.md +142 -6
  139. package/dist/testing/app_server.d.ts +46 -0
  140. package/dist/testing/app_server.d.ts.map +1 -1
  141. package/dist/testing/app_server.js +67 -8
  142. package/dist/testing/audit_completeness.d.ts.map +1 -1
  143. package/dist/testing/audit_completeness.js +67 -1
  144. package/dist/testing/cross_backend/account_lifecycle.d.ts +10 -0
  145. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -0
  146. package/dist/testing/cross_backend/account_lifecycle.js +144 -0
  147. package/dist/testing/cross_backend/actor_lookup.d.ts +10 -0
  148. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -0
  149. package/dist/testing/cross_backend/actor_lookup.js +83 -0
  150. package/dist/testing/cross_backend/actor_search.d.ts +6 -0
  151. package/dist/testing/cross_backend/actor_search.d.ts.map +1 -0
  152. package/dist/testing/cross_backend/actor_search.js +92 -0
  153. package/dist/testing/cross_backend/app_settings.d.ts +6 -0
  154. package/dist/testing/cross_backend/app_settings.d.ts.map +1 -0
  155. package/dist/testing/cross_backend/app_settings.js +95 -0
  156. package/dist/testing/cross_backend/backend_config.d.ts +1 -1
  157. package/dist/testing/cross_backend/capabilities.d.ts +29 -7
  158. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  159. package/dist/testing/cross_backend/capabilities.js +3 -1
  160. package/dist/testing/cross_backend/cell_cross_helpers.d.ts +39 -0
  161. package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +1 -0
  162. package/dist/testing/cross_backend/cell_cross_helpers.js +45 -0
  163. package/dist/testing/cross_backend/cell_crud.d.ts +4 -0
  164. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -0
  165. package/dist/testing/cross_backend/cell_crud.js +168 -0
  166. package/dist/testing/cross_backend/cell_grant_role.d.ts +8 -0
  167. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -0
  168. package/dist/testing/cross_backend/cell_grant_role.js +102 -0
  169. package/dist/testing/cross_backend/cell_relations.d.ts +4 -0
  170. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -0
  171. package/dist/testing/cross_backend/cell_relations.js +229 -0
  172. package/dist/testing/cross_backend/conformance_case.d.ts +144 -0
  173. package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -0
  174. package/dist/testing/cross_backend/conformance_case.js +132 -0
  175. package/dist/testing/cross_backend/conformance_table.d.ts +46 -0
  176. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -0
  177. package/dist/testing/cross_backend/conformance_table.js +199 -0
  178. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  179. package/dist/testing/cross_backend/default_backend_configs.js +6 -2
  180. package/dist/testing/cross_backend/default_spine_surface.d.ts +17 -9
  181. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  182. package/dist/testing/cross_backend/default_spine_surface.js +20 -12
  183. package/dist/testing/cross_backend/origin.d.ts +10 -0
  184. package/dist/testing/cross_backend/origin.d.ts.map +1 -0
  185. package/dist/testing/cross_backend/origin.js +73 -0
  186. package/dist/testing/cross_backend/setup.d.ts +22 -40
  187. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  188. package/dist/testing/cross_backend/setup.js +39 -5
  189. package/dist/testing/cross_backend/testing_reset_actions.d.ts +90 -2
  190. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  191. package/dist/testing/cross_backend/testing_reset_actions.js +91 -3
  192. package/dist/testing/cross_backend/xfail.d.ts +15 -0
  193. package/dist/testing/cross_backend/xfail.d.ts.map +1 -0
  194. package/dist/testing/cross_backend/xfail.js +37 -0
  195. package/dist/testing/entities.d.ts.map +1 -1
  196. package/dist/testing/entities.js +4 -0
  197. package/dist/testing/integration.d.ts +2 -3
  198. package/dist/testing/integration.d.ts.map +1 -1
  199. package/dist/testing/integration.js +20 -85
  200. package/dist/testing/rate_limiting.d.ts +1 -1
  201. package/dist/testing/rpc_helpers.d.ts +3 -3
  202. package/dist/testing/sse_round_trip.d.ts +1 -1
  203. package/dist/testing/stubs.d.ts.map +1 -1
  204. package/dist/testing/stubs.js +0 -1
  205. package/dist/testing/ws_round_trip.d.ts.map +1 -1
  206. package/dist/testing/ws_round_trip.js +4 -0
  207. package/dist/ui/AdminAccounts.svelte +84 -35
  208. package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
  209. package/dist/ui/AdminSessions.svelte +21 -23
  210. package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
  211. package/dist/ui/CLAUDE.md +17 -26
  212. package/dist/ui/OpenSignupToggle.svelte +2 -5
  213. package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
  214. package/dist/ui/account_sessions_state.svelte.d.ts +9 -10
  215. package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
  216. package/dist/ui/account_sessions_state.svelte.js +7 -17
  217. package/dist/ui/admin_accounts_state.svelte.d.ts +41 -20
  218. package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
  219. package/dist/ui/admin_accounts_state.svelte.js +52 -22
  220. package/dist/ui/admin_invites_state.svelte.d.ts +8 -11
  221. package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
  222. package/dist/ui/admin_invites_state.svelte.js +7 -16
  223. package/dist/ui/admin_rpc_adapters.d.ts +6 -2
  224. package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
  225. package/dist/ui/admin_rpc_adapters.js +5 -1
  226. package/dist/ui/admin_sessions_state.svelte.d.ts +6 -10
  227. package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
  228. package/dist/ui/admin_sessions_state.svelte.js +4 -14
  229. package/dist/ui/app_settings_state.svelte.d.ts +8 -12
  230. package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
  231. package/dist/ui/app_settings_state.svelte.js +6 -16
  232. package/dist/ui/audit_log_state.svelte.d.ts +9 -8
  233. package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
  234. package/dist/ui/audit_log_state.svelte.js +8 -20
  235. package/package.json +2 -2
@@ -149,7 +149,7 @@ Key module-scope values:
149
149
 
150
150
  - `stub_password_deps` — `PasswordHashDeps` hashing via `stub_hash_${password}` and verifying by equality. Deterministic, no Argon2 cost — use for every test not specifically exercising password hashing.
151
151
  - `TEST_COOKIE_SECRET` — 64-hex-char deterministic cookie secret. Produces a valid `Keyring` via `create_validated_keyring`. Never used in production — the stub guard plus fixed value is the contract.
152
- - `fallback_pglite_factory` — module-level PGlite factory `create_test_app_server` uses when no `db` is passed. Reuses the WASM cache via `create_pglite_factory`.
152
+ - `fallback_pglite_factory` — module-level auth-only PGlite factory `create_test_app_server` uses when no `db` is passed. Reuses the WASM cache via `create_pglite_factory`. When `migration_namespaces` is supplied, a memoized sibling factory (keyed by namespace set) migrating `[auth_migration_ns, ...migration_namespaces]` is used instead — same shared WASM, extra tables.
153
153
 
154
154
  Two helpers share the "insert account + actor + roles + API token + session +
155
155
  cookie" flow, split by intent:
@@ -167,7 +167,7 @@ pre-keeper, lock unflipped), use `create_test_app_for_bootstrap` — pair with
167
167
  Types:
168
168
 
169
169
  - `TestAppServer extends AppBackend` — adds `account`, `actor`, `api_token`, `session_cookie`, `keyring`, `cleanup()`.
170
- - `TestAppServerOptions` — `session_options` (required), optional `db`, `db_type`, `password`, `username`, `password_value`, `roles`, `audit_factory`. The optional `audit_factory` defaults to `default_audit_factory` (no-listener `create_audit_emitter` over the test backend's `{db, log}`); pass a custom factory to compose `on_audit_event` / `audit_log_config`, wrap with `emit_decorator` (via `create_emit_ordering_audit_factory`), or otherwise replace the emitter. Mirrors `CreateAppBackendOptions` end-to-end — the previous `on_audit_event` / `audit_log_config` sugar was removed alongside the production rename.
170
+ - `TestAppServerOptions` — `session_options` (required), optional `db`, `db_type`, `migration_namespaces`, `password`, `username`, `password_value`, `roles`, `audit_factory`. `migration_namespaces` runs extra namespaces after auth in the auto-created PGlite (mirrors `create_app_backend`); mutually exclusive with `db` (caller-migrated) — passing both throws. The optional `audit_factory` defaults to `default_audit_factory` (no-listener `create_audit_emitter` over the test backend's `{db, log}`); pass a custom factory to compose `on_audit_event` / `audit_log_config`, wrap with `emit_decorator` (via `create_emit_ordering_audit_factory`), or otherwise replace the emitter. Mirrors `CreateAppBackendOptions` end-to-end — the previous `on_audit_event` / `audit_log_config` sugar was removed alongside the production rename.
171
171
  - `CreateTestAppOptions extends TestAppServerOptions` — adds `create_route_specs` (required), `rpc_endpoints?: RpcEndpointsSuiteOption` (top-level only — single source of truth, symmetric with the suite-level option), `bootstrap?: BootstrapServerOptions` (top-level only — same precedent as `rpc_endpoints`), and `app_options?: SuiteAppOptions` (`Partial<AppServerOptions>` excluding the five fields the helper manages: `backend`, `session_options`, `create_route_specs`, `rpc_endpoints`, `bootstrap`).
172
172
  - `TestAccount` — `{account, actor, session_cookie, api_token, create_session_headers, create_bearer_headers}`.
173
173
  - `TestApp` — `{app, backend, surface_spec, surface, route_specs, create_session_headers, create_bearer_headers, create_daemon_token_headers, create_account, cleanup}`.
@@ -799,7 +799,11 @@ source of truth for wire-shape conformance.
799
799
 
800
800
  - `testing/cross_backend/setup.ts` — `SetupTest` / `TestFixture` /
801
801
  `TestAccountFixture` / `CreateTestAccountOptions` types,
802
- `default_in_process_setup(options)` (wraps `create_test_app`), and
802
+ `default_in_process_setup(options)` (wraps `create_test_app`; pass
803
+ `migration_namespaces` for suites needing tables beyond the auth-only
804
+ default — the cell parity suite passes `[CELL_MIGRATION_NS]`, and
805
+ `create_test_app` provisions a per-test fresh db migrating
806
+ `[auth_migration_ns, ...migration_namespaces]`), and
803
807
  `default_in_process_suite_options(options)` (emits the full Tier 1 suite
804
808
  options bag: the `{setup_test, surface_source, capabilities}` triple plus
805
809
  `session_options` / `create_route_specs` / `rpc_endpoints` pass-through;
@@ -842,8 +846,16 @@ source of truth for wire-shape conformance.
842
846
 
843
847
  - `testing/cross_backend/capabilities.ts` — `BackendCapabilities` vocabulary
844
848
  (`bearer_auth` / `trusted_proxy` / `login_rate_limit` / `ws` / `sse` /
845
- `in_process_only`), `test_if(cond, name, fn)` for capability-gated cases,
846
- and `in_process_capabilities` preset.
849
+ `cell_crud` / `cell_relations` / `account_lifecycle`),
850
+ `test_if(cond, name, fn)`
851
+ for capability-gated cases, and `in_process_capabilities` preset. `cell_crud`
852
+ gates the CRUD parity suite, `cell_relations` the relation / ACL / audit
853
+ parity suite — both `true` on every backend that live-mounts the full cell
854
+ surface (TS spine binary, in-process app, Rust stub). A backend mounting only
855
+ plain CRUD would declare `cell_crud: true, cell_relations: false`.
856
+ `account_lifecycle` gates `describe_account_lifecycle_cross_tests` (the
857
+ `account_delete` / `account_undelete` / `account_purge` parity suite) — also
858
+ off the declared surface like cells, `true` on every spine.
847
859
 
848
860
  ### `cross_backend/standard.ts` — `describe_standard_cross_process_tests`
849
861
 
@@ -873,6 +885,50 @@ consumer needs partial opt-out, add the knob then.
873
885
  `bootstrap`, `rate_limiting_app_options`, `bootstrap_token`) — those drive
874
886
  the omitted suites.
875
887
 
888
+ ### `cross_backend/conformance_table.ts` + `conformance_case.ts` + `xfail.ts` — declarative behavioral/security cases
889
+
890
+ The opinionated behavioral/security layer on top of the spec-derived
891
+ auto-enumeration (`describe_rpc_round_trip_tests` /
892
+ `describe_rpc_attack_surface_tests`). Where those assert wire-shape,
893
+ conformance cases assert _expected behavior_ — the security negatives
894
+ (must be refused / must not leak / found-vs-not-found same shape) a
895
+ wire-shape check passes green on even when behavior is wrong.
896
+
897
+ - `conformance_case.ts` — `ConformanceCase` Zod schema:
898
+ `{name, request: {method, params?, as, verb?}, expect: {status,
899
+ error_reason?, fields?}, note?, xfail?}`. A case is **data** — `method`
900
+ resolves its `input`/`output` from the live registry (RPC) or `RouteSpec`
901
+ (the 6 REST auth routes), so the case never carries a schema. `as` is the
902
+ closed `ConformancePrincipal` enum (`keeper` / `daemon` / `token` /
903
+ `anonymous` / `fresh_non_admin` / `role_holder` / `wrong_role` /
904
+ `expired_session`) — fixture accessors, never inline credential minting.
905
+ `expired_session` is the keeper behind an expired server-side session
906
+ (`fixture.mint_expired_session()`: a backdated `auth_session` row behind a
907
+ still-valid signed cookie, so the DB-row expiry gate is what refuses it).
908
+ `error_reason` is the imported
909
+ `ERROR_*` constant (asserted against the RPC `error.data.reason` or the
910
+ REST flat-body `error`; the bare `unauthenticated()` 401 carries no
911
+ reason, so `status` pins that denial class).
912
+ - `conformance_table.ts` — `describe_conformance_table_tests({cases,
913
+ setup_test, surface_source, capabilities, rpc_endpoints, session_options,
914
+ principals?, suite_name?})`. Same `{setup_test, surface_source,
915
+ capabilities}` protocol every Tier 1 suite uses, so **one case array runs
916
+ both transports** — in-process (`gro test`) and cross-process (the gate,
917
+ each backend's real auth resolution). `resolve_principal` maps the five
918
+ always-available principals to fixture accessors; `role_holder` /
919
+ `wrong_role` read a seeded `extra_accounts` username named via
920
+ `options.principals`.
921
+ - `xfail.ts` — `xfail_until(tracking_id, reason, name, fn)`, a thin
922
+ `test.fails` wrapper for deferred-by-design rows (visible + self-cleaning:
923
+ turns red when the gap closes, forcing marker removal). In-scope gaps fail
924
+ loud as a normal `test`, not via this marker. Sibling to `test_if` in
925
+ `capabilities.ts`.
926
+
927
+ Wire from a `.db.test.ts` (in-process) and a `.cross.test.ts`
928
+ (cross-process) with the same case array — fuz_app's own runner-proof is
929
+ `../../test/cross_backend/conformance.{db,cross}.test.ts` sharing
930
+ `conformance_proof_cases.ts`.
931
+
876
932
  ### `cross_backend/ws_round_trip.ts` — `describe_cross_process_ws_tests`
877
933
 
878
934
  Real-upgrade WebSocket coverage of a spawned backend — the cross-process
@@ -921,9 +977,54 @@ _own_ sessions are revoked (`account_session_revoke_all`) so the audit guard
921
977
  drops the live stream (asserted via `SseTransport.wait_for_close`). The
922
978
  data-frame + close cases gate on `rpc_path` (they drive the standard
923
979
  account/admin actions); all cases gate on `capabilities.sse`. Cross-process
924
- only — wire from a `*.cross.test.ts`. fuz_app's own wiring is
980
+ only — wire from a `*.cross.test.ts`. fuz*app's own wiring is
925
981
  `src/test/cross_backend/sse.cross.test.ts`; only the TS spines advertise
926
982
  `sse` (they wire `audit_log_sse`), so the Rust `spine_stub` cases `.skip`.
983
+ That file also registers one `xfail_until` (only when `sse: false`) asserting
984
+ the stream \_can't* open on a spine without SSE — a self-cleaning tripwire for
985
+ the spine that should grow it, distinct from the consumer-legit capability
986
+ skip the shared suite emits.
987
+
988
+ ### `cross_backend/cell_crud.ts` + `cell_relations.ts` — cell parity suites
989
+
990
+ The cell-layer parity coverage is split across two sibling suites. Cells
991
+ can't ride the generic `describe_rpc_round_trip_tests` (stateful verbs need a
992
+ real id threaded across calls; `cell_get` has a top-level `.refine()`), so —
993
+ like ws/sse — the full cell surface **live-mounts** on the spine RPC path but
994
+ stays **off** `create_spine_surface_spec`, and these dedicated suites are the
995
+ cell validators (`describe_standard_cross_process_tests`' generic round-trip
996
+ never sees them). Both parse every success response against the verb's Zod
997
+ **output** schema, so a TS↔Rust envelope drift fails the suite — not just a
998
+ payload-field drift. Call-site primitives (`rpc_call` / `error_reason` /
999
+ `expect_output` + the shared `CellCrossTestOptions`) live in
1000
+ `cross_backend/cell_cross_helpers.ts`.
1001
+
1002
+ - **`describe_cell_crud_cross_tests`** (gates on `capabilities.cell_crud`) —
1003
+ the create → get → update → delete → list lifecycle threading the id, plus
1004
+ the CRUD authz matrix (owner CRUD; anon-public-only / private-404; non-owner
1005
+ edit/read/delete → 404 IDOR mask; admin reaches any; dup active `path` → 409;
1006
+ `path` write by non-admin → 403 on create + update; `cell_get` with no
1007
+ id/path → `invalid_params`; null-auth `cell_list` `created_by` →
1008
+ `invalid_params`).
1009
+ - **`describe_cell_relations_cross_tests`** (gates on
1010
+ `capabilities.cell_relations`) — the verbs beyond CRUD: grant lifecycle
1011
+ (actor-shaped editor grant enables edit, manage-tier `cell_grant_list`,
1012
+ revoke), the now-reachable `cell_visibility_manage_only` 403 (editor-grant
1013
+ holder can't flip visibility), field set / forward+reverse list / idempotent
1014
+ delete, item insert / ordered forward+reverse list / move / idempotent
1015
+ delete, clone shallow (shares edges) vs deep (clones children), and
1016
+ manage-tier `cell_audit_list` (owner reads the timeline; a viewer-grant
1017
+ holder who can `cell_get` still gets the IDOR 404). Only **actor-shaped**
1018
+ grants are exercised — role-shaped principals need a closed role registry the
1019
+ Rust spine deliberately lacks.
1020
+
1021
+ Both gate `true` on TS + Rust (cells run on both, no `.skip`). Cross-process
1022
+ wiring is `src/test/cross_backend/cell.cross.test.ts` (both suites); the
1023
+ in-process legs (plain `gro test`) are `src/test/auth/cell_crud_parity.db.test.ts`
1024
+
1025
+ - `cell_relations_parity.db.test.ts`, sharing the full-surface
1026
+ `create_cell_parity_setup` (`cell_parity_helpers.ts`) which mounts every cell
1027
+ verb and registers `cell_audit_events` through the audit factory.
927
1028
 
928
1029
  ### Cross-process plumbing (consumed by `*.cross.test.ts` suites)
929
1030
 
@@ -1000,6 +1101,41 @@ only — wire from a `*.cross.test.ts`. fuz_app's own wiring is
1000
1101
  `testing_reset_actions.ts` TSDoc for the audit + WS fan-out rationale
1001
1102
  that rejected a `_testing_seed_role_grant` shape.
1002
1103
 
1104
+ Same module also exports `create_testing_drain_effects_action()` — the
1105
+ `_testing_drain_effects` RPC action (daemon-token-gated, like
1106
+ `_testing_reset`). It awaits in-flight fire-and-forget audit writes so a
1107
+ following `audit_log_list` is authoritative — the deterministic barrier a
1108
+ cross-process audit assertion fires before reading (no poll/sleep). On the
1109
+ TS spine it is **satisfied by construction** (the binary runs
1110
+ `await_pending_effects: true`, so each mutation's emits land before its
1111
+ response); the Rust spine does the real await in
1112
+ `AuditEmitter::drain_inflight`. `create_testing_actions` bundles it
1113
+ alongside `_testing_reset`; suites that mount their own endpoint (e.g. the
1114
+ in-process `account_lifecycle_parity.db.test.ts`) add it directly so the
1115
+ shared suite body can call the barrier on every backend uniformly.
1116
+
1117
+ Also bundled: `_testing_mint_session` — mints a backdated-expiry
1118
+ `auth_session` row for an account (via `mint_test_session` in `app_server.ts`)
1119
+ and returns its signed cookie value (future-dated payload). Backs the
1120
+ `expired_session` conformance principal: the backdated DB row + valid cookie
1121
+ payload isolate the authoritative server-side DB-row expiry gate
1122
+ (`query_session_get_valid` — `expires_at > NOW()`), the gate the in-process
1123
+ payload-expiry tests never reached. Daemon-token-gated like its siblings; the
1124
+ Rust mirror is `fuz_testing::create_testing_mint_session_action_spec`.
1125
+
1126
+ ### Origin verification parity — `cross_backend/origin.ts`
1127
+
1128
+ `describe_origin_cross_tests({setup_test, capabilities, rpc_path?})` — the
1129
+ imperative Origin-verification suite: disallowed `Origin` → 403 `forbidden_origin` (refused
1130
+ before dispatch), absent `Origin` → request passes (non-browser direct access).
1131
+ Imperative (not a conformance-table row) because origin rejection is
1132
+ middleware-level flat-REST, not the JSON-RPC envelope the table runner expects,
1133
+ and absent-Origin needs `fresh_transport({origin: null})`. Runs both legs (the
1134
+ in-process `auth/origin_parity.db.test.ts` + the cross-process
1135
+ `origin.cross.test.ts`). The promotion surfaced a twin-impl divergence — the
1136
+ Rust spine returned a plain-text body — now converged to the canonical TS
1137
+ `{error: "forbidden_origin"}` via `fuz_http::forbidden_origin_response()`.
1138
+
1003
1139
  ### Building a TS test-server binary — `testing_server_core.ts` + adapters
1004
1140
 
1005
1141
  The reusable shape for standing up a **spawnable TS** cross-process test
@@ -18,6 +18,7 @@ import { type Keyring } from '../auth/keyring.js';
18
18
  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
+ import { type MigrationNamespace } from '../db/migrate.js';
21
22
  import { type AppBackend, type AuditFactory } from '../server/app_backend.js';
22
23
  import { type AppServerOptions, type AppServerContext, type BootstrapServerOptions, type BootstrapLiveOptions } from '../server/app_server.js';
23
24
  import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
@@ -88,6 +89,40 @@ export declare const create_test_account_with_credentials: (options: CreateTestA
88
89
  api_token: string;
89
90
  session_cookie: string;
90
91
  }>;
92
+ /** Options for `mint_test_session`. */
93
+ export interface MintTestSessionOptions {
94
+ db: Db;
95
+ keyring: Keyring;
96
+ session_options: SessionOptions<string>;
97
+ /** Account the minted session belongs to. */
98
+ account_id: string;
99
+ /**
100
+ * Session lifetime offset in seconds applied to `NOW()` for the
101
+ * `auth_session.expires_at` row. A negative value backdates the row so
102
+ * the authoritative DB-row expiry gate (`query_session_get_valid` —
103
+ * `WHERE expires_at > NOW()`) rejects it, while the returned cookie's
104
+ * own signed payload stays valid (future). Resolution therefore passes
105
+ * the cookie-payload check in `parse_session` and is refused at the
106
+ * DB-row gate — the gate the in-process payload-expiry tests never
107
+ * reach and the one that structurally needs a server-side mint.
108
+ */
109
+ expires_in_seconds: number;
110
+ }
111
+ /**
112
+ * Mint a real `auth_session` row for an existing account and return a
113
+ * validly-signed session cookie value referencing it. Test-only — the
114
+ * forge behind the cross-backend expiry conformance cases (the
115
+ * `expired_session` principal): pass a negative `expires_in_seconds` to
116
+ * produce an *expired server-side session* whose signed cookie envelope is
117
+ * still well-formed. Both the TS `_testing_mint_session` action and the
118
+ * in-process `fixture.mint_expired_session()` seam call this so the write
119
+ * semantics match across transports.
120
+ *
121
+ * @mutates `options.db` — inserts one `auth_session` row.
122
+ */
123
+ export declare const mint_test_session: (options: MintTestSessionOptions) => Promise<{
124
+ session_cookie: string;
125
+ }>;
91
126
  /**
92
127
  * Bootstrap the test-DB keeper. Direct-query shortcut for the default
93
128
  * `create_test_app` path — bootstrap is not what most tests exercise, so
@@ -146,6 +181,17 @@ export interface TestAppServerOptions {
146
181
  db?: Db;
147
182
  /** Database driver type — only used when `db` is provided. Default: `'pglite-memory'`. */
148
183
  db_type?: DbType;
184
+ /**
185
+ * Extra migration namespaces run after the builtin auth namespace in the
186
+ * auto-created in-memory PGlite, mirroring `create_app_backend`'s
187
+ * `migration_namespaces`. For suites whose backend needs tables beyond
188
+ * auth — the cell parity suite passes `[CELL_MIGRATION_NS]`. The harness
189
+ * builds + caches a fresh-per-test factory migrating
190
+ * `[auth_migration_ns, ...migration_namespaces]`; the reset-on-`create`
191
+ * gives the same fresh-db isolation as the auth-only default. Mutually
192
+ * exclusive with `db` (which assumes the caller already migrated).
193
+ */
194
+ migration_namespaces?: ReadonlyArray<MigrationNamespace>;
149
195
  /** Password implementation. Default: `stub_password_deps`. Pass `argon2_password_deps` for tests that exercise login. */
150
196
  password?: PasswordHashDeps;
151
197
  /** Username for the bootstrapped account. Default: `'keeper'`. */
@@ -1 +1 @@
1
- {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
@@ -49,6 +49,35 @@ export const DEFAULT_TEST_PASSWORD = 'test-password-123';
49
49
  const fallback_pglite_factory = create_pglite_factory(async (db) => {
50
50
  await run_migrations(db, [auth_migration_ns]);
51
51
  });
52
+ // Auto-created PGlite factories keyed by extra-namespace identity. Suites
53
+ // whose backend needs tables beyond auth (e.g. the cell layer) share one
54
+ // factory per distinct namespace set — and the worker-cached WASM instance —
55
+ // rather than each hand-building its own. The cell parity suite is the first
56
+ // user.
57
+ const fallback_factories_by_namespaces = new Map();
58
+ /**
59
+ * Resolve the no-`db` PGlite factory for the requested extra migration
60
+ * namespaces. Empty / omitted → the shared auth-only `fallback_pglite_factory`;
61
+ * otherwise a memoized factory whose `init` runs
62
+ * `[auth_migration_ns, ...migration_namespaces]` on each reset. The
63
+ * reset-on-`create` gives the same fresh-db-per-test isolation as the
64
+ * auth-only default. Mirrors `create_app_backend`'s `migration_namespaces`
65
+ * seam at the test layer.
66
+ */
67
+ const resolve_fallback_factory = (migration_namespaces) => {
68
+ if (!migration_namespaces || migration_namespaces.length === 0) {
69
+ return fallback_pglite_factory;
70
+ }
71
+ const key = migration_namespaces.map((ns) => ns.namespace).join(',');
72
+ let factory = fallback_factories_by_namespaces.get(key);
73
+ if (!factory) {
74
+ factory = create_pglite_factory(async (db) => {
75
+ await run_migrations(db, [auth_migration_ns, ...migration_namespaces]);
76
+ });
77
+ fallback_factories_by_namespaces.set(key, factory);
78
+ }
79
+ return factory;
80
+ };
52
81
  /**
53
82
  * Create a test account with credentials. Use for additional accounts
54
83
  * minted alongside the keeper (e.g. `TestApp.create_account` for
@@ -77,12 +106,16 @@ export const create_test_account_with_credentials = async (options) => {
77
106
  // Create API token (account-scoped — acting actor is per-request)
78
107
  const { token: api_token, id: token_id, token_hash } = generate_api_token();
79
108
  await query_create_api_token(deps, token_id, account.id, 'test-cli', token_hash);
80
- // Create session (account-scoped — acting actor is per-request)
81
- const session_token = generate_session_token();
82
- const session_hash = hash_session_token(session_token);
83
- const expires_at = new Date(Date.now() + AUTH_SESSION_LIFETIME_MS);
84
- await query_create_session(deps, session_hash, account.id, expires_at);
85
- const session_cookie = await create_session_cookie_value(keyring, session_token, session_options);
109
+ // Create session (account-scoped — acting actor is per-request).
110
+ // Shares the mint primitive with `mint_test_session` / the
111
+ // `_testing_mint_session` action; here with the standard 30-day lifetime.
112
+ const { session_cookie } = await mint_test_session({
113
+ db,
114
+ keyring,
115
+ session_options,
116
+ account_id: account.id,
117
+ expires_in_seconds: AUTH_SESSION_LIFETIME_MS / 1000,
118
+ });
86
119
  return {
87
120
  account: { id: account.id, username: account.username },
88
121
  actor: { id: actor.id },
@@ -90,6 +123,27 @@ export const create_test_account_with_credentials = async (options) => {
90
123
  session_cookie,
91
124
  };
92
125
  };
126
+ /**
127
+ * Mint a real `auth_session` row for an existing account and return a
128
+ * validly-signed session cookie value referencing it. Test-only — the
129
+ * forge behind the cross-backend expiry conformance cases (the
130
+ * `expired_session` principal): pass a negative `expires_in_seconds` to
131
+ * produce an *expired server-side session* whose signed cookie envelope is
132
+ * still well-formed. Both the TS `_testing_mint_session` action and the
133
+ * in-process `fixture.mint_expired_session()` seam call this so the write
134
+ * semantics match across transports.
135
+ *
136
+ * @mutates `options.db` — inserts one `auth_session` row.
137
+ */
138
+ export const mint_test_session = async (options) => {
139
+ const { db, keyring, session_options, account_id, expires_in_seconds } = options;
140
+ const session_token = generate_session_token();
141
+ const session_hash = hash_session_token(session_token);
142
+ const expires_at = new Date(Date.now() + expires_in_seconds * 1000);
143
+ await query_create_session({ db }, session_hash, account_id, expires_at);
144
+ const session_cookie = await create_session_cookie_value(keyring, session_token, session_options);
145
+ return { session_cookie };
146
+ };
93
147
  /**
94
148
  * Bootstrap the test-DB keeper. Direct-query shortcut for the default
95
149
  * `create_test_app` path — bootstrap is not what most tests exercise, so
@@ -132,7 +186,11 @@ const default_test_fs_stubs = {
132
186
  * resets it to false before this runs).
133
187
  */
134
188
  const _build_test_backend = async (options) => {
135
- const { db: existing_db, db_type = 'pglite-memory', password = stub_password_deps, audit_factory = default_audit_factory, fs_stubs = default_test_fs_stubs, } = options;
189
+ const { db: existing_db, db_type = 'pglite-memory', password = stub_password_deps, audit_factory = default_audit_factory, fs_stubs = default_test_fs_stubs, migration_namespaces, } = options;
190
+ if (existing_db && migration_namespaces && migration_namespaces.length > 0) {
191
+ throw new Error('test app setup: pass either `db` (caller owns migrations) or `migration_namespaces` ' +
192
+ '(harness migrates), not both');
193
+ }
136
194
  const keyring_result = create_validated_keyring(TEST_COOKIE_SECRET);
137
195
  if (!keyring_result.ok) {
138
196
  throw new Error(`Test keyring failed: ${keyring_result.errors.join(', ')}`);
@@ -161,7 +219,8 @@ const _build_test_backend = async (options) => {
161
219
  // In-memory PGlite via cached factory — reuses the WASM instance from test_db.ts
162
220
  // instead of creating a new PGlite each time. Schema is reset and migrations re-run
163
221
  // on each call, but the expensive WASM cold start only happens once per worker thread.
164
- const db = await fallback_pglite_factory.create();
222
+ // `migration_namespaces` selects an auth+extras factory; auth-only is the default.
223
+ const db = await resolve_fallback_factory(migration_namespaces).create();
165
224
  const audit = audit_factory({ db, log: test_log });
166
225
  backend = {
167
226
  db_type: 'pglite-memory',
@@ -1 +1 @@
1
- {"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAS9D,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAwB1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AA8FD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAuhBzF,CAAC"}
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;AA2B1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AA8FD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAomBzF,CAAC"}
@@ -30,7 +30,7 @@ import { find_auth_route } from './integration_helpers.js';
30
30
  import { rpc_call_for_spec, require_rpc_endpoint_path, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
31
31
  import { role_grant_offer_and_accept } from './role_grant_helpers.js';
32
32
  import { role_grant_offer_accept_action_spec, role_grant_offer_create_action_spec, role_grant_revoke_action_spec, } from '../auth/role_grant_offer_action_specs.js';
33
- import { admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, app_settings_update_action_spec, audit_log_list_action_spec, AUDIT_LOG_LIST_LIMIT_MAX, invite_create_action_spec, invite_delete_action_spec, } from '../auth/admin_action_specs.js';
33
+ import { account_delete_action_spec, account_purge_action_spec, account_undelete_action_spec, admin_session_revoke_all_action_spec, admin_token_revoke_all_action_spec, app_settings_update_action_spec, audit_log_list_action_spec, AUDIT_LOG_LIST_LIMIT_MAX, invite_create_action_spec, invite_delete_action_spec, } from '../auth/admin_action_specs.js';
34
34
  import { account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from '../auth/account_action_specs.js';
35
35
  /**
36
36
  * Mint a dedicated admin account whose sole job is to read the audit log
@@ -418,6 +418,66 @@ export const describe_audit_completeness_tests = (options) => {
418
418
  assert_has_event(events, 'app_settings_update', 'app_settings_update RPC');
419
419
  });
420
420
  });
421
+ // --- Account deletion RPC actions ---
422
+ describe('account deletion audit events', () => {
423
+ test('account_delete (admin) produces account_delete + actor_delete events', async () => {
424
+ const fixture = await options.setup_test();
425
+ const observer = await create_admin_observer(fixture);
426
+ const target = await fixture.create_account({ username: 'audit_delete_target' });
427
+ const res = await rpc_call_for_spec({
428
+ app: { request: fixture.transport },
429
+ path: rpc_path,
430
+ spec: account_delete_action_spec,
431
+ params: { account_id: target.account.id },
432
+ headers: fixture.create_session_headers(),
433
+ });
434
+ assert.ok(res.ok, `account_delete failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
435
+ const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
436
+ assert_has_event(events, 'account_delete', 'account_delete RPC');
437
+ assert_has_event(events, 'actor_delete', 'account_delete RPC (per-actor cascade)');
438
+ });
439
+ test('account_purge (keeper) produces account_purge + actor_purge events', async () => {
440
+ const fixture = await options.setup_test();
441
+ const observer = await create_admin_observer(fixture);
442
+ const target = await fixture.create_account({ username: 'audit_purge_target' });
443
+ const res = await rpc_call_for_spec({
444
+ app: { request: fixture.transport },
445
+ path: rpc_path,
446
+ spec: account_purge_action_spec,
447
+ params: { account_id: target.account.id, confirm: true },
448
+ // Keeper-gated: daemon-token credential, not a session.
449
+ headers: fixture.create_daemon_token_headers(),
450
+ });
451
+ assert.ok(res.ok, `account_purge failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
452
+ const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
453
+ assert_has_event(events, 'account_purge', 'account_purge RPC');
454
+ assert_has_event(events, 'actor_purge', 'account_purge RPC (per-actor cascade)');
455
+ });
456
+ test('account_undelete (admin) produces account_undelete + actor_undelete events', async () => {
457
+ const fixture = await options.setup_test();
458
+ const observer = await create_admin_observer(fixture);
459
+ const target = await fixture.create_account({ username: 'audit_undelete_target' });
460
+ const del = await rpc_call_for_spec({
461
+ app: { request: fixture.transport },
462
+ path: rpc_path,
463
+ spec: account_delete_action_spec,
464
+ params: { account_id: target.account.id },
465
+ headers: fixture.create_session_headers(),
466
+ });
467
+ assert.ok(del.ok, `account_delete failed: ${del.ok ? '' : JSON.stringify(del.error)}`);
468
+ const res = await rpc_call_for_spec({
469
+ app: { request: fixture.transport },
470
+ path: rpc_path,
471
+ spec: account_undelete_action_spec,
472
+ params: { account_id: target.account.id },
473
+ headers: fixture.create_session_headers(),
474
+ });
475
+ assert.ok(res.ok, `account_undelete failed: ${res.ok ? '' : JSON.stringify(res.error)}`);
476
+ const events = await list_audit_events({ request: fixture.transport }, rpc_path, observer);
477
+ assert_has_event(events, 'account_undelete', 'account_undelete RPC');
478
+ assert_has_event(events, 'actor_undelete', 'account_undelete RPC (per-actor cascade)');
479
+ });
480
+ });
421
481
  // --- Signup route ---
422
482
  describe('signup audit events', () => {
423
483
  test('signup produces signup event', async () => {
@@ -474,6 +534,12 @@ export const describe_audit_completeness_tests = (options) => {
474
534
  'role_grant_revoke',
475
535
  'invite_create',
476
536
  'invite_delete',
537
+ 'account_delete',
538
+ 'account_purge',
539
+ 'account_undelete',
540
+ 'actor_delete',
541
+ 'actor_purge',
542
+ 'actor_undelete',
477
543
  'app_settings_update',
478
544
  ]);
479
545
  /** Event types excluded with justification. */
@@ -0,0 +1,10 @@
1
+ import '../assert_dev_env.js';
2
+ import { type CellCrossTestOptions } from './cell_cross_helpers.js';
3
+ /**
4
+ * Options for the account-lifecycle parity suite. Shares the shape of the
5
+ * cell suites (`setup_test` / `capabilities` / `rpc_path`); reuses
6
+ * `CellCrossTestOptions` rather than minting a structural duplicate.
7
+ */
8
+ export type AccountLifecycleCrossTestOptions = CellCrossTestOptions;
9
+ export declare const describe_account_lifecycle_cross_tests: (options: AccountLifecycleCrossTestOptions) => void;
10
+ //# sourceMappingURL=account_lifecycle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"account_lifecycle.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/account_lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiC9B,OAAO,EAIN,KAAK,oBAAoB,EACzB,MAAM,yBAAyB,CAAC;AAGjC;;;;GAIG;AACH,MAAM,MAAM,gCAAgC,GAAG,oBAAoB,CAAC;AAEpE,eAAO,MAAM,sCAAsC,GAClD,SAAS,gCAAgC,KACvC,IA+TF,CAAC"}