@fuzdev/fuz_app 0.55.0 → 0.56.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 +211 -155
- package/dist/actions/action_bridge.d.ts +8 -5
- package/dist/actions/action_bridge.d.ts.map +1 -1
- package/dist/actions/action_bridge.js +1 -11
- package/dist/actions/action_codegen.d.ts +19 -0
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +20 -14
- package/dist/actions/action_registry.d.ts.map +1 -1
- package/dist/actions/action_registry.js +5 -2
- package/dist/actions/action_rpc.d.ts +110 -44
- package/dist/actions/action_rpc.d.ts.map +1 -1
- package/dist/actions/action_rpc.js +92 -287
- package/dist/actions/action_spec.d.ts +55 -16
- package/dist/actions/action_spec.d.ts.map +1 -1
- package/dist/actions/action_spec.js +16 -11
- package/dist/actions/action_types.d.ts +28 -60
- package/dist/actions/action_types.d.ts.map +1 -1
- package/dist/actions/action_types.js +13 -5
- package/dist/actions/broadcast_api.d.ts +2 -2
- package/dist/actions/broadcast_api.js +2 -2
- package/dist/actions/compile_action_registry.d.ts +50 -0
- package/dist/actions/compile_action_registry.d.ts.map +1 -0
- package/dist/actions/compile_action_registry.js +69 -0
- package/dist/actions/heartbeat.d.ts +8 -4
- package/dist/actions/heartbeat.d.ts.map +1 -1
- package/dist/actions/heartbeat.js +5 -4
- package/dist/actions/perform_action.d.ts +145 -0
- package/dist/actions/perform_action.d.ts.map +1 -0
- package/dist/actions/perform_action.js +258 -0
- package/dist/actions/register_action_ws.d.ts +44 -38
- package/dist/actions/register_action_ws.d.ts.map +1 -1
- package/dist/actions/register_action_ws.js +101 -159
- package/dist/actions/register_ws_endpoint.d.ts +2 -10
- package/dist/actions/register_ws_endpoint.d.ts.map +1 -1
- package/dist/actions/register_ws_endpoint.js +32 -10
- package/dist/actions/transports_ws_auth_guard.d.ts +1 -1
- package/dist/actions/transports_ws_auth_guard.js +1 -1
- package/dist/actions/transports_ws_backend.d.ts +1 -1
- package/dist/actions/transports_ws_backend.js +1 -1
- package/dist/auth/CLAUDE.md +673 -442
- package/dist/auth/account_action_specs.d.ts +28 -7
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +7 -7
- package/dist/auth/account_actions.d.ts +8 -14
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +26 -32
- package/dist/auth/account_queries.d.ts +46 -13
- package/dist/auth/account_queries.d.ts.map +1 -1
- package/dist/auth/account_queries.js +73 -33
- package/dist/auth/account_routes.d.ts +4 -3
- package/dist/auth/account_routes.d.ts.map +1 -1
- package/dist/auth/account_routes.js +58 -33
- package/dist/auth/account_schema.d.ts +46 -54
- package/dist/auth/account_schema.d.ts.map +1 -1
- package/dist/auth/account_schema.js +21 -48
- package/dist/auth/admin_action_specs.d.ts +55 -21
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +42 -26
- package/dist/auth/admin_actions.d.ts +14 -21
- package/dist/auth/admin_actions.d.ts.map +1 -1
- package/dist/auth/admin_actions.js +47 -44
- package/dist/auth/audit_emitter.d.ts +160 -0
- package/dist/auth/audit_emitter.d.ts.map +1 -0
- package/dist/auth/audit_emitter.js +83 -0
- package/dist/auth/audit_log_queries.d.ts +17 -87
- package/dist/auth/audit_log_queries.d.ts.map +1 -1
- package/dist/auth/audit_log_queries.js +17 -96
- package/dist/auth/audit_log_routes.d.ts +1 -1
- package/dist/auth/audit_log_routes.d.ts.map +1 -1
- package/dist/auth/audit_log_routes.js +7 -3
- package/dist/auth/audit_log_schema.d.ts +48 -42
- package/dist/auth/audit_log_schema.d.ts.map +1 -1
- package/dist/auth/audit_log_schema.js +56 -43
- package/dist/auth/auth_guard_resolver.d.ts +44 -0
- package/dist/auth/auth_guard_resolver.d.ts.map +1 -0
- package/dist/auth/auth_guard_resolver.js +56 -0
- package/dist/auth/bootstrap_account.d.ts +7 -7
- package/dist/auth/bootstrap_account.d.ts.map +1 -1
- package/dist/auth/bootstrap_account.js +7 -7
- package/dist/auth/bootstrap_routes.d.ts.map +1 -1
- package/dist/auth/bootstrap_routes.js +11 -10
- package/dist/auth/cleanup.d.ts +20 -26
- package/dist/auth/cleanup.d.ts.map +1 -1
- package/dist/auth/cleanup.js +33 -47
- package/dist/auth/credential_type_schema.d.ts +115 -0
- package/dist/auth/credential_type_schema.d.ts.map +1 -0
- package/dist/auth/credential_type_schema.js +127 -0
- package/dist/auth/daemon_token_middleware.d.ts +1 -1
- package/dist/auth/daemon_token_middleware.js +3 -3
- package/dist/auth/ddl.d.ts +2 -2
- package/dist/auth/ddl.d.ts.map +1 -1
- package/dist/auth/ddl.js +6 -6
- package/dist/auth/deps.d.ts +7 -32
- package/dist/auth/deps.d.ts.map +1 -1
- package/dist/auth/grant_path_schema.d.ts +117 -0
- package/dist/auth/grant_path_schema.d.ts.map +1 -0
- package/dist/auth/grant_path_schema.js +137 -0
- package/dist/auth/invite_queries.d.ts +12 -1
- package/dist/auth/invite_queries.d.ts.map +1 -1
- package/dist/auth/invite_queries.js +12 -1
- package/dist/auth/invite_schema.d.ts +1 -1
- package/dist/auth/invite_schema.d.ts.map +1 -1
- package/dist/auth/invite_schema.js +1 -1
- package/dist/auth/middleware.d.ts.map +1 -1
- package/dist/auth/middleware.js +5 -2
- package/dist/auth/migrations.d.ts +22 -7
- package/dist/auth/migrations.d.ts.map +1 -1
- package/dist/auth/migrations.js +64 -25
- package/dist/auth/request_context.d.ts +157 -170
- package/dist/auth/request_context.d.ts.map +1 -1
- package/dist/auth/request_context.js +224 -268
- package/dist/auth/{permit_offer_action_specs.d.ts → role_grant_offer_action_specs.d.ts} +130 -100
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_action_specs.js +262 -0
- package/dist/auth/role_grant_offer_actions.d.ts +104 -0
- package/dist/auth/role_grant_offer_actions.d.ts.map +1 -0
- package/dist/auth/{permit_offer_actions.js → role_grant_offer_actions.js} +153 -140
- package/dist/auth/{permit_offer_notifications.d.ts → role_grant_offer_notifications.d.ts} +80 -70
- package/dist/auth/role_grant_offer_notifications.d.ts.map +1 -0
- package/dist/auth/role_grant_offer_notifications.js +182 -0
- package/dist/auth/{permit_offer_queries.d.ts → role_grant_offer_queries.d.ts} +64 -64
- package/dist/auth/role_grant_offer_queries.d.ts.map +1 -0
- package/dist/auth/{permit_offer_queries.js → role_grant_offer_queries.js} +136 -123
- package/dist/auth/role_grant_offer_schema.d.ts +150 -0
- package/dist/auth/role_grant_offer_schema.d.ts.map +1 -0
- package/dist/auth/{permit_offer_schema.js → role_grant_offer_schema.js} +55 -36
- package/dist/auth/role_grant_queries.d.ts +231 -0
- package/dist/auth/role_grant_queries.d.ts.map +1 -0
- package/dist/auth/role_grant_queries.js +320 -0
- package/dist/auth/role_schema.d.ts +150 -40
- package/dist/auth/role_schema.d.ts.map +1 -1
- package/dist/auth/role_schema.js +144 -45
- package/dist/auth/scope_kind_schema.d.ts +96 -0
- package/dist/auth/scope_kind_schema.d.ts.map +1 -0
- package/dist/auth/scope_kind_schema.js +94 -0
- package/dist/auth/self_service_role_action_specs.d.ts +4 -1
- package/dist/auth/self_service_role_action_specs.d.ts.map +1 -1
- package/dist/auth/self_service_role_action_specs.js +2 -2
- package/dist/auth/self_service_role_actions.d.ts +35 -29
- package/dist/auth/self_service_role_actions.d.ts.map +1 -1
- package/dist/auth/self_service_role_actions.js +58 -48
- package/dist/auth/session_cookie.d.ts +43 -6
- package/dist/auth/session_cookie.d.ts.map +1 -1
- package/dist/auth/session_cookie.js +31 -5
- package/dist/auth/session_middleware.d.ts +37 -3
- package/dist/auth/session_middleware.d.ts.map +1 -1
- package/dist/auth/session_middleware.js +33 -7
- package/dist/auth/signup_routes.d.ts.map +1 -1
- package/dist/auth/signup_routes.js +48 -19
- package/dist/auth/standard_action_specs.d.ts +2 -2
- package/dist/auth/standard_action_specs.js +4 -4
- package/dist/auth/standard_rpc_actions.d.ts +23 -19
- package/dist/auth/standard_rpc_actions.d.ts.map +1 -1
- package/dist/auth/standard_rpc_actions.js +12 -12
- package/dist/db/migrate.d.ts +1 -1
- package/dist/db/migrate.js +1 -1
- package/dist/dev/setup.d.ts +2 -2
- package/dist/dev/setup.d.ts.map +1 -1
- package/dist/dev/setup.js +4 -4
- package/dist/env/load.d.ts +1 -1
- package/dist/env/load.js +1 -1
- package/dist/hono_context.d.ts +27 -45
- package/dist/hono_context.d.ts.map +1 -1
- package/dist/hono_context.js +14 -28
- package/dist/http/CLAUDE.md +235 -121
- package/dist/http/auth_shape.d.ts +191 -0
- package/dist/http/auth_shape.d.ts.map +1 -0
- package/dist/http/auth_shape.js +237 -0
- package/dist/http/common_routes.js +3 -3
- package/dist/http/db_routes.d.ts +4 -0
- package/dist/http/db_routes.d.ts.map +1 -1
- package/dist/http/db_routes.js +44 -7
- package/dist/http/error_schemas.d.ts +56 -34
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +63 -28
- package/dist/http/pending_effects.d.ts +71 -18
- package/dist/http/pending_effects.d.ts.map +1 -1
- package/dist/http/pending_effects.js +87 -18
- package/dist/http/proxy.d.ts +52 -5
- package/dist/http/proxy.d.ts.map +1 -1
- package/dist/http/proxy.js +92 -14
- package/dist/http/route_spec.d.ts +89 -75
- package/dist/http/route_spec.d.ts.map +1 -1
- package/dist/http/route_spec.js +54 -72
- package/dist/http/schema_helpers.d.ts +3 -14
- package/dist/http/schema_helpers.d.ts.map +1 -1
- package/dist/http/schema_helpers.js +2 -14
- package/dist/http/surface.d.ts +2 -10
- package/dist/http/surface.d.ts.map +1 -1
- package/dist/http/surface.js +3 -4
- package/dist/http/surface_query.d.ts +39 -35
- package/dist/http/surface_query.d.ts.map +1 -1
- package/dist/http/surface_query.js +79 -36
- package/dist/primitive_schemas.d.ts +39 -0
- package/dist/primitive_schemas.d.ts.map +1 -0
- package/dist/primitive_schemas.js +40 -0
- package/dist/realtime/sse_auth_guard.d.ts +5 -5
- package/dist/realtime/sse_auth_guard.js +9 -9
- package/dist/runtime/mock.d.ts +1 -1
- package/dist/runtime/mock.js +1 -1
- package/dist/server/app_backend.d.ts +14 -11
- package/dist/server/app_backend.d.ts.map +1 -1
- package/dist/server/app_backend.js +12 -8
- package/dist/server/app_server.d.ts +7 -7
- package/dist/server/app_server.d.ts.map +1 -1
- package/dist/server/app_server.js +35 -40
- package/dist/server/validate_nginx.d.ts +1 -1
- package/dist/server/validate_nginx.js +1 -1
- package/dist/testing/CLAUDE.md +50 -38
- package/dist/testing/admin_integration.d.ts +5 -6
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +87 -85
- package/dist/testing/app_server.d.ts +11 -14
- package/dist/testing/app_server.d.ts.map +1 -1
- package/dist/testing/app_server.js +16 -15
- package/dist/testing/assertions.d.ts.map +1 -1
- package/dist/testing/assertions.js +2 -1
- package/dist/testing/attack_surface.d.ts.map +1 -1
- package/dist/testing/attack_surface.js +15 -9
- package/dist/testing/audit_completeness.d.ts +2 -2
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +36 -36
- package/dist/testing/auth_apps.d.ts +5 -4
- package/dist/testing/auth_apps.d.ts.map +1 -1
- package/dist/testing/auth_apps.js +22 -19
- package/dist/testing/data_exposure.d.ts.map +1 -1
- package/dist/testing/data_exposure.js +5 -5
- package/dist/testing/db.d.ts +1 -1
- package/dist/testing/db.d.ts.map +1 -1
- package/dist/testing/db.js +4 -4
- package/dist/testing/db_entities.d.ts +22 -0
- package/dist/testing/db_entities.d.ts.map +1 -0
- package/dist/testing/db_entities.js +28 -0
- package/dist/testing/entities.d.ts +8 -7
- package/dist/testing/entities.d.ts.map +1 -1
- package/dist/testing/entities.js +21 -18
- package/dist/testing/integration.d.ts.map +1 -1
- package/dist/testing/integration.js +13 -14
- package/dist/testing/integration_helpers.d.ts +4 -4
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +20 -18
- package/dist/testing/middleware.d.ts +4 -4
- package/dist/testing/middleware.d.ts.map +1 -1
- package/dist/testing/middleware.js +12 -11
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +40 -24
- package/dist/testing/rpc_round_trip.d.ts +1 -1
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +14 -13
- package/dist/testing/sse_round_trip.d.ts +3 -4
- package/dist/testing/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/sse_round_trip.js +7 -11
- package/dist/testing/standard.d.ts +1 -1
- package/dist/testing/stubs.d.ts +25 -0
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +43 -2
- package/dist/testing/surface_invariants.d.ts +2 -2
- package/dist/testing/ws_round_trip.d.ts +12 -13
- package/dist/testing/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/ws_round_trip.js +19 -11
- package/dist/ui/AdminAccounts.svelte +23 -20
- package/dist/ui/AdminOverview.svelte +15 -13
- package/dist/ui/AdminOverview.svelte.d.ts.map +1 -1
- package/dist/ui/{AdminPermitHistory.svelte → AdminRoleGrantHistory.svelte} +12 -12
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts +4 -0
- package/dist/ui/AdminRoleGrantHistory.svelte.d.ts.map +1 -0
- package/dist/ui/BootstrapForm.svelte +1 -1
- package/dist/ui/CLAUDE.md +60 -60
- package/dist/ui/{PermitOfferForm.svelte → RoleGrantOfferForm.svelte} +27 -26
- package/dist/ui/{PermitOfferForm.svelte.d.ts → RoleGrantOfferForm.svelte.d.ts} +7 -7
- package/dist/ui/RoleGrantOfferForm.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferHistory.svelte → RoleGrantOfferHistory.svelte} +12 -12
- package/dist/ui/{PermitOfferHistory.svelte.d.ts → RoleGrantOfferHistory.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferHistory.svelte.d.ts.map +1 -0
- package/dist/ui/{PermitOfferInbox.svelte → RoleGrantOfferInbox.svelte} +14 -14
- package/dist/ui/{PermitOfferInbox.svelte.d.ts → RoleGrantOfferInbox.svelte.d.ts} +4 -4
- package/dist/ui/RoleGrantOfferInbox.svelte.d.ts.map +1 -0
- package/dist/ui/SignupForm.svelte +1 -1
- package/dist/ui/SurfaceExplorer.svelte +35 -15
- package/dist/ui/SurfaceExplorer.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.d.ts +2 -3
- package/dist/ui/account_sessions_state.svelte.d.ts.map +1 -1
- package/dist/ui/account_sessions_state.svelte.js +2 -3
- package/dist/ui/admin_accounts_state.svelte.d.ts +18 -18
- package/dist/ui/admin_accounts_state.svelte.d.ts.map +1 -1
- package/dist/ui/admin_accounts_state.svelte.js +16 -16
- package/dist/ui/admin_rpc_adapters.d.ts +20 -20
- package/dist/ui/admin_rpc_adapters.d.ts.map +1 -1
- package/dist/ui/admin_rpc_adapters.js +17 -17
- package/dist/ui/admin_sessions_state.svelte.d.ts +2 -2
- package/dist/ui/admin_sessions_state.svelte.js +2 -2
- package/dist/ui/audit_log_state.svelte.d.ts +7 -7
- package/dist/ui/audit_log_state.svelte.d.ts.map +1 -1
- package/dist/ui/audit_log_state.svelte.js +6 -6
- package/dist/ui/auth_state.svelte.d.ts +3 -3
- package/dist/ui/auth_state.svelte.d.ts.map +1 -1
- package/dist/ui/auth_state.svelte.js +6 -6
- package/dist/ui/format_scope.d.ts +2 -2
- package/dist/ui/format_scope.js +2 -2
- package/dist/ui/{permit_offers_state.svelte.d.ts → role_grant_offers_state.svelte.d.ts} +30 -30
- package/dist/ui/role_grant_offers_state.svelte.d.ts.map +1 -0
- package/dist/ui/{permit_offers_state.svelte.js → role_grant_offers_state.svelte.js} +18 -18
- package/dist/ui/ui_format.js +2 -2
- package/package.json +3 -3
- package/dist/auth/permit_offer_action_specs.d.ts.map +0 -1
- package/dist/auth/permit_offer_action_specs.js +0 -258
- package/dist/auth/permit_offer_actions.d.ts +0 -110
- package/dist/auth/permit_offer_actions.d.ts.map +0 -1
- package/dist/auth/permit_offer_notifications.d.ts.map +0 -1
- package/dist/auth/permit_offer_notifications.js +0 -182
- package/dist/auth/permit_offer_queries.d.ts.map +0 -1
- package/dist/auth/permit_offer_schema.d.ts +0 -125
- package/dist/auth/permit_offer_schema.d.ts.map +0 -1
- package/dist/auth/permit_queries.d.ts +0 -222
- package/dist/auth/permit_queries.d.ts.map +0 -1
- package/dist/auth/permit_queries.js +0 -305
- package/dist/auth/require_keeper.d.ts +0 -20
- package/dist/auth/require_keeper.d.ts.map +0 -1
- package/dist/auth/require_keeper.js +0 -35
- package/dist/auth/route_guards.d.ts +0 -27
- package/dist/auth/route_guards.d.ts.map +0 -1
- package/dist/auth/route_guards.js +0 -38
- package/dist/auth/session_lifecycle.d.ts +0 -37
- package/dist/auth/session_lifecycle.d.ts.map +0 -1
- package/dist/auth/session_lifecycle.js +0 -29
- package/dist/ui/AdminPermitHistory.svelte.d.ts +0 -4
- package/dist/ui/AdminPermitHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferForm.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferHistory.svelte.d.ts.map +0 -1
- package/dist/ui/PermitOfferInbox.svelte.d.ts.map +0 -1
- package/dist/ui/permit_offers_state.svelte.d.ts.map +0 -1
|
@@ -26,6 +26,7 @@ import { RateLimiter } from '../rate_limiter.js';
|
|
|
26
26
|
import { run_migrations } from '../db/migrate.js';
|
|
27
27
|
import { ErrorCoverageCollector, assert_error_coverage, DEFAULT_INTEGRATION_ERROR_COVERAGE, } from './error_coverage.js';
|
|
28
28
|
import { ApiError, ERROR_FORBIDDEN_ORIGIN } from '../http/error_schemas.js';
|
|
29
|
+
import { is_public_auth } from '../http/auth_shape.js';
|
|
29
30
|
import { account_verify_action_spec, account_session_list_action_spec, account_session_revoke_action_spec, account_session_revoke_all_action_spec, account_token_create_action_spec, account_token_list_action_spec, account_token_revoke_action_spec, } from '../auth/account_action_specs.js';
|
|
30
31
|
import { invite_create_action_spec } from '../auth/admin_action_specs.js';
|
|
31
32
|
/**
|
|
@@ -93,7 +94,7 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
93
94
|
// dilute the coverage percentage; admin-role routes are scoped
|
|
94
95
|
// to the admin suite instead.
|
|
95
96
|
const auth_routes = captured_route_specs.filter((s) => {
|
|
96
|
-
if (s.auth.
|
|
97
|
+
if (s.auth.roles?.includes('admin') ?? false)
|
|
97
98
|
return false;
|
|
98
99
|
const rest_suffixes = ['/login', '/logout', '/password', '/signup', '/bootstrap'];
|
|
99
100
|
if (rest_suffixes.some((suffix) => s.path.endsWith(suffix)))
|
|
@@ -645,7 +646,7 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
645
646
|
const test_app = await create_test_app(build_test_app_options(options, get_db()));
|
|
646
647
|
// admin routes are optional in the base suite — admin-specific coverage
|
|
647
648
|
// lives in describe_standard_admin_integration_tests
|
|
648
|
-
const admin_route = test_app.route_specs.find((s) => s.auth.
|
|
649
|
+
const admin_route = test_app.route_specs.find((s) => s.auth.roles?.includes('admin') ?? false);
|
|
649
650
|
if (!admin_route)
|
|
650
651
|
return;
|
|
651
652
|
const res = await test_app.app.request(admin_route.path, {
|
|
@@ -788,11 +789,9 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
788
789
|
// --- 9. Response body validation ---
|
|
789
790
|
describe('response body validation', () => {
|
|
790
791
|
// `assert_response_matches_spec` validates REST `RouteSpec` outputs.
|
|
791
|
-
//
|
|
792
|
-
// /
|
|
793
|
-
//
|
|
794
|
-
// /password) against their declared schemas. RPC output validation is
|
|
795
|
-
// covered by `describe_rpc_round_trip_tests`.
|
|
792
|
+
// Session/token CRUD lives on the RPC surface; only /login, /logout,
|
|
793
|
+
// /password remain as REST routes whose responses we exercise here.
|
|
794
|
+
// RPC output validation is covered by `describe_rpc_round_trip_tests`.
|
|
796
795
|
test('POST /login 401 response matches declared error schema', async () => {
|
|
797
796
|
const test_app = await create_test_app(build_test_app_options(options, get_db()));
|
|
798
797
|
const login_route = find_auth_route(test_app.route_specs, '/login', 'POST');
|
|
@@ -1091,12 +1090,12 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
1091
1090
|
describe('signup invite edge cases', () => {
|
|
1092
1091
|
test('signup with non-matching email cannot claim another email invite', async () => {
|
|
1093
1092
|
const test_app = await create_test_app(build_test_app_options(options, get_db()));
|
|
1094
|
-
const signup_route = test_app.route_specs.find((s) => s.method === 'POST' && s.path.endsWith('/signup') && s.auth
|
|
1093
|
+
const signup_route = test_app.route_specs.find((s) => s.method === 'POST' && s.path.endsWith('/signup') && is_public_auth(s.auth));
|
|
1095
1094
|
if (!signup_route)
|
|
1096
1095
|
return; // signup is optional
|
|
1097
|
-
// `invite_create`
|
|
1098
|
-
//
|
|
1099
|
-
//
|
|
1096
|
+
// `invite_create` lives on the RPC surface; consumers that don't
|
|
1097
|
+
// wire admin RPC actions can't exercise invites — skip the test
|
|
1098
|
+
// rather than fail.
|
|
1100
1099
|
if (!find_rpc_action(rpc_endpoints_for_setup, invite_create_action_spec.method))
|
|
1101
1100
|
return;
|
|
1102
1101
|
// Create an admin to manage invites
|
|
@@ -1137,11 +1136,11 @@ export const describe_standard_integration_tests = (options) => {
|
|
|
1137
1136
|
test('no-invite and conflict failure responses are structurally identical', async () => {
|
|
1138
1137
|
const test_app = await create_test_app(build_test_app_options(options, get_db()));
|
|
1139
1138
|
// Find signup route (POST ending in /signup, public)
|
|
1140
|
-
const signup_route = test_app.route_specs.find((s) => s.method === 'POST' && s.path.endsWith('/signup') && s.auth
|
|
1139
|
+
const signup_route = test_app.route_specs.find((s) => s.method === 'POST' && s.path.endsWith('/signup') && is_public_auth(s.auth));
|
|
1141
1140
|
if (!signup_route)
|
|
1142
1141
|
return; // signup is optional
|
|
1143
|
-
// `invite_create`
|
|
1144
|
-
//
|
|
1142
|
+
// `invite_create` lives on the RPC surface; consumers that don't
|
|
1143
|
+
// wire admin RPC actions can't exercise invites.
|
|
1145
1144
|
if (!find_rpc_action(rpc_endpoints_for_setup, invite_create_action_spec.method))
|
|
1146
1145
|
return;
|
|
1147
1146
|
// We need admin access — create an admin account
|
|
@@ -12,10 +12,10 @@ import type { TestApp, TestAccount } from './app_server.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export declare const find_route_spec: (specs: Array<RouteSpec>, method: string, path: string) => RouteSpec | undefined;
|
|
14
14
|
/**
|
|
15
|
-
* REST auth route suffixes
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
15
|
+
* REST auth route suffixes on the account/bootstrap surface — the only
|
|
16
|
+
* routes still REST. `find_auth_route` rejects any other suffix at runtime;
|
|
17
|
+
* session/token CRUD, admin operations, and role_grant flows live on the RPC
|
|
18
|
+
* surface and should be reached via `rpc_call`.
|
|
19
19
|
*/
|
|
20
20
|
export declare const REST_AUTH_ROUTE_SUFFIXES: readonly ["/login", "/logout", "/password", "/verify", "/signup", "/bootstrap"];
|
|
21
21
|
export type RestAuthRouteSuffix = (typeof REST_AUTH_ROUTE_SUFFIXES)[number];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"integration_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/integration_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,uBAAuB,CAAC;
|
|
1
|
+
{"version":3,"file":"integration_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/integration_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAU7B,OAAO,KAAK,EAAC,SAAS,EAAE,WAAW,EAAC,MAAM,uBAAuB,CAAC;AAGlE,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,oBAAoB,CAAC;AAChD,OAAO,EAA8B,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE3F,OAAO,KAAK,EAAC,OAAO,EAAE,WAAW,EAAC,MAAM,iBAAiB,CAAC;AAE1D;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAC3B,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,QAAQ,MAAM,EACd,MAAM,MAAM,KACV,SAAS,GAAG,SAad,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,iFAO3B,CAAC;AACX,MAAM,MAAM,mBAAmB,GAAG,CAAC,OAAO,wBAAwB,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,GAC3B,OAAO,KAAK,CAAC,SAAS,CAAC,EACvB,QAAQ,mBAAmB,EAC3B,QAAQ,WAAW,KACjB,SAAS,GAAG,SAOd,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,GACxC,aAAa,KAAK,CAAC,SAAS,CAAC,EAC7B,QAAQ,MAAM,EACd,MAAM,MAAM,EACZ,UAAU,QAAQ,KAChB,OAAO,CAAC,IAAI,CAmDd,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,0BAA0B,GACtC,SAAS,OAAO,EAChB,iBAAiB,cAAc,CAAC,MAAM,CAAC,KACrC,OAAO,CAAC,MAAM,CAGhB,CAAC;AAgCF;;;;;;;GAOG;AACH,eAAO,MAAM,2BAA2B,GAAI,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,KAAK,CAAC,MAAM,CAQvF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,4BAA4B,GAAI,MAAM,OAAO,EAAE,SAAS,MAAM,KAAG,IAoB7E,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,oCAAoC,GAChD,UAAU,QAAQ,EAClB,MAAM;IAAC,WAAW,EAAE,MAAM,CAAA;CAAC,KACzB,IAUF,CAAC;AAIF,oEAAoE;AACpE,eAAO,MAAM,yBAAyB,EAAE,aAAa,CAAC,MAAM,CAAmC,CAAC;AAEhG,0EAA0E;AAC1E,eAAO,MAAM,0BAA0B,EAAE,aAAa,CAAC,MAAM,CAAgC,CAAC;AAE9F;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,GAAI,OAAO,OAAO,KAAG,GAAG,CAAC,MAAM,CAetE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,kCAAkC,GAC9C,MAAM,OAAO,EACb,WAAW,aAAa,CAAC,MAAM,CAAC,EAChC,SAAS,MAAM,KACb,IAKF,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,iBAAiB,GAC7B,MAAM,SAAS,EACf,UAAU,OAAO,EACjB,gBAAgB,WAAW,EAC3B,eAAe,WAAW,KACxB,MAAM,CAAC,MAAM,EAAE,MAAM,CAevB,CAAC"}
|
|
@@ -6,6 +6,7 @@ import './assert_dev_env.js';
|
|
|
6
6
|
*/
|
|
7
7
|
import { assert } from 'vitest';
|
|
8
8
|
import { is_null_schema, merge_error_schemas } from '../http/schema_helpers.js';
|
|
9
|
+
import { is_public_auth } from '../http/auth_shape.js';
|
|
9
10
|
import { create_session_cookie_value } from '../auth/session_cookie.js';
|
|
10
11
|
import { ROLE_ADMIN } from '../auth/role_schema.js';
|
|
11
12
|
/**
|
|
@@ -32,10 +33,10 @@ export const find_route_spec = (specs, method, path) => {
|
|
|
32
33
|
});
|
|
33
34
|
};
|
|
34
35
|
/**
|
|
35
|
-
* REST auth route suffixes
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
36
|
+
* REST auth route suffixes on the account/bootstrap surface — the only
|
|
37
|
+
* routes still REST. `find_auth_route` rejects any other suffix at runtime;
|
|
38
|
+
* session/token CRUD, admin operations, and role_grant flows live on the RPC
|
|
39
|
+
* surface and should be reached via `rpc_call`.
|
|
39
40
|
*/
|
|
40
41
|
export const REST_AUTH_ROUTE_SUFFIXES = [
|
|
41
42
|
'/login',
|
|
@@ -133,9 +134,9 @@ export const create_expired_test_cookie = async (keyring, session_options) => {
|
|
|
133
134
|
const KNOWN_SAFE_ERROR_FIELDS = new Set([
|
|
134
135
|
'error',
|
|
135
136
|
'issues',
|
|
136
|
-
'
|
|
137
|
+
'required_roles',
|
|
138
|
+
'required_credential_types',
|
|
137
139
|
'retry_after',
|
|
138
|
-
'credential_type',
|
|
139
140
|
'has_references',
|
|
140
141
|
'ok',
|
|
141
142
|
]);
|
|
@@ -254,17 +255,18 @@ export const assert_no_sensitive_fields_in_json = (body, blocklist, context) =>
|
|
|
254
255
|
* - `keeper` — the test app's daemon token
|
|
255
256
|
*/
|
|
256
257
|
export const pick_auth_headers = (spec, test_app, authed_account, admin_account) => {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
return
|
|
267
|
-
|
|
268
|
-
|
|
258
|
+
const { auth } = spec;
|
|
259
|
+
if (is_public_auth(auth)) {
|
|
260
|
+
return { host: 'localhost', origin: 'http://localhost:5173' };
|
|
261
|
+
}
|
|
262
|
+
if (auth.credential_types?.includes('daemon_token')) {
|
|
263
|
+
return test_app.create_daemon_token_headers();
|
|
264
|
+
}
|
|
265
|
+
if (auth.roles?.length) {
|
|
266
|
+
if (auth.roles.includes(ROLE_ADMIN)) {
|
|
267
|
+
return admin_account.create_session_headers();
|
|
268
|
+
}
|
|
269
|
+
return test_app.create_session_headers();
|
|
269
270
|
}
|
|
271
|
+
return authed_account.create_session_headers();
|
|
270
272
|
};
|
|
@@ -27,8 +27,8 @@ export interface BearerAuthTestOptions {
|
|
|
27
27
|
mock_find_by_id_result?: unknown;
|
|
28
28
|
/** What `query_actor_by_id()` returns. */
|
|
29
29
|
mock_find_actor_by_id_result?: unknown;
|
|
30
|
-
/** What `
|
|
31
|
-
|
|
30
|
+
/** What `query_role_grant_find_active_for_actor()` returns. */
|
|
31
|
+
mock_role_grants_result?: unknown;
|
|
32
32
|
/** Expected HTTP status, or `'next'` if the middleware should call `next()`. */
|
|
33
33
|
expected_status: number | 'next';
|
|
34
34
|
/** Expected `error` field in JSON response body. */
|
|
@@ -63,12 +63,12 @@ export interface BearerAuthMocks {
|
|
|
63
63
|
* Create mock dependencies for `create_bearer_auth_middleware`, configured per test case.
|
|
64
64
|
*
|
|
65
65
|
* Configures the module-level mocks for `query_validate_api_token`,
|
|
66
|
-
* `query_account_by_id`, `query_actor_by_id`, and `
|
|
66
|
+
* `query_account_by_id`, `query_actor_by_id`, and `query_role_grant_find_active_for_actor`
|
|
67
67
|
* so each test case controls return values independently.
|
|
68
68
|
*
|
|
69
69
|
* @returns mocks bundle with spy references
|
|
70
70
|
* @mutates module-level `vi.mock` registrations for `api_token_queries`,
|
|
71
|
-
* `account_queries`, and `
|
|
71
|
+
* `account_queries`, and `role_grant_queries` — each call resets and re-binds
|
|
72
72
|
* the four spies, so cases run in sequence without bleeding state.
|
|
73
73
|
*/
|
|
74
74
|
export declare const create_bearer_auth_mocks: (tc: BearerAuthTestOptions) => BearerAuthMocks;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAE7B;;;;;;;;GAQG;AAEH,OAAO,EAAC,EAAE,EAAyB,MAAM,QAAQ,CAAC;AAClD,OAAO,EAAC,IAAI,EAAC,MAAM,MAAM,CAAC;AAC1B,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAc3B,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAsB,KAAK,cAAc,EAAC,MAAM,4BAA4B,CAAC;AA2BpF,gEAAgE;AAChE,MAAM,WAAW,qBAAqB;IACrC,wBAAwB;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,uBAAuB;IACvB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,oEAAoE;IACpE,WAAW,CAAC,EAAE,cAAc,CAAC;IAC7B,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,4CAA4C;IAC5C,sBAAsB,CAAC,EAAE,OAAO,CAAC;IACjC,0CAA0C;IAC1C,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC
|
|
1
|
+
{"version":3,"file":"middleware.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/middleware.ts"],"names":[],"mappings":"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"}
|
|
@@ -14,7 +14,7 @@ import { Logger } from '@fuzdev/fuz_util/log.js';
|
|
|
14
14
|
import { create_bearer_auth_middleware } from '../auth/bearer_auth.js';
|
|
15
15
|
import { query_validate_api_token } from '../auth/api_token_queries.js';
|
|
16
16
|
import { query_account_by_id, query_actor_by_id, query_actors_by_account, } from '../auth/account_queries.js';
|
|
17
|
-
import {
|
|
17
|
+
import { query_role_grant_find_active_for_actor } from '../auth/role_grant_queries.js';
|
|
18
18
|
import { create_proxy_middleware, get_client_ip } from '../http/proxy.js';
|
|
19
19
|
import { verify_request_source, parse_allowed_origins } from '../http/origin.js';
|
|
20
20
|
import { REQUEST_CONTEXT_KEY } from '../auth/request_context.js';
|
|
@@ -30,8 +30,8 @@ vi.mock('../auth/account_queries.js', () => ({
|
|
|
30
30
|
query_actor_by_id: vi.fn(),
|
|
31
31
|
query_actors_by_account: vi.fn(),
|
|
32
32
|
}));
|
|
33
|
-
vi.mock('../auth/
|
|
34
|
-
|
|
33
|
+
vi.mock('../auth/role_grant_queries.js', () => ({
|
|
34
|
+
query_role_grant_find_active_for_actor: vi.fn(),
|
|
35
35
|
}));
|
|
36
36
|
/** Stub `QueryDeps` for bearer auth tests (no real DB needed). */
|
|
37
37
|
const STUB_DEPS = { db: {} };
|
|
@@ -39,12 +39,12 @@ const STUB_DEPS = { db: {} };
|
|
|
39
39
|
* Create mock dependencies for `create_bearer_auth_middleware`, configured per test case.
|
|
40
40
|
*
|
|
41
41
|
* Configures the module-level mocks for `query_validate_api_token`,
|
|
42
|
-
* `query_account_by_id`, `query_actor_by_id`, and `
|
|
42
|
+
* `query_account_by_id`, `query_actor_by_id`, and `query_role_grant_find_active_for_actor`
|
|
43
43
|
* so each test case controls return values independently.
|
|
44
44
|
*
|
|
45
45
|
* @returns mocks bundle with spy references
|
|
46
46
|
* @mutates module-level `vi.mock` registrations for `api_token_queries`,
|
|
47
|
-
* `account_queries`, and `
|
|
47
|
+
* `account_queries`, and `role_grant_queries` — each call resets and re-binds
|
|
48
48
|
* the four spies, so cases run in sequence without bleeding state.
|
|
49
49
|
*/
|
|
50
50
|
export const create_bearer_auth_mocks = (tc) => {
|
|
@@ -52,7 +52,7 @@ export const create_bearer_auth_mocks = (tc) => {
|
|
|
52
52
|
const mock_find_by_id = vi.mocked(query_account_by_id);
|
|
53
53
|
const mock_find_actor_by_id = vi.mocked(query_actor_by_id);
|
|
54
54
|
const mock_find_actors_by_account = vi.mocked(query_actors_by_account);
|
|
55
|
-
const mock_find_active_for_actor = vi.mocked(
|
|
55
|
+
const mock_find_active_for_actor = vi.mocked(query_role_grant_find_active_for_actor);
|
|
56
56
|
mock_validate
|
|
57
57
|
.mockReset()
|
|
58
58
|
.mockImplementation(() => Promise.resolve(tc.mock_validate_result));
|
|
@@ -73,7 +73,7 @@ export const create_bearer_auth_mocks = (tc) => {
|
|
|
73
73
|
});
|
|
74
74
|
mock_find_active_for_actor
|
|
75
75
|
.mockReset()
|
|
76
|
-
.mockImplementation(() => Promise.resolve(tc.
|
|
76
|
+
.mockImplementation(() => Promise.resolve(tc.mock_role_grants_result ?? []));
|
|
77
77
|
return {
|
|
78
78
|
mock_validate,
|
|
79
79
|
mock_find_by_id,
|
|
@@ -97,9 +97,10 @@ export const create_bearer_auth_test_app = (tc, ip_rate_limiter = null) => {
|
|
|
97
97
|
// inject pre-existing session identity if the test case specifies one.
|
|
98
98
|
// `pre_context` simulates the session middleware having authenticated
|
|
99
99
|
// the caller — sets `ACCOUNT_ID_KEY` (the account-grain identity bearer
|
|
100
|
-
// auth checks) and
|
|
101
|
-
//
|
|
102
|
-
//
|
|
100
|
+
// auth checks) and pre-populates `REQUEST_CONTEXT_KEY` as a test
|
|
101
|
+
// escape-hatch (production bearer middleware is account-only and never
|
|
102
|
+
// sets this key — see `auth/CLAUDE.md` Production-middleware invariant)
|
|
103
|
+
// so consumer expectations on the full context shape stay testable.
|
|
103
104
|
if (tc.pre_context) {
|
|
104
105
|
const pre_context = tc.pre_context;
|
|
105
106
|
app.use('*', async (c, next) => {
|
|
@@ -210,7 +211,7 @@ export const create_test_middleware_stack_app = (options) => {
|
|
|
210
211
|
const mock_find_by_id = vi.mocked(query_account_by_id);
|
|
211
212
|
const mock_find_actor_by_id = vi.mocked(query_actor_by_id);
|
|
212
213
|
const mock_find_actors_by_account = vi.mocked(query_actors_by_account);
|
|
213
|
-
const mock_find_active_for_actor = vi.mocked(
|
|
214
|
+
const mock_find_active_for_actor = vi.mocked(query_role_grant_find_active_for_actor);
|
|
214
215
|
mock_validate.mockReset().mockImplementation(() => Promise.resolve(undefined));
|
|
215
216
|
mock_find_by_id.mockReset().mockImplementation(() => Promise.resolve(undefined));
|
|
216
217
|
mock_find_actor_by_id.mockReset().mockImplementation(() => Promise.resolve(undefined));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc_attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAA6C,cAAc,EAAC,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc_attack_surface.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_attack_surface.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAkB7B,OAAO,KAAK,EAA6C,cAAc,EAAC,MAAM,oBAAoB,CAAC;AAqBnG,uDAAuD;AACvD,MAAM,WAAW,uBAAuB;IACvC,+FAA+F;IAC/F,KAAK,EAAE,MAAM,cAAc,CAAC;IAC5B,yDAAyD;IACzD,KAAK,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;CACrB;AA0dD;;;;;;;;;GASG;AACH,eAAO,MAAM,iCAAiC,GAAI,SAAS,uBAAuB,KAAG,IAOpF,CAAC"}
|
|
@@ -13,18 +13,23 @@ import './assert_dev_env.js';
|
|
|
13
13
|
* @module
|
|
14
14
|
*/
|
|
15
15
|
import { test, assert, describe } from 'vitest';
|
|
16
|
+
import { is_keeper_auth, is_public_auth, is_role_auth } from '../http/auth_shape.js';
|
|
16
17
|
import { JSONRPC_ERROR_CODES } from '../http/jsonrpc_errors.js';
|
|
17
18
|
import { create_auth_test_apps, create_test_app_from_specs, create_test_request_context, select_auth_app, } from './auth_apps.js';
|
|
18
19
|
import { generate_input_test_cases } from './adversarial_input.js';
|
|
20
|
+
import { generate_valid_body } from './schema_generators.js';
|
|
19
21
|
import { ERROR_INVALID_JSON_BODY } from '../http/error_schemas.js';
|
|
20
22
|
import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response, } from './rpc_helpers.js';
|
|
21
23
|
// --- Helpers ---
|
|
22
24
|
/** Filter RPC methods that require any form of authentication. */
|
|
23
|
-
const filter_protected_rpc_methods = (endpoint) => endpoint.methods.filter((m) => m.auth
|
|
24
|
-
/** Filter RPC methods that
|
|
25
|
-
const filter_role_rpc_methods = (endpoint) => endpoint.methods.filter((m) => m.auth
|
|
26
|
-
/**
|
|
27
|
-
|
|
25
|
+
const filter_protected_rpc_methods = (endpoint) => endpoint.methods.filter((m) => !is_public_auth(m.auth));
|
|
26
|
+
/** Filter RPC methods that declare a role gate (`auth.roles?.length`). */
|
|
27
|
+
const filter_role_rpc_methods = (endpoint) => endpoint.methods.filter((m) => is_role_auth(m.auth));
|
|
28
|
+
/**
|
|
29
|
+
* Filter RPC methods that require daemon-token credentials. Today this
|
|
30
|
+
* is the keeper bucket; future credential gates will widen the filter.
|
|
31
|
+
*/
|
|
32
|
+
const filter_keeper_rpc_methods = (endpoint) => endpoint.methods.filter((m) => is_keeper_auth(m.auth));
|
|
28
33
|
/** Find the `RpcAction` source spec for a surface method. */
|
|
29
34
|
const find_rpc_action = (rpc_endpoint_specs, endpoint_path, method_name) => {
|
|
30
35
|
const ep = rpc_endpoint_specs.find((e) => e.path === endpoint_path);
|
|
@@ -47,7 +52,7 @@ const find_rpc_action = (rpc_endpoint_specs, endpoint_path, method_name) => {
|
|
|
47
52
|
*/
|
|
48
53
|
const describe_rpc_auth = (options) => {
|
|
49
54
|
const { build, roles } = options;
|
|
50
|
-
const { surface, route_specs } = build();
|
|
55
|
+
const { surface, route_specs, rpc_endpoints: rpc_endpoint_specs } = build();
|
|
51
56
|
if (surface.rpc_endpoints.length === 0)
|
|
52
57
|
return;
|
|
53
58
|
const apps = create_auth_test_apps(route_specs, roles);
|
|
@@ -71,13 +76,18 @@ const describe_rpc_auth = (options) => {
|
|
|
71
76
|
if (role_methods.length > 0) {
|
|
72
77
|
describe('wrong role → forbidden', () => {
|
|
73
78
|
for (const method of role_methods) {
|
|
74
|
-
const
|
|
79
|
+
const required_roles = method.auth.roles ?? [];
|
|
80
|
+
const wrong_roles = roles.filter((r) => !required_roles.includes(r));
|
|
75
81
|
for (const wrong_role of wrong_roles) {
|
|
76
|
-
test(`${method.name} (${wrong_role} instead of ${
|
|
82
|
+
test(`${method.name} (${wrong_role} instead of ${required_roles.join('|')})`, async () => {
|
|
77
83
|
const app = apps.by_role.get(wrong_role);
|
|
78
84
|
if (!app)
|
|
79
85
|
throw new Error(`No test app for role '${wrong_role}'`);
|
|
80
|
-
|
|
86
|
+
// Send valid params so we trip the role gate (403), not input
|
|
87
|
+
// validation (400). Dispatcher ordering: 401 → 400 → 403.
|
|
88
|
+
const action = find_rpc_action(rpc_endpoint_specs, endpoint.path, method.name);
|
|
89
|
+
const params = action ? generate_valid_body(action.spec.input) : undefined;
|
|
90
|
+
const res = await app.request(endpoint.path, create_rpc_post_init(method.name, params));
|
|
81
91
|
assert.strictEqual(res.status, 403, `${method.name} should return 403`);
|
|
82
92
|
const body = await res.json();
|
|
83
93
|
assert_jsonrpc_error_response(body, JSONRPC_ERROR_CODES.forbidden);
|
|
@@ -87,8 +97,12 @@ const describe_rpc_auth = (options) => {
|
|
|
87
97
|
});
|
|
88
98
|
describe('authenticated without role → forbidden', () => {
|
|
89
99
|
for (const method of role_methods) {
|
|
90
|
-
test(`${method.name} (${method.auth.
|
|
91
|
-
|
|
100
|
+
test(`${method.name} (${(method.auth.roles ?? []).join('|')})`, async () => {
|
|
101
|
+
// Send valid params so we trip the role gate (403), not input
|
|
102
|
+
// validation (400).
|
|
103
|
+
const action = find_rpc_action(rpc_endpoint_specs, endpoint.path, method.name);
|
|
104
|
+
const params = action ? generate_valid_body(action.spec.input) : undefined;
|
|
105
|
+
const res = await apps.authed.request(endpoint.path, create_rpc_post_init(method.name, params));
|
|
92
106
|
assert.strictEqual(res.status, 403, `${method.name} should return 403`);
|
|
93
107
|
const body = await res.json();
|
|
94
108
|
assert_jsonrpc_error_response(body, JSONRPC_ERROR_CODES.forbidden);
|
|
@@ -102,14 +116,16 @@ const describe_rpc_auth = (options) => {
|
|
|
102
116
|
const session_app = create_test_app_from_specs(route_specs, create_test_request_context('keeper'), 'session');
|
|
103
117
|
const api_token_app = create_test_app_from_specs(route_specs, create_test_request_context('keeper'), 'api_token');
|
|
104
118
|
for (const method of keeper_methods) {
|
|
119
|
+
const action = find_rpc_action(rpc_endpoint_specs, endpoint.path, method.name);
|
|
120
|
+
const valid_params = action ? generate_valid_body(action.spec.input) : undefined;
|
|
105
121
|
test(`${method.name} rejects session credential`, async () => {
|
|
106
|
-
const res = await session_app.request(endpoint.path, create_rpc_post_init(method.name));
|
|
122
|
+
const res = await session_app.request(endpoint.path, create_rpc_post_init(method.name, valid_params));
|
|
107
123
|
assert.strictEqual(res.status, 403, `${method.name} should reject session credential`);
|
|
108
124
|
const body = await res.json();
|
|
109
125
|
assert_jsonrpc_error_response(body, JSONRPC_ERROR_CODES.forbidden);
|
|
110
126
|
});
|
|
111
127
|
test(`${method.name} rejects api_token credential`, async () => {
|
|
112
|
-
const res = await api_token_app.request(endpoint.path, create_rpc_post_init(method.name));
|
|
128
|
+
const res = await api_token_app.request(endpoint.path, create_rpc_post_init(method.name, valid_params));
|
|
113
129
|
assert.strictEqual(res.status, 403, `${method.name} should reject api_token credential`);
|
|
114
130
|
const body = await res.json();
|
|
115
131
|
assert_jsonrpc_error_response(body, JSONRPC_ERROR_CODES.forbidden);
|
|
@@ -279,7 +295,7 @@ const describe_rpc_adversarial_envelopes = (options) => {
|
|
|
279
295
|
return;
|
|
280
296
|
// valid JSON but not an object — hits dispatcher's params validation
|
|
281
297
|
const res = await apps.public.request(`${endpoint.path}?method=${read_method.name}&id=test¶ms=42`);
|
|
282
|
-
// should reject: either invalid_params (
|
|
298
|
+
// should reject: either invalid_params (validation) or auth error
|
|
283
299
|
assert.ok(res.status >= 400, `expected error status for non-object params, got ${res.status}`);
|
|
284
300
|
const body = await res.json();
|
|
285
301
|
assert_jsonrpc_error_response(body);
|
|
@@ -362,16 +378,16 @@ const describe_rpc_adversarial_params = (options) => {
|
|
|
362
378
|
// --- Helpers (formatting) ---
|
|
363
379
|
/** Format a `RouteAuth` as a human-readable label. */
|
|
364
380
|
const format_auth = (auth) => {
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
381
|
+
if (is_public_auth(auth))
|
|
382
|
+
return 'public';
|
|
383
|
+
const parts = [];
|
|
384
|
+
parts.push(`account:${auth.account}`);
|
|
385
|
+
parts.push(`actor:${auth.actor}`);
|
|
386
|
+
if (auth.roles?.length)
|
|
387
|
+
parts.push(`roles:${auth.roles.join('|')}`);
|
|
388
|
+
if (auth.credential_types?.length)
|
|
389
|
+
parts.push(`creds:${auth.credential_types.join('|')}`);
|
|
390
|
+
return parts.join(' ');
|
|
375
391
|
};
|
|
376
392
|
// --- Public API ---
|
|
377
393
|
/**
|
|
@@ -27,7 +27,7 @@ export interface RpcRoundTripTestOptions {
|
|
|
27
27
|
app_options?: SuiteAppOptions;
|
|
28
28
|
/** Database factories to run tests against. Default: pglite only. */
|
|
29
29
|
db_factories?: Array<DbFactory>;
|
|
30
|
-
/** Methods to skip, by name (e.g., `'
|
|
30
|
+
/** Methods to skip, by name (e.g., `'zap_plan'`). */
|
|
31
31
|
skip_methods?: Array<string>;
|
|
32
32
|
/** Override generated params for specific methods (method name → params). */
|
|
33
33
|
input_overrides?: Map<string, Record<string, unknown>>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rpc_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAEN,KAAK,eAAe,EAGpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"rpc_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/rpc_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAe7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAE9D,OAAO,EAEN,KAAK,eAAe,EAGpB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AAO9D,OAAO,EAMN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAE1B,mDAAmD;AACnD,MAAM,WAAW,uBAAuB;IACvC,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,wDAAwD;IACxD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE;;;;;;;;;;OAUG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC,qDAAqD;IACrD,YAAY,CAAC,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;IAC7B,6EAA6E;IAC7E,eAAe,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACvD;AA2BD;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,6BAA6B,GAAI,SAAS,uBAAuB,KAAG,IA8IhF,CAAC"}
|
|
@@ -16,25 +16,26 @@ import { create_pglite_factory } from './db.js';
|
|
|
16
16
|
import { generate_valid_body } from './schema_generators.js';
|
|
17
17
|
import { run_migrations } from '../db/migrate.js';
|
|
18
18
|
import { AUTH_MIGRATION_NS } from '../auth/migrations.js';
|
|
19
|
+
import { is_public_auth } from '../http/auth_shape.js';
|
|
19
20
|
import { create_rpc_post_init, create_rpc_get_url, assert_jsonrpc_error_response, assert_jsonrpc_success_response, resolve_rpc_endpoints_for_setup, } from './rpc_helpers.js';
|
|
20
21
|
/**
|
|
21
22
|
* Pick auth headers matching an RPC method's auth requirement.
|
|
22
23
|
*/
|
|
23
24
|
const pick_rpc_auth_headers = (method, test_app, authed_account, admin_account) => {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
case 'authenticated':
|
|
28
|
-
return authed_account.create_session_headers();
|
|
29
|
-
case 'role':
|
|
30
|
-
if (method.auth.role === ROLE_ADMIN) {
|
|
31
|
-
return admin_account.create_session_headers();
|
|
32
|
-
}
|
|
33
|
-
// keeper role uses the bootstrapped account
|
|
34
|
-
return test_app.create_session_headers();
|
|
35
|
-
case 'keeper':
|
|
36
|
-
return test_app.create_daemon_token_headers();
|
|
25
|
+
const { auth } = method;
|
|
26
|
+
if (is_public_auth(auth)) {
|
|
27
|
+
return { host: 'localhost', origin: 'http://localhost:5173' };
|
|
37
28
|
}
|
|
29
|
+
if (auth.credential_types?.includes('daemon_token')) {
|
|
30
|
+
return test_app.create_daemon_token_headers();
|
|
31
|
+
}
|
|
32
|
+
if (auth.roles?.length) {
|
|
33
|
+
if (auth.roles.includes(ROLE_ADMIN)) {
|
|
34
|
+
return admin_account.create_session_headers();
|
|
35
|
+
}
|
|
36
|
+
return test_app.create_session_headers();
|
|
37
|
+
}
|
|
38
|
+
return authed_account.create_session_headers();
|
|
38
39
|
};
|
|
39
40
|
/**
|
|
40
41
|
* Run schema-driven round-trip validation for RPC endpoints.
|
|
@@ -9,7 +9,7 @@ import { type DbFactory } from './db.js';
|
|
|
9
9
|
import { type RpcEndpointsSuiteOption } from './rpc_helpers.js';
|
|
10
10
|
/** Config for a single SSE route under test. */
|
|
11
11
|
export interface SseRouteTestSpec {
|
|
12
|
-
/** Full HTTP path of the SSE endpoint (e.g., `'/api/
|
|
12
|
+
/** Full HTTP path of the SSE endpoint (e.g., `'/api/zap/subscribe'`). */
|
|
13
13
|
path: string;
|
|
14
14
|
/**
|
|
15
15
|
* Fire an event matching one of the declared `event_specs` that should
|
|
@@ -51,9 +51,8 @@ export interface SseRouteTestOptions {
|
|
|
51
51
|
on_audit_event?: (event: AuditLogEvent) => void;
|
|
52
52
|
/**
|
|
53
53
|
* RPC endpoint specs — required so the close-on-revoke assertion can
|
|
54
|
-
* dispatch `account_session_revoke_all` via RPC (
|
|
55
|
-
*
|
|
56
|
-
* migration). Hard-fails via `require_rpc_endpoint_path` on setup.
|
|
54
|
+
* dispatch `account_session_revoke_all` via RPC (there is no REST
|
|
55
|
+
* equivalent). Hard-fails via `require_rpc_endpoint_path` on setup.
|
|
57
56
|
*
|
|
58
57
|
* Accepts either an array (eager) or a factory
|
|
59
58
|
* `(ctx: AppServerContext) => Array<RpcEndpointSpec>` — the factory form
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"sse_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/sse_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAwB,KAAK,SAAS,EAAuB,MAAM,oBAAoB,CAAC;AAE/F,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AAE9D,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAM1B,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,
|
|
1
|
+
{"version":3,"file":"sse_round_trip.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/sse_round_trip.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAmB7B,OAAO,KAAK,EAAC,SAAS,EAAC,MAAM,uBAAuB,CAAC;AACrD,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAwB,KAAK,SAAS,EAAuB,MAAM,oBAAoB,CAAC;AAE/F,OAAO,KAAK,EAAC,aAAa,EAAC,MAAM,6BAA6B,CAAC;AAC/D,OAAO,EAEN,KAAK,eAAe,EACpB,KAAK,OAAO,EACZ,KAAK,WAAW,EAChB,MAAM,iBAAiB,CAAC;AACzB,OAAO,EAAwB,KAAK,SAAS,EAAC,MAAM,SAAS,CAAC;AAE9D,OAAO,EAIN,KAAK,uBAAuB,EAC5B,MAAM,kBAAkB,CAAC;AAM1B,gDAAgD;AAChD,MAAM,WAAW,gBAAgB;IAChC,yEAAyE;IACzE,IAAI,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,OAAO,EAAE,CAAC,GAAG,EAAE;QAAC,QAAQ,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,WAAW,CAAA;KAAC,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAC3E;;;OAGG;IACH,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,OAAO,CAAC;CAClC;AAED,8CAA8C;AAC9C,MAAM,WAAW,mBAAmB;IACnC,4CAA4C;IAC5C,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,qDAAqD;IACrD,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,iDAAiD;IACjD,WAAW,CAAC,EAAE,eAAe,CAAC;IAC9B,qEAAqE;IACrE,YAAY,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAChC;;;;;OAKG;IACH,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,aAAa,KAAK,IAAI,CAAC;IAChD;;;;;;;;;;;;OAYG;IACH,aAAa,EAAE,uBAAuB,CAAC;IACvC,8BAA8B;IAC9B,MAAM,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC;CAChC;AAyHD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GAAI,SAAS,mBAAmB,KAAG,IA2HvE,CAAC"}
|
|
@@ -229,18 +229,14 @@ export const describe_sse_route_tests = (options) => {
|
|
|
229
229
|
* (keeper, other roles) uses the bootstrapped keeper account.
|
|
230
230
|
*/
|
|
231
231
|
const pick_account_for_auth = (spec, test_app, authed_account, admin_account) => {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
return admin_account;
|
|
238
|
-
// keeper role — bootstrapped account is the keeper; model it as a TestAccount
|
|
239
|
-
return bootstrap_as_account(test_app);
|
|
240
|
-
case 'keeper':
|
|
241
|
-
case 'none':
|
|
242
|
-
return bootstrap_as_account(test_app);
|
|
232
|
+
const { auth } = spec;
|
|
233
|
+
if (auth.roles?.includes(ROLE_ADMIN))
|
|
234
|
+
return admin_account;
|
|
235
|
+
if (auth.account === 'required' && !auth.roles?.length && !auth.credential_types?.length) {
|
|
236
|
+
return authed_account;
|
|
243
237
|
}
|
|
238
|
+
// keeper / other-role / public — bootstrapped account
|
|
239
|
+
return bootstrap_as_account(test_app);
|
|
244
240
|
};
|
|
245
241
|
/**
|
|
246
242
|
* Treat the bootstrapped `TestApp` account as a `TestAccount` for revocation.
|
|
@@ -36,7 +36,7 @@ export interface StandardTestOptions {
|
|
|
36
36
|
/**
|
|
37
37
|
* RPC endpoint specs — required. The standard integration tests drive
|
|
38
38
|
* `account_verify`, `account_session_*`, `account_token_*` through the
|
|
39
|
-
* RPC surface (and admin tests, when wired, drive
|
|
39
|
+
* RPC surface (and admin tests, when wired, drive role_grant grant/revoke
|
|
40
40
|
* through it too).
|
|
41
41
|
*
|
|
42
42
|
* Accepts either an array (eager) or a factory
|
package/dist/testing/stubs.d.ts
CHANGED
|
@@ -3,11 +3,13 @@ import type { z } from 'zod';
|
|
|
3
3
|
import type { SessionOptions } from '../auth/session_cookie.js';
|
|
4
4
|
import type { MiddlewareSpec } from '../http/middleware_spec.js';
|
|
5
5
|
import type { AppDeps } from '../auth/deps.js';
|
|
6
|
+
import type { AuditEmitter } from '../auth/audit_emitter.js';
|
|
6
7
|
import type { AppServerContext } from '../server/app_server.js';
|
|
7
8
|
import { Db } from '../db/db.js';
|
|
8
9
|
import { type RouteSpec } from '../http/route_spec.js';
|
|
9
10
|
import { type AppSurfaceSpec, type RpcEndpointSpec } from '../http/surface.js';
|
|
10
11
|
import type { EventSpec } from '../realtime/sse.js';
|
|
12
|
+
import { type AuditLogSse } from '../realtime/sse_auth_guard.js';
|
|
11
13
|
/**
|
|
12
14
|
* Create a Proxy that throws descriptive errors on any property access or method call.
|
|
13
15
|
*
|
|
@@ -51,6 +53,29 @@ export declare const create_stub_db: () => Db;
|
|
|
51
53
|
export declare const stub_handler: () => Response;
|
|
52
54
|
/** Stub middleware that passes through. */
|
|
53
55
|
export declare const stub_mw: (_c: any, next: any) => Promise<void>;
|
|
56
|
+
/**
|
|
57
|
+
* Build a no-op `AuditEmitter` for tests that don't assert on audit fan-out.
|
|
58
|
+
*
|
|
59
|
+
* `emit` / `emit_role_grant_target` are no-ops; `emit_pool` resolves
|
|
60
|
+
* immediately; `notify` is a no-op; `on_event_chain` is a frozen empty
|
|
61
|
+
* array — pushing onto it throws at runtime, so a test that wires a
|
|
62
|
+
* listener fails loudly instead of silently never firing. Tests asserting
|
|
63
|
+
* on real audit-row persistence (or on listener fan-out) build a real
|
|
64
|
+
* emitter via `create_audit_emitter` against a stub or real DB —
|
|
65
|
+
* `create_test_app` already does this on the test backend.
|
|
66
|
+
*/
|
|
67
|
+
export declare const create_test_audit_emitter: () => AuditEmitter;
|
|
68
|
+
/**
|
|
69
|
+
* Build a no-op `AuditLogSse` for tests that wire `audit_sse` into the
|
|
70
|
+
* surface helper but don't assert on SSE fan-out or subscriber state.
|
|
71
|
+
*
|
|
72
|
+
* `subscribe` returns a no-op cleanup; `on_audit_event` is a no-op; the
|
|
73
|
+
* `registry` is a fresh `SubscriberRegistry` instance (call sites that
|
|
74
|
+
* inspect `.size` or call `.close_*` see a real registry, so writes are
|
|
75
|
+
* isolated per test). Tests that need real SSE plumbing build it via
|
|
76
|
+
* `create_audit_log_sse` against `create_test_app`.
|
|
77
|
+
*/
|
|
78
|
+
export declare const create_stub_audit_sse: () => AuditLogSse;
|
|
54
79
|
/** Stub `AppDeps` for auth surface tests — throws on any method access. */
|
|
55
80
|
export declare const stub_app_deps: AppDeps;
|
|
56
81
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAa7B,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAC/B,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAGzE,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,SAAS,
|
|
1
|
+
{"version":3,"file":"stubs.d.ts","sourceRoot":"../src/lib/","sources":["../../src/lib/testing/stubs.ts"],"names":[],"mappings":"AAAA,OAAO,qBAAqB,CAAC;AAa7B,OAAO,KAAK,EAAC,CAAC,EAAC,MAAM,KAAK,CAAC;AAE3B,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,2BAA2B,CAAC;AAC9D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,4BAA4B,CAAC;AAE/D,OAAO,KAAK,EAAC,OAAO,EAAC,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAC,YAAY,EAAC,MAAM,0BAA0B,CAAC;AAE3D,OAAO,KAAK,EAAC,gBAAgB,EAAC,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAC,EAAE,EAAC,MAAM,aAAa,CAAC;AAC/B,OAAO,EAAqB,KAAK,SAAS,EAAC,MAAM,uBAAuB,CAAC;AAGzE,OAAO,EAEN,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,MAAM,oBAAoB,CAAC;AAC5B,OAAO,KAAK,EAAC,SAAS,EAAkB,MAAM,oBAAoB,CAAC;AACnE,OAAO,EAA8B,KAAK,WAAW,EAAC,MAAM,+BAA+B,CAAC;AAM5F;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,GAAG,GAAG,EAAE,OAAO,MAAM,KAAG,CAqBtD,CAAC;AAET;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,GAAI,CAAC,GAAG,GAAG,EAAE,QAAQ,MAAM,EAAE,YAAY,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAG,CAOxF,CAAC;AAET,iEAAiE;AACjE,eAAO,MAAM,IAAI,EAAE,GAAkC,CAAC;AAEtD;;;;;;;GAOG;AACH,eAAO,MAAM,cAAc,QAAO,EAI/B,CAAC;AAEJ,gDAAgD;AAChD,eAAO,MAAM,YAAY,QAAO,QAAgC,CAAC;AAEjE,2CAA2C;AAC3C,eAAO,MAAM,OAAO,GAAU,IAAI,GAAG,EAAE,MAAM,GAAG,KAAG,OAAO,CAAC,IAAI,CAAW,CAAC;AAI3E;;;;;;;;;;GAUG;AACH,eAAO,MAAM,yBAAyB,QAAO,YAM3C,CAAC;AAEH;;;;;;;;;GASG;AACH,eAAO,MAAM,qBAAqB,QAAO,WAUxC,CAAC;AAEF,2EAA2E;AAC3E,eAAO,MAAM,aAAa,EAAE,OAS3B,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,oBAAoB,QAAO,OAStC,CAAC;AAEH,2FAA2F;AAC3F,eAAO,MAAM,0BAA0B,GAAI,UAAU;IACpD,iDAAiD;IACjD,oBAAoB,CAAC,EAAE,OAAO,CAAC;CAC/B,KAAG,KAAK,CAAC,cAAc,CAqBvB,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,8BAA8B,GAC1C,iBAAiB,cAAc,CAAC,MAAM,CAAC,KACrC,gBAqBF,CAAC;AAEF,kDAAkD;AAClD,MAAM,WAAW,+BAA+B;IAC/C,6DAA6D;IAC7D,eAAe,EAAE,cAAc,CAAC,MAAM,CAAC,CAAC;IACxC,qFAAqF;IACrF,kBAAkB,EAAE,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,SAAS,CAAC,CAAC;IAChE,oEAAoE;IACpE,UAAU,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC;IACzB,8CAA8C;IAC9C,WAAW,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IAC/B;;;;;;;;OAQG;IACH,aAAa,CAAC,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,gBAAgB,KAAK,KAAK,CAAC,eAAe,CAAC,CAAC,CAAC;IAC7F,iFAAiF;IACjF,oBAAoB,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,cAAc,CAAC,KAAK,KAAK,CAAC,cAAc,CAAC,CAAC;IAC/E,0DAA0D;IAC1D,sBAAsB,CAAC,EAAE,MAAM,CAAC;CAChC;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,+BAA+B,KACtC,cA2CF,CAAC"}
|