@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.
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +10 -3
- package/dist/auth/CLAUDE.md +99 -5
- package/dist/auth/account_queries.d.ts +87 -4
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +107 -17
- package/dist/auth/account_schema.d.ts +19 -0
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +8 -0
- package/dist/auth/admin_action_specs.d.ts +170 -3
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +148 -4
- package/dist/auth/admin_actions.d.ts +4 -14
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +246 -40
- package/dist/auth/audit_log_ddl.d.ts +10 -1
- package/dist/auth/audit_log_ddl.d.ts.map +1 -1
- package/dist/auth/audit_log_ddl.js +13 -4
- package/dist/auth/audit_log_schema.d.ts +34 -1
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +73 -0
- package/dist/auth/auth_ddl.d.ts +2 -2
- package/dist/auth/auth_ddl.d.ts.map +1 -1
- package/dist/auth/auth_ddl.js +10 -2
- package/dist/auth/cell_action_specs.d.ts +1295 -0
- package/dist/auth/cell_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_action_specs.js +397 -0
- package/dist/auth/cell_actions.d.ts +63 -0
- package/dist/auth/cell_actions.d.ts.map +1 -0
- package/dist/auth/cell_actions.js +546 -0
- package/dist/auth/cell_audit_action_specs.d.ts +131 -0
- package/dist/auth/cell_audit_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_audit_action_specs.js +70 -0
- package/dist/auth/cell_audit_actions.d.ts +18 -0
- package/dist/auth/cell_audit_actions.d.ts.map +1 -0
- package/dist/auth/cell_audit_actions.js +59 -0
- package/dist/auth/cell_audit_events.d.ts +28 -0
- package/dist/auth/cell_audit_events.d.ts.map +1 -0
- package/dist/auth/cell_audit_events.js +42 -0
- package/dist/auth/cell_audit_metadata.d.ts +48 -0
- package/dist/auth/cell_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_audit_metadata.js +46 -0
- package/dist/auth/cell_authorize.d.ts +88 -0
- package/dist/auth/cell_authorize.d.ts.map +1 -0
- package/dist/auth/cell_authorize.js +172 -0
- package/dist/auth/cell_data_schema.d.ts +44 -0
- package/dist/auth/cell_data_schema.d.ts.map +1 -0
- package/dist/auth/cell_data_schema.js +42 -0
- package/dist/auth/cell_field_action_specs.d.ts +244 -0
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_field_action_specs.js +136 -0
- package/dist/auth/cell_field_actions.d.ts +34 -0
- package/dist/auth/cell_field_actions.d.ts.map +1 -0
- package/dist/auth/cell_field_actions.js +153 -0
- package/dist/auth/cell_field_audit_metadata.d.ts +30 -0
- package/dist/auth/cell_field_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_field_audit_metadata.js +28 -0
- package/dist/auth/cell_grant_action_specs.d.ts +333 -0
- package/dist/auth/cell_grant_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_grant_action_specs.js +148 -0
- package/dist/auth/cell_grant_actions.d.ts +50 -0
- package/dist/auth/cell_grant_actions.d.ts.map +1 -0
- package/dist/auth/cell_grant_actions.js +208 -0
- package/dist/auth/cell_grant_audit_metadata.d.ts +75 -0
- package/dist/auth/cell_grant_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_grant_audit_metadata.js +54 -0
- package/dist/auth/cell_item_action_specs.d.ts +331 -0
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -0
- package/dist/auth/cell_item_action_specs.js +182 -0
- package/dist/auth/cell_item_actions.d.ts +37 -0
- package/dist/auth/cell_item_actions.d.ts.map +1 -0
- package/dist/auth/cell_item_actions.js +204 -0
- package/dist/auth/cell_item_audit_metadata.d.ts +35 -0
- package/dist/auth/cell_item_audit_metadata.d.ts.map +1 -0
- package/dist/auth/cell_item_audit_metadata.js +32 -0
- package/dist/auth/cell_relation_visibility.d.ts +32 -0
- package/dist/auth/cell_relation_visibility.d.ts.map +1 -0
- package/dist/auth/cell_relation_visibility.js +57 -0
- package/dist/auth/deps.d.ts +9 -0
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.d.ts +30 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -1
- package/dist/auth/role_grant_queries.js +54 -0
- package/dist/auth/signup_routes.d.ts +0 -3
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +9 -3
- package/dist/auth/standard_rpc_actions.d.ts +5 -5
- package/dist/auth/standard_rpc_actions.js +4 -4
- package/dist/db/CLAUDE.md +118 -0
- package/dist/db/cell_audit_queries.d.ts +26 -0
- package/dist/db/cell_audit_queries.d.ts.map +1 -0
- package/dist/db/cell_audit_queries.js +53 -0
- package/dist/db/cell_ddl.d.ts +151 -0
- package/dist/db/cell_ddl.d.ts.map +1 -0
- package/dist/db/cell_ddl.js +247 -0
- package/dist/db/cell_field_queries.d.ts +105 -0
- package/dist/db/cell_field_queries.d.ts.map +1 -0
- package/dist/db/cell_field_queries.js +113 -0
- package/dist/db/cell_grant_queries.d.ts +132 -0
- package/dist/db/cell_grant_queries.d.ts.map +1 -0
- package/dist/db/cell_grant_queries.js +145 -0
- package/dist/db/cell_history_ddl.d.ts +38 -0
- package/dist/db/cell_history_ddl.d.ts.map +1 -0
- package/dist/db/cell_history_ddl.js +61 -0
- package/dist/db/cell_item_queries.d.ts +107 -0
- package/dist/db/cell_item_queries.d.ts.map +1 -0
- package/dist/db/cell_item_queries.js +119 -0
- package/dist/db/cell_queries.d.ts +327 -0
- package/dist/db/cell_queries.d.ts.map +1 -0
- package/dist/db/cell_queries.js +431 -0
- package/dist/db/fact_ddl.d.ts +38 -0
- package/dist/db/fact_ddl.d.ts.map +1 -0
- package/dist/db/fact_ddl.js +71 -0
- package/dist/db/fact_queries.d.ts +140 -0
- package/dist/db/fact_queries.d.ts.map +1 -0
- package/dist/db/fact_queries.js +161 -0
- package/dist/db/fact_store.d.ts +112 -0
- package/dist/db/fact_store.d.ts.map +1 -0
- package/dist/db/fact_store.js +225 -0
- package/dist/server/app_server.d.ts +1 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +1 -5
- package/dist/server/env.d.ts +2 -0
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +6 -0
- package/dist/server/fact_write.d.ts +32 -0
- package/dist/server/fact_write.d.ts.map +1 -0
- package/dist/server/fact_write.js +56 -0
- package/dist/server/file_fact_fetcher.d.ts +42 -0
- package/dist/server/file_fact_fetcher.d.ts.map +1 -0
- package/dist/server/file_fact_fetcher.js +60 -0
- package/dist/server/file_fact_url.d.ts +53 -0
- package/dist/server/file_fact_url.d.ts.map +1 -0
- package/dist/server/file_fact_url.js +52 -0
- package/dist/server/serve_fact_route.d.ts +78 -0
- package/dist/server/serve_fact_route.d.ts.map +1 -0
- package/dist/server/serve_fact_route.js +205 -0
- package/dist/testing/CLAUDE.md +142 -6
- package/dist/testing/app_server.d.ts +46 -0
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +67 -8
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +67 -1
- package/dist/testing/cross_backend/account_lifecycle.d.ts +10 -0
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -0
- package/dist/testing/cross_backend/account_lifecycle.js +144 -0
- package/dist/testing/cross_backend/actor_lookup.d.ts +10 -0
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -0
- package/dist/testing/cross_backend/actor_lookup.js +83 -0
- package/dist/testing/cross_backend/actor_search.d.ts +6 -0
- package/dist/testing/cross_backend/actor_search.d.ts.map +1 -0
- package/dist/testing/cross_backend/actor_search.js +92 -0
- package/dist/testing/cross_backend/app_settings.d.ts +6 -0
- package/dist/testing/cross_backend/app_settings.d.ts.map +1 -0
- package/dist/testing/cross_backend/app_settings.js +95 -0
- package/dist/testing/cross_backend/backend_config.d.ts +1 -1
- package/dist/testing/cross_backend/capabilities.d.ts +29 -7
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +3 -1
- package/dist/testing/cross_backend/cell_cross_helpers.d.ts +39 -0
- package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_cross_helpers.js +45 -0
- package/dist/testing/cross_backend/cell_crud.d.ts +4 -0
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_crud.js +168 -0
- package/dist/testing/cross_backend/cell_grant_role.d.ts +8 -0
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_grant_role.js +102 -0
- package/dist/testing/cross_backend/cell_relations.d.ts +4 -0
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_relations.js +229 -0
- package/dist/testing/cross_backend/conformance_case.d.ts +144 -0
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -0
- package/dist/testing/cross_backend/conformance_case.js +132 -0
- package/dist/testing/cross_backend/conformance_table.d.ts +46 -0
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -0
- package/dist/testing/cross_backend/conformance_table.js +199 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +6 -2
- package/dist/testing/cross_backend/default_spine_surface.d.ts +17 -9
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +20 -12
- package/dist/testing/cross_backend/origin.d.ts +10 -0
- package/dist/testing/cross_backend/origin.d.ts.map +1 -0
- package/dist/testing/cross_backend/origin.js +73 -0
- package/dist/testing/cross_backend/setup.d.ts +22 -40
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +39 -5
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +90 -2
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +91 -3
- package/dist/testing/cross_backend/xfail.d.ts +15 -0
- package/dist/testing/cross_backend/xfail.d.ts.map +1 -0
- package/dist/testing/cross_backend/xfail.js +37 -0
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +4 -0
- package/dist/testing/integration.d.ts +2 -3
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +20 -85
- package/dist/testing/rate_limiting.d.ts +1 -1
- package/dist/testing/rpc_helpers.d.ts +3 -3
- package/dist/testing/sse_round_trip.d.ts +1 -1
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +0 -1
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +4 -0
- package/dist/ui/AdminAccounts.svelte +84 -35
- package/dist/ui/AdminAccounts.svelte.d.ts.map +1 -1
- package/dist/ui/AdminSessions.svelte +21 -23
- package/dist/ui/AdminSessions.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +17 -26
- package/dist/ui/OpenSignupToggle.svelte +2 -5
- package/dist/ui/OpenSignupToggle.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +9 -10
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +7 -17
- package/dist/ui/admin_accounts_state.svelte.d.ts +41 -20
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +52 -22
- package/dist/ui/admin_invites_state.svelte.d.ts +8 -11
- package/dist/ui/admin_invites_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_invites_state.svelte.js +7 -16
- package/dist/ui/admin_rpc_adapters.d.ts +6 -2
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +5 -1
- package/dist/ui/admin_sessions_state.svelte.d.ts +6 -10
- package/dist/ui/admin_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_sessions_state.svelte.js +4 -14
- package/dist/ui/app_settings_state.svelte.d.ts +8 -12
- package/dist/ui/app_settings_state.svelte.d.ts.map +1 -1
- package/dist/ui/app_settings_state.svelte.js +6 -16
- package/dist/ui/audit_log_state.svelte.d.ts +9 -8
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +8 -20
- package/package.json +2 -2
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -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
|
|
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
|
-
`
|
|
846
|
-
|
|
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`.
|
|
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":"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;
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
const
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
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;
|
|
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"}
|