@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
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { describe_standard_integration_tests } from '../integration.js';
|
|
3
|
+
import { describe_standard_admin_integration_tests } from '../admin_integration.js';
|
|
4
|
+
import { describe_round_trip_validation } from '../round_trip.js';
|
|
5
|
+
import { describe_rpc_round_trip_tests } from '../rpc_round_trip.js';
|
|
6
|
+
import { describe_data_exposure_tests } from '../data_exposure.js';
|
|
7
|
+
/**
|
|
8
|
+
* Run the cross-process standard test bundle — integration, admin (when
|
|
9
|
+
* `roles` provided), round trip, RPC round trip, data exposure. See the
|
|
10
|
+
* module doc for the suites omitted from this bundle and why.
|
|
11
|
+
*/
|
|
12
|
+
export const describe_standard_cross_process_tests = (options) => {
|
|
13
|
+
describe_standard_integration_tests({
|
|
14
|
+
setup_test: options.setup_test,
|
|
15
|
+
surface_source: options.surface_source,
|
|
16
|
+
capabilities: options.capabilities,
|
|
17
|
+
session_options: options.session_options,
|
|
18
|
+
rpc_endpoints: options.rpc_endpoints,
|
|
19
|
+
error_coverage_min: options.error_coverage_min,
|
|
20
|
+
});
|
|
21
|
+
describe_round_trip_validation({
|
|
22
|
+
setup_test: options.setup_test,
|
|
23
|
+
surface_source: options.surface_source,
|
|
24
|
+
capabilities: options.capabilities,
|
|
25
|
+
});
|
|
26
|
+
describe_rpc_round_trip_tests({
|
|
27
|
+
setup_test: options.setup_test,
|
|
28
|
+
surface_source: options.surface_source,
|
|
29
|
+
capabilities: options.capabilities,
|
|
30
|
+
session_options: options.session_options,
|
|
31
|
+
rpc_endpoints: options.rpc_endpoints,
|
|
32
|
+
});
|
|
33
|
+
describe_data_exposure_tests({
|
|
34
|
+
setup_test: options.setup_test,
|
|
35
|
+
surface_source: options.surface_source,
|
|
36
|
+
capabilities: options.capabilities,
|
|
37
|
+
});
|
|
38
|
+
if (options.roles) {
|
|
39
|
+
describe_standard_admin_integration_tests({
|
|
40
|
+
setup_test: options.setup_test,
|
|
41
|
+
surface_source: options.surface_source,
|
|
42
|
+
capabilities: options.capabilities,
|
|
43
|
+
session_options: options.session_options,
|
|
44
|
+
roles: options.roles,
|
|
45
|
+
rpc_endpoints: options.rpc_endpoints,
|
|
46
|
+
admin_prefix: options.admin_prefix,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
};
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Test-binary RPC actions for cross-process integration tests.
|
|
4
|
+
*
|
|
5
|
+
* Single daemon-token-authed action: **`_testing_reset`** — full DB
|
|
6
|
+
* wipe + keeper re-seed + optional secondary-account seeding. The
|
|
7
|
+
* handler wipes every auth-namespace row (no keeper-preserve filter),
|
|
8
|
+
* flips `bootstrap_lock` back to its post-bootstrap shape, seeds a
|
|
9
|
+
* fresh keeper account inline (reusing `create_test_account_with_credentials`
|
|
10
|
+
* so cross-process matches in-process write semantics), seeds any
|
|
11
|
+
* caller-requested `extra_accounts` (also direct-inserted at this
|
|
12
|
+
* setup step), refreshes the daemon-token cache to point at the new
|
|
13
|
+
* keeper, and fires the consumer-supplied domain-state callback. The
|
|
14
|
+
* new keeper + secondary credentials return as the action output so
|
|
15
|
+
* the per-test fixture closes over them.
|
|
16
|
+
*
|
|
17
|
+
* The redesign converges in-process and cross-process keeper
|
|
18
|
+
* lifetimes: both modes now run against a freshly bootstrapped keeper
|
|
19
|
+
* per test. Mutation-cascade tests (password change, revoke-all,
|
|
20
|
+
* hardcoded-username signup uniqueness) and direct keeper-vs-admin
|
|
21
|
+
* probes work uniformly cross-process.
|
|
22
|
+
*
|
|
23
|
+
* **Keeper ≠ admin.** The `keeper` and `admin` roles are independent.
|
|
24
|
+
* Keeper authorizes daemon-token / bootstrap paths; admin authorizes
|
|
25
|
+
* the user-facing admin RPC surface. `_testing_reset` seeds the keeper
|
|
26
|
+
* account with `[ROLE_KEEPER, ROLE_ADMIN]` by default — matching the
|
|
27
|
+
* production `bootstrap_account` flow — plus any roles passed via
|
|
28
|
+
* `extra_keeper_roles`. Tests probing the keeper-vs-admin separation
|
|
29
|
+
* (a keeper-only account must 403 on admin RPCs) declare a secondary
|
|
30
|
+
* via `extra_accounts: [{username, roles: [ROLE_KEEPER]}]` so the
|
|
31
|
+
* account is seeded at this same bootstrap-equivalent step.
|
|
32
|
+
*
|
|
33
|
+
* **No free-form runtime bypass.** Earlier drafts considered a separate
|
|
34
|
+
* `_testing_seed_role_grant` action for arbitrary direct grants; that
|
|
35
|
+
* was rejected because a runtime bypass would let tests skip the
|
|
36
|
+
* production consent flow's side-effects (audit emit, WS fan-out) and
|
|
37
|
+
* silently mask bugs in those paths. The bypass that does exist —
|
|
38
|
+
* `extra_accounts` — is framed as bootstrap-time seeding, the same
|
|
39
|
+
* shape `bootstrap_account` itself uses to grant the initial
|
|
40
|
+
* `KEEPER` + `ADMIN` pair. Tests that want a role on a *post-bootstrap*
|
|
41
|
+
* account must route through `role_grant_offer_create` +
|
|
42
|
+
* `role_grant_offer_accept` (the production path); they observe the
|
|
43
|
+
* full event chain.
|
|
44
|
+
*
|
|
45
|
+
* Production safety: this module lives under `cross_backend/` and starts
|
|
46
|
+
* with `import '../assert_dev_env.js';` — production bundles either
|
|
47
|
+
* tree-shake the module out or throw at startup. The Rust mirror
|
|
48
|
+
* (`fuz_testing` crate) ships a parallel action; `cargo xtask
|
|
49
|
+
* check-release` blocks `fuz_testing` from entering production dep
|
|
50
|
+
* graphs.
|
|
51
|
+
*
|
|
52
|
+
* @module
|
|
53
|
+
*/
|
|
54
|
+
import { z } from 'zod';
|
|
55
|
+
import { type RpcAction } from '../../actions/action_rpc.js';
|
|
56
|
+
import type { AppDeps } from '../../auth/deps.js';
|
|
57
|
+
import type { SessionOptions } from '../../auth/session_cookie.js';
|
|
58
|
+
import type { DaemonTokenState } from '../../auth/daemon_token.js';
|
|
59
|
+
/**
|
|
60
|
+
* The `_testing_reset` action spec.
|
|
61
|
+
*
|
|
62
|
+
* Input:
|
|
63
|
+
* - `extra_keeper_roles` — roles to grant the fresh keeper *in addition
|
|
64
|
+
* to* `[ROLE_KEEPER, ROLE_ADMIN]` (matching production bootstrap).
|
|
65
|
+
* - `extra_accounts` — additional accounts to seed at this same
|
|
66
|
+
* bootstrap-equivalent step. Each entry's `roles` are direct-granted
|
|
67
|
+
* (bypassing offer/accept) because the seed is *part of bootstrap*,
|
|
68
|
+
* not a post-bootstrap action. Use this for accounts whose required
|
|
69
|
+
* roles aren't admin-grantable via offer/accept (e.g. `ROLE_KEEPER`,
|
|
70
|
+
* whose `RoleSpec.grant_paths` is bootstrap-only). For
|
|
71
|
+
* admin-grantable roles, prefer `fixture.create_account({roles})`
|
|
72
|
+
* (offer/accept production path).
|
|
73
|
+
*
|
|
74
|
+
* Output: keeper credentials plus a parallel array of seeded
|
|
75
|
+
* `extra_accounts` (same order as input). The per-test fixture closes
|
|
76
|
+
* over the returned values; subsequent calls in the same test see the
|
|
77
|
+
* fresh keeper and any requested secondaries.
|
|
78
|
+
*
|
|
79
|
+
* `auth` gates on the daemon-token credential — the keeper holds it
|
|
80
|
+
* exclusively. The action is internally privileged (it runs direct
|
|
81
|
+
* DB writes the production wire never exposes); daemon-token auth is
|
|
82
|
+
* the structural fence.
|
|
83
|
+
*/
|
|
84
|
+
export declare const testing_reset_action_spec: {
|
|
85
|
+
readonly method: "_testing_reset";
|
|
86
|
+
readonly kind: "request_response";
|
|
87
|
+
readonly initiator: "frontend";
|
|
88
|
+
readonly auth: {
|
|
89
|
+
readonly account: "required";
|
|
90
|
+
readonly actor: "none";
|
|
91
|
+
readonly credential_types: readonly ["daemon_token"];
|
|
92
|
+
};
|
|
93
|
+
readonly side_effects: true;
|
|
94
|
+
readonly input: z.ZodObject<{
|
|
95
|
+
extra_keeper_roles: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
96
|
+
extra_accounts: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
97
|
+
username: z.ZodString;
|
|
98
|
+
password_value: z.ZodOptional<z.ZodString>;
|
|
99
|
+
roles: z.ZodArray<z.ZodString>;
|
|
100
|
+
}, z.core.$strict>>>;
|
|
101
|
+
}, z.core.$strict>;
|
|
102
|
+
readonly output: z.ZodObject<{
|
|
103
|
+
account: z.ZodObject<{
|
|
104
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
105
|
+
username: z.ZodString;
|
|
106
|
+
}, z.core.$strict>;
|
|
107
|
+
actor: z.ZodObject<{
|
|
108
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
109
|
+
}, z.core.$strict>;
|
|
110
|
+
api_token: z.ZodString;
|
|
111
|
+
session_cookie: z.ZodString;
|
|
112
|
+
extra_accounts: z.ZodArray<z.ZodObject<{
|
|
113
|
+
account: z.ZodObject<{
|
|
114
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
115
|
+
username: z.ZodString;
|
|
116
|
+
}, z.core.$strict>;
|
|
117
|
+
actor: z.ZodObject<{
|
|
118
|
+
id: z.core.$ZodBranded<z.ZodUUID, "Uuid", "out">;
|
|
119
|
+
}, z.core.$strict>;
|
|
120
|
+
api_token: z.ZodString;
|
|
121
|
+
session_cookie: z.ZodString;
|
|
122
|
+
}, z.core.$strict>>;
|
|
123
|
+
}, z.core.$strict>;
|
|
124
|
+
readonly async: true;
|
|
125
|
+
readonly description: "Test-binary only — wipe auth tables, re-bootstrap a fresh keeper (+ optional extras), fire the domain-state reset.";
|
|
126
|
+
};
|
|
127
|
+
/** Options for `create_testing_actions`. */
|
|
128
|
+
export interface CreateTestingActionsOptions {
|
|
129
|
+
/**
|
|
130
|
+
* Session cookie options — the reset action uses these when signing
|
|
131
|
+
* the fresh keeper's (and any extra accounts') session cookies.
|
|
132
|
+
* Pass the same `SessionOptions` the live `create_app_server` call
|
|
133
|
+
* was wired with.
|
|
134
|
+
*/
|
|
135
|
+
readonly session_options: SessionOptions<string>;
|
|
136
|
+
/**
|
|
137
|
+
* Daemon-token runtime state — the reset action mutates
|
|
138
|
+
* `state.keeper_account_id` to point at the freshly seeded keeper
|
|
139
|
+
* after the old row is wiped. Pass the same `DaemonTokenState`
|
|
140
|
+
* instance the daemon-token middleware reads.
|
|
141
|
+
*/
|
|
142
|
+
readonly daemon_token_state: DaemonTokenState;
|
|
143
|
+
/**
|
|
144
|
+
* Consumer-supplied callback invoked after the auth-table reset.
|
|
145
|
+
* `testing_zzz_server` clears workspace registry + terminals + the
|
|
146
|
+
* scoped FS scratch dir here; `testing_spine_stub` has no domain
|
|
147
|
+
* layer and passes a no-op (or omits the option). Runs inside the
|
|
148
|
+
* same RPC dispatch as the auth-table writes, so a throw surfaces
|
|
149
|
+
* to the caller as a JSON-RPC error and the per-test fixture
|
|
150
|
+
* short-circuits.
|
|
151
|
+
*/
|
|
152
|
+
readonly reset_state?: () => Promise<void> | void;
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* Build the testing RPC actions for a test binary's registry.
|
|
156
|
+
*
|
|
157
|
+
* Returns `_testing_reset` — the single privileged action test binaries
|
|
158
|
+
* register. The test binary calls this at server-assembly time and
|
|
159
|
+
* registers the result on its dispatcher.
|
|
160
|
+
*
|
|
161
|
+
* The reset action's table-wipe list mirrors
|
|
162
|
+
* `auth_integration_truncate_tables` from `testing/db.ts` — the
|
|
163
|
+
* canonical "auth tables a between-test reset must clear" set.
|
|
164
|
+
* `testing_reset_actions.coverage.test.ts` enforces the set-equality
|
|
165
|
+
* invariant so a future auth migration that adds a table to that list
|
|
166
|
+
* without updating this handler fails CI.
|
|
167
|
+
*/
|
|
168
|
+
export declare const create_testing_actions: (deps: AppDeps, options: CreateTestingActionsOptions) => Array<RpcAction>;
|
|
169
|
+
/** Set of auth-namespace tables `_testing_reset` wipes. Mirrored by the coverage test. */
|
|
170
|
+
export declare const testing_reset_wiped_tables: string[];
|
|
171
|
+
//# sourceMappingURL=testing_reset_actions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing_reset_actions.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_reset_actions.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmDG;AAEH,OAAO,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAGtB,OAAO,EAAa,KAAK,SAAS,EAAC,MAAM,6BAA6B,CAAC;AAEvE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAC;AACjE,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,4BAA4B,CAAC;AAajE;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,eAAO,MAAM,yBAAyB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwBQ,CAAC;AAE/C,4CAA4C;AAC5C,MAAM,WAAW,2BAA2B;IAC3C;;;;;OAKG;IACH,QAAQ,CAAC,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACjD;;;;;OAKG;IACH,QAAQ,CAAC,kBAAkB,EAAE,gBAAgB,CAAC;IAC9C;;;;;;;;OAQG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC;CAClD;AAED;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,sBAAsB,GAClC,MAAM,OAAO,EACb,SAAS,2BAA2B,KAClC,KAAK,CAAC,SAAS,CA4FjB,CAAC;AAEF,0FAA0F;AAC1F,eAAO,MAAM,0BAA0B,UAAmC,CAAC"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Test-binary RPC actions for cross-process integration tests.
|
|
4
|
+
*
|
|
5
|
+
* Single daemon-token-authed action: **`_testing_reset`** — full DB
|
|
6
|
+
* wipe + keeper re-seed + optional secondary-account seeding. The
|
|
7
|
+
* handler wipes every auth-namespace row (no keeper-preserve filter),
|
|
8
|
+
* flips `bootstrap_lock` back to its post-bootstrap shape, seeds a
|
|
9
|
+
* fresh keeper account inline (reusing `create_test_account_with_credentials`
|
|
10
|
+
* so cross-process matches in-process write semantics), seeds any
|
|
11
|
+
* caller-requested `extra_accounts` (also direct-inserted at this
|
|
12
|
+
* setup step), refreshes the daemon-token cache to point at the new
|
|
13
|
+
* keeper, and fires the consumer-supplied domain-state callback. The
|
|
14
|
+
* new keeper + secondary credentials return as the action output so
|
|
15
|
+
* the per-test fixture closes over them.
|
|
16
|
+
*
|
|
17
|
+
* The redesign converges in-process and cross-process keeper
|
|
18
|
+
* lifetimes: both modes now run against a freshly bootstrapped keeper
|
|
19
|
+
* per test. Mutation-cascade tests (password change, revoke-all,
|
|
20
|
+
* hardcoded-username signup uniqueness) and direct keeper-vs-admin
|
|
21
|
+
* probes work uniformly cross-process.
|
|
22
|
+
*
|
|
23
|
+
* **Keeper ≠ admin.** The `keeper` and `admin` roles are independent.
|
|
24
|
+
* Keeper authorizes daemon-token / bootstrap paths; admin authorizes
|
|
25
|
+
* the user-facing admin RPC surface. `_testing_reset` seeds the keeper
|
|
26
|
+
* account with `[ROLE_KEEPER, ROLE_ADMIN]` by default — matching the
|
|
27
|
+
* production `bootstrap_account` flow — plus any roles passed via
|
|
28
|
+
* `extra_keeper_roles`. Tests probing the keeper-vs-admin separation
|
|
29
|
+
* (a keeper-only account must 403 on admin RPCs) declare a secondary
|
|
30
|
+
* via `extra_accounts: [{username, roles: [ROLE_KEEPER]}]` so the
|
|
31
|
+
* account is seeded at this same bootstrap-equivalent step.
|
|
32
|
+
*
|
|
33
|
+
* **No free-form runtime bypass.** Earlier drafts considered a separate
|
|
34
|
+
* `_testing_seed_role_grant` action for arbitrary direct grants; that
|
|
35
|
+
* was rejected because a runtime bypass would let tests skip the
|
|
36
|
+
* production consent flow's side-effects (audit emit, WS fan-out) and
|
|
37
|
+
* silently mask bugs in those paths. The bypass that does exist —
|
|
38
|
+
* `extra_accounts` — is framed as bootstrap-time seeding, the same
|
|
39
|
+
* shape `bootstrap_account` itself uses to grant the initial
|
|
40
|
+
* `KEEPER` + `ADMIN` pair. Tests that want a role on a *post-bootstrap*
|
|
41
|
+
* account must route through `role_grant_offer_create` +
|
|
42
|
+
* `role_grant_offer_accept` (the production path); they observe the
|
|
43
|
+
* full event chain.
|
|
44
|
+
*
|
|
45
|
+
* Production safety: this module lives under `cross_backend/` and starts
|
|
46
|
+
* with `import '../assert_dev_env.js';` — production bundles either
|
|
47
|
+
* tree-shake the module out or throw at startup. The Rust mirror
|
|
48
|
+
* (`fuz_testing` crate) ships a parallel action; `cargo xtask
|
|
49
|
+
* check-release` blocks `fuz_testing` from entering production dep
|
|
50
|
+
* graphs.
|
|
51
|
+
*
|
|
52
|
+
* @module
|
|
53
|
+
*/
|
|
54
|
+
import { z } from 'zod';
|
|
55
|
+
import { Uuid } from '@fuzdev/fuz_util/id.js';
|
|
56
|
+
import { rpc_action } from '../../actions/action_rpc.js';
|
|
57
|
+
import { ROLE_ADMIN, ROLE_KEEPER } from '../../auth/role_schema.js';
|
|
58
|
+
import { auth_integration_truncate_tables } from '../db.js';
|
|
59
|
+
import { create_test_account_with_credentials, DEFAULT_TEST_PASSWORD } from '../app_server.js';
|
|
60
|
+
/** Output shape for an individual seeded account (keeper or extra). */
|
|
61
|
+
const SeededAccountShape = z.strictObject({
|
|
62
|
+
account: z.strictObject({ id: Uuid, username: z.string() }),
|
|
63
|
+
actor: z.strictObject({ id: Uuid }),
|
|
64
|
+
api_token: z.string(),
|
|
65
|
+
session_cookie: z.string(),
|
|
66
|
+
});
|
|
67
|
+
/**
|
|
68
|
+
* The `_testing_reset` action spec.
|
|
69
|
+
*
|
|
70
|
+
* Input:
|
|
71
|
+
* - `extra_keeper_roles` — roles to grant the fresh keeper *in addition
|
|
72
|
+
* to* `[ROLE_KEEPER, ROLE_ADMIN]` (matching production bootstrap).
|
|
73
|
+
* - `extra_accounts` — additional accounts to seed at this same
|
|
74
|
+
* bootstrap-equivalent step. Each entry's `roles` are direct-granted
|
|
75
|
+
* (bypassing offer/accept) because the seed is *part of bootstrap*,
|
|
76
|
+
* not a post-bootstrap action. Use this for accounts whose required
|
|
77
|
+
* roles aren't admin-grantable via offer/accept (e.g. `ROLE_KEEPER`,
|
|
78
|
+
* whose `RoleSpec.grant_paths` is bootstrap-only). For
|
|
79
|
+
* admin-grantable roles, prefer `fixture.create_account({roles})`
|
|
80
|
+
* (offer/accept production path).
|
|
81
|
+
*
|
|
82
|
+
* Output: keeper credentials plus a parallel array of seeded
|
|
83
|
+
* `extra_accounts` (same order as input). The per-test fixture closes
|
|
84
|
+
* over the returned values; subsequent calls in the same test see the
|
|
85
|
+
* fresh keeper and any requested secondaries.
|
|
86
|
+
*
|
|
87
|
+
* `auth` gates on the daemon-token credential — the keeper holds it
|
|
88
|
+
* exclusively. The action is internally privileged (it runs direct
|
|
89
|
+
* DB writes the production wire never exposes); daemon-token auth is
|
|
90
|
+
* the structural fence.
|
|
91
|
+
*/
|
|
92
|
+
export const testing_reset_action_spec = {
|
|
93
|
+
method: '_testing_reset',
|
|
94
|
+
kind: 'request_response',
|
|
95
|
+
initiator: 'frontend',
|
|
96
|
+
auth: { account: 'required', actor: 'none', credential_types: ['daemon_token'] },
|
|
97
|
+
side_effects: true,
|
|
98
|
+
input: z.strictObject({
|
|
99
|
+
extra_keeper_roles: z.array(z.string()).optional(),
|
|
100
|
+
extra_accounts: z
|
|
101
|
+
.array(z.strictObject({
|
|
102
|
+
username: z.string(),
|
|
103
|
+
password_value: z.string().optional(),
|
|
104
|
+
roles: z.array(z.string()),
|
|
105
|
+
}))
|
|
106
|
+
.optional(),
|
|
107
|
+
}),
|
|
108
|
+
output: SeededAccountShape.extend({
|
|
109
|
+
extra_accounts: z.array(SeededAccountShape),
|
|
110
|
+
}),
|
|
111
|
+
async: true,
|
|
112
|
+
description: 'Test-binary only — wipe auth tables, re-bootstrap a fresh keeper (+ optional extras), fire the domain-state reset.',
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Build the testing RPC actions for a test binary's registry.
|
|
116
|
+
*
|
|
117
|
+
* Returns `_testing_reset` — the single privileged action test binaries
|
|
118
|
+
* register. The test binary calls this at server-assembly time and
|
|
119
|
+
* registers the result on its dispatcher.
|
|
120
|
+
*
|
|
121
|
+
* The reset action's table-wipe list mirrors
|
|
122
|
+
* `auth_integration_truncate_tables` from `testing/db.ts` — the
|
|
123
|
+
* canonical "auth tables a between-test reset must clear" set.
|
|
124
|
+
* `testing_reset_actions.coverage.test.ts` enforces the set-equality
|
|
125
|
+
* invariant so a future auth migration that adds a table to that list
|
|
126
|
+
* without updating this handler fails CI.
|
|
127
|
+
*/
|
|
128
|
+
export const create_testing_actions = (deps, options) => {
|
|
129
|
+
const { session_options, daemon_token_state, reset_state } = options;
|
|
130
|
+
const log = deps.log;
|
|
131
|
+
return [
|
|
132
|
+
rpc_action(testing_reset_action_spec, async (input, ctx) => {
|
|
133
|
+
log.info('[_testing_reset] resetting auth state + re-seeding keeper');
|
|
134
|
+
// 1. Wipe every auth-namespace row. No keeper-preserve filter —
|
|
135
|
+
// the fresh-keeper-per-test contract means mutation-cascade
|
|
136
|
+
// tests (password change, revoke-all) and hardcoded-username
|
|
137
|
+
// signup-uniqueness tests can't leak between cases.
|
|
138
|
+
//
|
|
139
|
+
// `audit_log` has no FK to account so it wipes wholesale.
|
|
140
|
+
// `role_grant_offer` + `invite` likewise (callers don't carry
|
|
141
|
+
// state across resets). For account-FK tables we wipe rows
|
|
142
|
+
// referencing actor/account first to satisfy FK order.
|
|
143
|
+
await ctx.db.query('DELETE FROM audit_log');
|
|
144
|
+
await ctx.db.query('DELETE FROM role_grant_offer');
|
|
145
|
+
await ctx.db.query('DELETE FROM invite');
|
|
146
|
+
await ctx.db.query('DELETE FROM api_token');
|
|
147
|
+
await ctx.db.query('DELETE FROM auth_session');
|
|
148
|
+
await ctx.db.query('DELETE FROM role_grant');
|
|
149
|
+
await ctx.db.query('DELETE FROM actor');
|
|
150
|
+
await ctx.db.query('DELETE FROM account');
|
|
151
|
+
// 2. Reset singleton `app_settings` to production defaults
|
|
152
|
+
// (matches in-process `_build_test_backend` behavior in
|
|
153
|
+
// `app_server.ts`). Tests that flipped `open_signup` mid-run
|
|
154
|
+
// revert.
|
|
155
|
+
await ctx.db.query('UPDATE app_settings SET open_signup = false, updated_at = NULL, updated_by = NULL ' +
|
|
156
|
+
'WHERE open_signup = true OR updated_at IS NOT NULL');
|
|
157
|
+
// 3. Flip `bootstrap_lock` to its post-bootstrap shape. Production
|
|
158
|
+
// `bootstrap_account` flips this to `true` on success; the
|
|
159
|
+
// in-process `bootstrap_test_keeper` mirrors the flip. We're
|
|
160
|
+
// about to seed the keeper here, so the final state needs to
|
|
161
|
+
// be `bootstrapped = true`. We don't need an intermediate flip
|
|
162
|
+
// to `false` — nothing reads it between our DELETEs and the
|
|
163
|
+
// UPDATE.
|
|
164
|
+
await ctx.db.query('UPDATE bootstrap_lock SET bootstrapped = true WHERE id = 1');
|
|
165
|
+
// 4. Seed the fresh keeper inline. Reuses the same primitive
|
|
166
|
+
// in-process tests use (`create_test_account_with_credentials`)
|
|
167
|
+
// so cross-process and in-process write semantics stay in
|
|
168
|
+
// parity — same hash, same account+actor+role_grants+
|
|
169
|
+
// api_token+session_cookie shape.
|
|
170
|
+
//
|
|
171
|
+
// Roles default to `[ROLE_KEEPER, ROLE_ADMIN]` to match
|
|
172
|
+
// production `bootstrap_account`. `extra_keeper_roles` adds
|
|
173
|
+
// on top.
|
|
174
|
+
const keeper = await create_test_account_with_credentials({
|
|
175
|
+
db: ctx.db,
|
|
176
|
+
keyring: deps.keyring,
|
|
177
|
+
session_options,
|
|
178
|
+
password: deps.password,
|
|
179
|
+
password_value: DEFAULT_TEST_PASSWORD,
|
|
180
|
+
roles: [ROLE_KEEPER, ROLE_ADMIN, ...(input.extra_keeper_roles ?? [])],
|
|
181
|
+
});
|
|
182
|
+
// 5. Seed any caller-requested extras. These are bootstrap-time
|
|
183
|
+
// secondaries — the bypass exists in the same cradle the
|
|
184
|
+
// keeper does, not as a free-form runtime action.
|
|
185
|
+
const extras = [];
|
|
186
|
+
for (const spec of input.extra_accounts ?? []) {
|
|
187
|
+
const seeded = await create_test_account_with_credentials({
|
|
188
|
+
db: ctx.db,
|
|
189
|
+
keyring: deps.keyring,
|
|
190
|
+
session_options,
|
|
191
|
+
password: deps.password,
|
|
192
|
+
username: spec.username,
|
|
193
|
+
password_value: spec.password_value ?? DEFAULT_TEST_PASSWORD,
|
|
194
|
+
roles: spec.roles,
|
|
195
|
+
});
|
|
196
|
+
extras.push(seeded);
|
|
197
|
+
}
|
|
198
|
+
// 6. Refresh the daemon-token cache so subsequent daemon-token
|
|
199
|
+
// requests resolve to the freshly seeded keeper. The
|
|
200
|
+
// middleware's lazy-refresh path only fires when the cached
|
|
201
|
+
// id is null; setting it directly here avoids one round-trip
|
|
202
|
+
// of stale-id-then-refresh on the next call.
|
|
203
|
+
daemon_token_state.keeper_account_id = keeper.account.id;
|
|
204
|
+
// 7. Fire domain-state reset (zzz workspaces/terminals/scratch,
|
|
205
|
+
// or no-op for spine_stub).
|
|
206
|
+
if (reset_state)
|
|
207
|
+
await reset_state();
|
|
208
|
+
return { ...keeper, extra_accounts: extras };
|
|
209
|
+
}),
|
|
210
|
+
];
|
|
211
|
+
};
|
|
212
|
+
/** Set of auth-namespace tables `_testing_reset` wipes. Mirrored by the coverage test. */
|
|
213
|
+
export const testing_reset_wiped_tables = auth_integration_truncate_tables;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { TestingServerAdapter } from './testing_server_core.js';
|
|
3
|
+
/** Build the Bun {@link TestingServerAdapter}. */
|
|
4
|
+
export declare const create_bun_testing_adapter: () => TestingServerAdapter;
|
|
5
|
+
//# sourceMappingURL=testing_server_bun.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing_server_bun.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_bun.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA6B9B,OAAO,KAAK,EAAc,oBAAoB,EAAC,MAAM,0BAA0B,CAAC;AAehF,kDAAkD;AAClD,eAAO,MAAM,0BAA0B,QAAO,oBA+B5C,CAAC"}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Bun runtime adapter for spawnable cross-process test server binaries.
|
|
4
|
+
*
|
|
5
|
+
* Binds `Bun.serve` and `hono/bun`'s module-level `upgradeWebSocket` +
|
|
6
|
+
* `websocket` handler. The shared `testing_server_core.ts` owns the rest.
|
|
7
|
+
* Third sibling to `testing_server_node.ts` / `testing_server_deno.ts` —
|
|
8
|
+
* together the three isolate the JS-runtime axis (Node V8 / Deno V8 / Bun
|
|
9
|
+
* JSC) on identical TS surfaces, and the Rust spine binary covers the
|
|
10
|
+
* cross-language axis.
|
|
11
|
+
*
|
|
12
|
+
* Needs **no extra deps**: `hono/bun` ships with the `hono` peer dep and
|
|
13
|
+
* `Bun.serve` is built in (unlike Node, which pulls `@hono/node-server` +
|
|
14
|
+
* `@hono/node-ws`). `RuntimeDeps` reuse `create_node_runtime` — Bun
|
|
15
|
+
* implements the `node:fs` / `node:process` surface `RuntimeDeps` +
|
|
16
|
+
* `cli/daemon` touch.
|
|
17
|
+
*
|
|
18
|
+
* `Bun.serve` is declared locally (mirroring `testing_server_deno.ts`'s
|
|
19
|
+
* `Deno` declaration) so this module typechecks under fuz_app's Node-based
|
|
20
|
+
* config without `@types/bun`. It is only ever *run* under Bun.
|
|
21
|
+
*
|
|
22
|
+
* @module
|
|
23
|
+
*/
|
|
24
|
+
import process from 'node:process';
|
|
25
|
+
import { getConnInfo, upgradeWebSocket, websocket } from 'hono/bun';
|
|
26
|
+
import { create_node_runtime } from '../../runtime/node.js';
|
|
27
|
+
/** Build the Bun {@link TestingServerAdapter}. */
|
|
28
|
+
export const create_bun_testing_adapter = () => ({
|
|
29
|
+
runtime_label: 'Bun',
|
|
30
|
+
runtime: create_node_runtime(),
|
|
31
|
+
get_connection_ip: (c) => getConnInfo(c).remote.address,
|
|
32
|
+
// Bun's WS upgrade is module-level and stateless (like Deno) — no
|
|
33
|
+
// post-serve attach. The `websocket` handler is threaded into `serve`
|
|
34
|
+
// below, where `Bun.serve` wants it.
|
|
35
|
+
prepare_websocket: () => ({ upgrade_websocket: upgradeWebSocket }),
|
|
36
|
+
serve: ({ fetch, port, hostname }) => {
|
|
37
|
+
const server = Bun.serve({
|
|
38
|
+
fetch: fetch,
|
|
39
|
+
port,
|
|
40
|
+
hostname,
|
|
41
|
+
// Harmless for HTTP-only binaries — Bun only invokes it for sockets
|
|
42
|
+
// upgraded via `upgradeWebSocket`.
|
|
43
|
+
websocket,
|
|
44
|
+
});
|
|
45
|
+
const handle = {
|
|
46
|
+
shutdown: async () => {
|
|
47
|
+
await server.stop();
|
|
48
|
+
},
|
|
49
|
+
native: server,
|
|
50
|
+
};
|
|
51
|
+
return handle;
|
|
52
|
+
},
|
|
53
|
+
pid: process.pid,
|
|
54
|
+
register_shutdown_signals: (handler) => {
|
|
55
|
+
process.on('SIGINT', () => void handler());
|
|
56
|
+
process.on('SIGTERM', () => void handler());
|
|
57
|
+
},
|
|
58
|
+
exit: (code) => process.exit(code),
|
|
59
|
+
});
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Runtime-agnostic core for spawnable cross-process **test** server
|
|
4
|
+
* binaries.
|
|
5
|
+
*
|
|
6
|
+
* A test binary mounts a fuz_app-derived surface over a real HTTP socket so
|
|
7
|
+
* the `cross_backend/*` suites (and the cross-impl bench) can drive it the
|
|
8
|
+
* same way they drive the Rust spine. This module owns the runtime-neutral
|
|
9
|
+
* orchestration — stale-daemon check, daemon-info write, serve, post-serve
|
|
10
|
+
* WS attach, graceful drain shutdown — and delegates the runtime-boundary
|
|
11
|
+
* primitives (HTTP serve, WS upgrade construction, signals, pid, exit) to a
|
|
12
|
+
* {@link TestingServerAdapter}. The two shipped adapters are
|
|
13
|
+
* `testing_server_node.ts` (`@hono/node-server` + `@hono/node-ws`) and
|
|
14
|
+
* `testing_server_deno.ts` (`Deno.serve` + `hono/deno`).
|
|
15
|
+
*
|
|
16
|
+
* The app itself — routes, RPC, DB, `_testing_reset`, optional WS mount —
|
|
17
|
+
* is the caller's {@link StartTestingServerOptions.build_app} seam, so this
|
|
18
|
+
* core stays domain-free. fuz_app's own `testing_spine_server` passes a
|
|
19
|
+
* no-domain build; consumers (zzz, fuz_forge) pass their domain build.
|
|
20
|
+
*
|
|
21
|
+
* **NEVER ships in a release.** This module lives under `cross_backend/` and
|
|
22
|
+
* opens with `import '../assert_dev_env.js';`, which throws on
|
|
23
|
+
* production-bundle load. The runtime adapters reach for the optional
|
|
24
|
+
* `@hono/node-server` / `@hono/node-ws` peer deps; only test binaries import
|
|
25
|
+
* them.
|
|
26
|
+
*
|
|
27
|
+
* @module
|
|
28
|
+
*/
|
|
29
|
+
import type { Context, Hono } from 'hono';
|
|
30
|
+
import type { UpgradeWebSocket } from 'hono/ws';
|
|
31
|
+
import { type Logger as LoggerType } from '@fuzdev/fuz_util/log.js';
|
|
32
|
+
import type { RuntimeDeps } from '../../runtime/deps.js';
|
|
33
|
+
/**
|
|
34
|
+
* Adapter-built handle to a bound HTTP server.
|
|
35
|
+
*
|
|
36
|
+
* `shutdown` stops accepting new connections and drains in-flight ones.
|
|
37
|
+
* `native` is an adapter-specific server reference — used by Node's
|
|
38
|
+
* `@hono/node-ws` `injectWebSocket(server)` post-serve hook; Deno leaves it
|
|
39
|
+
* unset.
|
|
40
|
+
*/
|
|
41
|
+
export interface ServeHandle {
|
|
42
|
+
shutdown: () => Promise<void>;
|
|
43
|
+
/** Adapter-specific server ref for post-serve hooks. Type-erased at the seam. */
|
|
44
|
+
native?: unknown;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Result of an adapter's WS preparation step.
|
|
48
|
+
*
|
|
49
|
+
* `upgrade_websocket` is the Hono `UpgradeWebSocket` closure the caller's
|
|
50
|
+
* WS mount uses to register the endpoint. `attach_to_server` runs after
|
|
51
|
+
* `serve()` returns a {@link ServeHandle} — Node uses it for
|
|
52
|
+
* `injectWebSocket(server)`; Deno leaves it undefined.
|
|
53
|
+
*/
|
|
54
|
+
export interface PreparedWebsocket {
|
|
55
|
+
upgrade_websocket: UpgradeWebSocket;
|
|
56
|
+
attach_to_server?: (handle: ServeHandle) => void;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Runtime adapter contract for the test-binary entry. Each adapter
|
|
60
|
+
* (`testing_server_node.ts`, `testing_server_deno.ts`) implements this and
|
|
61
|
+
* hands the shape to {@link start_testing_server}.
|
|
62
|
+
*/
|
|
63
|
+
export interface TestingServerAdapter {
|
|
64
|
+
/** Human-readable runtime label for log output (e.g. `"Node"`, `"Deno"`). */
|
|
65
|
+
runtime_label: string;
|
|
66
|
+
/** `RuntimeDeps` capability bundle from `create_node_runtime` / `create_deno_runtime`. */
|
|
67
|
+
runtime: RuntimeDeps;
|
|
68
|
+
/** Extract the raw TCP connection IP from a Hono context. */
|
|
69
|
+
get_connection_ip: (c: Context) => string | undefined;
|
|
70
|
+
/** Build the WS upgrade closure after the caller's `build_app` returns the app. */
|
|
71
|
+
prepare_websocket: (app: Hono) => PreparedWebsocket;
|
|
72
|
+
/** Bind `app.fetch` to `port` on `hostname`; return a {@link ServeHandle}. */
|
|
73
|
+
serve: (options: {
|
|
74
|
+
fetch: Hono['fetch'];
|
|
75
|
+
port: number;
|
|
76
|
+
hostname: string;
|
|
77
|
+
}) => ServeHandle;
|
|
78
|
+
/** Current process pid (for `daemon.json`). */
|
|
79
|
+
pid: number;
|
|
80
|
+
/** Register SIGINT/SIGTERM listeners that invoke `handler` once each. */
|
|
81
|
+
register_shutdown_signals: (handler: () => Promise<void>) => void;
|
|
82
|
+
/** Forceful exit on graceful-shutdown completion or fatal error. */
|
|
83
|
+
exit: (code: number) => never;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* The assembled app a {@link StartTestingServerOptions.build_app} seam
|
|
87
|
+
* returns.
|
|
88
|
+
*
|
|
89
|
+
* `mount_websocket` is invoked by the core after the app exists and the
|
|
90
|
+
* adapter prepared the WS upgrade closure — the closure mounts the WS
|
|
91
|
+
* endpoint(s) (e.g. via `register_ws_endpoint`) and wires any
|
|
92
|
+
* audit-revocation guards. Omit it for an HTTP-only binary.
|
|
93
|
+
*/
|
|
94
|
+
export interface BuiltTestingApp {
|
|
95
|
+
/** The assembled Hono app (HTTP routes + RPC already mounted). */
|
|
96
|
+
app: Hono;
|
|
97
|
+
/** Tear down backend(s) + DB + any rotation on graceful shutdown. */
|
|
98
|
+
close: () => Promise<void>;
|
|
99
|
+
/** Mount WS endpoint(s) given the runtime-prepared upgrade closure. */
|
|
100
|
+
mount_websocket?: (upgrade_websocket: UpgradeWebSocket) => void;
|
|
101
|
+
}
|
|
102
|
+
/** Options for {@link start_testing_server}. */
|
|
103
|
+
export interface StartTestingServerOptions {
|
|
104
|
+
/** Runtime-boundary adapter (Node or Deno). */
|
|
105
|
+
adapter: TestingServerAdapter;
|
|
106
|
+
/**
|
|
107
|
+
* Daemon-info namespace — the `cli/daemon` key the `daemon.json` is
|
|
108
|
+
* written under (e.g. `'fuz_app_spine'`). The cross-process harness
|
|
109
|
+
* reads the daemon token from the rotation file, not this; `daemon.json`
|
|
110
|
+
* is for stale-process detection + parity with production daemon
|
|
111
|
+
* lifecycle.
|
|
112
|
+
*/
|
|
113
|
+
daemon_name: string;
|
|
114
|
+
/** Bind host (e.g. `'localhost'`). */
|
|
115
|
+
host: string;
|
|
116
|
+
/** Bind port. */
|
|
117
|
+
port: number;
|
|
118
|
+
/** App version recorded in `daemon.json`. */
|
|
119
|
+
app_version?: string;
|
|
120
|
+
/**
|
|
121
|
+
* Build the app. Closes over the entry's runtime + connection-IP getter
|
|
122
|
+
* + password deps + resolved config — so this core never touches the
|
|
123
|
+
* domain. Returns the assembled app, a `close` teardown, and an optional
|
|
124
|
+
* `mount_websocket` hook.
|
|
125
|
+
*/
|
|
126
|
+
build_app: () => Promise<BuiltTestingApp>;
|
|
127
|
+
/** Optional logger; defaults to a `[daemon_name]`-namespaced `Logger`. */
|
|
128
|
+
log?: LoggerType;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Boot a test-mode server using the supplied runtime adapter.
|
|
132
|
+
*
|
|
133
|
+
* Mirrors a production `start_server` at the surface level — stale-daemon
|
|
134
|
+
* check, daemon-info write, bind, graceful drain — but the app is the
|
|
135
|
+
* caller's no-domain (or domain) {@link StartTestingServerOptions.build_app}
|
|
136
|
+
* and the runtime boundary is the {@link TestingServerAdapter}. Refuses to
|
|
137
|
+
* bind an open host (the test binary must stay on loopback).
|
|
138
|
+
*/
|
|
139
|
+
export declare const start_testing_server: (options: StartTestingServerOptions) => Promise<void>;
|
|
140
|
+
//# sourceMappingURL=testing_server_core.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"testing_server_core.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/testing_server_core.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAE9B;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAEH,OAAO,KAAK,EAAC,OAAO,EAAE,IAAI,EAAC,MAAM,MAAM,CAAC;AACxC,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,SAAS,CAAC;AAC9C,OAAO,EAAS,KAAK,MAAM,IAAI,UAAU,EAAC,MAAM,yBAAyB,CAAC;AAG1E,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAEvD;;;;;;;GAOG;AACH,MAAM,WAAW,WAAW;IAC3B,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B,iFAAiF;IACjF,MAAM,CAAC,EAAE,OAAO,CAAC;CACjB;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,iBAAiB;IACjC,iBAAiB,EAAE,gBAAgB,CAAC;IACpC,gBAAgB,CAAC,EAAE,CAAC,MAAM,EAAE,WAAW,KAAK,IAAI,CAAC;CACjD;AAED;;;;GAIG;AACH,MAAM,WAAW,oBAAoB;IACpC,6EAA6E;IAC7E,aAAa,EAAE,MAAM,CAAC;IACtB,0FAA0F;IAC1F,OAAO,EAAE,WAAW,CAAC;IACrB,6DAA6D;IAC7D,iBAAiB,EAAE,CAAC,CAAC,EAAE,OAAO,KAAK,MAAM,GAAG,SAAS,CAAC;IACtD,mFAAmF;IACnF,iBAAiB,EAAE,CAAC,GAAG,EAAE,IAAI,KAAK,iBAAiB,CAAC;IACpD,8EAA8E;IAC9E,KAAK,EAAE,CAAC,OAAO,EAAE;QAAC,KAAK,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAC,KAAK,WAAW,CAAC;IACxF,+CAA+C;IAC/C,GAAG,EAAE,MAAM,CAAC;IACZ,yEAAyE;IACzE,yBAAyB,EAAE,CAAC,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;IAClE,oEAAoE;IACpE,IAAI,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,KAAK,CAAC;CAC9B;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,eAAe;IAC/B,kEAAkE;IAClE,GAAG,EAAE,IAAI,CAAC;IACV,qEAAqE;IACrE,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3B,uEAAuE;IACvE,eAAe,CAAC,EAAE,CAAC,iBAAiB,EAAE,gBAAgB,KAAK,IAAI,CAAC;CAChE;AAED,gDAAgD;AAChD,MAAM,WAAW,yBAAyB;IACzC,+CAA+C;IAC/C,OAAO,EAAE,oBAAoB,CAAC;IAC9B;;;;;;OAMG;IACH,WAAW,EAAE,MAAM,CAAC;IACpB,sCAAsC;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,6CAA6C;IAC7C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;;;;OAKG;IACH,SAAS,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAC;IAC1C,0EAA0E;IAC1E,GAAG,CAAC,EAAE,UAAU,CAAC;CACjB;AAKD;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAU,SAAS,yBAAyB,KAAG,OAAO,CAAC,IAAI,CA4D3F,CAAC"}
|