@fuzdev/fuz_app 0.64.0 → 0.65.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions/CLAUDE.md +513 -928
- 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 +1 -4
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +1 -4
- package/dist/actions/register_action_ws.d.ts +2 -2
- package/dist/actions/register_ws_endpoint.d.ts +1 -1
- package/dist/actions/transports_ws_auth_guard.d.ts +1 -2
- package/dist/actions/transports_ws_auth_guard.d.ts.map +1 -1
- package/dist/actions/transports_ws_auth_guard.js +1 -2
- package/dist/auth/CLAUDE.md +591 -1871
- package/dist/auth/account_schema.d.ts +1 -1
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/api_token_queries.js +1 -1
- 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/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +1 -5
- package/dist/auth/bootstrap_routes.d.ts +7 -1
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +15 -11
- 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/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 +224 -498
- 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 +5 -4
- package/dist/http/ip_canonical.d.ts.map +1 -1
- package/dist/http/ip_canonical.js +8 -4
- package/dist/http/origin.d.ts +1 -1
- package/dist/http/origin.js +1 -1
- package/dist/runtime/mock.js +1 -1
- package/dist/server/app_server.d.ts +41 -10
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +10 -4
- 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/static.d.ts +4 -4
- package/dist/server/static.js +7 -7
- package/dist/testing/CLAUDE.md +220 -46
- 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 -201
- package/dist/testing/app_server.d.ts +125 -38
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +140 -42
- 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 -156
- 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/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 -291
- 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 +9 -0
- 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_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 -104
- 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/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 +11 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +24 -21
- 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/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,31 @@ 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
|
+
|
|
85
107
|
### `audit_drift_guard.ts` — audit-emission validation
|
|
86
108
|
|
|
87
109
|
| Helper | Role |
|
|
@@ -154,18 +176,35 @@ Key module-scope values:
|
|
|
154
176
|
`create_test_app_server` uses when no `db` is passed. Reuses the WASM
|
|
155
177
|
cache via `create_pglite_factory`.
|
|
156
178
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
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.
|
|
161
198
|
|
|
162
199
|
| Type | Shape |
|
|
163
200
|
| --------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
164
201
|
| `TestAppServer extends AppBackend` | Adds `account`, `actor`, `api_token`, `session_cookie`, `keyring`, `cleanup()`. |
|
|
165
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. |
|
|
166
|
-
| `CreateTestAppOptions extends TestAppServerOptions` | Adds `create_route_specs` (required), `rpc_endpoints?: RpcEndpointsSuiteOption` (top-level only — single source of truth, symmetric with the suite-level option), and `app_options?: SuiteAppOptions` (`Partial<AppServerOptions>` excluding the
|
|
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`). |
|
|
167
204
|
| `TestAccount` | `{account, actor, session_cookie, api_token, create_session_headers, create_bearer_headers}`. |
|
|
168
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). |
|
|
169
208
|
|
|
170
209
|
`create_test_app` hard-codes the test-friendly `AppServerOptions`:
|
|
171
210
|
`allowed_origins: [/^http:\/\/localhost/]`, stub proxy pinned to
|
|
@@ -200,6 +239,41 @@ hatch is test-only by construction.
|
|
|
200
239
|
| `select_auth_app(apps, auth)` | Map `RouteAuth` → matching Hono app. Throws for missing `role:*` entries. |
|
|
201
240
|
| `resolve_test_path(path)` | `:foo` → `test_foo` — adequate for routes without format-constrained params. |
|
|
202
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
|
+
|
|
203
277
|
## Assertions, coverage, helpers
|
|
204
278
|
|
|
205
279
|
### `assertions.ts` — surface + error-schema assertions
|
|
@@ -463,7 +537,7 @@ validates the response against declared schemas. DB-backed via
|
|
|
463
537
|
`create_test_app`. Per-route test (`test.each`) — one line per route
|
|
464
538
|
in the vitest output.
|
|
465
539
|
|
|
466
|
-
Options: `{
|
|
540
|
+
Options: `{setup_test, surface_source, capabilities, skip_routes?, input_overrides?}`.
|
|
467
541
|
`input_overrides` is a `Map<"METHOD /path", body>` — override generated
|
|
468
542
|
bodies for routes whose input schema can't round-trip cleanly (e.g.
|
|
469
543
|
fields that must reference DB state).
|
|
@@ -476,7 +550,7 @@ picks them up separately.
|
|
|
476
550
|
DB-backed round-trip for RPC: one POST test for all methods, one GET
|
|
477
551
|
test for `side_effects: false` methods. Successful responses validate
|
|
478
552
|
against `action.spec.output`; error responses validate as well-formed
|
|
479
|
-
JSON-RPC error envelopes.
|
|
553
|
+
JSON-RPC error envelopes. Options: `{setup_test, surface_source, capabilities, session_options, rpc_endpoints, skip_methods?, input_overrides?}`.
|
|
480
554
|
The admin RPC auth test picks a session-based identity (`authed` /
|
|
481
555
|
`admin` / bootstrapped keeper) based on `method.auth`; keeper uses the
|
|
482
556
|
daemon token.
|
|
@@ -553,7 +627,7 @@ Support functions: `collect_json_schema_property_names(schema)` (walks
|
|
|
553
627
|
`assert_output_schemas_no_sensitive_fields(surface, fields?)`,
|
|
554
628
|
`assert_non_admin_schemas_no_admin_fields(surface, fields?)`.
|
|
555
629
|
|
|
556
|
-
Options: `{
|
|
630
|
+
Options: `{setup_test, surface_source, capabilities, sensitive_fields?, admin_only_fields?, skip_routes?}`.
|
|
557
631
|
|
|
558
632
|
### `rate_limiting.ts` — `describe_rate_limiting_tests`
|
|
559
633
|
|
|
@@ -567,7 +641,7 @@ Each group asserts its required route exists with a descriptive
|
|
|
567
641
|
message. Creates a tight rate limiter (default `max_attempts: 2`,
|
|
568
642
|
`window_ms: 60_000`) per test and disposes it in `finally`.
|
|
569
643
|
|
|
570
|
-
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.
|
|
571
645
|
|
|
572
646
|
## Integration suites
|
|
573
647
|
|
|
@@ -587,24 +661,39 @@ these thematic areas:
|
|
|
587
661
|
8. Bearer auth + browser-context discard on mutations
|
|
588
662
|
9. Token revocation + cross-account isolation
|
|
589
663
|
10. Response body schema validation + error-response information leakage
|
|
590
|
-
11. Signup invite edge cases +
|
|
664
|
+
11. Signup invite edge cases + expired credential rejection + error-coverage breadth
|
|
591
665
|
|
|
592
666
|
An `ErrorCoverageCollector` runs across groups; `afterAll` filters to
|
|
593
667
|
auth-related routes (login/logout/verify/sessions/tokens/password/
|
|
594
|
-
signup
|
|
595
|
-
|
|
596
|
-
|
|
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).
|
|
597
675
|
|
|
598
|
-
Options: `{session_options,
|
|
676
|
+
Options: `{setup_test, surface_source, capabilities, session_options, rpc_endpoints, error_coverage_min?}`.
|
|
599
677
|
|
|
600
678
|
### `admin_integration.ts` — `describe_standard_admin_integration_tests`
|
|
601
679
|
|
|
602
680
|
7 test groups covering admin surface: account listing, role_grant grant
|
|
603
|
-
lifecycle (via `role_grant_offer_create` + `
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
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?}`.
|
|
608
697
|
|
|
609
698
|
`rpc_endpoints` is `Array<RpcEndpointSpec> | ((ctx: AppServerContext) => Array<RpcEndpointSpec>)` —
|
|
610
699
|
the same `RpcEndpointsSuiteOption` union every DB-backed suite accepts
|
|
@@ -644,28 +733,70 @@ branch.
|
|
|
644
733
|
|
|
645
734
|
### `audit_completeness.ts` — `describe_audit_completeness_tests`
|
|
646
735
|
|
|
647
|
-
Verifies every auth mutation produces the expected `audit_log` row
|
|
648
|
-
|
|
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
|
+
|
|
649
743
|
Same `rpc_endpoints` hard-fail as the admin suite — the mutation-audit
|
|
650
744
|
tests drive role_grant flow, session/token revoke-all, and invite
|
|
651
745
|
create/delete through `role_grant_offer_create_action_spec` /
|
|
652
|
-
`
|
|
746
|
+
`role_grant_offer_accept_action_spec` / `role_grant_revoke_action_spec` /
|
|
747
|
+
`admin_session_revoke_all_action_spec` /
|
|
653
748
|
`admin_token_revoke_all_action_spec` / `app_settings_update_action_spec` /
|
|
654
749
|
`invite_create_action_spec` / `invite_delete_action_spec`.
|
|
655
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
|
+
|
|
656
757
|
Bootstrap audit logging is excluded because `create_test_app` doesn't
|
|
657
758
|
provide the filesystem token state; covered separately in
|
|
658
759
|
`bootstrap_account.db.test.ts`.
|
|
659
760
|
|
|
660
761
|
### `standard.ts` — `describe_standard_tests`
|
|
661
762
|
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
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.
|
|
669
800
|
|
|
670
801
|
## RPC helpers
|
|
671
802
|
|
|
@@ -718,7 +849,7 @@ Registry lookups:
|
|
|
718
849
|
- unauthenticated → `unauthenticated` (code -32001)
|
|
719
850
|
- wrong role → `forbidden` (-32002)
|
|
720
851
|
- authenticated without role → `forbidden`
|
|
721
|
-
- **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).
|
|
722
853
|
- correct auth passes (not 401/403)
|
|
723
854
|
- GET unauthenticated for `side_effects: false` reads
|
|
724
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`.
|
|
@@ -764,3 +895,46 @@ fuz_app-specific points:
|
|
|
764
895
|
(`create_bearer_auth_test_app`, `create_test_middleware_stack_app`)
|
|
765
896
|
alongside the assertions in `src/test/auth/*.test.ts`. Drift surfaces
|
|
766
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"}
|