@fuzdev/fuz_app 0.63.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/CLAUDE.md +525 -827
- package/dist/actions/broadcast_api.d.ts +1 -1
- package/dist/actions/broadcast_api.js +1 -1
- package/dist/actions/cancel.d.ts +2 -2
- package/dist/actions/cancel.js +3 -3
- package/dist/actions/connection_closer.d.ts +65 -0
- package/dist/actions/connection_closer.d.ts.map +1 -0
- package/dist/actions/connection_closer.js +38 -0
- package/dist/actions/register_action_ws.d.ts +2 -2
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +23 -2
- package/dist/actions/register_ws_endpoint.d.ts +12 -10
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +5 -5
- package/dist/actions/transports_ws_auth_guard.d.ts +25 -10
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +24 -9
- package/dist/actions/ws_endpoint_spec.d.ts +119 -0
- package/dist/actions/ws_endpoint_spec.d.ts.map +1 -0
- package/dist/actions/ws_endpoint_spec.js +13 -0
- package/dist/auth/CLAUDE.md +592 -1808
- package/dist/auth/account_action_specs.d.ts +1 -1
- package/dist/auth/account_actions.d.ts +13 -0
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +31 -1
- package/dist/auth/account_routes.d.ts +12 -2
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +55 -8
- package/dist/auth/account_schema.d.ts +4 -4
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.d.ts +8 -8
- package/dist/auth/admin_actions.d.ts +11 -0
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +25 -0
- package/dist/auth/api_token_queries.js +1 -1
- package/dist/auth/audit_emitter.d.ts +56 -12
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +38 -12
- package/dist/auth/audit_log_ddl.d.ts +1 -1
- package/dist/auth/audit_log_ddl.d.ts.map +1 -1
- package/dist/auth/audit_log_ddl.js +1 -1
- package/dist/auth/audit_log_schema.d.ts +5 -3
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +5 -3
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +1 -5
- package/dist/auth/bootstrap_routes.d.ts +8 -2
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +15 -11
- package/dist/auth/invite_schema.d.ts +2 -2
- package/dist/auth/keyring.d.ts +6 -6
- package/dist/auth/keyring.js +8 -8
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +4 -2
- package/dist/auth/signup_routes.d.ts +1 -1
- package/dist/auth/standard_rpc_actions.d.ts +1 -0
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +1 -0
- package/dist/db/create_db.d.ts.map +1 -1
- package/dist/db/create_db.js +13 -0
- package/dist/dev/setup.d.ts +2 -2
- package/dist/dev/setup.js +3 -3
- package/dist/http/CLAUDE.md +225 -483
- package/dist/http/error_schemas.d.ts +0 -4
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +0 -4
- package/dist/http/ip_canonical.d.ts +100 -0
- package/dist/http/ip_canonical.d.ts.map +1 -0
- package/dist/http/ip_canonical.js +195 -0
- package/dist/http/origin.d.ts +14 -6
- package/dist/http/origin.d.ts.map +1 -1
- package/dist/http/origin.js +14 -32
- package/dist/http/pending_effects.d.ts +1 -1
- package/dist/http/pending_effects.js +1 -1
- package/dist/http/proxy.d.ts +13 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +15 -23
- package/dist/http/surface.d.ts +50 -0
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +27 -1
- package/dist/primitive_schemas.d.ts +20 -4
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +25 -4
- package/dist/realtime/sse_auth_guard.d.ts +16 -4
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +15 -3
- package/dist/runtime/mock.js +1 -1
- package/dist/server/app_backend.d.ts +66 -19
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +57 -34
- package/dist/server/app_server.d.ts +101 -10
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +105 -6
- package/dist/server/env.d.ts +7 -7
- package/dist/server/env.d.ts.map +1 -1
- package/dist/server/env.js +14 -14
- package/dist/server/startup.d.ts.map +1 -1
- package/dist/server/startup.js +12 -0
- package/dist/server/static.d.ts +4 -4
- package/dist/server/static.js +7 -7
- package/dist/testing/CLAUDE.md +269 -59
- package/dist/testing/admin_integration.d.ts +18 -23
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +159 -202
- package/dist/testing/adversarial_headers.d.ts +6 -0
- package/dist/testing/adversarial_headers.d.ts.map +1 -1
- package/dist/testing/adversarial_headers.js +13 -5
- package/dist/testing/app_server.d.ts +148 -60
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +143 -54
- package/dist/testing/attack_surface.d.ts +8 -7
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +12 -8
- package/dist/testing/audit_completeness.d.ts +23 -22
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +199 -158
- package/dist/testing/audit_drift_guard.d.ts +116 -0
- package/dist/testing/audit_drift_guard.d.ts.map +1 -0
- package/dist/testing/audit_drift_guard.js +134 -0
- package/dist/testing/bootstrap_success.d.ts +28 -0
- package/dist/testing/bootstrap_success.d.ts.map +1 -0
- package/dist/testing/bootstrap_success.js +144 -0
- package/dist/testing/connection_closer_helpers.d.ts +44 -0
- package/dist/testing/connection_closer_helpers.d.ts.map +1 -0
- package/dist/testing/connection_closer_helpers.js +48 -0
- package/dist/testing/cross_backend/capabilities.d.ts +64 -0
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -0
- package/dist/testing/cross_backend/capabilities.js +47 -0
- package/dist/testing/cross_backend/setup.d.ts +215 -0
- package/dist/testing/cross_backend/setup.d.ts.map +1 -0
- package/dist/testing/cross_backend/setup.js +101 -0
- package/dist/testing/data_exposure.d.ts +14 -15
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +127 -146
- package/dist/testing/db_entities.d.ts +11 -1
- package/dist/testing/db_entities.d.ts.map +1 -1
- package/dist/testing/db_entities.js +13 -1
- package/dist/testing/integration.d.ts +35 -21
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +231 -293
- package/dist/testing/integration_helpers.d.ts +16 -6
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +7 -7
- package/dist/testing/mock_fs.d.ts.map +1 -1
- package/dist/testing/mock_fs.js +0 -2
- package/dist/testing/rate_limiting.d.ts.map +1 -1
- package/dist/testing/rate_limiting.js +13 -4
- package/dist/testing/role_grant_helpers.d.ts +31 -0
- package/dist/testing/role_grant_helpers.d.ts.map +1 -0
- package/dist/testing/role_grant_helpers.js +46 -0
- package/dist/testing/round_trip.d.ts +21 -16
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +65 -86
- package/dist/testing/rpc_helpers.d.ts +2 -1
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.d.ts +24 -21
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +91 -106
- package/dist/testing/schema_introspect.d.ts +106 -0
- package/dist/testing/schema_introspect.d.ts.map +1 -0
- package/dist/testing/schema_introspect.js +123 -0
- package/dist/testing/schema_parity.d.ts +144 -0
- package/dist/testing/schema_parity.d.ts.map +1 -0
- package/dist/testing/schema_parity.js +233 -0
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +12 -6
- package/dist/testing/standard.d.ts +57 -25
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +62 -5
- package/dist/testing/stubs.d.ts +22 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +28 -21
- package/dist/testing/surface_invariants.d.ts +66 -1
- package/dist/testing/surface_invariants.d.ts.map +1 -1
- package/dist/testing/surface_invariants.js +103 -1
- package/dist/testing/transports/surface_source.d.ts +51 -0
- package/dist/testing/transports/surface_source.d.ts.map +1 -0
- package/dist/testing/transports/surface_source.js +19 -0
- package/dist/ui/SurfaceExplorer.svelte +161 -2
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -22,21 +22,21 @@ Enforced by grep, not a linter; make this the first line in new modules.
|
|
|
22
22
|
|
|
23
23
|
### `stubs.ts` — `AppDeps` + `AppServerContext` stubs
|
|
24
24
|
|
|
25
|
-
| Helper | Role
|
|
26
|
-
| ----------------------------------------------------- |
|
|
27
|
-
| `create_throwing_stub<T>(label)` | Proxy whose every property access throws `Throwing stub 'label' — unexpected access to 'prop'`; JS-internal probes return `undefined`; `toJSON` returns `"[throwing_stub:label]"` so accidental serialization is visible rather than `{}`.
|
|
28
|
-
| `create_noop_stub<T>(label, overrides?)` | Proxy whose every method returns `async () => undefined`; `overrides` lets callers pin specific props.
|
|
29
|
-
| `stub` | Pre-built throwing stub labelled `'stub'`.
|
|
30
|
-
| `create_stub_db()` | Returns a real `Db` whose `client.query` yields `{rows: []}` and whose `transaction(fn)` synchronously calls `fn(inner_stub_db)`. Safe for `apply_route_specs`'s declarative transaction wrapper.
|
|
31
|
-
| `stub_handler()` | Returns a fresh `Response('stub')`.
|
|
32
|
-
| `stub_mw` | Pass-through middleware handler (`async (_c, next) => next()`).
|
|
33
|
-
| `stub_app_deps` | Frozen `AppDeps` — every capability is a throwing stub, `audit` is a no-op `AuditEmitter` from `create_test_audit_emitter`.
|
|
34
|
-
| `create_stub_app_deps()` | Factory returning fresh `AppDeps` with no-op FS/keyring/password, a `create_noop_stub` DB, silent `Logger`, no-op `audit`.
|
|
35
|
-
| `create_test_audit_emitter()` | No-op `AuditEmitter` for tests that don't assert on audit fan-out. `emit` / `emit_role_grant_target` are no-ops; `emit_pool` resolves immediately; `notify` is a no-op; `on_event_chain` is empty.
|
|
36
|
-
| `create_stub_audit_sse()` | No-op `AuditLogSse` for surface-test wiring without booting real SSE. `subscribe` returns a no-op cleanup; `on_audit_event` is a no-op; the `registry` is a fresh `SubscriberRegistry` (live `.size` / `.close_*` for tests touching registry state, isolated per call). For real SSE plumbing, build via `create_audit_log_sse` against `create_test_app`.
|
|
37
|
-
| `create_stub_api_middleware({include_daemon_token?})` | Stub `MiddlewareSpec[]` matching `create_auth_middleware_specs`'s output (origin/session/request_context/bearer_auth, optional daemon_token) for surface generation without booting real auth. See `auth/CLAUDE.md` §Middleware for the real stack.
|
|
38
|
-
| `create_stub_app_server_context(session_options)` | Stub `AppServerContext` — rate limiters null, `bootstrap_status.available: false`, `app_settings.open_signup: false`.
|
|
39
|
-
| `create_test_app_surface_spec(options)` | Builds an `AppSurfaceSpec` that mirrors `create_app_server`'s route assembly: consumer routes +
|
|
25
|
+
| Helper | Role |
|
|
26
|
+
| ----------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
27
|
+
| `create_throwing_stub<T>(label)` | Proxy whose every property access throws `Throwing stub 'label' — unexpected access to 'prop'`; JS-internal probes return `undefined`; `toJSON` returns `"[throwing_stub:label]"` so accidental serialization is visible rather than `{}`. |
|
|
28
|
+
| `create_noop_stub<T>(label, overrides?)` | Proxy whose every method returns `async () => undefined`; `overrides` lets callers pin specific props. |
|
|
29
|
+
| `stub` | Pre-built throwing stub labelled `'stub'`. |
|
|
30
|
+
| `create_stub_db()` | Returns a real `Db` whose `client.query` yields `{rows: []}` and whose `transaction(fn)` synchronously calls `fn(inner_stub_db)`. Safe for `apply_route_specs`'s declarative transaction wrapper. |
|
|
31
|
+
| `stub_handler()` | Returns a fresh `Response('stub')`. |
|
|
32
|
+
| `stub_mw` | Pass-through middleware handler (`async (_c, next) => next()`). |
|
|
33
|
+
| `stub_app_deps` | Frozen `AppDeps` — every capability is a throwing stub, `audit` is a no-op `AuditEmitter` from `create_test_audit_emitter`. |
|
|
34
|
+
| `create_stub_app_deps()` | Factory returning fresh `AppDeps` with no-op FS/keyring/password, a `create_noop_stub` DB, silent `Logger`, no-op `audit`. |
|
|
35
|
+
| `create_test_audit_emitter()` | No-op `AuditEmitter` for tests that don't assert on audit fan-out. `emit` / `emit_role_grant_target` are no-ops; `emit_pool` resolves immediately; `notify` is a no-op; `on_event_chain` is empty. |
|
|
36
|
+
| `create_stub_audit_sse()` | No-op `AuditLogSse` for surface-test wiring without booting real SSE. `subscribe` returns a no-op cleanup; `on_audit_event` is a no-op; the `registry` is a fresh `SubscriberRegistry` (live `.size` / `.close_*` for tests touching registry state, isolated per call). For real SSE plumbing, build via `create_audit_log_sse` against `create_test_app`. |
|
|
37
|
+
| `create_stub_api_middleware({include_daemon_token?})` | Stub `MiddlewareSpec[]` matching `create_auth_middleware_specs`'s output (origin/session/request_context/bearer_auth, optional daemon_token) for surface generation without booting real auth. See `auth/CLAUDE.md` §Middleware for the real stack. |
|
|
38
|
+
| `create_stub_app_server_context(session_options)` | Stub `AppServerContext` — rate limiters null, `bootstrap_status.available: false`, `app_settings.open_signup: false`. |
|
|
39
|
+
| `create_test_app_surface_spec(options)` | Builds an `AppSurfaceSpec` that mirrors `create_app_server`'s route assembly: consumer routes + stub middleware + surface generation. `CreateTestAppSurfaceSpecOptions` accepts `session_options`, `create_route_specs`, `env_schema?`, `event_specs?`, `rpc_endpoints?`, `ws_endpoints?`, `transform_middleware?`, `bootstrap?`. Bootstrap is opt-in (symmetric with `create_app_server` — omit to skip; pass the same value you'd pass in production to mount the routes at `bootstrap.route_prefix ?? '/api/account'`). Single source of truth for attack-surface tests — track `create_app_server` wiring changes here. |
|
|
40
40
|
|
|
41
41
|
Throwing stubs surface mock escape: a test that accidentally reaches into
|
|
42
42
|
stub territory breaks immediately with a label-scoped error rather than
|
|
@@ -79,9 +79,50 @@ Returns `{account, actor}`. Replaces the per-file `create_user` /
|
|
|
79
79
|
`create_test_actor` / `create_test_account` helpers that had accumulated
|
|
80
80
|
across the auth test suite. Use for query-level tests that need real
|
|
81
81
|
DB rows but not a full session/token bundle. For tests that also need
|
|
82
|
-
an API token + session cookie + role_grants, use `
|
|
82
|
+
an API token + session cookie + role_grants, use `bootstrap_test_keeper`
|
|
83
83
|
from `app_server.ts` instead.
|
|
84
84
|
|
|
85
|
+
`create_test_role_grant_direct(db, input)` wraps `query_create_role_grant`
|
|
86
|
+
for tests that need an active role_grant seeded directly, bypassing the
|
|
87
|
+
production offer/accept consent flow. Use only when the test focuses on
|
|
88
|
+
revoke or isolation semantics rather than the consent path itself — the
|
|
89
|
+
schema permits null `source_offer_id` for exactly this case. For tests
|
|
90
|
+
that exercise the production grant flow, drive
|
|
91
|
+
`role_grant_offer_and_accept` from `role_grant_helpers.ts` instead.
|
|
92
|
+
|
|
93
|
+
### `role_grant_helpers.ts` — RPC-flow role_grant helpers
|
|
94
|
+
|
|
95
|
+
`role_grant_offer_and_accept({app, rpc_path, grantor, recipient, role})`
|
|
96
|
+
drives the full consent flow (grantor `role_grant_offer_create` →
|
|
97
|
+
recipient `role_grant_offer_accept`) over the production RPC surface and
|
|
98
|
+
returns `{offer_id, role_grant_id}`. Sibling to
|
|
99
|
+
`create_test_role_grant_direct` in `db_entities.ts` — that one bypasses
|
|
100
|
+
the consent flow; this one exercises it end-to-end so the suite picks up
|
|
101
|
+
post-commit fan-out (audit, SSE broadcasts, `_supersede` notifications)
|
|
102
|
+
that a direct DB seed would miss. `grantor` and `recipient` accept
|
|
103
|
+
`TestApp | TestAccount` / `TestAccount` so the call site passes the same
|
|
104
|
+
object that already owns the headers + account id, ruling out caller-side
|
|
105
|
+
mismatch.
|
|
106
|
+
|
|
107
|
+
### `audit_drift_guard.ts` — audit-emission validation
|
|
108
|
+
|
|
109
|
+
| Helper | Role |
|
|
110
|
+
| ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
111
|
+
| `install_audit_drift_guard()` | `beforeEach` resets + `afterEach` zero-checks the `audit_metadata_validation_failures` + `audit_unknown_event_type_failures` counters from `auth/audit_log_queries.ts`. Call once at the top of any `describe_db` block that fires audit emits — production validation is fail-open, so without this any regression that ships a typo'd `event_type` or an undeclared metadata field is silent. Pair with `await_pending_effects: true` (the `create_test_app` default) so fire-and-forget audit writes have completed by response time. |
|
|
112
|
+
| `create_emit_ordering_audit_factory<E>(seq_ref, events_ref, build_inner)` | Returns an `AuditFactory` that wraps the result of `build_inner({db, log})` so every `emit` call pushes `{kind: 'emit', at: seq.value++}` into a shared sequence + events array. Pass through `create_test_app({audit_factory: …})` — the test backend invokes it with its constructed `{db, log}` and lands the wrapped emitter on `deps.audit`. Generic `E extends {kind: string; at: number}` so the events array typechecks against the caller's own `close` / custom marker shape. Pair with `create_recording_closer(seq_ref)` (in `connection_closer_helpers.ts`) for close-vs-emit ordering tests. Scope is `emit` only — `emit_role_grant_target`, `emit_pool`, `notify` forward to the inner emitter unwrapped (same caveat as the previous `patch_audit_emit_capture`). |
|
|
113
|
+
| `AuditEmitMarker` | `{kind: 'emit'; at: number}` — the type of marker `create_emit_ordering_audit_factory` pushes. |
|
|
114
|
+
| `create_recording_audit_emitter(calls_ref?)` | Build a no-op `AuditEmitter` that pushes every `emit` and `emit_pool` call into `calls`. Pass `calls_ref` to write into a caller-owned array; omit to let the helper allocate one. Returns `{emitter, calls}` — destructure `emitter` as the `audit` dep and read `calls` to assert on captured metadata. Replaces per-file capturing emitters previously duplicated across `password_change.test.ts`, `audit_log.test.ts`, etc. |
|
|
115
|
+
| `RecordingAuditEmitter` | `{emitter: AuditEmitter; calls: Array<AuditLogInput>}` — return shape of `create_recording_audit_emitter`. |
|
|
116
|
+
|
|
117
|
+
### `connection_closer_helpers.ts` — `ConnectionCloser` test doubles
|
|
118
|
+
|
|
119
|
+
| Helper | Role |
|
|
120
|
+
| ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
121
|
+
| `create_recording_closer(seq_ref?)` | Returns `{closer, calls}` where every method on `closer` records `{method, id, at}` into `calls`. Pass `seq_ref` to share the sequence counter with `create_emit_ordering_audit_factory` so close + emit markers compose for ordering tests. |
|
|
122
|
+
| `assert_close_call(call, method, id)` | Pins `{method, id}` on a single recorded close call without baking in the `at: N` sequence number. Use at every "did the closer fire?" assertion site; reserve `at: N` assertions for the dedicated ordering test paired with the capture helper. |
|
|
123
|
+
| `RecordedClose` | `{method: 'session' \| 'token' \| 'account', id, at}` — recorded shape pushed by the closer. |
|
|
124
|
+
| `RecordingCloser` | `{closer, calls}` — return shape of `create_recording_closer`. |
|
|
125
|
+
|
|
85
126
|
## Database — `db.ts`
|
|
86
127
|
|
|
87
128
|
Factory builders for parameterized DB tests. Consumer projects pass their
|
|
@@ -135,18 +176,35 @@ Key module-scope values:
|
|
|
135
176
|
`create_test_app_server` uses when no `db` is passed. Reuses the WASM
|
|
136
177
|
cache via `create_pglite_factory`.
|
|
137
178
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
179
|
+
Two helpers share the "insert account + actor + roles + API token +
|
|
180
|
+
session + cookie" flow, split by intent:
|
|
181
|
+
|
|
182
|
+
- `bootstrap_test_keeper(options)` — keeper path used by
|
|
183
|
+
`create_test_app_server`. Same body as the general helper plus a
|
|
184
|
+
lock flip (`UPDATE bootstrap_lock SET bootstrapped = true ...`) so
|
|
185
|
+
test DB state matches a real bootstrap completion, letting
|
|
186
|
+
production code trust the lock as the single signal.
|
|
187
|
+
- `create_test_account_with_credentials(options)` — general path used
|
|
188
|
+
by `TestApp.create_account` for additional non-keeper accounts. Same
|
|
189
|
+
body, no lock interaction (additional accounts aren't bootstraps).
|
|
190
|
+
|
|
191
|
+
Both take `{db, keyring, session_options, password, username?, password_value?, roles?}`
|
|
192
|
+
(the shared `CreateTestAccountWithCredentialsOptions` / `BootstrapTestKeeperOptions`).
|
|
193
|
+
|
|
194
|
+
For exercising the bootstrap success path end-to-end against an empty
|
|
195
|
+
DB (no pre-keeper, lock unflipped), use `create_test_app_for_bootstrap`
|
|
196
|
+
instead — pair with `describe_bootstrap_success_tests` for the
|
|
197
|
+
consumer-runnable suite.
|
|
198
|
+
|
|
199
|
+
| Type | Shape |
|
|
200
|
+
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
201
|
+
| `TestAppServer extends AppBackend` | Adds `account`, `actor`, `api_token`, `session_cookie`, `keyring`, `cleanup()`. |
|
|
202
|
+
| `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. |
|
|
203
|
+
| `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`). |
|
|
204
|
+
| `TestAccount` | `{account, actor, session_cookie, api_token, create_session_headers, create_bearer_headers}`. |
|
|
205
|
+
| `TestApp` | `{app, backend, surface_spec, surface, route_specs, create_session_headers, create_bearer_headers, create_daemon_token_headers, create_account, cleanup}`. |
|
|
206
|
+
| `CreateTestAppForBootstrapOptions` | `{session_options, create_route_specs, rpc_endpoints?, bootstrap: BootstrapLiveOptions, bootstrap_token, app_options?, db?, db_type?, password?, audit_factory?}`. `bootstrap` is required + narrowed to `live` mode (the helper exists for the success-path test). |
|
|
207
|
+
| `TestAppForBootstrap` | `{app, backend, surface_spec, surface, route_specs, create_request_headers, cleanup}`. No keeper credentials (test drives bootstrap itself). |
|
|
150
208
|
|
|
151
209
|
`create_test_app` hard-codes the test-friendly `AppServerOptions`:
|
|
152
210
|
`allowed_origins: [/^http:\/\/localhost/]`, stub proxy pinned to
|
|
@@ -181,6 +239,41 @@ hatch is test-only by construction.
|
|
|
181
239
|
| `select_auth_app(apps, auth)` | Map `RouteAuth` → matching Hono app. Throws for missing `role:*` entries. |
|
|
182
240
|
| `resolve_test_path(path)` | `:foo` → `test_foo` — adequate for routes without format-constrained params. |
|
|
183
241
|
|
|
242
|
+
## Cross-impl schema parity
|
|
243
|
+
|
|
244
|
+
### `schema_introspect.ts` — `query_schema_snapshot`
|
|
245
|
+
|
|
246
|
+
| Helper | Role |
|
|
247
|
+
| ------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
248
|
+
| `query_schema_snapshot(db, options?)` | Introspect a live DB into a deterministic `SchemaSnapshot` via `pg_catalog` + `information_schema`. Captures tables, columns (with `udt_name` to distinguish int4/int8), indexes (`indexdef`), constraints (`pg_get_constraintdef`), sequences, and `schema_version` rows. |
|
|
249
|
+
| `SchemaSnapshot` | Fully JSON-serializable shape — every collection is deterministically sorted on capture so structural equality is stable across runs. `applied_at` is excluded from `schema_version` rows so timestamps don't drift the snapshot. |
|
|
250
|
+
|
|
251
|
+
### `schema_parity.ts` — `assert_schema_snapshots_equal`
|
|
252
|
+
|
|
253
|
+
| Helper | Role |
|
|
254
|
+
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
255
|
+
| `diff_schema_snapshots(a, b)` | Structured `Array<SchemaDiff>` between two snapshots — empty array means parity holds. |
|
|
256
|
+
| `format_schema_diffs(diffs, labels?)` | Human-readable multi-line rendering; labels name the impl on each side (e.g., `{a: 'deno', b: 'rust'}`). |
|
|
257
|
+
| `assert_schema_snapshots_equal(a, b, labels?)` | Throw on drift with a fully-formatted diff message. |
|
|
258
|
+
| `SchemaDiff` | Tagged-union for each drift kind: `schema_version_only_in`, `schema_version_sequence_differs`, `table_only_in`, `column_only_in`, `column_field_differs`, `index_only_in`, `index_definition_differs`, `constraint_only_in`, `constraint_differs`, `sequence_only_in`, `sequence_data_type_differs`. |
|
|
259
|
+
|
|
260
|
+
**Cross-impl gate pattern** — consumers running two backends against a
|
|
261
|
+
shared schema (zzz's `--backend=both`, fuz_app's cross-backend suite)
|
|
262
|
+
bootstrap each impl against an isolated DB, snapshot, then compare:
|
|
263
|
+
|
|
264
|
+
```ts
|
|
265
|
+
await drop_recreate_db('zzz_test');
|
|
266
|
+
await spawn_backend(deno_config);
|
|
267
|
+
const snapshot_deno = await query_schema_snapshot(db);
|
|
268
|
+
await drop_recreate_db('zzz_test');
|
|
269
|
+
await spawn_backend(rust_config);
|
|
270
|
+
const snapshot_rust = await query_schema_snapshot(db);
|
|
271
|
+
assert_schema_snapshots_equal(snapshot_deno, snapshot_rust, {a: 'deno', b: 'rust'});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
Each impl's _own_ tests still gate its DDL correctness independently —
|
|
275
|
+
this pair is purely the cross-impl drift check.
|
|
276
|
+
|
|
184
277
|
## Assertions, coverage, helpers
|
|
185
278
|
|
|
186
279
|
### `assertions.ts` — surface + error-schema assertions
|
|
@@ -213,6 +306,21 @@ Structural invariants (options-free, apply universally):
|
|
|
213
306
|
| `assert_error_code_status_consistency` | The same `z.literal()` error code never appears at two different HTTP statuses. |
|
|
214
307
|
| `assert_404_schemas_use_specific_errors` | Routes with params declaring 404 must use `z.literal()` or `z.enum()`, not generic `z.string()`. |
|
|
215
308
|
|
|
309
|
+
RPC / WS structural invariants (options-free, apply universally over
|
|
310
|
+
`surface.rpc_endpoints` + `surface.ws_endpoints`):
|
|
311
|
+
|
|
312
|
+
| Assertion | Checks |
|
|
313
|
+
| ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
|
314
|
+
| `assert_rpc_method_descriptions_present` | Every RPC method on every endpoint has a non-empty `description`. |
|
|
315
|
+
| `assert_ws_method_descriptions_present` | Every WS method on every endpoint has a non-empty `description`. |
|
|
316
|
+
| `assert_ws_endpoints_include_protocol_actions` | Every WS endpoint includes `heartbeat` + `cancel` (the `protocol_actions` spread from `actions/protocol.js`). |
|
|
317
|
+
| `assert_ws_notifications_have_null_auth` | WS method `kind === 'remote_notification' ⟺ auth === null` — guards against drift between spec union and surface emitter. |
|
|
318
|
+
|
|
319
|
+
Per-endpoint duplicate method names and the auth-shape biconditional are
|
|
320
|
+
already enforced at startup by `compile_action_registry` (see
|
|
321
|
+
`actions/CLAUDE.md` §Registry compile) — these assertions only cover
|
|
322
|
+
contract-surface concerns a runtime registration check cannot reach.
|
|
323
|
+
|
|
216
324
|
Policy invariants (configurable, sensible defaults):
|
|
217
325
|
|
|
218
326
|
| Assertion | Checks |
|
|
@@ -248,7 +356,8 @@ Tightness audit:
|
|
|
248
356
|
|
|
249
357
|
Aggregate runners (called by the standard attack-surface suite):
|
|
250
358
|
|
|
251
|
-
- `assert_surface_invariants(surface)` — runs all structural assertions.
|
|
359
|
+
- `assert_surface_invariants(surface)` — runs all route-level structural assertions.
|
|
360
|
+
- `assert_rpc_ws_surface_invariants(surface)` — runs all RPC/WS structural assertions.
|
|
252
361
|
- `assert_surface_security_policy(surface, options?)` — runs all policy assertions.
|
|
253
362
|
|
|
254
363
|
### `error_coverage.ts` — reachability tracking
|
|
@@ -331,7 +440,7 @@ Single-call bundle of 5 top-level groups (10 named tests + every
|
|
|
331
440
|
adversarial case per route):
|
|
332
441
|
|
|
333
442
|
1. **attack surface snapshot** — `matches committed snapshot`, `is deterministic`.
|
|
334
|
-
2. **attack surface structure** — `only expected public routes`, `full middleware stack on API routes`, `surface invariants`, `security policy`, `error schema tightness` (logs counts and asserts against `default_error_schema_tightness` by default; pass an override config or `null` via `error_schema_tightness`).
|
|
443
|
+
2. **attack surface structure** — `only expected public routes`, `full middleware stack on API routes`, `surface invariants`, `rpc/ws surface invariants`, `security policy`, `error schema tightness` (logs counts and asserts against `default_error_schema_tightness` by default; pass an override config or `null` via `error_schema_tightness`).
|
|
335
444
|
3. **adversarial HTTP auth enforcement** — `unauthenticated → 401`, `wrong role → 403` × roles, `authenticated without role → 403`, `keeper routes reject session credential → 403`, `correct auth passes guard`.
|
|
336
445
|
4. **adversarial input validation** — delegated to `describe_adversarial_input`.
|
|
337
446
|
5. **adversarial 404 response validation** — delegated to `describe_adversarial_404`.
|
|
@@ -384,8 +493,8 @@ body matches the declared 404 Zod schema. No DB needed.
|
|
|
384
493
|
3. no auth headers → passes through
|
|
385
494
|
4. bearer + empty Origin → 403 `ERROR_FORBIDDEN_ORIGIN` (defense-in-depth)
|
|
386
495
|
5. lowercase `bearer` scheme → RFC 7235 §2.1 soft-fail
|
|
387
|
-
6. bearer + rogue Referer →
|
|
388
|
-
7. bearer + allowed Referer → bearer silently discarded
|
|
496
|
+
6. bearer + rogue Referer (no Origin) → passes origin check (Origin-only posture), bearer silently discarded (Referer is still a browser-context indicator for bearer auth)
|
|
497
|
+
7. bearer + allowed Referer (no Origin) → bearer silently discarded (browser context)
|
|
389
498
|
|
|
390
499
|
Each case declares `validate_expectation: 'called' | 'not_called'` so the
|
|
391
500
|
suite asserts that short-circuit middleware actually fires before token
|
|
@@ -428,7 +537,7 @@ validates the response against declared schemas. DB-backed via
|
|
|
428
537
|
`create_test_app`. Per-route test (`test.each`) — one line per route
|
|
429
538
|
in the vitest output.
|
|
430
539
|
|
|
431
|
-
Options: `{
|
|
540
|
+
Options: `{setup_test, surface_source, capabilities, skip_routes?, input_overrides?}`.
|
|
432
541
|
`input_overrides` is a `Map<"METHOD /path", body>` — override generated
|
|
433
542
|
bodies for routes whose input schema can't round-trip cleanly (e.g.
|
|
434
543
|
fields that must reference DB state).
|
|
@@ -441,7 +550,7 @@ picks them up separately.
|
|
|
441
550
|
DB-backed round-trip for RPC: one POST test for all methods, one GET
|
|
442
551
|
test for `side_effects: false` methods. Successful responses validate
|
|
443
552
|
against `action.spec.output`; error responses validate as well-formed
|
|
444
|
-
JSON-RPC error envelopes.
|
|
553
|
+
JSON-RPC error envelopes. Options: `{setup_test, surface_source, capabilities, session_options, rpc_endpoints, skip_methods?, input_overrides?}`.
|
|
445
554
|
The admin RPC auth test picks a session-based identity (`authed` /
|
|
446
555
|
`admin` / bootstrapped keeper) based on `method.auth`; keeper uses the
|
|
447
556
|
daemon token.
|
|
@@ -518,7 +627,7 @@ Support functions: `collect_json_schema_property_names(schema)` (walks
|
|
|
518
627
|
`assert_output_schemas_no_sensitive_fields(surface, fields?)`,
|
|
519
628
|
`assert_non_admin_schemas_no_admin_fields(surface, fields?)`.
|
|
520
629
|
|
|
521
|
-
Options: `{
|
|
630
|
+
Options: `{setup_test, surface_source, capabilities, sensitive_fields?, admin_only_fields?, skip_routes?}`.
|
|
522
631
|
|
|
523
632
|
### `rate_limiting.ts` — `describe_rate_limiting_tests`
|
|
524
633
|
|
|
@@ -532,7 +641,7 @@ Each group asserts its required route exists with a descriptive
|
|
|
532
641
|
message. Creates a tight rate limiter (default `max_attempts: 2`,
|
|
533
642
|
`window_ms: 60_000`) per test and disposes it in `finally`.
|
|
534
643
|
|
|
535
|
-
Options: `{session_options, create_route_specs, app_options?, db_factories?, max_attempts?}`.
|
|
644
|
+
Options: `{session_options, create_route_specs, rpc_endpoints, app_options?, db_factories?, max_attempts?}`. Reads inputs directly from the options bag instead of going through the `setup_test` fixture protocol — the per-test rate-limiter overrides need a fresh `TestApp` per test that the single-fixture model can't carry. Consumers still pass `default_in_process_suite_options(...)` for shape uniformity; the extra `{setup_test, surface_source, capabilities}` fields on the spread are ignored by the suite.
|
|
536
645
|
|
|
537
646
|
## Integration suites
|
|
538
647
|
|
|
@@ -552,31 +661,47 @@ these thematic areas:
|
|
|
552
661
|
8. Bearer auth + browser-context discard on mutations
|
|
553
662
|
9. Token revocation + cross-account isolation
|
|
554
663
|
10. Response body schema validation + error-response information leakage
|
|
555
|
-
11. Signup invite edge cases +
|
|
664
|
+
11. Signup invite edge cases + expired credential rejection + error-coverage breadth
|
|
556
665
|
|
|
557
666
|
An `ErrorCoverageCollector` runs across groups; `afterAll` filters to
|
|
558
667
|
auth-related routes (login/logout/verify/sessions/tokens/password/
|
|
559
|
-
signup
|
|
560
|
-
|
|
561
|
-
|
|
668
|
+
signup) and asserts `DEFAULT_INTEGRATION_ERROR_COVERAGE` (20%). Bootstrap
|
|
669
|
+
is excluded because no describe block in this suite drives it — its
|
|
670
|
+
declared codes would always be uncovered. Consumer-specific routes
|
|
671
|
+
aren't exercised here either — they don't count against the baseline.
|
|
672
|
+
Override the threshold with the `error_coverage_min?: number` option
|
|
673
|
+
(set to `0` to skip the assertion entirely — useful for minimal route
|
|
674
|
+
sets whose declared error codes outpace the suite's denial-path drivers).
|
|
562
675
|
|
|
563
|
-
Options: `{session_options,
|
|
676
|
+
Options: `{setup_test, surface_source, capabilities, session_options, rpc_endpoints, error_coverage_min?}`.
|
|
564
677
|
|
|
565
678
|
### `admin_integration.ts` — `describe_standard_admin_integration_tests`
|
|
566
679
|
|
|
567
680
|
7 test groups covering admin surface: account listing, role_grant grant
|
|
568
|
-
lifecycle (via `role_grant_offer_create` + `
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
681
|
+
lifecycle (via `role_grant_offer_create` + `role_grant_offer_accept` +
|
|
682
|
+
`role_grant_revoke` RPC flows — **not** REST, **not** direct
|
|
683
|
+
`query_accept_offer`; see `auth/CLAUDE.md` for
|
|
684
|
+
`role_grant_offer_action_specs.ts` + `role_grant_offer_actions.ts`),
|
|
685
|
+
session / token management, audit log reads (RPC), admin-to-admin
|
|
686
|
+
isolation, error coverage, response schema validation.
|
|
687
|
+
|
|
688
|
+
The shared `role_grant_offer_and_accept` helper (`role_grant_helpers.ts`)
|
|
689
|
+
composes both RPCs end-to-end and takes
|
|
690
|
+
`{grantor: TestApp | TestAccount, recipient: TestAccount}` — closing
|
|
691
|
+
the headers/account loop on a single object per party rules out caller-side
|
|
692
|
+
header/account mismatch. Direct-grant fixtures (where the test focuses on
|
|
693
|
+
revoke or isolation, not the consent path) go through
|
|
694
|
+
`create_test_role_grant_direct` from `db_entities.ts`.
|
|
695
|
+
|
|
696
|
+
Required options: `{setup_test, surface_source, capabilities, session_options, rpc_endpoints: RpcEndpointsSuiteOption, roles: RoleSchemaResult, admin_prefix?}`.
|
|
573
697
|
|
|
574
698
|
`rpc_endpoints` is `Array<RpcEndpointSpec> | ((ctx: AppServerContext) => Array<RpcEndpointSpec>)` —
|
|
575
699
|
the same `RpcEndpointsSuiteOption` union every DB-backed suite accepts
|
|
576
700
|
(`integration`, `admin_integration`, `audit_completeness`, `rate_limiting`,
|
|
577
701
|
`rpc_round_trip`, `sse_round_trip`). Prefer the factory form: it forwards
|
|
578
|
-
raw to `
|
|
579
|
-
with the real ctx — the only way
|
|
702
|
+
raw to the top-level `rpc_endpoints` slot on `CreateTestAppOptions` so
|
|
703
|
+
`create_app_server` resolves it per-test with the real ctx — the only way
|
|
704
|
+
action handlers can close over
|
|
580
705
|
`ctx.deps` / `ctx.app_settings` (e.g. `create_standard_rpc_actions(ctx.deps,
|
|
581
706
|
{app_settings: ctx.app_settings})`). Factory must return the same endpoint
|
|
582
707
|
`path` regardless of ctx — `resolve_rpc_endpoints_for_setup` invokes it
|
|
@@ -608,28 +733,70 @@ branch.
|
|
|
608
733
|
|
|
609
734
|
### `audit_completeness.ts` — `describe_audit_completeness_tests`
|
|
610
735
|
|
|
611
|
-
Verifies every auth mutation produces the expected `audit_log` row
|
|
612
|
-
|
|
736
|
+
Verifies every auth mutation produces the expected `audit_log` row.
|
|
737
|
+
Mutations fire over the real middleware stack; reads go back through the
|
|
738
|
+
`audit_log_list` RPC (the same path the admin UI consumes) — intentional
|
|
739
|
+
end-to-end coverage of emit → persist → query → wire response. For
|
|
740
|
+
unit-level "did the handler emit?" assertions without the persistence
|
|
741
|
+
path, use `create_recording_audit_emitter` from `audit_drift_guard.ts`.
|
|
742
|
+
|
|
613
743
|
Same `rpc_endpoints` hard-fail as the admin suite — the mutation-audit
|
|
614
744
|
tests drive role_grant flow, session/token revoke-all, and invite
|
|
615
745
|
create/delete through `role_grant_offer_create_action_spec` /
|
|
616
|
-
`
|
|
746
|
+
`role_grant_offer_accept_action_spec` / `role_grant_revoke_action_spec` /
|
|
747
|
+
`admin_session_revoke_all_action_spec` /
|
|
617
748
|
`admin_token_revoke_all_action_spec` / `app_settings_update_action_spec` /
|
|
618
749
|
`invite_create_action_spec` / `invite_delete_action_spec`.
|
|
619
750
|
|
|
751
|
+
**Observer-account pattern.** Each audit-touching test mints a dedicated
|
|
752
|
+
admin account (`create_admin_observer`) whose sole job is reading the
|
|
753
|
+
audit log via RPC. Decoupling the observer from the subject keeps the
|
|
754
|
+
helper shape uniform across every test — even mutations that revoke the
|
|
755
|
+
bootstrapped admin's credentials (logout, session_revoke, password_change).
|
|
756
|
+
|
|
620
757
|
Bootstrap audit logging is excluded because `create_test_app` doesn't
|
|
621
758
|
provide the filesystem token state; covered separately in
|
|
622
759
|
`bootstrap_account.db.test.ts`.
|
|
623
760
|
|
|
624
761
|
### `standard.ts` — `describe_standard_tests`
|
|
625
762
|
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
763
|
+
Bundles every DB-backed suite carrying the standard option shape, each
|
|
764
|
+
gated on its relevant config — silent-skip when the gate isn't met:
|
|
765
|
+
|
|
766
|
+
| Suite | Gate |
|
|
767
|
+
| -------------------- | --------------------------------------------------------------------------------------------------------- |
|
|
768
|
+
| `integration` | always |
|
|
769
|
+
| `admin` | `roles` provided |
|
|
770
|
+
| `audit_completeness` | `roles` provided (proxy for consumer admin wiring; `rpc_endpoints` is bundle-required) |
|
|
771
|
+
| `bootstrap_success` | `bootstrap.mode === 'live'` |
|
|
772
|
+
| `round_trip` | always |
|
|
773
|
+
| `rpc_round_trip` | `rpc_endpoints` provided |
|
|
774
|
+
| `data_exposure` | always |
|
|
775
|
+
| `rate_limiting` | always (owns its own per-test setup, bypasses the fixture protocol — needs `create_route_specs` directly) |
|
|
776
|
+
|
|
777
|
+
Realization that lifted the bundle from 2 suites to 8: fold-in cost
|
|
778
|
+
between suites is zero because each `describe_*` block owns its own
|
|
779
|
+
setup via the `{setup_test, surface_source, capabilities}` protocol, so
|
|
780
|
+
suites whose tests need opposite-shaped default DB state (e.g. the
|
|
781
|
+
bootstrap-success suite needs an empty DB while the integration suite
|
|
782
|
+
needs the pre-bootstrapped keeper) coexist in one bundle without cost.
|
|
783
|
+
Each test invokes the right per-test fixture. Consumers wiring the
|
|
784
|
+
standard surface call once instead of seven times; forgetting a suite
|
|
785
|
+
no longer silently loses coverage.
|
|
786
|
+
|
|
787
|
+
`StandardTestOptions` requires `create_route_specs` (for rate_limiting)
|
|
788
|
+
and `rpc_endpoints` (for admin/audit_completeness/rpc_round_trip); the
|
|
789
|
+
admin suite's requirement is enforced at the type level so a missing
|
|
790
|
+
`rpc_endpoints` is a compile error rather than a runtime throw. Optional
|
|
791
|
+
`bootstrap` (top-level, same precedent as `rpc_endpoints`) feeds both
|
|
792
|
+
the disabled/surface_only/live wire-shape gating and the
|
|
793
|
+
bootstrap-success suite gate.
|
|
794
|
+
|
|
795
|
+
Attack surface suites stay separate — their option shape is
|
|
796
|
+
`{build, snapshot_path, expected_public_routes, ...}` rather than the
|
|
797
|
+
shared `{setup_test, surface_source, capabilities}`. A peer
|
|
798
|
+
`describe_standard_surface_tests` bundler lives for that side if/when
|
|
799
|
+
needed.
|
|
633
800
|
|
|
634
801
|
## RPC helpers
|
|
635
802
|
|
|
@@ -682,7 +849,7 @@ Registry lookups:
|
|
|
682
849
|
- unauthenticated → `unauthenticated` (code -32001)
|
|
683
850
|
- wrong role → `forbidden` (-32002)
|
|
684
851
|
- authenticated without role → `forbidden`
|
|
685
|
-
- **keeper rejects non-daemon credentials** — session and api_token credentials are rejected even when the account has the keeper role (only `daemon_token` passes). The credential-type gate fires before the role gate (see `auth/CLAUDE.md`
|
|
852
|
+
- **keeper rejects non-daemon credentials** — session and api_token credentials are rejected even when the account has the keeper role (only `daemon_token` passes). The credential-type gate fires before the role gate (see `auth/CLAUDE.md` §Keeper auth shape).
|
|
686
853
|
- correct auth passes (not 401/403)
|
|
687
854
|
- GET unauthenticated for `side_effects: false` reads
|
|
688
855
|
2. **RPC adversarial envelopes** — fixed set exercising dispatcher steps 1–2: non-JSON body, wrong `jsonrpc` version, missing `jsonrpc` / `method` / `id`, batch array, unknown method, GET missing `method`/`id`, GET invalid JSON params, GET non-object params, GET mutation method → `invalid_request`.
|
|
@@ -728,3 +895,46 @@ fuz_app-specific points:
|
|
|
728
895
|
(`create_bearer_auth_test_app`, `create_test_middleware_stack_app`)
|
|
729
896
|
alongside the assertions in `src/test/auth/*.test.ts`. Drift surfaces
|
|
730
897
|
as a missed assertion, not a test failure.
|
|
898
|
+
|
|
899
|
+
## Cross-backend integration layer
|
|
900
|
+
|
|
901
|
+
The standard test suites take a unified
|
|
902
|
+
`{setup_test, surface_source, capabilities}` shape so the same suite
|
|
903
|
+
bodies run against an in-process Hono harness today and against a
|
|
904
|
+
spawned non-TS backend (Rust `zzz_server`, `fuz_webui`) over real HTTP
|
|
905
|
+
once the cross-process transport lands. In-process is the fast feedback
|
|
906
|
+
path; cross-process is the source of truth for wire-shape conformance.
|
|
907
|
+
|
|
908
|
+
The shape:
|
|
909
|
+
|
|
910
|
+
- `testing/cross_backend/setup.ts` — `SetupTest` / `TestFixture` /
|
|
911
|
+
`TestAccountFixture` / `CreateTestAccountOptions` types,
|
|
912
|
+
`default_in_process_setup(options)` (wraps `create_test_app`), and
|
|
913
|
+
`default_in_process_suite_options(options)` (emits the full Tier 1
|
|
914
|
+
suite options bag: the `{setup_test, surface_source, capabilities}`
|
|
915
|
+
triple plus `session_options` / `create_route_specs` / `rpc_endpoints`
|
|
916
|
+
pass-through; call sites pass the output directly or spread it
|
|
917
|
+
alongside suite-specific extras like `roles`, `skip_routes`,
|
|
918
|
+
`input_overrides`, `db_factories`).
|
|
919
|
+
- `testing/cross_backend/capabilities.ts` — `BackendCapabilities`
|
|
920
|
+
vocabulary (`bearer_auth` / `trusted_proxy` / `login_rate_limit` /
|
|
921
|
+
`ws` / `sse` / `in_process_only`), `test_if(cond, name, fn)` for
|
|
922
|
+
capability-gated cases, and `in_process_capabilities` preset.
|
|
923
|
+
- `testing/transports/surface_source.ts` — `SurfaceSource` union
|
|
924
|
+
(`inline` for source-of-truth route closures; `snapshot` for
|
|
925
|
+
committed JSON read cross-process).
|
|
926
|
+
|
|
927
|
+
Three suites stay in-process by design — `ws_round_trip` (no HTTP
|
|
928
|
+
transport at all), `sse_round_trip` (streaming + in-process audit
|
|
929
|
+
hook for close-on-revoke), `audit_completeness` (FK-structural
|
|
930
|
+
introspection beyond the `audit_log_list` RPC reads). Cross-process
|
|
931
|
+
variants land alongside the spawned-backend work for the first two;
|
|
932
|
+
audit_completeness's introspection is structurally in-process.
|
|
933
|
+
|
|
934
|
+
The auth-cost handling for cross-process testing is consumer-side:
|
|
935
|
+
each consumer ships a separate test binary wiring a fast-params
|
|
936
|
+
`TestingArgon2idHasher` from a sibling Rust testing crate. Cross-process
|
|
937
|
+
`bootstrap` + `create_account` are then plain RPC calls against the
|
|
938
|
+
test binary — no DB-direct surgery in fuz_app's testing library, no
|
|
939
|
+
runtime knobs in production code, no shared cookie key with the
|
|
940
|
+
backend.
|
|
@@ -1,33 +1,35 @@
|
|
|
1
1
|
import './assert_dev_env.js';
|
|
2
2
|
import type { SessionOptions } from '../auth/session_cookie.js';
|
|
3
|
-
import type { AppServerContext } from '../server/app_server.js';
|
|
4
|
-
import type { RouteSpec } from '../http/route_spec.js';
|
|
5
3
|
import { type RoleSchemaResult } from '../auth/role_schema.js';
|
|
6
|
-
import { type SuiteAppOptions } from './app_server.js';
|
|
7
|
-
import { type DbFactory } from './db.js';
|
|
8
4
|
import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
5
|
+
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
6
|
+
import type { SetupTest } from './cross_backend/setup.js';
|
|
7
|
+
import type { SurfaceSource } from './transports/surface_source.js';
|
|
9
8
|
/**
|
|
10
9
|
* Configuration for `describe_standard_admin_integration_tests`.
|
|
11
10
|
*/
|
|
12
11
|
export interface StandardAdminIntegrationTestOptions {
|
|
13
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Per-test fixture-producing function. The admin suite calls this in
|
|
14
|
+
* every `test()` body — auth_integration_truncate_tables clears
|
|
15
|
+
* `account`, so each test re-bootstraps the keeper.
|
|
16
|
+
*/
|
|
17
|
+
setup_test: SetupTest;
|
|
18
|
+
/**
|
|
19
|
+
* Source of the app surface for route iteration and error-coverage
|
|
20
|
+
* scoping. Currently requires `kind: 'inline'` — the cross-process
|
|
21
|
+
* snapshot variant lands alongside the spawned-backend transport plumbing.
|
|
22
|
+
*/
|
|
23
|
+
surface_source: SurfaceSource;
|
|
24
|
+
/** Backend capability declarations. */
|
|
25
|
+
capabilities: BackendCapabilities;
|
|
26
|
+
/** Session config — needed for cookie_name + factory-form rpc_endpoints resolution. */
|
|
14
27
|
session_options: SessionOptions<string>;
|
|
15
|
-
/** Route spec factory — same one used in production. */
|
|
16
|
-
create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
|
|
17
28
|
/** Role schema result from `create_role_schema()` — used to determine valid/invalid/web-grantable roles. */
|
|
18
29
|
roles: RoleSchemaResult;
|
|
19
30
|
/**
|
|
20
31
|
* RPC endpoint specs — the source `RpcAction` arrays. Required; role_grant
|
|
21
32
|
* grant/revoke are RPC-only and the suite hard-fails without them.
|
|
22
|
-
*
|
|
23
|
-
* Accepts either an array (eager) or a factory
|
|
24
|
-
* `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
|
|
25
|
-
* is required when action handlers must close over the per-test
|
|
26
|
-
* `ctx.app_settings` / `ctx.deps` (e.g. the canonical
|
|
27
|
-
* `create_standard_rpc_actions(ctx.deps, {app_settings: ctx.app_settings})`
|
|
28
|
-
* pattern). The factory must return the same endpoint `path` regardless
|
|
29
|
-
* of ctx — it is invoked once at setup with a stub ctx for path lookup
|
|
30
|
-
* and again per-test by `create_app_server` for live dispatch.
|
|
31
33
|
*/
|
|
32
34
|
rpc_endpoints: RpcEndpointsSuiteOption;
|
|
33
35
|
/**
|
|
@@ -37,13 +39,6 @@ export interface StandardAdminIntegrationTestOptions {
|
|
|
37
39
|
* stub deps. Default `'/api/admin'`.
|
|
38
40
|
*/
|
|
39
41
|
admin_prefix?: string;
|
|
40
|
-
/** Optional overrides for `AppServerOptions`. */
|
|
41
|
-
app_options?: SuiteAppOptions;
|
|
42
|
-
/**
|
|
43
|
-
* Database factories to run tests against. Default: pglite only.
|
|
44
|
-
* Pass consumer factories (e.g. `[pglite_factory, pg_factory]`) to also test against PostgreSQL.
|
|
45
|
-
*/
|
|
46
|
-
db_factories?: Array<DbFactory>;
|
|
47
42
|
}
|
|
48
43
|
/**
|
|
49
44
|
* Standard admin integration test suite for fuz_app admin routes.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"admin_integration.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/admin_integration.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAgC7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAA0B,KAAK,gBAAgB,EAAC,MAAM,wBAAwB,CAAC;AAWtF,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAiB1B,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AACrE,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,gCAAgC,CAAC;AAElE;;GAEG;AACH,MAAM,WAAW,mCAAmC;IACnD;;;;OAIG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;OAIG;IACH,cAAc,EAAE,aAAa,CAAC;IAC9B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,uFAAuF;IACvF,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,4GAA4G;IAC5G,KAAK,EAAE,gBAAgB,CAAC;IACxB;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACtB;AAiBD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,yCAAyC,GACrD,SAAS,mCAAmC,KAC1C,IAqzBF,CAAC"}
|