@fuzdev/fuz_app 0.127.0 → 0.129.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 +35 -18
- package/dist/actions/connection_closer.d.ts +16 -4
- package/dist/actions/connection_closer.d.ts.map +1 -1
- package/dist/actions/connection_closer.js +20 -4
- package/dist/actions/register_action_ws.d.ts +10 -3
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +6 -3
- package/dist/actions/register_ws_endpoint.d.ts +3 -1
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +3 -1
- package/dist/auth/CLAUDE.md +14 -10
- package/dist/auth/actor_lookup_action_specs.d.ts +2 -2
- package/dist/auth/actor_lookup_action_specs.js +2 -2
- package/dist/auth/audit_emitter.d.ts +73 -19
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +57 -21
- package/dist/auth/bearer_auth.d.ts +3 -2
- package/dist/auth/bearer_auth.d.ts.map +1 -1
- package/dist/auth/bearer_auth.js +3 -2
- package/dist/auth/middleware.d.ts +30 -0
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +59 -0
- package/dist/auth/request_context.d.ts +1 -1
- package/dist/auth/request_context.js +1 -1
- package/dist/auth/token_scope.d.ts +4 -2
- package/dist/auth/token_scope.d.ts.map +1 -1
- package/dist/auth/token_scope.js +10 -2
- package/dist/http/CLAUDE.md +3 -4
- package/dist/http/auth_shape.d.ts +3 -2
- package/dist/http/auth_shape.d.ts.map +1 -1
- package/dist/http/auth_shape.js +3 -2
- package/dist/http/common_routes.d.ts +1 -14
- package/dist/http/common_routes.d.ts.map +1 -1
- package/dist/http/common_routes.js +1 -19
- package/dist/http/ip_canonical.d.ts +1 -1
- package/dist/http/ip_canonical.js +1 -1
- package/dist/realtime/sse_auth_guard.d.ts +6 -2
- package/dist/realtime/sse_auth_guard.d.ts.map +1 -1
- package/dist/realtime/sse_auth_guard.js +3 -1
- package/dist/server/app_server.d.ts +157 -19
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +226 -128
- package/dist/server/surface_route.d.ts +32 -0
- package/dist/server/surface_route.d.ts.map +1 -0
- package/dist/server/surface_route.js +46 -0
- package/dist/testing/CLAUDE.md +50 -32
- package/dist/testing/app_server.d.ts +40 -5
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +84 -68
- package/dist/testing/attack_surface.d.ts +1 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +1 -1
- package/dist/testing/audit_drift_guard.d.ts +5 -3
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +14 -4
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +1 -2
- package/dist/testing/cross_backend/capabilities.d.ts +3 -1
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.d.ts +2 -2
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +5 -2
- package/dist/testing/cross_backend/identity_parity.js +1 -1
- package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/in_process_setup.js +4 -1
- package/dist/testing/cross_backend/method_coverage.d.ts +1 -1
- package/dist/testing/cross_backend/method_coverage.d.ts.map +1 -1
- package/dist/testing/cross_backend/method_coverage.js +2 -2
- package/dist/testing/cross_backend/peer_ping_ws.d.ts +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.d.ts +4 -4
- package/dist/testing/cross_backend/setup.js +1 -1
- package/dist/testing/cross_backend/sse_round_trip.d.ts +1 -1
- package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +51 -32
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +70 -51
- package/dist/testing/cross_backend/testing_server_bun.d.ts +1 -1
- package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_bun.js +80 -64
- package/dist/testing/cross_backend/testing_server_core.d.ts +10 -4
- package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_deno.d.ts +1 -1
- package/dist/testing/cross_backend/testing_server_deno.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_deno.js +1 -1
- package/dist/testing/cross_backend/testing_server_node.d.ts +1 -1
- package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_server_node.js +2 -2
- package/dist/testing/cross_backend/ws_round_trip.d.ts +3 -2
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.js +51 -13
- package/dist/testing/stubs.d.ts +65 -10
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +95 -41
- package/dist/testing/ws_round_trip.d.ts +1 -3
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/ui/AdminSurface.svelte +3 -1
- package/dist/ui/AdminSurface.svelte.d.ts.map +1 -1
- package/dist/ui/CLAUDE.md +3 -1
- package/dist/ui/async_slot.svelte.d.ts +4 -4
- package/dist/ui/async_slot.svelte.js +4 -4
- package/dist/ui/keyed_async_slot.svelte.d.ts +4 -4
- package/dist/ui/keyed_async_slot.svelte.js +3 -3
- package/package.json +1 -1
package/dist/testing/CLAUDE.md
CHANGED
|
@@ -31,13 +31,13 @@ time (never runtime), where a throwing guard would break `vite build`.
|
|
|
31
31
|
- `create_stub_db()` — real `Db` whose `client.query` yields `{rows: []}` and `transaction(fn)` synchronously calls `fn(inner_stub_db)`. Safe for `apply_route_specs`'s declarative transaction wrapper.
|
|
32
32
|
- `stub_handler()` — fresh `Response('stub')`.
|
|
33
33
|
- `stub_mw` — pass-through middleware (`async (_c, next) => next()`).
|
|
34
|
-
- `stub_app_deps` — frozen `AppDeps`, every capability throwing, `audit` a no-op `AuditEmitter` from `create_test_audit_emitter`, `connection_closer` an empty `RealtimeCloser`.
|
|
35
34
|
- `create_stub_app_deps()` — factory: fresh `AppDeps` with no-op keyring/password/`delete_file`, a `read_secure_file` that throws ENOENT (the no-token-file state), a `create_noop_stub` DB, silent `Logger`, no-op `audit`, an empty `connection_closer`.
|
|
36
|
-
- `create_test_audit_emitter()` — no-op `AuditEmitter`; `emit` / `emit_role_grant_target` no-op, `emit_pool` resolves immediately, `notify` no-op, `add_listener` throws (use `create_recording_audit_emitter` for a listener-accepting emitter), `listener_count` returns 0.
|
|
35
|
+
- `create_test_audit_emitter()` — no-op `AuditEmitter`; `emit` / `emit_role_grant_target` no-op, `emit_pool` resolves immediately, `notify` no-op, `add_listener` throws (use `create_recording_audit_emitter` for a listener-accepting emitter), `listener_count` returns 0, `drain_inflight` resolves immediately.
|
|
37
36
|
- `create_stub_audit_sse()` — no-op `AuditLogSse` for surface-test wiring without booting real SSE. `on_audit_event` no-op (nothing is broadcast); `registry` is a fresh `SubscriberRegistry` (live `.count` / `.close_*` for registry-state tests, isolated per call). For real SSE plumbing build via `create_audit_log_sse` against `create_test_app`.
|
|
38
|
-
- `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.
|
|
39
|
-
- `
|
|
40
|
-
- `
|
|
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 — the same names, paths, order, and declared `errors` (`origin` declares a 403; `bearer_auth` and `daemon_token` declare none, since both soft-fail through `next()`), pinned against the real factory by `auth/middleware.mounted.test.ts`. The surface merges middleware errors into every route under the path, so a stub declaring more would widen the test surface with statuses the live server never returns. Each spec gets its own pass-through handler (not the shared `stub_mw`), so `assert_middleware_stack_mounted` tells the layers apart. See `auth/CLAUDE.md` §Middleware for the real stack.
|
|
38
|
+
- `create_stub_app_backend(options?)` — stub `AppBackend` with no database: `create_stub_app_deps()` (or `options.deps`), no migration results, a `close` that does nothing (or `options.close`). For assembling a server in tests that never reach the database.
|
|
39
|
+
- `create_stub_app_server_context(session_options)` — stub `AppServerContext` over `create_stub_app_backend()`; rate limiters null, `bootstrap_status.available: false`.
|
|
40
|
+
- `create_test_app_surface_spec(options)` — builds an `AppSurfaceSpec` mirroring `create_app_server`'s route assembly, in its order (consumer routes, then bootstrap routes, RPC endpoint routes, and `GET /api/surface`, + stub middleware + surface generation). `GET /api/surface` is listed only with `surface_route: true`, as `create_app_server` mounts it only when opted in — pass the same value to both so the surface-derived suites probe it exactly when the live server serves it. `daemon_token: true` lists the `daemon_token` layer `create_app_server` mounts when given a `daemon_token_state` (`create_test_app` always passes one, and `default_in_process_suite_options` sets it, along with `app_options.surface_route`); `testing/test_app_server.db.test.ts` pins the stub's routes (order, auth, error schemas, applicable middleware) and middleware names against `create_test_app`'s for every `surface_route` setting. Its middleware list mirrors the real one — a stub `trusted_proxy` spec at `'*'`, then the stub auth stack — so every route's `applicable_middleware` starts with `trusted_proxy` (a route outside `/api/*` now lists just `['trusted_proxy']`), as on the live server. `CreateTestAppSurfaceSpecOptions`: `session_options`, `create_route_specs`, `env_schema?`, `event_specs?`, `rpc_endpoints?`, `ws_endpoints?`, `allowed_origins?`, `transform_middleware?`, `bootstrap?`, `surface_route?`, `daemon_token?`. Throws, like `create_app_server`, on an endpoint path outside `AUTH_MIDDLEWARE_PATH` (`assert_endpoint_in_auth_scope`) and on a `transform_middleware` that moves, drops, duplicates, reorders, wraps, or replaces the stub proxy spec or a stub auth spec (`assert_middleware_stack_mounted`); the transform receives shallow copies in a fresh array, so an in-place mutation can't slip past the check. Throws on a `ws_endpoints` spec with no `allowed_origins` of its own when no `allowed_origins` option is given (there is no server list to default from). Bootstrap is opt-in (symmetric with `create_app_server` — omit to skip; pass the same value as prod to mount routes at `bootstrap.route_prefix ?? '/api/account'`). Single source of truth for attack-surface tests — track `create_app_server` wiring changes here.
|
|
41
41
|
|
|
42
42
|
Throwing stubs surface mock escape: a test that accidentally reaches into
|
|
43
43
|
stub territory breaks immediately with a label-scoped error rather than
|
|
@@ -100,7 +100,7 @@ mismatch.
|
|
|
100
100
|
### `audit_drift_guard.ts` — audit-emission validation
|
|
101
101
|
|
|
102
102
|
- `install_audit_drift_guard()` — `beforeEach` resets + `afterEach` zero-checks `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 firing audit emits — production validation is fail-open, so without this any regression shipping a typo'd `event_type` or undeclared metadata field is silent. Pair with `await_pending_effects: true` (the `create_test_app` default) so fire-and-forget audit writes complete by response time.
|
|
103
|
-
- `create_recording_audit_emitter(calls_ref?)` — no-op `AuditEmitter` pushing every `emit` and `emit_pool` call into `calls`. Pass `calls_ref` to write into a caller-owned array; omit to let the helper allocate. Returns `{emitter, calls}` — destructure `emitter` as the `audit` dep and read `calls` to assert. Replaces per-file capturing emitters previously duplicated across `password_change.test.ts`, `audit_log.test.ts`, etc.
|
|
103
|
+
- `create_recording_audit_emitter(calls_ref?)` — no-op `AuditEmitter` pushing every `emit` and `emit_pool` call into `calls`. Pass `calls_ref` to write into a caller-owned array; omit to let the helper allocate. Returns `{emitter, calls}` — destructure `emitter` as the `audit` dep and read `calls` to assert. `add_listener` records (never fires) and returns a remover with the production semantics, so `listener_count` tracks what a server registered and released. Replaces per-file capturing emitters previously duplicated across `password_change.test.ts`, `audit_log.test.ts`, etc.
|
|
104
104
|
- `RecordingAuditEmitter` — `{emitter: AuditEmitter; calls: Array<AuditLogInput>}`.
|
|
105
105
|
|
|
106
106
|
### `connection_closer_helpers.ts` — `ConnectionCloser` test doubles
|
|
@@ -150,6 +150,7 @@ fully assembled Hono app + the backend + helpers.
|
|
|
150
150
|
Key module-scope values:
|
|
151
151
|
|
|
152
152
|
- `stub_password_deps` — `PasswordHashDeps` hashing via `stub_hash_${password}` and verifying by equality. Deterministic, no Argon2 cost — use for every test not specifically exercising password hashing.
|
|
153
|
+
- `create_loopback_app_server_options()` — the loopback `create_app_server` options an in-process test server takes (`localhost` origins, the loopback proxy trusted, an empty env schema), fresh per call; `create_test_app` and `create_test_app_for_bootstrap` spread it under their own defaults (`rate_limiters: 'disabled_for_testing'`, `await_pending_effects: true`).
|
|
153
154
|
- `TEST_COOKIE_SECRET` — 64-hex-char deterministic cookie secret. Produces a valid `Keyring` via `create_validated_keyring`. Never used in production — the stub guard plus fixed value is the contract.
|
|
154
155
|
- `fallback_pglite_factory` — module-level auth-only PGlite factory `create_test_app_server` uses when no `db` is passed. Reuses the WASM cache via `create_pglite_factory`. When `migration_namespaces` is supplied, a memoized sibling factory (keyed by namespace set) migrating `[auth_migration_ns, ...migration_namespaces]` is used instead — same shared WASM, extra tables.
|
|
155
156
|
|
|
@@ -172,9 +173,9 @@ Types:
|
|
|
172
173
|
- `TestAppServerOptions` — `session_options` (required), optional `db`, `db_type`, `migration_namespaces`, `password`, `username`, `password_value`, `roles`, `audit_factory`. `migration_namespaces` runs extra namespaces after auth in the auto-created PGlite (mirrors `create_app_backend`); mutually exclusive with `db` (caller-migrated) — passing both throws. The optional `audit_factory` defaults to `default_audit_factory` (no-listener `create_audit_emitter` over the test backend's `{db, log}`); pass a custom factory to compose `on_audit_event` / `audit_log_config`, instrument `emit` with `emit_decorator`, 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.
|
|
173
174
|
- `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`).
|
|
174
175
|
- `TestAccount` — `{account, actor, session_cookie, api_token, create_session_headers, create_bearer_headers}`.
|
|
175
|
-
- `TestApp` — `{app, backend, surface_spec, surface, route_specs, create_session_headers, create_bearer_headers, create_daemon_token_headers, create_account, cleanup}`.
|
|
176
|
+
- `TestApp` — `{app, backend, server, surface_spec, surface, route_specs, create_session_headers, create_bearer_headers, create_daemon_token_headers, create_account, cleanup}`. `server` is the assembled `AppServer`, for what `create_app_server` built beyond the lifted fields — `require_audit_sse(test_app.server)` with `audit_log_sse` in `app_options`, `test_app.server.ws_endpoints[path]` — instead of capturing them from a `create_route_specs` closure.
|
|
176
177
|
- `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).
|
|
177
|
-
- `TestAppForBootstrap` — `{app, backend, surface_spec, surface, route_specs, create_request_headers, cleanup}`. No keeper credentials (test drives bootstrap itself).
|
|
178
|
+
- `TestAppForBootstrap` — `{app, backend, server, surface_spec, surface, route_specs, create_request_headers, cleanup}`. No keeper credentials (test drives bootstrap itself).
|
|
178
179
|
|
|
179
180
|
`create_test_app` hard-codes the test-friendly `AppServerOptions`:
|
|
180
181
|
`allowed_origins: [/^http:\/\/localhost/]`, stub proxy pinned to `127.0.0.1`,
|
|
@@ -187,8 +188,9 @@ vitest project, once per run under `isolate: false`, ../../../docs/usage.md §Ra
|
|
|
187
188
|
the response returns so tests can assert on side effects inline), and silent
|
|
188
189
|
logger. Override via `app_options`. `create_test_app_for_bootstrap` disables
|
|
189
190
|
the limiters the same way. `cleanup()` is the assembled server's idempotent
|
|
190
|
-
`close` — it also closes the server's live connections
|
|
191
|
-
limiters it built
|
|
191
|
+
`close` — it also closes the server's live connections, disposes the
|
|
192
|
+
limiters it built, and removes the audit listeners and closer members it
|
|
193
|
+
added.
|
|
192
194
|
|
|
193
195
|
`auth_cleanup` is left at its default, **off**, here and in
|
|
194
196
|
`create_test_app_for_bootstrap`: a background pass would delete rows and
|
|
@@ -978,10 +980,11 @@ source of truth for wire-shape conformance.
|
|
|
978
980
|
same TCP write as the upgrade request reach the socket; `false` Bun, whose
|
|
979
981
|
HTTP parser answers such a request `400`, and `false` in-process, where there
|
|
980
982
|
is no socket to write raw bytes to).
|
|
981
|
-
`ws_account_actions`
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
983
|
+
`ws_account_actions` (the WS endpoint mounts the account actions — `true` on
|
|
984
|
+
fuz_app's own spine presets, `false` in the family defaults) gates the WS
|
|
985
|
+
round-trip suite's self-revocation case and, with `rpc_path`, its
|
|
986
|
+
WS-mutation audit case (which calls `_testing_drain_effects`);
|
|
987
|
+
`ws_self_revocation_reply` says what the self-revocation case requires: the reply ahead of the close (`true`, the TS
|
|
985
988
|
family) or the close alone (`false`, the Rust family, whose socket loop
|
|
986
989
|
drops the response once the connection is closed — a recorded divergence).
|
|
987
990
|
|
|
@@ -1126,11 +1129,15 @@ time**, so the negative upgrade cases assert the upgrade itself rejects, not
|
|
|
1126
1129
|
a per-message error; the close cases prove a revocation closes an
|
|
1127
1130
|
already-open socket, since per-message dispatch never re-checks credential
|
|
1128
1131
|
validity. Omit `rpc_path` to skip the close cases (consumers without the
|
|
1129
|
-
standard account and admin actions on their RPC endpoint).
|
|
1132
|
+
standard account and admin actions on their RPC endpoint). Two cases are gated
|
|
1130
1133
|
on `capabilities.ws_account_actions` (the WS endpoint mounts the account
|
|
1131
1134
|
actions): a socket that revokes its own sessions over that socket is closed
|
|
1132
1135
|
with `WS_CLOSE_SESSION_REVOKED`, and — where the backend declares
|
|
1133
|
-
`capabilities.ws_self_revocation_reply` — reads the reply first
|
|
1136
|
+
`capabilities.ws_self_revocation_reply` — reads the reply first; and (with
|
|
1137
|
+
`rpc_path`) a socket mints an API token over WS, and its `token_create` audit
|
|
1138
|
+
row is read back after `_testing_drain_effects` — a WS reply can precede its
|
|
1139
|
+
audit write, so the drain is the barrier (the binary mounts it via
|
|
1140
|
+
`create_testing_actions`).
|
|
1134
1141
|
Otherwise **consumer-agnostic** — it drives only the `heartbeat` protocol action
|
|
1135
1142
|
(guaranteed on every WS endpoint by `assert_ws_endpoints_include_protocol_actions`),
|
|
1136
1143
|
so it validates the transport without touching domain WS methods. Gated on
|
|
@@ -1436,8 +1443,11 @@ are `src/test/auth/cell_crud_parity.db.test.ts`
|
|
|
1436
1443
|
`create_test_account_with_credentials` (same primitive in-process uses,
|
|
1437
1444
|
keeping write semantics in parity), seeds any `extra_accounts` at the
|
|
1438
1445
|
same bootstrap-equivalent step (the only path for roles like
|
|
1439
|
-
`ROLE_KEEPER` whose `grant_paths` is bootstrap-only),
|
|
1440
|
-
`DaemonTokenState.keeper_account_id` to the new row
|
|
1446
|
+
`ROLE_KEEPER` whose `grant_paths` is bootstrap-only), queues the refresh
|
|
1447
|
+
of `DaemonTokenState.keeper_account_id` to the new row as a post-commit
|
|
1448
|
+
effect (so a reset that rolls back — a throwing `reset_state`, a failed
|
|
1449
|
+
commit — leaves the cache on the keeper still in the database, and the
|
|
1450
|
+
next reset can still authenticate), then fires the
|
|
1441
1451
|
consumer-supplied `reset_state(db)` callback for domain-state reset —
|
|
1442
1452
|
passed the **transactional** `Db` the auth wipe ran on, so DB-domain
|
|
1443
1453
|
consumers (e.g. fuz_forge truncating its cell / fact / file tables) reset
|
|
@@ -1449,18 +1459,26 @@ are `src/test/auth/cell_crud_parity.db.test.ts`
|
|
|
1449
1459
|
`testing_reset_actions.ts` TSDoc for the audit + WS fan-out rationale
|
|
1450
1460
|
that rejected a `_testing_seed_role_grant` shape.
|
|
1451
1461
|
|
|
1452
|
-
Same module also exports `create_testing_drain_effects_action()` — the
|
|
1462
|
+
Same module also exports `create_testing_drain_effects_action(audit)` — the
|
|
1453
1463
|
`_testing_drain_effects` RPC action (daemon-token-gated, like
|
|
1454
|
-
`_testing_reset`). It awaits
|
|
1455
|
-
|
|
1456
|
-
cross-process audit assertion fires before reading (no poll/sleep).
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
+
`_testing_reset`). It awaits `audit.drain_inflight()` so a following
|
|
1465
|
+
`audit_log_list` is authoritative — the deterministic barrier a
|
|
1466
|
+
cross-process audit assertion fires before reading (no poll/sleep). The
|
|
1467
|
+
emitter must be built with `create_audit_emitter({track_inflight: true})`
|
|
1468
|
+
(the spine binary's `audit_factory` does — pinned by
|
|
1469
|
+
`cross_backend/testing_spine_server.audit.test.ts`), or the drain resolves at once.
|
|
1470
|
+
The barrier is needed whatever the transport: HTTP on a binary running
|
|
1471
|
+
`await_pending_effects: true` settles a mutation's writes before
|
|
1472
|
+
responding, but WS replies before its effects flush, so a WS mutation's
|
|
1473
|
+
audit write can still be in flight when its reply arrives. The drain waits
|
|
1474
|
+
until no tracked `emit` write is outstanding — writes started while it
|
|
1475
|
+
waits included — the same wait-for-zero as the Rust spine's
|
|
1476
|
+
`AuditEmitter::drain_inflight` over `new_with_inflight_tracking`.
|
|
1477
|
+
`create_testing_actions` bundles it over `deps.audit`; suites that mount
|
|
1478
|
+
their own endpoint (e.g. the in-process
|
|
1479
|
+
`account_lifecycle_parity.db.test.ts`) add it directly, over a tracked
|
|
1480
|
+
emitter, so the shared suite body can call the barrier on every backend
|
|
1481
|
+
uniformly.
|
|
1464
1482
|
|
|
1465
1483
|
Also bundled: `_testing_mint_session` — mints a backdated-expiry
|
|
1466
1484
|
`auth_session` row for an account (via `mint_test_session` in `app_server.ts`)
|
|
@@ -1801,10 +1819,10 @@ binary (the TS analog of the Rust `testing_spine_stub`), so consumers don't
|
|
|
1801
1819
|
re-roll the serve / daemon-info / WS-attach / drain boilerplate:
|
|
1802
1820
|
|
|
1803
1821
|
- `testing/cross_backend/testing_server_core.ts` — `start_testing_server({adapter, daemon_name, host, port, app_version?, build_app})`. Owns the runtime-neutral orchestration: open-host refusal, stale-daemon check, daemon-info write, `serve`, post-serve WS attach, graceful shutdown. Domain-free — the app is the caller's `build_app(context: BuildTestingAppContext): Promise<BuiltTestingApp>` seam (`{app, close}`). WebSocket endpoints mount through `create_app_server`'s `ws_endpoints` on every runtime: the build calls `context.prepare_websocket({max_message_bytes?})`, which asks the adapter for a `PreparedWebsocket` (`{create_upgrade_websocket: (app) => UpgradeWebSocket, attach_to_server?}`) and returns its factory, to pass as `create_upgrade_websocket`; the core runs each `attach_to_server` once `serve` returns (Node's `injectWebSocket`). Shutdown starts the listener's `shutdown` (stop accepting), runs `close` — which must end live WebSockets, as `AppServer.close` does — then awaits the drain, since open sockets would otherwise hold it. Exports the `TestingServerAdapter` (`prepare_websocket: (options?: PrepareWebsocketOptions) => PreparedWebsocket`) / `ServeHandle` / `PreparedWebsocket` / `PrepareWebsocketOptions` / `BuildTestingAppContext` interfaces.
|
|
1804
|
-
- `testing/cross_backend/testing_server_node.ts` — `create_node_testing_adapter()` (`@hono/node-server` + `@hono/node-ws`). Optional peer deps (like `ws`); only test binaries import them. Its factory sets the `ws` server's `maxPayload` to `max_message_bytes` (default `DEFAULT_WS_MAX_MESSAGE_BYTES`) — `@hono/node-ws` takes no option for it, and `ws`'s own default is 100 MiB — so an oversized message is refused at the frame layer (still `WS_CLOSE_MESSAGE_TOO_BIG`) before it is buffered whole. Deno
|
|
1822
|
+
- `testing/cross_backend/testing_server_node.ts` — `create_node_testing_adapter()` (`@hono/node-server` + `@hono/node-ws`). Optional peer deps (like `ws`); only test binaries import them. Its factory sets the `ws` server's `maxPayload` to `max_message_bytes` (default `DEFAULT_WS_MAX_MESSAGE_BYTES`) — `@hono/node-ws` takes no option for it, and `ws`'s own default is 100 MiB — so an oversized message is refused at the frame layer (still `WS_CLOSE_MESSAGE_TOO_BIG`) before it is buffered whole. Bun's adapter sets twice it as its frame cap (see below); Deno takes no frame cap and keeps the per-message check alone.
|
|
1805
1823
|
- `testing/cross_backend/testing_server_deno.ts` — `create_deno_testing_adapter()` (`Deno.serve` + `@hono/deno`, an optional peer dep; `Deno` declared locally so it typechecks under the Node toolchain). Spawn the entry with `--sloppy-imports` (Deno doesn't do `.js`→`.ts`; Gro's loader does, so the Node path needs no flag).
|
|
1806
|
-
- `testing/cross_backend/testing_server_bun.ts` — `create_bun_testing_adapter()` (`Bun.serve` + `@hono/bun`'s module-level `getConnInfo`, `upgradeWebSocket`, and `websocket`; `Bun.serve` declared locally so it typechecks under the Node toolchain). Needs the optional `@hono/bun` peer dep (the counterpart to Node's `@hono/node-server` + `@hono/node-ws`; `Bun.serve` is built in), and Bun resolves `.js`→`.ts` natively (no flag, unlike Deno). Reuses `create_node_runtime` (Bun implements the `node:fs`/`node:process` surface). WS is module-level + stateless (like Deno) — the `websocket` handler is threaded into `serve`, where `Bun.serve` wants it, so no post-serve attach.
|
|
1807
|
-
- `testing/cross_backend/default_spine_surface.ts` — the canonical no-domain spine surface (account/admin/audit/signup + bootstrap): `spine_session_options`, `spine_roles`, `create_spine_route_specs`, `spine_rpc_endpoints`, `create_spine_surface_spec`. This is the **declared** surface — the `create_standard_rpc_actions` bundle the spec-derived suites auto-enumerate. `$lib`-free (it's reached by the spawned binary under Gro's loader, which doesn't resolve `$lib`), so keep it on relative imports. Shared by the spine_stub cross test, the TS cross tests, and the binary.
|
|
1824
|
+
- `testing/cross_backend/testing_server_bun.ts` — `create_bun_testing_adapter()` (`Bun.serve` + `@hono/bun`'s module-level `getConnInfo`, `upgradeWebSocket`, and `websocket`; `Bun.serve` declared locally so it typechecks under the Node toolchain). Needs the optional `@hono/bun` peer dep (the counterpart to Node's `@hono/node-server` + `@hono/node-ws`; `Bun.serve` is built in), and Bun resolves `.js`→`.ts` natively (no flag, unlike Deno). Reuses `create_node_runtime` (Bun implements the `node:fs`/`node:process` surface). WS is module-level + stateless (like Deno) — the `websocket` handler is threaded into `serve`, where `Bun.serve` wants it, so no post-serve attach. It carries `maxPayloadLength` at twice `max_message_bytes` rather than Bun's 16 MiB default: past its cap Bun drops the socket without a close frame (1006), so the headroom leaves a message just over the limit to the per-message check (`WS_CLOSE_MESSAGE_TOO_BIG`, as on Node), while a grossly oversized one is refused before Bun buffers it whole.
|
|
1825
|
+
- `testing/cross_backend/default_spine_surface.ts` — the canonical no-domain spine surface (account/admin/audit/signup + bootstrap): `spine_session_options`, `spine_roles`, `create_spine_route_specs`, `spine_rpc_endpoints`, `create_spine_surface_spec`. This is the **declared** surface — the `create_standard_rpc_actions` bundle the spec-derived suites auto-enumerate. It passes `daemon_token: true` (both spine binaries mount the daemon-token layer) and leaves `GET /api/surface` at its default, off — the TS spine binary does too, and the Rust `testing_spine_stub` serves none. `$lib`-free (it's reached by the spawned binary under Gro's loader, which doesn't resolve `$lib`), so keep it on relative imports. Shared by the spine_stub cross test, the TS cross tests, and the binary.
|
|
1808
1826
|
- `testing/cross_backend/full_spine_mount.ts` — `build_full_spine_rpc_actions(deps, options)` — the **full** live RPC mount: the declared bundle **plus** the off-declared-surface families the binary live-mounts (`_testing_*` backdoors, the cell verb set, the opt-in `actor_lookup` / `actor_search` resolvers). `build_full_spine_mount(deps, options)` builds it once for both of the binary's endpoints, returning `FullSpineMount` (`{rpc_actions, ws_actions}`): the WS list is `protocol_actions` plus every RPC action not already among them, twinning the Rust `testing_spine_stub`, which serves one registry on RPC and WS (the one difference is `cancel` — a registered protocol action on TS, owned by the read loop on Rust). Single-sources the mount so the binary and the `spine_method_coverage` reconciliation test build the same list. Also `$lib`-free.
|
|
1809
1827
|
- `testing/cross_backend/ts_spine_backend_config.ts` — `ts_spine_node_backend_config()` / `ts_spine_deno_backend_config()` / `ts_spine_bun_backend_config()` presets (in-memory PGlite, no external infra), the TS analog of `rust_spine_stub_backend_config()`.
|
|
1810
1828
|
|
|
@@ -21,7 +21,7 @@ import type { PasswordHashDeps } from '../auth/password.js';
|
|
|
21
21
|
import { type SessionOptions } from '../auth/session_cookie.js';
|
|
22
22
|
import { type MigrationNamespace } from '../db/migrate.js';
|
|
23
23
|
import { type AppBackend, type AuditFactory } from '../server/app_backend.js';
|
|
24
|
-
import { type AppServerOptions, type BootstrapServerOptions, type BootstrapLiveOptions } from '../server/app_server.js';
|
|
24
|
+
import { type AppServer, type AppServerOptions, type BootstrapServerOptions, type BootstrapLiveOptions } from '../server/app_server.js';
|
|
25
25
|
import type { AppServerContext } from '../server/app_server_context.js';
|
|
26
26
|
import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
|
|
27
27
|
import type { RouteSpec } from '../http/route_spec.js';
|
|
@@ -201,11 +201,30 @@ export interface TestAppServerOptions {
|
|
|
201
201
|
*
|
|
202
202
|
* Matches the production shape — `create_app_backend` requires an
|
|
203
203
|
* `audit_factory` and `create_test_app_server` mirrors that contract
|
|
204
|
-
* end-to-end.
|
|
205
|
-
* fields were removed alongside the `CreateAppBackendOptions` rename.
|
|
204
|
+
* end-to-end.
|
|
206
205
|
*/
|
|
207
206
|
audit_factory?: AuditFactory;
|
|
208
207
|
}
|
|
208
|
+
/**
|
|
209
|
+
* Create an app server with a bootstrapped account for testing.
|
|
210
|
+
*
|
|
211
|
+
* Sets up:
|
|
212
|
+
* - Auth tables (via cached PGlite factory, or reuses existing `db`)
|
|
213
|
+
* - A keeper account with hashed password
|
|
214
|
+
* - Role role_grants for each role in `options.roles`
|
|
215
|
+
* - An API token for Bearer auth
|
|
216
|
+
* - A session with a signed cookie value
|
|
217
|
+
*
|
|
218
|
+
* Uses `stub_password_deps` by default — deterministic hashing that works
|
|
219
|
+
* correctly for login/logout tests without Argon2 overhead.
|
|
220
|
+
*
|
|
221
|
+
* @param options - session options and optional overrides
|
|
222
|
+
* @returns a `TestAppServer` ready for HTTP testing
|
|
223
|
+
* @mutates the underlying database — when `db` is supplied, first resets the
|
|
224
|
+
* `app_settings` row (`open_signup`, `updated_at`, `updated_by`); in either
|
|
225
|
+
* branch inserts an account, actor, role role_grants, API token, and session
|
|
226
|
+
* row, and flips `bootstrap_lock.bootstrapped` (`bootstrap_test_keeper`).
|
|
227
|
+
*/
|
|
209
228
|
export declare const create_test_app_server: (options: TestAppServerOptions) => Promise<TestAppServer>;
|
|
210
229
|
/**
|
|
211
230
|
* Configuration for `create_test_app`.
|
|
@@ -291,6 +310,13 @@ export interface CreateTestAppAccountArgs {
|
|
|
291
310
|
export interface TestApp {
|
|
292
311
|
app: Hono;
|
|
293
312
|
backend: TestAppServer;
|
|
313
|
+
/**
|
|
314
|
+
* The assembled `AppServer` — reach for what `create_app_server` built
|
|
315
|
+
* beyond the fields lifted onto `TestApp`: `audit_sse` (with
|
|
316
|
+
* `audit_log_sse` in `app_options`), the `ws_endpoints` transports,
|
|
317
|
+
* `bootstrap_status`, `migration_results`.
|
|
318
|
+
*/
|
|
319
|
+
server: AppServer;
|
|
294
320
|
surface_spec: AppSurfaceSpec;
|
|
295
321
|
surface: AppSurface;
|
|
296
322
|
route_specs: Array<RouteSpec>;
|
|
@@ -302,9 +328,16 @@ export interface TestApp {
|
|
|
302
328
|
create_daemon_token_headers: (extra?: Record<string, string>) => Record<string, string>;
|
|
303
329
|
/** Create an additional account with credentials. */
|
|
304
330
|
create_account: (options?: CreateTestAppAccountArgs) => Promise<TestAccount>;
|
|
305
|
-
/** Cleanup resources — `AppServer.close`: stops an opted-in auth cleanup schedule, closes live connections, disposes the limiters the server built, then the backend's `close`. Idempotent. */
|
|
331
|
+
/** Cleanup resources — `AppServer.close`: stops an opted-in auth cleanup schedule, closes live connections, disposes the limiters the server built and removes the audit listeners and closer members it added, then the backend's `close`. Idempotent. */
|
|
306
332
|
cleanup: () => Promise<void>;
|
|
307
333
|
}
|
|
334
|
+
/**
|
|
335
|
+
* The loopback `create_app_server` options an in-process test server takes:
|
|
336
|
+
* `localhost` origins allowed, the loopback proxy trusted (so
|
|
337
|
+
* `X-Forwarded-For` sets the client IP), and an empty env schema. A fresh
|
|
338
|
+
* object per call — spread it ahead of the options a test varies.
|
|
339
|
+
*/
|
|
340
|
+
export declare const create_loopback_app_server_options: () => Pick<AppServerOptions, "allowed_origins" | "proxy" | "env_schema">;
|
|
308
341
|
/**
|
|
309
342
|
* Create a fully assembled test app with a Hono server, middleware, and routes.
|
|
310
343
|
*
|
|
@@ -354,12 +387,14 @@ export interface CreateTestAppForBootstrapOptions {
|
|
|
354
387
|
export interface TestAppForBootstrap {
|
|
355
388
|
app: Hono;
|
|
356
389
|
backend: AppBackend;
|
|
390
|
+
/** The assembled `AppServer`, as on `TestApp` — `bootstrap_status`, `audit_sse`, the `ws_endpoints` transports. */
|
|
391
|
+
server: AppServer;
|
|
357
392
|
surface_spec: AppSurfaceSpec;
|
|
358
393
|
surface: AppSurface;
|
|
359
394
|
route_specs: Array<RouteSpec>;
|
|
360
395
|
/** Build host/origin request headers for the anonymous bootstrap POST. */
|
|
361
396
|
create_request_headers: (extra?: Record<string, string>) => Record<string, string>;
|
|
362
|
-
/**
|
|
397
|
+
/** Cleanup resources — `AppServer.close`, as on `TestApp`; the backend's own `close` leaves the injected or factory-cached database open. Idempotent. */
|
|
363
398
|
cleanup: () => Promise<void>;
|
|
364
399
|
}
|
|
365
400
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -185,26 +185,13 @@ const _build_test_backend = async (options) => {
|
|
|
185
185
|
if (!keyring_result.ok) {
|
|
186
186
|
throw new Error(`Test keyring failed: ${keyring_result.errors.join(', ')}`);
|
|
187
187
|
}
|
|
188
|
-
let
|
|
188
|
+
let db;
|
|
189
|
+
let db_name;
|
|
189
190
|
if (existing_db) {
|
|
190
191
|
// Reset singleton config row from a previous test (harmless on fresh pglite).
|
|
191
192
|
await existing_db.query('UPDATE app_settings SET open_signup = false, updated_at = NULL, updated_by = NULL WHERE open_signup = true OR updated_at IS NOT NULL');
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
db_type,
|
|
195
|
-
db_name: 'test',
|
|
196
|
-
migration_results: [], // migrations ran in the factory's init_schema
|
|
197
|
-
close: async () => { },
|
|
198
|
-
deps: {
|
|
199
|
-
keyring: keyring_result.keyring,
|
|
200
|
-
password,
|
|
201
|
-
db: existing_db,
|
|
202
|
-
log: test_log,
|
|
203
|
-
audit,
|
|
204
|
-
connection_closer: create_realtime_closer(),
|
|
205
|
-
...fs_stubs
|
|
206
|
-
}
|
|
207
|
-
};
|
|
193
|
+
db = existing_db;
|
|
194
|
+
db_name = 'test';
|
|
208
195
|
}
|
|
209
196
|
else {
|
|
210
197
|
// In-memory PGlite via cached factory — reuses the WASM instance from `testing/db.ts`
|
|
@@ -213,30 +200,52 @@ const _build_test_backend = async (options) => {
|
|
|
213
200
|
// copy of `testing/db.ts` — under the `db` project's `isolate: false` +
|
|
214
201
|
// `fileParallelism: false` that is once for the whole run.
|
|
215
202
|
// `migration_namespaces` selects an auth+extras factory; auth-only is the default.
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
// here, and no caller reads this field to pick behavior — it is reported, not
|
|
219
|
-
// dispatched on.
|
|
220
|
-
const db = await resolve_fallback_factory(migration_namespaces).create();
|
|
221
|
-
const audit = audit_factory({ db, log: test_log });
|
|
222
|
-
backend = {
|
|
223
|
-
db_type: 'pglite-memory',
|
|
224
|
-
db_name: '(memory)',
|
|
225
|
-
migration_results: [],
|
|
226
|
-
close: async () => { },
|
|
227
|
-
deps: {
|
|
228
|
-
keyring: keyring_result.keyring,
|
|
229
|
-
password,
|
|
230
|
-
db,
|
|
231
|
-
log: test_log,
|
|
232
|
-
audit,
|
|
233
|
-
connection_closer: create_realtime_closer(),
|
|
234
|
-
...fs_stubs
|
|
235
|
-
}
|
|
236
|
-
};
|
|
203
|
+
db = await resolve_fallback_factory(migration_namespaces).create();
|
|
204
|
+
db_name = '(memory)';
|
|
237
205
|
}
|
|
206
|
+
const backend = {
|
|
207
|
+
// a supplied `db` reports the caller's `db_type`; the auto-created one names
|
|
208
|
+
// PGlite unconditionally — a run that installs a substitute factory
|
|
209
|
+
// (`set_substitute_db_factory`) before this module loads gets that driver
|
|
210
|
+
// here, and no caller reads this field to pick behavior: it is reported,
|
|
211
|
+
// not dispatched on
|
|
212
|
+
db_type: existing_db ? db_type : 'pglite-memory',
|
|
213
|
+
db_name,
|
|
214
|
+
// the factory's init ran the migrations, for a supplied `db` and the auto-created one alike
|
|
215
|
+
migration_results: [],
|
|
216
|
+
close: async () => { },
|
|
217
|
+
deps: {
|
|
218
|
+
keyring: keyring_result.keyring,
|
|
219
|
+
password,
|
|
220
|
+
db,
|
|
221
|
+
log: test_log,
|
|
222
|
+
audit: audit_factory({ db, log: test_log }),
|
|
223
|
+
connection_closer: create_realtime_closer(),
|
|
224
|
+
...fs_stubs
|
|
225
|
+
}
|
|
226
|
+
};
|
|
238
227
|
return { backend, keyring: keyring_result.keyring };
|
|
239
228
|
};
|
|
229
|
+
/**
|
|
230
|
+
* Create an app server with a bootstrapped account for testing.
|
|
231
|
+
*
|
|
232
|
+
* Sets up:
|
|
233
|
+
* - Auth tables (via cached PGlite factory, or reuses existing `db`)
|
|
234
|
+
* - A keeper account with hashed password
|
|
235
|
+
* - Role role_grants for each role in `options.roles`
|
|
236
|
+
* - An API token for Bearer auth
|
|
237
|
+
* - A session with a signed cookie value
|
|
238
|
+
*
|
|
239
|
+
* Uses `stub_password_deps` by default — deterministic hashing that works
|
|
240
|
+
* correctly for login/logout tests without Argon2 overhead.
|
|
241
|
+
*
|
|
242
|
+
* @param options - session options and optional overrides
|
|
243
|
+
* @returns a `TestAppServer` ready for HTTP testing
|
|
244
|
+
* @mutates the underlying database — when `db` is supplied, first resets the
|
|
245
|
+
* `app_settings` row (`open_signup`, `updated_at`, `updated_by`); in either
|
|
246
|
+
* branch inserts an account, actor, role role_grants, API token, and session
|
|
247
|
+
* row, and flips `bootstrap_lock.bootstrapped` (`bootstrap_test_keeper`).
|
|
248
|
+
*/
|
|
240
249
|
export const create_test_app_server = async (options) => {
|
|
241
250
|
const { session_options, password = stub_password_deps, username = 'keeper', password_value = DEFAULT_TEST_PASSWORD, roles = [ROLE_KEEPER] } = options;
|
|
242
251
|
const { backend, keyring } = await _build_test_backend(options);
|
|
@@ -257,18 +266,40 @@ export const create_test_app_server = async (options) => {
|
|
|
257
266
|
};
|
|
258
267
|
};
|
|
259
268
|
/**
|
|
260
|
-
* The `create_app_server` options
|
|
261
|
-
*
|
|
269
|
+
* The loopback `create_app_server` options an in-process test server takes:
|
|
270
|
+
* `localhost` origins allowed, the loopback proxy trusted (so
|
|
271
|
+
* `X-Forwarded-For` sets the client IP), and an empty env schema. A fresh
|
|
272
|
+
* object per call — spread it ahead of the options a test varies.
|
|
262
273
|
*/
|
|
263
|
-
const
|
|
274
|
+
export const create_loopback_app_server_options = () => ({
|
|
264
275
|
allowed_origins: [/^http:\/\/localhost/],
|
|
265
276
|
proxy: { trusted_proxies: ['127.0.0.1'], get_connection_ip: () => '127.0.0.1' },
|
|
266
|
-
env_schema: z.object({})
|
|
277
|
+
env_schema: z.object({})
|
|
278
|
+
});
|
|
279
|
+
/**
|
|
280
|
+
* The `create_app_server` options both test-app builders share — a fresh
|
|
281
|
+
* object per call, spread ahead of the per-builder options and `app_options`.
|
|
282
|
+
*/
|
|
283
|
+
const test_app_options = () => ({
|
|
284
|
+
...create_loopback_app_server_options(),
|
|
267
285
|
// every limiter off unless `app_options` passes one — a rate-limit test
|
|
268
286
|
// enables just the limiter it pins
|
|
269
287
|
rate_limiters: 'disabled_for_testing',
|
|
270
288
|
await_pending_effects: true
|
|
271
289
|
});
|
|
290
|
+
/** Build request headers carrying `session_cookie` in the `cookie_name` cookie. */
|
|
291
|
+
const create_session_headers_builder = (cookie_name, session_cookie) => (extra) => ({
|
|
292
|
+
host: 'localhost',
|
|
293
|
+
origin: 'http://localhost:5173',
|
|
294
|
+
cookie: `${cookie_name}=${session_cookie}`,
|
|
295
|
+
...extra
|
|
296
|
+
});
|
|
297
|
+
/** Build request headers carrying `api_token` as a Bearer token. */
|
|
298
|
+
const create_bearer_headers_builder = (api_token) => (extra) => ({
|
|
299
|
+
host: 'localhost',
|
|
300
|
+
authorization: `Bearer ${api_token}`,
|
|
301
|
+
...extra
|
|
302
|
+
});
|
|
272
303
|
/**
|
|
273
304
|
* Create a fully assembled test app with a Hono server, middleware, and routes.
|
|
274
305
|
*
|
|
@@ -296,7 +327,7 @@ export const create_test_app = async (options) => {
|
|
|
296
327
|
keeper_account_id: test_server.account.id
|
|
297
328
|
};
|
|
298
329
|
const result = await create_app_server({
|
|
299
|
-
...
|
|
330
|
+
...test_app_options(),
|
|
300
331
|
backend: test_server,
|
|
301
332
|
session_options: options.session_options,
|
|
302
333
|
daemon_token_state,
|
|
@@ -311,17 +342,8 @@ export const create_test_app = async (options) => {
|
|
|
311
342
|
const { app, surface_spec } = result;
|
|
312
343
|
const { cookie_name } = options.session_options;
|
|
313
344
|
const { password = stub_password_deps } = options;
|
|
314
|
-
const create_session_headers = (
|
|
315
|
-
|
|
316
|
-
origin: 'http://localhost:5173',
|
|
317
|
-
cookie: `${cookie_name}=${test_server.session_cookie}`,
|
|
318
|
-
...extra
|
|
319
|
-
});
|
|
320
|
-
const create_bearer_headers = (extra) => ({
|
|
321
|
-
host: 'localhost',
|
|
322
|
-
authorization: `Bearer ${test_server.api_token}`,
|
|
323
|
-
...extra
|
|
324
|
-
});
|
|
345
|
+
const create_session_headers = create_session_headers_builder(cookie_name, test_server.session_cookie);
|
|
346
|
+
const create_bearer_headers = create_bearer_headers_builder(test_server.api_token);
|
|
325
347
|
const create_daemon_token_headers = (extra) => ({
|
|
326
348
|
host: 'localhost',
|
|
327
349
|
[DAEMON_TOKEN_HEADER]: test_daemon_token,
|
|
@@ -342,22 +364,14 @@ export const create_test_app = async (options) => {
|
|
|
342
364
|
});
|
|
343
365
|
return {
|
|
344
366
|
...bootstrapped,
|
|
345
|
-
create_session_headers: (
|
|
346
|
-
|
|
347
|
-
origin: 'http://localhost:5173',
|
|
348
|
-
cookie: `${cookie_name}=${bootstrapped.session_cookie}`,
|
|
349
|
-
...extra
|
|
350
|
-
}),
|
|
351
|
-
create_bearer_headers: (extra) => ({
|
|
352
|
-
host: 'localhost',
|
|
353
|
-
authorization: `Bearer ${bootstrapped.api_token}`,
|
|
354
|
-
...extra
|
|
355
|
-
})
|
|
367
|
+
create_session_headers: create_session_headers_builder(cookie_name, bootstrapped.session_cookie),
|
|
368
|
+
create_bearer_headers: create_bearer_headers_builder(bootstrapped.api_token)
|
|
356
369
|
};
|
|
357
370
|
};
|
|
358
371
|
return {
|
|
359
372
|
app,
|
|
360
373
|
backend: test_server,
|
|
374
|
+
server: result,
|
|
361
375
|
surface_spec,
|
|
362
376
|
surface: surface_spec.surface,
|
|
363
377
|
route_specs: surface_spec.route_specs,
|
|
@@ -416,8 +430,9 @@ export const create_test_app_for_bootstrap = async (options) => {
|
|
|
416
430
|
}
|
|
417
431
|
};
|
|
418
432
|
const { backend } = await _build_test_backend({ ...options, fs_stubs });
|
|
419
|
-
//
|
|
420
|
-
//
|
|
433
|
+
// No keeper account exists pre-bootstrap, so no daemon token can authenticate
|
|
434
|
+
// — a placeholder, passed so the daemon-token middleware mounts as it does
|
|
435
|
+
// under `create_test_app` and the two surfaces match.
|
|
421
436
|
const daemon_token_state = {
|
|
422
437
|
current_token: generate_daemon_token(),
|
|
423
438
|
previous_token: null,
|
|
@@ -425,7 +440,7 @@ export const create_test_app_for_bootstrap = async (options) => {
|
|
|
425
440
|
keeper_account_id: null
|
|
426
441
|
};
|
|
427
442
|
const result = await create_app_server({
|
|
428
|
-
...
|
|
443
|
+
...test_app_options(),
|
|
429
444
|
backend,
|
|
430
445
|
session_options,
|
|
431
446
|
daemon_token_state,
|
|
@@ -442,6 +457,7 @@ export const create_test_app_for_bootstrap = async (options) => {
|
|
|
442
457
|
return {
|
|
443
458
|
app: result.app,
|
|
444
459
|
backend,
|
|
460
|
+
server: result,
|
|
445
461
|
surface_spec: result.surface_spec,
|
|
446
462
|
surface: result.surface_spec.surface,
|
|
447
463
|
route_specs: result.surface_spec.route_specs,
|
|
@@ -53,7 +53,7 @@ export interface StandardAttackSurfaceOptions {
|
|
|
53
53
|
snapshot_path: string;
|
|
54
54
|
/** Expected public routes, e.g. `['GET /health', 'POST /api/account/login']`. */
|
|
55
55
|
expected_public_routes: Array<string>;
|
|
56
|
-
/** Expected middleware names for API routes, e.g. `['origin', 'session', 'request_context', 'bearer_auth']`. */
|
|
56
|
+
/** Expected middleware names for API routes, e.g. `['trusted_proxy', 'origin', 'session', 'request_context', 'bearer_auth']`. */
|
|
57
57
|
expected_api_middleware: Array<string>;
|
|
58
58
|
/** All roles in the app (e.g. `['admin', 'keeper']`). */
|
|
59
59
|
roles: Array<string>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAQN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAejC,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAsDpF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IA2J3E,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uCAAuC,GACnD,UAAU,2BAA2B,GAAG,IAAI,GAAG,SAAS,KACtD,2BAA2B,GAAG,IAWhC,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,
|
|
1
|
+
{"version":3,"file":"attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAoB7B,OAAO,EAQN,KAAK,4BAA4B,EACjC,KAAK,2BAA2B,EAChC,MAAM,yBAAyB,CAAC;AAejC,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,oBAAoB,CAAC;AAsDpF,oFAAoF;AACpF,MAAM,WAAW,sBAAsB;IACtC,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,sBAAsB,KAAG,IA2J3E,CAAC;AAIF;;;;;;;;;;;;;;GAcG;AACH,eAAO,MAAM,uCAAuC,GACnD,UAAU,2BAA2B,GAAG,IAAI,GAAG,SAAS,KACtD,2BAA2B,GAAG,IAWhC,CAAC;AAEF,0DAA0D;AAC1D,MAAM,WAAW,4BAA4B;IAC5C,+EAA+E;IAC/E,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,aAAa,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,sBAAsB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACtC,iIAAiI;IACjI,uBAAuB,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACvC,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IACrB,qEAAqE;IACrE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iEAAiE;IACjE,eAAe,CAAC,EAAE,4BAA4B,CAAC;IAC/C;;;;;;;;;;;OAWG;IACH,sBAAsB,CAAC,EAAE,2BAA2B,GAAG,IAAI,CAAC;IAC5D;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,sCAAsC,GAClD,SAAS,4BAA4B,KACnC,IA4EF,CAAC"}
|
|
@@ -51,7 +51,7 @@ const create_admin_observer = (fixture) => fixture.create_account({ username: 'a
|
|
|
51
51
|
* future-proofs against tests with more emissions; per-test
|
|
52
52
|
* `auth_integration_truncate_tables` keeps the table empty between cases.
|
|
53
53
|
*
|
|
54
|
-
* `observer` is a dedicated admin account (see
|
|
54
|
+
* `observer` is a dedicated admin account (see `create_admin_observer`)
|
|
55
55
|
* — its credentials are never the subject of the mutation under test, so the
|
|
56
56
|
* read works uniformly across every flow including session-revoking ones.
|
|
57
57
|
*/
|
|
@@ -22,7 +22,7 @@ import type { AuditLogInput } from '../auth/audit_log_schema.js';
|
|
|
22
22
|
*/
|
|
23
23
|
export declare const install_audit_drift_guard: () => void;
|
|
24
24
|
/**
|
|
25
|
-
* Pair returned by
|
|
25
|
+
* Pair returned by `create_recording_audit_emitter` — the
|
|
26
26
|
* `AuditEmitter` to inject as `deps.audit`, plus the shared `calls`
|
|
27
27
|
* array that records every captured emission. Both fields are live —
|
|
28
28
|
* callers read `calls` after exercising the handler to assert on the
|
|
@@ -46,8 +46,10 @@ export interface RecordingAuditEmitter {
|
|
|
46
46
|
* forward from the input envelope. Tests asserting on role-grant-shape
|
|
47
47
|
* emissions read out of the same homogeneous `calls` array.
|
|
48
48
|
* `notify` is a no-op; `add_listener` records into a local array that
|
|
49
|
-
* `listener_count` reports
|
|
50
|
-
*
|
|
49
|
+
* `listener_count` reports, and returns a remover with the production
|
|
50
|
+
* semantics (per registration, idempotent) — registered listeners never fire,
|
|
51
|
+
* since this emitter captures `emit` shapes, not fan-out. `drain_inflight` resolves at once:
|
|
52
|
+
* a recorded call writes nothing, so nothing is ever in flight.
|
|
51
53
|
*
|
|
52
54
|
* `emit` AND `emit_pool` both append to `calls`, so a test reads either
|
|
53
55
|
* entry point's writes off the same array.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAiB5C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED
|
|
1
|
+
{"version":3,"file":"audit_drift_guard.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_drift_guard.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAC;AAC7D,OAAO,KAAK,EAAiB,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAEhF;;;;;;;;;;;;;;;;;;GAkBG;AACH,eAAO,MAAM,yBAAyB,QAAO,IAiB5C,CAAC;AAEF;;;;;;GAMG;AACH,MAAM,WAAW,qBAAqB;IACrC,OAAO,EAAE,YAAY,CAAC;IACtB,KAAK,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;CAC5B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,eAAO,MAAM,8BAA8B,GAC1C,YAAY,KAAK,CAAC,aAAa,CAAC,KAC9B,qBAqCF,CAAC"}
|