@fuzdev/fuz_app 0.64.0 → 0.66.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 +510 -946
- package/dist/actions/action_codegen.d.ts +1 -1
- package/dist/actions/action_codegen.js +1 -1
- package/dist/actions/action_event_data.d.ts +1 -1
- 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 +570 -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/audit_log_schema.js +2 -2
- 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/daemon_token_middleware.d.ts +15 -5
- package/dist/auth/daemon_token_middleware.d.ts.map +1 -1
- package/dist/auth/daemon_token_middleware.js +24 -15
- package/dist/auth/invite_queries.d.ts +17 -7
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +19 -8
- package/dist/auth/keyring.d.ts +6 -6
- package/dist/auth/keyring.js +8 -8
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_actions.js +4 -2
- package/dist/auth/signup_routes.d.ts +47 -1
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +103 -52
- 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/env/resolve.d.ts +44 -7
- package/dist/env/resolve.d.ts.map +1 -1
- package/dist/env/resolve.js +94 -27
- package/dist/http/CLAUDE.md +243 -522
- 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/jsonrpc.d.ts +23 -7
- package/dist/http/jsonrpc.d.ts.map +1 -1
- package/dist/http/jsonrpc.js +19 -3
- package/dist/http/origin.d.ts +1 -1
- package/dist/http/origin.js +1 -1
- package/dist/http/surface.d.ts +9 -2
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/runtime/mock.d.ts +1 -1
- package/dist/runtime/mock.js +2 -2
- 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 +740 -418
- 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 +230 -216
- package/dist/testing/app_server.d.ts +141 -39
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +157 -44
- package/dist/testing/audit_completeness.d.ts +25 -22
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +198 -159
- 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/backend_config.d.ts +113 -0
- package/dist/testing/cross_backend/backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/backend_config.js +1 -0
- package/dist/testing/cross_backend/bench/bench_report.d.ts +46 -0
- package/dist/testing/cross_backend/bench/bench_report.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/bench_report.js +83 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts +44 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/run_cross_impl_bench.js +38 -0
- package/dist/testing/cross_backend/bench/scenario.d.ts +57 -0
- package/dist/testing/cross_backend/bench/scenario.d.ts.map +1 -0
- package/dist/testing/cross_backend/bench/scenario.js +28 -0
- package/dist/testing/cross_backend/bootstrap_backend.d.ts +41 -0
- package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -0
- package/dist/testing/cross_backend/bootstrap_backend.js +34 -0
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts +24 -0
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -0
- package/dist/testing/cross_backend/build_test_backend_paths.js +33 -0
- package/dist/testing/cross_backend/capabilities.d.ts +65 -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/default_backend_configs.d.ts +122 -0
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_backend_configs.js +111 -0
- package/dist/testing/cross_backend/default_secrets.d.ts +40 -0
- package/dist/testing/cross_backend/default_secrets.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_secrets.js +39 -0
- package/dist/testing/cross_backend/default_spine_surface.d.ts +64 -0
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -0
- package/dist/testing/cross_backend/default_spine_surface.js +121 -0
- package/dist/testing/cross_backend/setup.d.ts +451 -0
- package/dist/testing/cross_backend/setup.d.ts.map +1 -0
- package/dist/testing/cross_backend/setup.js +581 -0
- package/dist/testing/cross_backend/spawn_backend.d.ts +58 -0
- package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -0
- package/dist/testing/cross_backend/spawn_backend.js +229 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.d.ts +66 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/spine_stub_backend_config.js +49 -0
- package/dist/testing/cross_backend/sse_round_trip.d.ts +37 -0
- package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -0
- package/dist/testing/cross_backend/sse_round_trip.js +137 -0
- package/dist/testing/cross_backend/standard.d.ts +96 -0
- package/dist/testing/cross_backend/standard.d.ts.map +1 -0
- package/dist/testing/cross_backend/standard.js +49 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +171 -0
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_reset_actions.js +213 -0
- package/dist/testing/cross_backend/testing_server_bun.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_bun.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_bun.js +59 -0
- package/dist/testing/cross_backend/testing_server_core.d.ts +140 -0
- package/dist/testing/cross_backend/testing_server_core.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_core.js +68 -0
- package/dist/testing/cross_backend/testing_server_deno.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_deno.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_deno.js +37 -0
- package/dist/testing/cross_backend/testing_server_node.d.ts +5 -0
- package/dist/testing/cross_backend/testing_server_node.d.ts.map +1 -0
- package/dist/testing/cross_backend/testing_server_node.js +50 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +72 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.js +112 -0
- package/dist/testing/cross_backend/ws_round_trip.d.ts +35 -0
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_round_trip.js +113 -0
- package/dist/testing/data_exposure.d.ts +11 -14
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +123 -146
- package/dist/testing/db_entities.d.ts +22 -1
- package/dist/testing/db_entities.d.ts.map +1 -1
- package/dist/testing/db_entities.js +24 -1
- package/dist/testing/integration.d.ts +56 -21
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +294 -319
- 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 +20 -16
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +61 -86
- package/dist/testing/rpc_helpers.d.ts +10 -4
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +1 -1
- package/dist/testing/rpc_round_trip.d.ts +24 -21
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +87 -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/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +1 -68
- package/dist/testing/standard.d.ts +56 -25
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +62 -5
- package/dist/testing/stubs.d.ts +21 -6
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +33 -23
- package/dist/testing/testing_rate_limiter.d.ts +59 -0
- package/dist/testing/testing_rate_limiter.d.ts.map +1 -0
- package/dist/testing/testing_rate_limiter.js +74 -0
- package/dist/testing/transports/bootstrap.d.ts +52 -0
- package/dist/testing/transports/bootstrap.d.ts.map +1 -0
- package/dist/testing/transports/bootstrap.js +70 -0
- package/dist/testing/transports/fetch_transport.d.ts +81 -0
- package/dist/testing/transports/fetch_transport.d.ts.map +1 -0
- package/dist/testing/transports/fetch_transport.js +74 -0
- package/dist/testing/transports/sse_frame_reader.d.ts +41 -0
- package/dist/testing/transports/sse_frame_reader.d.ts.map +1 -0
- package/dist/testing/transports/sse_frame_reader.js +84 -0
- package/dist/testing/transports/sse_transport.d.ts +54 -0
- package/dist/testing/transports/sse_transport.d.ts.map +1 -0
- package/dist/testing/transports/sse_transport.js +51 -0
- package/dist/testing/transports/ws_client.d.ts +108 -0
- package/dist/testing/transports/ws_client.d.ts.map +1 -0
- package/dist/testing/transports/ws_client.js +56 -0
- package/dist/testing/transports/ws_transport.d.ts +43 -0
- package/dist/testing/transports/ws_transport.d.ts.map +1 -0
- package/dist/testing/transports/ws_transport.js +169 -0
- package/dist/testing/ws_round_trip.d.ts +21 -103
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +42 -40
- package/dist/ui/CLAUDE.md +5 -3
- package/dist/ui/MenuLink.svelte +16 -16
- package/dist/ui/MenuLink.svelte.d.ts +13 -4
- package/dist/ui/MenuLink.svelte.d.ts.map +1 -1
- package/package.json +10 -4
|
@@ -19,7 +19,7 @@ import type { Db, DbType } from '../db/db.js';
|
|
|
19
19
|
import type { PasswordHashDeps } from '../auth/password.js';
|
|
20
20
|
import { type SessionOptions } from '../auth/session_cookie.js';
|
|
21
21
|
import { type AppBackend, type AuditFactory } from '../server/app_backend.js';
|
|
22
|
-
import { type AppServerOptions, type AppServerContext } from '../server/app_server.js';
|
|
22
|
+
import { type AppServerOptions, type AppServerContext, type BootstrapServerOptions, type BootstrapLiveOptions } from '../server/app_server.js';
|
|
23
23
|
import type { AppSurface, AppSurfaceSpec } from '../http/surface.js';
|
|
24
24
|
import type { RouteSpec } from '../http/route_spec.js';
|
|
25
25
|
import type { RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
@@ -33,9 +33,27 @@ export declare const stub_password_deps: PasswordHashDeps;
|
|
|
33
33
|
/** 64-hex-char test cookie secret — deterministic, never used in production. */
|
|
34
34
|
export declare const TEST_COOKIE_SECRET: string;
|
|
35
35
|
/**
|
|
36
|
-
*
|
|
36
|
+
* Default password for bootstrapped test accounts. Shared between the
|
|
37
|
+
* in-process keeper bootstrap (`bootstrap_test_keeper`,
|
|
38
|
+
* `create_test_account_with_credentials`, `create_test_app_server`,
|
|
39
|
+
* `TestApp.create_account`) and the cross-process bootstrap
|
|
40
|
+
* (`cross_backend/setup.ts`). The two paths MUST agree — when they
|
|
41
|
+
* diverged during the 3d cross-process lift, ~20 login tests 401'd
|
|
42
|
+
* silently against the cross-process backend because the per-test
|
|
43
|
+
* fixture minted accounts under a different default than the
|
|
44
|
+
* integration suite's hardcoded login bodies expected. Consumers
|
|
45
|
+
* hardcoding the literal string in test bodies should import this
|
|
46
|
+
* constant instead so a future divergence becomes a typecheck miss
|
|
47
|
+
* rather than a runtime password mismatch.
|
|
37
48
|
*/
|
|
38
|
-
export
|
|
49
|
+
export declare const DEFAULT_TEST_PASSWORD = "test-password-123";
|
|
50
|
+
/**
|
|
51
|
+
* Options for `bootstrap_test_keeper` and `create_test_account_with_credentials`.
|
|
52
|
+
*
|
|
53
|
+
* Same shape for both — the data inserted is identical; the only behavioral
|
|
54
|
+
* difference is the lock flip on the keeper path.
|
|
55
|
+
*/
|
|
56
|
+
export interface CreateTestAccountWithCredentialsOptions {
|
|
39
57
|
db: Db;
|
|
40
58
|
keyring: Keyring;
|
|
41
59
|
session_options: SessionOptions<string>;
|
|
@@ -44,17 +62,48 @@ export interface BootstrapTestAccountOptions {
|
|
|
44
62
|
password_value?: string;
|
|
45
63
|
roles?: Array<string>;
|
|
46
64
|
}
|
|
65
|
+
/** Alias for the keeper-flavored call site. Same shape. */
|
|
66
|
+
export type BootstrapTestKeeperOptions = CreateTestAccountWithCredentialsOptions;
|
|
47
67
|
/**
|
|
48
|
-
*
|
|
68
|
+
* Create a test account with credentials. Use for additional accounts
|
|
69
|
+
* minted alongside the keeper (e.g. `TestApp.create_account` for
|
|
70
|
+
* cross-account / multi-user tests). Does NOT flip `bootstrap_lock` —
|
|
71
|
+
* non-keeper accounts should not appear to the system as bootstrap
|
|
72
|
+
* having happened.
|
|
49
73
|
*
|
|
50
74
|
* Creates an account with actor, grants roles, creates an API token,
|
|
51
|
-
* creates a session, and signs a session cookie.
|
|
52
|
-
* `create_test_app_server` and `TestApp.create_account`.
|
|
75
|
+
* creates a session, and signs a session cookie.
|
|
53
76
|
*
|
|
54
77
|
* @mutates the underlying `options.db` — inserts rows into `account`, `actor`,
|
|
55
78
|
* `role_grant` (one per role), `api_token`, and `auth_session`.
|
|
56
79
|
*/
|
|
57
|
-
export declare const
|
|
80
|
+
export declare const create_test_account_with_credentials: (options: CreateTestAccountWithCredentialsOptions) => Promise<{
|
|
81
|
+
account: {
|
|
82
|
+
id: Uuid;
|
|
83
|
+
username: string;
|
|
84
|
+
};
|
|
85
|
+
actor: {
|
|
86
|
+
id: Uuid;
|
|
87
|
+
};
|
|
88
|
+
api_token: string;
|
|
89
|
+
session_cookie: string;
|
|
90
|
+
}>;
|
|
91
|
+
/**
|
|
92
|
+
* Bootstrap the test-DB keeper. Direct-query shortcut for the default
|
|
93
|
+
* `create_test_app` path — bootstrap is not what most tests exercise, so
|
|
94
|
+
* we skip the real `bootstrap_account` flow (no audit row, no
|
|
95
|
+
* `on_bootstrap` callback). Tests that need the full success-path flow
|
|
96
|
+
* use `create_test_app_for_bootstrap` instead.
|
|
97
|
+
*
|
|
98
|
+
* Flips `bootstrap_lock.bootstrapped = true` so the post-insert DB state
|
|
99
|
+
* matches a real bootstrap completion — production code can trust the
|
|
100
|
+
* lock as the single signal without a belt-and-suspenders
|
|
101
|
+
* `query_account_has_any` defense.
|
|
102
|
+
*
|
|
103
|
+
* @mutates the underlying `options.db` — inserts the account/actor/roles/
|
|
104
|
+
* API token/session_cookie rows AND flips `bootstrap_lock.bootstrapped`.
|
|
105
|
+
*/
|
|
106
|
+
export declare const bootstrap_test_keeper: (options: BootstrapTestKeeperOptions) => Promise<{
|
|
58
107
|
account: {
|
|
59
108
|
id: Uuid;
|
|
60
109
|
username: string;
|
|
@@ -101,7 +150,7 @@ export interface TestAppServerOptions {
|
|
|
101
150
|
password?: PasswordHashDeps;
|
|
102
151
|
/** Username for the bootstrapped account. Default: `'keeper'`. */
|
|
103
152
|
username?: string;
|
|
104
|
-
/** Password for the bootstrapped account. Default: `
|
|
153
|
+
/** Password for the bootstrapped account. Default: `DEFAULT_TEST_PASSWORD`. */
|
|
105
154
|
password_value?: string;
|
|
106
155
|
/** Roles to grant. Default: `[ROLE_KEEPER]`. */
|
|
107
156
|
roles?: Array<string>;
|
|
@@ -122,26 +171,6 @@ export interface TestAppServerOptions {
|
|
|
122
171
|
*/
|
|
123
172
|
audit_factory?: AuditFactory;
|
|
124
173
|
}
|
|
125
|
-
/**
|
|
126
|
-
* Create an app server with a bootstrapped account for testing.
|
|
127
|
-
*
|
|
128
|
-
* Sets up:
|
|
129
|
-
* - Auth tables (via cached PGlite factory, or reuses existing `db`)
|
|
130
|
-
* - A keeper account with hashed password
|
|
131
|
-
* - Role role_grants for each role in `options.roles`
|
|
132
|
-
* - An API token for Bearer auth
|
|
133
|
-
* - A session with a signed cookie value
|
|
134
|
-
*
|
|
135
|
-
* Uses `stub_password_deps` by default — deterministic hashing that works
|
|
136
|
-
* correctly for login/logout tests without Argon2 overhead.
|
|
137
|
-
*
|
|
138
|
-
* @param options - session options and optional overrides
|
|
139
|
-
* @returns a `TestAppServer` ready for HTTP testing
|
|
140
|
-
* @mutates the underlying database — when `db` is supplied, resets singleton
|
|
141
|
-
* state (`bootstrap_lock.bootstrapped`, `app_settings.open_signup`) before
|
|
142
|
-
* bootstrapping; in either branch inserts an account, actor, role role_grants,
|
|
143
|
-
* API token, and session row.
|
|
144
|
-
*/
|
|
145
174
|
export declare const create_test_app_server: (options: TestAppServerOptions) => Promise<TestAppServer>;
|
|
146
175
|
/**
|
|
147
176
|
* Configuration for `create_test_app`.
|
|
@@ -159,23 +188,35 @@ export interface CreateTestAppOptions extends TestAppServerOptions {
|
|
|
159
188
|
*/
|
|
160
189
|
rpc_endpoints?: RpcEndpointsSuiteOption;
|
|
161
190
|
/**
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
191
|
+
* Bootstrap config — symmetric with `AppServerOptions.bootstrap`. Same
|
|
192
|
+
* single-source-of-truth precedent as `rpc_endpoints`: setup-time surface
|
|
193
|
+
* generation and runtime dispatch both read this slot, so the equivalent
|
|
194
|
+
* field under `app_options` is `Omit`'d. Discriminated union over
|
|
195
|
+
* `{mode: 'disabled' | 'surface_only' | 'live'}`. Omit (or pass
|
|
196
|
+
* `{mode: 'disabled'}`) for the default — no bootstrap route mounted.
|
|
197
|
+
*
|
|
198
|
+
* For tests that exercise the bootstrap success path against a real
|
|
199
|
+
* token + empty DB, use `create_test_app_for_bootstrap` instead — it
|
|
200
|
+
* skips the keeper pre-creation that blocks the success branch.
|
|
201
|
+
*/
|
|
202
|
+
bootstrap?: BootstrapServerOptions;
|
|
203
|
+
/**
|
|
204
|
+
* Optional overrides for `AppServerOptions`. Excludes fields
|
|
205
|
+
* `create_test_app` manages directly: `backend`, `session_options`,
|
|
206
|
+
* `create_route_specs`, `rpc_endpoints`, `bootstrap` (top-level slots
|
|
207
|
+
* above).
|
|
165
208
|
*/
|
|
166
209
|
app_options?: SuiteAppOptions;
|
|
167
210
|
}
|
|
168
211
|
/**
|
|
169
212
|
* `app_options` shape accepted by `create_test_app` and the DB-backed suite
|
|
170
|
-
* helpers
|
|
171
|
-
* `
|
|
172
|
-
*
|
|
173
|
-
*
|
|
174
|
-
*
|
|
175
|
-
* in the suites) so setup-time path lookup and runtime dispatch read from
|
|
176
|
-
* one source of truth.
|
|
213
|
+
* helpers. Excludes fields the helpers manage directly — `backend` /
|
|
214
|
+
* `session_options` / `create_route_specs` are constructed by the helper
|
|
215
|
+
* itself; `rpc_endpoints` and `bootstrap` live on top-level options so
|
|
216
|
+
* setup-time surface lookup and runtime dispatch read from one source of
|
|
217
|
+
* truth.
|
|
177
218
|
*/
|
|
178
|
-
export type SuiteAppOptions = Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs' | 'rpc_endpoints'>>;
|
|
219
|
+
export type SuiteAppOptions = Partial<Omit<AppServerOptions, 'backend' | 'session_options' | 'create_route_specs' | 'rpc_endpoints' | 'bootstrap'>>;
|
|
179
220
|
/**
|
|
180
221
|
* A bootstrapped test account with credentials.
|
|
181
222
|
*/
|
|
@@ -234,4 +275,65 @@ export interface TestApp {
|
|
|
234
275
|
* @returns a `TestApp` ready for HTTP testing
|
|
235
276
|
*/
|
|
236
277
|
export declare const create_test_app: (options: CreateTestAppOptions) => Promise<TestApp>;
|
|
278
|
+
/**
|
|
279
|
+
* Configuration for `create_test_app_for_bootstrap`. Like
|
|
280
|
+
* `CreateTestAppOptions` but the keeper-related fields drop (no
|
|
281
|
+
* pre-bootstrap keeper) and `bootstrap` is required + narrowed to
|
|
282
|
+
* `live` mode (the helper exists specifically to drive the success
|
|
283
|
+
* path).
|
|
284
|
+
*/
|
|
285
|
+
export interface CreateTestAppForBootstrapOptions {
|
|
286
|
+
session_options: SessionOptions<string>;
|
|
287
|
+
create_route_specs: (context: AppServerContext) => Array<RouteSpec>;
|
|
288
|
+
rpc_endpoints?: RpcEndpointsSuiteOption;
|
|
289
|
+
app_options?: SuiteAppOptions;
|
|
290
|
+
/** Live bootstrap config — the test drives `POST /bootstrap` against this. */
|
|
291
|
+
bootstrap: BootstrapLiveOptions;
|
|
292
|
+
/**
|
|
293
|
+
* Token contents the stub fs returns when reading `bootstrap.token_path`.
|
|
294
|
+
* The test posts a body containing this same value as `token` to satisfy
|
|
295
|
+
* the timing-safe equality check inside `bootstrap_account`.
|
|
296
|
+
*/
|
|
297
|
+
bootstrap_token: string;
|
|
298
|
+
db?: Db;
|
|
299
|
+
db_type?: DbType;
|
|
300
|
+
password?: PasswordHashDeps;
|
|
301
|
+
audit_factory?: AuditFactory;
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* A fully assembled test app in the pre-bootstrap state — empty DB,
|
|
305
|
+
* `bootstrap_lock.bootstrapped = false`, no keeper account. Test drives
|
|
306
|
+
* `POST /bootstrap` itself.
|
|
307
|
+
*/
|
|
308
|
+
export interface TestAppForBootstrap {
|
|
309
|
+
app: Hono;
|
|
310
|
+
backend: AppBackend;
|
|
311
|
+
surface_spec: AppSurfaceSpec;
|
|
312
|
+
surface: AppSurface;
|
|
313
|
+
route_specs: Array<RouteSpec>;
|
|
314
|
+
/** Build host/origin request headers for the anonymous bootstrap POST. */
|
|
315
|
+
create_request_headers: (extra?: Record<string, string>) => Record<string, string>;
|
|
316
|
+
/** Release test resources (no-op when DB is injected or factory-cached). */
|
|
317
|
+
cleanup: () => Promise<void>;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Create a test app in the pre-bootstrap state for exercising the
|
|
321
|
+
* bootstrap success path end-to-end.
|
|
322
|
+
*
|
|
323
|
+
* Skips the keeper pre-creation `create_test_app` does by default —
|
|
324
|
+
* `bootstrap_lock.bootstrapped` stays at `false` and the DB has no
|
|
325
|
+
* accounts. The fs stubs return `options.bootstrap_token` when the
|
|
326
|
+
* bootstrap handler reads `bootstrap.token_path`, so a `POST /bootstrap`
|
|
327
|
+
* with `{token: bootstrap_token, username, password}` reaches the
|
|
328
|
+
* success branch.
|
|
329
|
+
*
|
|
330
|
+
* Pair with `describe_bootstrap_success_tests` for the consumer-runnable
|
|
331
|
+
* suite that drives the full happy path + adjacent assertions on
|
|
332
|
+
* observable state (account exists, audit row emitted, on_bootstrap
|
|
333
|
+
* callback fired).
|
|
334
|
+
*
|
|
335
|
+
* @param options - bootstrap config + factory inputs
|
|
336
|
+
* @returns a `TestAppForBootstrap` ready for the test to drive bootstrap
|
|
337
|
+
*/
|
|
338
|
+
export declare const create_test_app_for_bootstrap: (options: CreateTestAppForBootstrapOptions) => Promise<TestAppForBootstrap>;
|
|
237
339
|
//# sourceMappingURL=app_server.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAG/B,OAAO,KAAK,EAAC,IAAI,EAAC,MAAM,wBAAwB,CAAC;AAGjD,OAAO,EAA2B,KAAK,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAE1E,OAAO,KAAK,EAAC,EAAE,EAAE,MAAM,EAAC,MAAM,aAAa,CAAC;AAC5C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,qBAAqB,CAAC;AAU1D,OAAO,EAA8B,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAG3F,OAAO,EAAwB,KAAK,UAAU,EAAE,KAAK,YAAY,EAAC,MAAM,0BAA0B,CAAC;AACnG,OAAO,EAEN,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,MAAM,yBAAyB,CAAC;AACjC,OAAO,KAAK,EAAC,UAAU,EAAE,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACnE,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAOrD,OAAO,KAAK,EAAC,uBAAuB,EAAC,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"app_server.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/app_server.ts"],"names":[],"mappings":"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"}
|
|
@@ -15,7 +15,6 @@ import { default_audit_factory } from '../server/app_backend.js';
|
|
|
15
15
|
import { create_app_server, } from '../server/app_server.js';
|
|
16
16
|
import { generate_daemon_token, DAEMON_TOKEN_HEADER, } from '../auth/daemon_token.js';
|
|
17
17
|
import { create_pglite_factory } from './db.js';
|
|
18
|
-
/* eslint-disable @typescript-eslint/require-await */
|
|
19
18
|
/**
|
|
20
19
|
* Fast password stub for tests that don't exercise login/password flows.
|
|
21
20
|
*
|
|
@@ -29,6 +28,21 @@ export const stub_password_deps = {
|
|
|
29
28
|
};
|
|
30
29
|
/** 64-hex-char test cookie secret — deterministic, never used in production. */
|
|
31
30
|
export const TEST_COOKIE_SECRET = 'a'.repeat(64);
|
|
31
|
+
/**
|
|
32
|
+
* Default password for bootstrapped test accounts. Shared between the
|
|
33
|
+
* in-process keeper bootstrap (`bootstrap_test_keeper`,
|
|
34
|
+
* `create_test_account_with_credentials`, `create_test_app_server`,
|
|
35
|
+
* `TestApp.create_account`) and the cross-process bootstrap
|
|
36
|
+
* (`cross_backend/setup.ts`). The two paths MUST agree — when they
|
|
37
|
+
* diverged during the 3d cross-process lift, ~20 login tests 401'd
|
|
38
|
+
* silently against the cross-process backend because the per-test
|
|
39
|
+
* fixture minted accounts under a different default than the
|
|
40
|
+
* integration suite's hardcoded login bodies expected. Consumers
|
|
41
|
+
* hardcoding the literal string in test bodies should import this
|
|
42
|
+
* constant instead so a future divergence becomes a typecheck miss
|
|
43
|
+
* rather than a runtime password mismatch.
|
|
44
|
+
*/
|
|
45
|
+
export const DEFAULT_TEST_PASSWORD = 'test-password-123';
|
|
32
46
|
// Module-level PGlite factory for create_test_app_server when no db is provided.
|
|
33
47
|
// Shares the WASM instance cache from test_db.ts, avoiding redundant cold starts
|
|
34
48
|
// within the same vitest worker thread. Schema is reset on each create() call.
|
|
@@ -36,17 +50,20 @@ const fallback_pglite_factory = create_pglite_factory(async (db) => {
|
|
|
36
50
|
await run_migrations(db, [auth_migration_ns]);
|
|
37
51
|
});
|
|
38
52
|
/**
|
|
39
|
-
*
|
|
53
|
+
* Create a test account with credentials. Use for additional accounts
|
|
54
|
+
* minted alongside the keeper (e.g. `TestApp.create_account` for
|
|
55
|
+
* cross-account / multi-user tests). Does NOT flip `bootstrap_lock` —
|
|
56
|
+
* non-keeper accounts should not appear to the system as bootstrap
|
|
57
|
+
* having happened.
|
|
40
58
|
*
|
|
41
59
|
* Creates an account with actor, grants roles, creates an API token,
|
|
42
|
-
* creates a session, and signs a session cookie.
|
|
43
|
-
* `create_test_app_server` and `TestApp.create_account`.
|
|
60
|
+
* creates a session, and signs a session cookie.
|
|
44
61
|
*
|
|
45
62
|
* @mutates the underlying `options.db` — inserts rows into `account`, `actor`,
|
|
46
63
|
* `role_grant` (one per role), `api_token`, and `auth_session`.
|
|
47
64
|
*/
|
|
48
|
-
export const
|
|
49
|
-
const { db, keyring, session_options, password, username = 'keeper', password_value =
|
|
65
|
+
export const create_test_account_with_credentials = async (options) => {
|
|
66
|
+
const { db, keyring, session_options, password, username = 'keeper', password_value = DEFAULT_TEST_PASSWORD, roles = [], } = options;
|
|
50
67
|
const deps = { db };
|
|
51
68
|
const password_hash = await password.hash_password(password_value);
|
|
52
69
|
const { account, actor } = await query_create_account_with_actor(deps, {
|
|
@@ -73,54 +90,63 @@ export const bootstrap_test_account = async (options) => {
|
|
|
73
90
|
session_cookie,
|
|
74
91
|
};
|
|
75
92
|
};
|
|
76
|
-
/** Silent logger for tests — suppresses all output. */
|
|
77
|
-
const test_log = new Logger('test', { level: 'off' });
|
|
78
93
|
/**
|
|
79
|
-
*
|
|
94
|
+
* Bootstrap the test-DB keeper. Direct-query shortcut for the default
|
|
95
|
+
* `create_test_app` path — bootstrap is not what most tests exercise, so
|
|
96
|
+
* we skip the real `bootstrap_account` flow (no audit row, no
|
|
97
|
+
* `on_bootstrap` callback). Tests that need the full success-path flow
|
|
98
|
+
* use `create_test_app_for_bootstrap` instead.
|
|
80
99
|
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
83
|
-
*
|
|
84
|
-
*
|
|
85
|
-
* - An API token for Bearer auth
|
|
86
|
-
* - A session with a signed cookie value
|
|
100
|
+
* Flips `bootstrap_lock.bootstrapped = true` so the post-insert DB state
|
|
101
|
+
* matches a real bootstrap completion — production code can trust the
|
|
102
|
+
* lock as the single signal without a belt-and-suspenders
|
|
103
|
+
* `query_account_has_any` defense.
|
|
87
104
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
105
|
+
* @mutates the underlying `options.db` — inserts the account/actor/roles/
|
|
106
|
+
* API token/session_cookie rows AND flips `bootstrap_lock.bootstrapped`.
|
|
107
|
+
*/
|
|
108
|
+
export const bootstrap_test_keeper = async (options) => {
|
|
109
|
+
const result = await create_test_account_with_credentials(options);
|
|
110
|
+
// Lock flip — mirrors production `bootstrap_account` so test/prod write
|
|
111
|
+
// semantics stay in parity.
|
|
112
|
+
await options.db.query('UPDATE bootstrap_lock SET bootstrapped = true WHERE id = 1 AND bootstrapped = false');
|
|
113
|
+
return result;
|
|
114
|
+
};
|
|
115
|
+
/** Silent logger for tests — suppresses all output. */
|
|
116
|
+
const test_log = new Logger('test', { level: 'off' });
|
|
117
|
+
const default_test_fs_stubs = {
|
|
118
|
+
stat: async () => null,
|
|
119
|
+
read_text_file: async () => '',
|
|
120
|
+
delete_file: async () => { },
|
|
121
|
+
};
|
|
122
|
+
/**
|
|
123
|
+
* Shared backend-assembly path for `create_test_app_server` and
|
|
124
|
+
* `create_test_app_for_bootstrap`. Returns the raw `AppBackend` + the
|
|
125
|
+
* keyring used to sign session cookies; callers wrap with their own
|
|
126
|
+
* concerns (keeper pre-creation vs. pre-bootstrap state).
|
|
90
127
|
*
|
|
91
|
-
*
|
|
92
|
-
*
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
* API token, and session row.
|
|
128
|
+
* Resets `app_settings` singleton row for caller-supplied DBs so prior
|
|
129
|
+
* tests don't leak `open_signup`. Does NOT reset `bootstrap_lock` —
|
|
130
|
+
* callers own that policy (`create_test_app_server` lets
|
|
131
|
+
* `bootstrap_test_keeper` flip it; `create_test_app_for_bootstrap`
|
|
132
|
+
* resets it to false before this runs).
|
|
97
133
|
*/
|
|
98
|
-
|
|
99
|
-
const {
|
|
100
|
-
// Keyring from test secret
|
|
134
|
+
const _build_test_backend = async (options) => {
|
|
135
|
+
const { db: existing_db, db_type = 'pglite-memory', password = stub_password_deps, audit_factory = default_audit_factory, fs_stubs = default_test_fs_stubs, } = options;
|
|
101
136
|
const keyring_result = create_validated_keyring(TEST_COOKIE_SECRET);
|
|
102
137
|
if (!keyring_result.ok) {
|
|
103
138
|
throw new Error(`Test keyring failed: ${keyring_result.errors.join(', ')}`);
|
|
104
139
|
}
|
|
105
|
-
const fs_stubs = {
|
|
106
|
-
stat: async () => null,
|
|
107
|
-
read_text_file: async () => '',
|
|
108
|
-
delete_file: async (_path) => { }, // eslint-disable-line @typescript-eslint/no-empty-function
|
|
109
|
-
};
|
|
110
140
|
let backend;
|
|
111
141
|
if (existing_db) {
|
|
112
|
-
// Reset singleton config
|
|
113
|
-
// Harmless for fresh pglite (these are already at defaults).
|
|
114
|
-
await existing_db.query('UPDATE bootstrap_lock SET bootstrapped = false WHERE bootstrapped = true');
|
|
142
|
+
// Reset singleton config row from a previous test (harmless on fresh pglite).
|
|
115
143
|
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');
|
|
116
|
-
// Use the caller's database — tables already created by the factory's init_schema.
|
|
117
|
-
// Caller owns the DB lifecycle — close is a no-op.
|
|
118
144
|
const audit = audit_factory({ db: existing_db, log: test_log });
|
|
119
145
|
backend = {
|
|
120
146
|
db_type,
|
|
121
147
|
db_name: 'test',
|
|
122
|
-
migration_results: [], // migrations ran in the factory's init_schema
|
|
123
|
-
close: async () => { },
|
|
148
|
+
migration_results: [], // migrations ran in the factory's init_schema
|
|
149
|
+
close: async () => { },
|
|
124
150
|
deps: {
|
|
125
151
|
keyring: keyring_result.keyring,
|
|
126
152
|
password,
|
|
@@ -141,7 +167,7 @@ export const create_test_app_server = async (options) => {
|
|
|
141
167
|
db_type: 'pglite-memory',
|
|
142
168
|
db_name: '(memory)',
|
|
143
169
|
migration_results: [],
|
|
144
|
-
close: async () => { },
|
|
170
|
+
close: async () => { },
|
|
145
171
|
deps: {
|
|
146
172
|
keyring: keyring_result.keyring,
|
|
147
173
|
password,
|
|
@@ -152,9 +178,14 @@ export const create_test_app_server = async (options) => {
|
|
|
152
178
|
},
|
|
153
179
|
};
|
|
154
180
|
}
|
|
155
|
-
|
|
181
|
+
return { backend, keyring: keyring_result.keyring };
|
|
182
|
+
};
|
|
183
|
+
export const create_test_app_server = async (options) => {
|
|
184
|
+
const { session_options, password = stub_password_deps, username = 'keeper', password_value = DEFAULT_TEST_PASSWORD, roles = [ROLE_KEEPER], } = options;
|
|
185
|
+
const { backend, keyring } = await _build_test_backend(options);
|
|
186
|
+
const bootstrapped = await bootstrap_test_keeper({
|
|
156
187
|
db: backend.deps.db,
|
|
157
|
-
keyring
|
|
188
|
+
keyring,
|
|
158
189
|
session_options,
|
|
159
190
|
password,
|
|
160
191
|
username,
|
|
@@ -164,7 +195,7 @@ export const create_test_app_server = async (options) => {
|
|
|
164
195
|
return {
|
|
165
196
|
...backend,
|
|
166
197
|
...bootstrapped,
|
|
167
|
-
keyring
|
|
198
|
+
keyring,
|
|
168
199
|
cleanup: () => backend.close(),
|
|
169
200
|
};
|
|
170
201
|
};
|
|
@@ -205,6 +236,7 @@ export const create_test_app = async (options) => {
|
|
|
205
236
|
await_pending_effects: true,
|
|
206
237
|
daemon_token_state,
|
|
207
238
|
rpc_endpoints: options.rpc_endpoints,
|
|
239
|
+
bootstrap: options.bootstrap,
|
|
208
240
|
...options.app_options,
|
|
209
241
|
create_route_specs: options.create_route_specs,
|
|
210
242
|
});
|
|
@@ -230,13 +262,13 @@ export const create_test_app = async (options) => {
|
|
|
230
262
|
let account_counter = 0;
|
|
231
263
|
const create_account = async (account_options) => {
|
|
232
264
|
account_counter++;
|
|
233
|
-
const bootstrapped = await
|
|
265
|
+
const bootstrapped = await create_test_account_with_credentials({
|
|
234
266
|
db: test_server.deps.db,
|
|
235
267
|
keyring: test_server.keyring,
|
|
236
268
|
session_options: options.session_options,
|
|
237
269
|
password,
|
|
238
270
|
username: account_options?.username ?? `test_user_${account_counter}`,
|
|
239
|
-
password_value: account_options?.password_value ??
|
|
271
|
+
password_value: account_options?.password_value ?? DEFAULT_TEST_PASSWORD,
|
|
240
272
|
roles: account_options?.roles ?? [],
|
|
241
273
|
});
|
|
242
274
|
return {
|
|
@@ -267,3 +299,84 @@ export const create_test_app = async (options) => {
|
|
|
267
299
|
cleanup: () => test_server.cleanup(),
|
|
268
300
|
};
|
|
269
301
|
};
|
|
302
|
+
/**
|
|
303
|
+
* Create a test app in the pre-bootstrap state for exercising the
|
|
304
|
+
* bootstrap success path end-to-end.
|
|
305
|
+
*
|
|
306
|
+
* Skips the keeper pre-creation `create_test_app` does by default —
|
|
307
|
+
* `bootstrap_lock.bootstrapped` stays at `false` and the DB has no
|
|
308
|
+
* accounts. The fs stubs return `options.bootstrap_token` when the
|
|
309
|
+
* bootstrap handler reads `bootstrap.token_path`, so a `POST /bootstrap`
|
|
310
|
+
* with `{token: bootstrap_token, username, password}` reaches the
|
|
311
|
+
* success branch.
|
|
312
|
+
*
|
|
313
|
+
* Pair with `describe_bootstrap_success_tests` for the consumer-runnable
|
|
314
|
+
* suite that drives the full happy path + adjacent assertions on
|
|
315
|
+
* observable state (account exists, audit row emitted, on_bootstrap
|
|
316
|
+
* callback fired).
|
|
317
|
+
*
|
|
318
|
+
* @param options - bootstrap config + factory inputs
|
|
319
|
+
* @returns a `TestAppForBootstrap` ready for the test to drive bootstrap
|
|
320
|
+
*/
|
|
321
|
+
export const create_test_app_for_bootstrap = async (options) => {
|
|
322
|
+
const { session_options, bootstrap, bootstrap_token } = options;
|
|
323
|
+
// Caller-supplied DB may carry lock state from a prior test — reset to false
|
|
324
|
+
// before `_build_test_backend` runs (which doesn't touch the lock itself).
|
|
325
|
+
// Fresh pglite already starts at false (factory init).
|
|
326
|
+
if (options.db) {
|
|
327
|
+
await options.db.query('UPDATE bootstrap_lock SET bootstrapped = false WHERE bootstrapped = true');
|
|
328
|
+
}
|
|
329
|
+
// Token-aware fs stubs: the bootstrap route's filesystem operations resolve
|
|
330
|
+
// against the configured token_path; everything else returns no-op defaults.
|
|
331
|
+
let token_file_deleted = false;
|
|
332
|
+
const fs_stubs = {
|
|
333
|
+
stat: async (path) => path === bootstrap.token_path && !token_file_deleted
|
|
334
|
+
? { is_file: true, is_directory: false }
|
|
335
|
+
: null,
|
|
336
|
+
read_text_file: async (path) => path === bootstrap.token_path && !token_file_deleted ? bootstrap_token : '',
|
|
337
|
+
delete_file: async (path) => {
|
|
338
|
+
if (path === bootstrap.token_path)
|
|
339
|
+
token_file_deleted = true;
|
|
340
|
+
},
|
|
341
|
+
};
|
|
342
|
+
const { backend } = await _build_test_backend({ ...options, fs_stubs });
|
|
343
|
+
// Daemon token state isn't reachable pre-bootstrap (no keeper account)
|
|
344
|
+
// but the field is required by AppServerOptions; pass a placeholder.
|
|
345
|
+
const daemon_token_state = {
|
|
346
|
+
current_token: generate_daemon_token(),
|
|
347
|
+
previous_token: null,
|
|
348
|
+
rotated_at: new Date(),
|
|
349
|
+
keeper_account_id: null,
|
|
350
|
+
};
|
|
351
|
+
const result = await create_app_server({
|
|
352
|
+
backend,
|
|
353
|
+
session_options,
|
|
354
|
+
allowed_origins: [/^http:\/\/localhost/],
|
|
355
|
+
proxy: { trusted_proxies: ['127.0.0.1'], get_connection_ip: () => '127.0.0.1' },
|
|
356
|
+
env_schema: z.object({}),
|
|
357
|
+
ip_rate_limiter: null,
|
|
358
|
+
login_account_rate_limiter: null,
|
|
359
|
+
signup_account_rate_limiter: null,
|
|
360
|
+
bearer_ip_rate_limiter: null,
|
|
361
|
+
await_pending_effects: true,
|
|
362
|
+
daemon_token_state,
|
|
363
|
+
rpc_endpoints: options.rpc_endpoints,
|
|
364
|
+
bootstrap,
|
|
365
|
+
...options.app_options,
|
|
366
|
+
create_route_specs: options.create_route_specs,
|
|
367
|
+
});
|
|
368
|
+
const create_request_headers = (extra) => ({
|
|
369
|
+
host: 'localhost',
|
|
370
|
+
origin: 'http://localhost:5173',
|
|
371
|
+
...extra,
|
|
372
|
+
});
|
|
373
|
+
return {
|
|
374
|
+
app: result.app,
|
|
375
|
+
backend,
|
|
376
|
+
surface_spec: result.surface_spec,
|
|
377
|
+
surface: result.surface_spec.surface,
|
|
378
|
+
route_specs: result.surface_spec.route_specs,
|
|
379
|
+
create_request_headers,
|
|
380
|
+
cleanup: () => backend.close(),
|
|
381
|
+
};
|
|
382
|
+
};
|
|
@@ -1,42 +1,45 @@
|
|
|
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
|
-
import { type SuiteAppOptions } from './app_server.js';
|
|
6
|
-
import { type DbFactory } from './db.js';
|
|
7
3
|
import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
4
|
+
import type { AppSurfaceSpec } from '../http/surface.js';
|
|
5
|
+
import type { BackendCapabilities } from './cross_backend/capabilities.js';
|
|
6
|
+
import type { SetupTest } from './cross_backend/setup.js';
|
|
8
7
|
/**
|
|
9
8
|
* Configuration for `describe_audit_completeness_tests`.
|
|
10
9
|
*/
|
|
11
10
|
export interface AuditCompletenessTestOptions {
|
|
12
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Per-test fixture-producing function. The audit suite calls this
|
|
13
|
+
* in every `test()` body — `auth_integration_truncate_tables` clears
|
|
14
|
+
* the audit log between tests, so each test re-bootstraps the
|
|
15
|
+
* keeper and the observer admin against a fresh table.
|
|
16
|
+
*/
|
|
17
|
+
setup_test: SetupTest;
|
|
18
|
+
/**
|
|
19
|
+
* App surface (with route specs). Constructed in TS by the consumer;
|
|
20
|
+
* same shape for in-process and cross-process tests. The audit suite is
|
|
21
|
+
* Tier 2 today (stays in-process per the cross-backend-integration
|
|
22
|
+
* design), but takes the same surface shape as Tier 1 suites for
|
|
23
|
+
* options uniformity.
|
|
24
|
+
*/
|
|
25
|
+
surface_source: AppSurfaceSpec;
|
|
26
|
+
/** Backend capability declarations. */
|
|
27
|
+
capabilities: BackendCapabilities;
|
|
28
|
+
/** Session config — needed for factory-form rpc_endpoints resolution. */
|
|
13
29
|
session_options: SessionOptions<string>;
|
|
14
|
-
/** Route spec factory — same one used in production. */
|
|
15
|
-
create_route_specs: (ctx: AppServerContext) => Array<RouteSpec>;
|
|
16
30
|
/**
|
|
17
|
-
* RPC endpoint specs —
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* Accepts either an array (eager) or a factory
|
|
21
|
-
* `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
|
|
22
|
-
* is required when action handlers must close over the per-test
|
|
23
|
-
* `ctx.app_settings` / `ctx.deps` (e.g. exercising `app_settings_update`).
|
|
24
|
-
* The factory must return the same endpoint `path` regardless of ctx —
|
|
25
|
-
* it is invoked once at setup with a stub ctx for path lookup and again
|
|
26
|
-
* per-test by `create_app_server` for live dispatch.
|
|
31
|
+
* RPC endpoint specs — required. The admin role_grant flow is RPC-only
|
|
32
|
+
* and the suite hard-fails without it.
|
|
27
33
|
*/
|
|
28
34
|
rpc_endpoints: RpcEndpointsSuiteOption;
|
|
29
|
-
/** Optional overrides for `AppServerOptions`. */
|
|
30
|
-
app_options?: SuiteAppOptions;
|
|
31
|
-
/** Database factories to run tests against. Default: pglite only. */
|
|
32
|
-
db_factories?: Array<DbFactory>;
|
|
33
35
|
}
|
|
34
36
|
/**
|
|
35
37
|
* Composable audit log completeness test suite.
|
|
36
38
|
*
|
|
37
39
|
* Verifies that every auth mutation route produces the correct audit log
|
|
38
40
|
* event type. Exercises routes via HTTP requests against a real PGlite
|
|
39
|
-
* database, then
|
|
41
|
+
* database, then reads events back through the `audit_log_list` RPC
|
|
42
|
+
* (the production observation path the admin UI consumes).
|
|
40
43
|
*
|
|
41
44
|
* @throws Error at setup time when `options.rpc_endpoints` is empty — the
|
|
42
45
|
* mutation-audit tests drive role_grant flow, session/token revoke-all, and
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"audit_completeness.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/audit_completeness.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AA4B7B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAS9D,OAAO,EAKN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAwB1B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAC,mBAAmB,EAAC,MAAM,iCAAiC,CAAC;AACzE,OAAO,KAAK,EAAC,SAAS,EAAc,MAAM,0BAA0B,CAAC;AAErE;;GAEG;AACH,MAAM,WAAW,4BAA4B;IAC5C;;;;;OAKG;IACH,UAAU,EAAE,SAAS,CAAC;IACtB;;;;;;OAMG;IACH,cAAc,EAAE,cAAc,CAAC;IAC/B,uCAAuC;IACvC,YAAY,EAAE,mBAAmB,CAAC;IAClC,yEAAyE;IACzE,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC;;;OAGG;IACH,aAAa,EAAE,uBAAuB,CAAC;CACvC;AA8FD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,4BAA4B,KAAG,IAuhBzF,CAAC"}
|