@fuzdev/fuz_app 0.147.0 → 0.149.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 +8 -6
- package/dist/actions/action_codegen.d.ts +15 -1
- package/dist/actions/action_codegen.d.ts.map +1 -1
- package/dist/actions/action_codegen.js +23 -2
- package/dist/actions/action_event.d.ts.map +1 -1
- package/dist/actions/action_event.js +14 -1
- package/dist/actions/perform_action.d.ts +17 -1
- package/dist/actions/perform_action.d.ts.map +1 -1
- package/dist/actions/perform_action.js +23 -3
- package/dist/auth/CLAUDE.md +3 -2
- package/dist/auth/account_action_specs.d.ts +12 -0
- package/dist/auth/account_action_specs.d.ts.map +1 -1
- package/dist/auth/account_action_specs.js +16 -2
- package/dist/auth/account_actions.d.ts.map +1 -1
- package/dist/auth/account_actions.js +7 -4
- package/dist/auth/admin_action_specs.d.ts.map +1 -1
- package/dist/auth/admin_action_specs.js +29 -8
- package/dist/auth/audit_emitter.d.ts +14 -0
- package/dist/auth/audit_emitter.d.ts.map +1 -1
- package/dist/auth/audit_emitter.js +5 -1
- package/dist/auth/cell_action_specs.d.ts +3 -1
- package/dist/auth/cell_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_action_specs.js +5 -5
- package/dist/auth/cell_actions.d.ts.map +1 -1
- package/dist/auth/cell_actions.js +2 -3
- package/dist/auth/cell_data_schema.d.ts +1 -1
- package/dist/auth/cell_data_schema.js +1 -1
- package/dist/auth/cell_field_action_specs.d.ts +4 -3
- package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_field_action_specs.js +5 -6
- package/dist/auth/cell_field_actions.d.ts.map +1 -1
- package/dist/auth/cell_field_actions.js +8 -1
- package/dist/auth/cell_item_action_specs.d.ts +4 -2
- package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
- package/dist/auth/cell_item_action_specs.js +5 -5
- package/dist/auth/cell_item_actions.d.ts.map +1 -1
- package/dist/auth/cell_item_actions.js +8 -1
- package/dist/auth/role_grant_offer_action_specs.d.ts +2 -0
- package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
- package/dist/auth/role_grant_offer_action_specs.js +8 -4
- package/dist/http/CLAUDE.md +14 -2
- package/dist/http/error_schemas.d.ts +8 -0
- package/dist/http/error_schemas.d.ts.map +1 -1
- package/dist/http/error_schemas.js +12 -0
- package/dist/http/jsonrpc_errors.d.ts +40 -15
- package/dist/http/jsonrpc_errors.d.ts.map +1 -1
- package/dist/http/jsonrpc_errors.js +90 -35
- package/dist/primitive_schemas.d.ts +1 -1
- package/dist/primitive_schemas.d.ts.map +1 -1
- package/dist/primitive_schemas.js +2 -1
- package/dist/schema_meta.d.ts +80 -0
- package/dist/schema_meta.d.ts.map +1 -1
- package/dist/schema_meta.js +71 -1
- package/dist/testing/CLAUDE.md +853 -146
- package/dist/testing/admin_integration.d.ts.map +1 -1
- package/dist/testing/admin_integration.js +73 -5
- package/dist/testing/adversarial_input.d.ts +0 -47
- package/dist/testing/adversarial_input.d.ts.map +1 -1
- package/dist/testing/adversarial_input.js +4 -340
- package/dist/testing/adversarial_input_cases.d.ts +124 -0
- package/dist/testing/adversarial_input_cases.d.ts.map +1 -0
- package/dist/testing/adversarial_input_cases.js +393 -0
- package/dist/testing/audit_completeness.d.ts.map +1 -1
- package/dist/testing/audit_completeness.js +24 -20
- package/dist/testing/audit_drift_guard.d.ts +30 -3
- package/dist/testing/audit_drift_guard.d.ts.map +1 -1
- package/dist/testing/audit_drift_guard.js +55 -3
- package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
- package/dist/testing/cross_backend/account_lifecycle.js +29 -12
- package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
- package/dist/testing/cross_backend/actor_lookup.js +7 -25
- package/dist/testing/cross_backend/app_settings.d.ts.map +1 -1
- package/dist/testing/cross_backend/app_settings.js +4 -6
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts +57 -0
- package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -0
- package/dist/testing/cross_backend/audit_cross_helpers.js +89 -0
- package/dist/testing/cross_backend/backend_config.d.ts +2 -1
- package/dist/testing/cross_backend/backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.d.ts +6 -0
- package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size.js +26 -44
- package/dist/testing/cross_backend/body_size_smuggling.d.ts +5 -0
- package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
- package/dist/testing/cross_backend/body_size_smuggling.js +7 -14
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts +3 -1
- package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.d.ts +78 -59
- package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
- package/dist/testing/cross_backend/capabilities.js +25 -24
- package/dist/testing/cross_backend/cargo_binary.d.ts +70 -0
- package/dist/testing/cross_backend/cargo_binary.d.ts.map +1 -0
- package/dist/testing/cross_backend/cargo_binary.js +148 -0
- package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_crud.js +174 -3
- package/dist/testing/cross_backend/cell_gated_create.d.ts +0 -60
- package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_gated_create.js +9 -487
- package/dist/testing/cross_backend/cell_grant_role.d.ts +14 -2
- package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_grant_role.js +22 -12
- package/dist/testing/cross_backend/cell_moderate.d.ts +66 -0
- package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -0
- package/dist/testing/cross_backend/cell_moderate.js +511 -0
- package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_ref_admission.js +1 -1
- package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
- package/dist/testing/cross_backend/cell_relations.js +40 -19
- package/dist/testing/cross_backend/conformance_case.d.ts +43 -4
- package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_case.js +29 -1
- package/dist/testing/cross_backend/conformance_rpc_auth.d.ts +93 -0
- package/dist/testing/cross_backend/conformance_rpc_auth.d.ts.map +1 -0
- package/dist/testing/cross_backend/conformance_rpc_auth.js +125 -0
- package/dist/testing/cross_backend/conformance_table.d.ts +144 -4
- package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
- package/dist/testing/cross_backend/conformance_table.js +297 -65
- package/dist/testing/cross_backend/cookie_attributes.d.ts.map +1 -1
- package/dist/testing/cross_backend/cookie_attributes.js +3 -2
- package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts +38 -5
- package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/create_cross_backend_global_setup.js +24 -3
- package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts +5 -5
- package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/credential_deadline.d.ts +10 -6
- package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -1
- package/dist/testing/cross_backend/credential_deadline.js +32 -31
- package/dist/testing/cross_backend/credential_header_robustness.d.ts.map +1 -1
- package/dist/testing/cross_backend/credential_header_robustness.js +4 -6
- package/dist/testing/cross_backend/{cell_cross_helpers.d.ts → cross_rpc.d.ts} +14 -2
- package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -0
- package/dist/testing/cross_backend/{cell_cross_helpers.js → cross_rpc.js} +31 -7
- package/dist/testing/cross_backend/default_backend_configs.d.ts +58 -61
- package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_backend_configs.js +97 -91
- package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/default_spine_surface.js +2 -2
- package/dist/testing/cross_backend/differential.d.ts +126 -0
- package/dist/testing/cross_backend/differential.d.ts.map +1 -0
- package/dist/testing/cross_backend/differential.js +201 -0
- package/dist/testing/cross_backend/differential_normalize.d.ts +150 -0
- package/dist/testing/cross_backend/differential_normalize.d.ts.map +1 -0
- package/dist/testing/cross_backend/differential_normalize.js +153 -0
- package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
- package/dist/testing/cross_backend/fact_serving.js +27 -17
- package/dist/testing/cross_backend/full_spine_mount.d.ts +15 -0
- package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
- package/dist/testing/cross_backend/full_spine_mount.js +55 -1
- package/dist/testing/cross_backend/identity_parity.d.ts.map +1 -1
- package/dist/testing/cross_backend/identity_parity.js +6 -5
- package/dist/testing/cross_backend/in_process_setup.d.ts +12 -2
- package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/in_process_setup.js +10 -7
- package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
- package/dist/testing/cross_backend/login_security.js +13 -18
- package/dist/testing/cross_backend/origin.js +1 -1
- package/dist/testing/cross_backend/origin_conformance.js +1 -1
- package/dist/testing/cross_backend/origin_scope.d.ts +6 -0
- package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -1
- package/dist/testing/cross_backend/origin_scope.js +13 -12
- package/dist/testing/cross_backend/peer_ping_ws.d.ts +2 -5
- package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/peer_ping_ws.js +14 -24
- package/dist/testing/cross_backend/postgres_database.d.ts +85 -0
- package/dist/testing/cross_backend/postgres_database.d.ts.map +1 -0
- package/dist/testing/cross_backend/postgres_database.js +140 -0
- package/dist/testing/cross_backend/provided_context.d.ts +28 -0
- package/dist/testing/cross_backend/provided_context.d.ts.map +1 -0
- package/dist/testing/cross_backend/provided_context.js +35 -0
- package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
- package/dist/testing/cross_backend/query_shape.js +10 -12
- package/dist/testing/cross_backend/ready.js +2 -2
- package/dist/testing/cross_backend/revocation_drivers.d.ts +4 -3
- package/dist/testing/cross_backend/revocation_drivers.d.ts.map +1 -1
- package/dist/testing/cross_backend/revocation_drivers.js +1 -1
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +3 -2
- package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts +13 -0
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -0
- package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +203 -0
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +2 -5
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +203 -111
- package/dist/testing/cross_backend/role_grant_participation.d.ts +13 -0
- package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_participation.js +32 -21
- package/dist/testing/cross_backend/role_grant_role_grammar.d.ts.map +1 -1
- package/dist/testing/cross_backend/role_grant_role_grammar.js +6 -9
- package/dist/testing/cross_backend/rpc_adversarial.d.ts +47 -0
- package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -0
- package/dist/testing/cross_backend/rpc_adversarial.js +302 -0
- package/dist/testing/cross_backend/rpc_success_fixtures.d.ts +24 -0
- package/dist/testing/cross_backend/rpc_success_fixtures.d.ts.map +1 -0
- package/dist/testing/cross_backend/rpc_success_fixtures.js +124 -0
- package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +101 -0
- package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts.map +1 -0
- package/dist/testing/cross_backend/rust_spine_consumer_surface.js +113 -0
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +10 -0
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +33 -21
- package/dist/testing/cross_backend/session_cap.d.ts +9 -8
- package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
- package/dist/testing/cross_backend/session_cap.js +9 -4
- package/dist/testing/cross_backend/session_cookie_codec.js +1 -1
- package/dist/testing/cross_backend/setup.d.ts +58 -56
- package/dist/testing/cross_backend/setup.d.ts.map +1 -1
- package/dist/testing/cross_backend/setup.js +29 -29
- package/dist/testing/cross_backend/simple_request.d.ts.map +1 -1
- package/dist/testing/cross_backend/simple_request.js +4 -4
- package/dist/testing/cross_backend/spawn_backend.d.ts +59 -10
- package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -1
- package/dist/testing/cross_backend/spawn_backend.js +219 -60
- package/dist/testing/cross_backend/spine_suites.d.ts +311 -0
- package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -0
- package/dist/testing/cross_backend/spine_suites.js +897 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts +44 -0
- package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
- package/dist/testing/cross_backend/spine_surface_constants.js +53 -0
- package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/sse_round_trip.js +3 -4
- package/dist/testing/cross_backend/standard.d.ts +18 -1
- package/dist/testing/cross_backend/standard.d.ts.map +1 -1
- package/dist/testing/cross_backend/standard.js +2 -0
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +24 -0
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
- package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +15 -0
- package/dist/testing/cross_backend/testing_backdoor.d.ts +29 -12
- package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_backdoor.js +143 -51
- package/dist/testing/cross_backend/testing_reset_actions.d.ts +30 -14
- package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
- package/dist/testing/cross_backend/testing_reset_actions.js +58 -37
- package/dist/testing/cross_backend/testing_server_bun.js +1 -1
- package/dist/testing/cross_backend/token_lifetime.js +1 -1
- package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
- package/dist/testing/cross_backend/token_scope_surface.js +9 -13
- package/dist/testing/cross_backend/transport_composition.d.ts +26 -2
- package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -1
- package/dist/testing/cross_backend/transport_composition.js +81 -51
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +8 -0
- package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
- package/dist/testing/cross_backend/ts_spine_backend_config.js +23 -13
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +2 -5
- package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_action_rate_limit.js +8 -14
- package/dist/testing/cross_backend/ws_connection_cap.d.ts +4 -9
- package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_connection_cap.js +11 -12
- package/dist/testing/cross_backend/ws_endpoint.d.ts +52 -0
- package/dist/testing/cross_backend/ws_endpoint.d.ts.map +1 -0
- package/dist/testing/cross_backend/ws_endpoint.js +54 -0
- package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts +2 -5
- package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_inbound_dispatch.js +9 -15
- package/dist/testing/cross_backend/ws_outbound_overflow.d.ts +2 -7
- package/dist/testing/cross_backend/ws_outbound_overflow.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_outbound_overflow.js +5 -9
- package/dist/testing/cross_backend/ws_role_gated.d.ts +4 -5
- package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_role_gated.js +12 -21
- package/dist/testing/cross_backend/ws_round_trip.d.ts +12 -9
- package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
- package/dist/testing/cross_backend/ws_round_trip.js +29 -34
- package/dist/testing/db_entities.d.ts +16 -3
- package/dist/testing/db_entities.d.ts.map +1 -1
- package/dist/testing/db_entities.js +28 -5
- package/dist/testing/integration_helpers.d.ts +61 -2
- package/dist/testing/integration_helpers.d.ts.map +1 -1
- package/dist/testing/integration_helpers.js +75 -18
- package/dist/testing/round_trip.d.ts +17 -0
- package/dist/testing/round_trip.d.ts.map +1 -1
- package/dist/testing/round_trip.js +67 -24
- package/dist/testing/rpc_attack_surface.d.ts +10 -0
- package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
- package/dist/testing/rpc_attack_surface.js +27 -140
- package/dist/testing/rpc_envelope_cases.d.ts +114 -0
- package/dist/testing/rpc_envelope_cases.d.ts.map +1 -0
- package/dist/testing/rpc_envelope_cases.js +243 -0
- package/dist/testing/rpc_helpers.d.ts +31 -0
- package/dist/testing/rpc_helpers.d.ts.map +1 -1
- package/dist/testing/rpc_helpers.js +35 -14
- package/dist/testing/rpc_round_trip.d.ts +62 -6
- package/dist/testing/rpc_round_trip.d.ts.map +1 -1
- package/dist/testing/rpc_round_trip.js +151 -39
- package/dist/testing/schema_generators.d.ts +99 -2
- package/dist/testing/schema_generators.d.ts.map +1 -1
- package/dist/testing/schema_generators.js +261 -73
- package/dist/testing/standard.d.ts +26 -0
- package/dist/testing/standard.d.ts.map +1 -1
- package/dist/testing/standard.js +4 -1
- package/dist/testing/stubs.d.ts +4 -3
- package/dist/testing/stubs.d.ts.map +1 -1
- package/dist/testing/stubs.js +6 -4
- package/dist/testing/transports/raw_http.d.ts +21 -0
- package/dist/testing/transports/raw_http.d.ts.map +1 -1
- package/dist/testing/transports/raw_http.js +30 -0
- package/dist/zod.d.ts +9 -0
- package/dist/zod.d.ts.map +1 -1
- package/dist/zod.js +20 -0
- package/package.json +6 -4
- package/dist/testing/cross_backend/actor_search.d.ts +0 -6
- package/dist/testing/cross_backend/actor_search.d.ts.map +0 -1
- package/dist/testing/cross_backend/actor_search.js +0 -90
- package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +0 -1
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import { type ReconstructedBootstrappedBackendHandle, type SerializableBootstrappedBackendHandle } from './setup.js';
|
|
3
|
+
declare module 'vitest' {
|
|
4
|
+
interface ProvidedContext {
|
|
5
|
+
/** The single-backend project's handle (`create_cross_backend_global_setup`). */
|
|
6
|
+
backend_handle: SerializableBootstrappedBackendHandle;
|
|
7
|
+
/** A dual-spawn project's first handle (`create_dual_spawn_global_setup`'s default keys). */
|
|
8
|
+
parity_handle_a: SerializableBootstrappedBackendHandle;
|
|
9
|
+
/** A dual-spawn project's second handle. */
|
|
10
|
+
parity_handle_b: SerializableBootstrappedBackendHandle;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The backend the project's `globalSetup` spawned, rebuilt from the provided
|
|
15
|
+
* `backend_handle`. Call at a test file's top level.
|
|
16
|
+
*/
|
|
17
|
+
export declare const inject_backend_handle: () => ReconstructedBootstrappedBackendHandle;
|
|
18
|
+
/** The two backends a dual-spawn project's `globalSetup` spawned, under the default keys. */
|
|
19
|
+
export interface InjectedParityHandles {
|
|
20
|
+
readonly a: ReconstructedBootstrappedBackendHandle;
|
|
21
|
+
readonly b: ReconstructedBootstrappedBackendHandle;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* The two backends a dual-spawn project provides as `parity_handle_a` /
|
|
25
|
+
* `parity_handle_b`, rebuilt. Call at a test file's top level.
|
|
26
|
+
*/
|
|
27
|
+
export declare const inject_parity_handles: () => InjectedParityHandles;
|
|
28
|
+
//# sourceMappingURL=provided_context.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provided_context.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/provided_context.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAwB9B,OAAO,EAEN,KAAK,sCAAsC,EAC3C,KAAK,qCAAqC,EAC1C,MAAM,YAAY,CAAC;AAEpB,OAAO,QAAQ,QAAQ,CAAC;IACvB,UAAiB,eAAe;QAC/B,iFAAiF;QACjF,cAAc,EAAE,qCAAqC,CAAC;QACtD,6FAA6F;QAC7F,eAAe,EAAE,qCAAqC,CAAC;QACvD,4CAA4C;QAC5C,eAAe,EAAE,qCAAqC,CAAC;KACvD;CACD;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAO,sCACiB,CAAC;AAE3D,6FAA6F;AAC7F,MAAM,WAAW,qBAAqB;IACrC,QAAQ,CAAC,CAAC,EAAE,sCAAsC,CAAC;IACnD,QAAQ,CAAC,CAAC,EAAE,sCAAsC,CAAC;CACnD;AAED;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAO,qBAGvC,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* The vitest `ProvidedContext` keys the cross-backend global setups provide,
|
|
4
|
+
* and the helpers a `*.cross.test.ts` file reads them with.
|
|
5
|
+
*
|
|
6
|
+
* `create_cross_backend_global_setup` provides one backend's handle as
|
|
7
|
+
* `backend_handle`; `create_dual_spawn_global_setup` provides two, as
|
|
8
|
+
* `parity_handle_a` / `parity_handle_b` by default. This module augments
|
|
9
|
+
* vitest's `ProvidedContext` with those keys, so a test file that imports
|
|
10
|
+
* `inject_backend_handle` (or `inject_parity_handles`) gets the typed `inject`
|
|
11
|
+
* with no augmentation of its own. A project providing other keys — a dual
|
|
12
|
+
* spawn under its own `provide_keys`, a probe result — augments
|
|
13
|
+
* `ProvidedContext` for those in its own module, beside this one.
|
|
14
|
+
*
|
|
15
|
+
* vitest refuses a value that isn't serializable, so the handles cross as
|
|
16
|
+
* `SerializableBootstrappedBackendHandle`; the helpers rebuild a usable handle
|
|
17
|
+
* with `reconstruct_bootstrapped_handle`.
|
|
18
|
+
*
|
|
19
|
+
* @module
|
|
20
|
+
*/
|
|
21
|
+
import { inject } from 'vitest';
|
|
22
|
+
import { reconstruct_bootstrapped_handle } from './setup.js';
|
|
23
|
+
/**
|
|
24
|
+
* The backend the project's `globalSetup` spawned, rebuilt from the provided
|
|
25
|
+
* `backend_handle`. Call at a test file's top level.
|
|
26
|
+
*/
|
|
27
|
+
export const inject_backend_handle = () => reconstruct_bootstrapped_handle(inject('backend_handle'));
|
|
28
|
+
/**
|
|
29
|
+
* The two backends a dual-spawn project provides as `parity_handle_a` /
|
|
30
|
+
* `parity_handle_b`, rebuilt. Call at a test file's top level.
|
|
31
|
+
*/
|
|
32
|
+
export const inject_parity_handles = () => ({
|
|
33
|
+
a: reconstruct_bootstrapped_handle(inject('parity_handle_a')),
|
|
34
|
+
b: reconstruct_bootstrapped_handle(inject('parity_handle_b'))
|
|
35
|
+
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"query_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/query_shape.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAwC9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAG1E;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG,uCAAuC,CAAC;
|
|
1
|
+
{"version":3,"file":"query_shape.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/query_shape.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAwC9B,OAAO,KAAK,EAAE,uCAAuC,EAAE,MAAM,YAAY,CAAC;AAG1E;;;;;GAKG;AACH,MAAM,MAAM,0BAA0B,GAAG,uCAAuC,CAAC;AAKjF,eAAO,MAAM,gCAAgC,GAAI,SAAS,0BAA0B,KAAG,IAqJtF,CAAC"}
|
|
@@ -19,7 +19,7 @@ import '../assert_dev_env.js';
|
|
|
19
19
|
* first-wins; a duplicated `method` must dispatch the first one on every
|
|
20
20
|
* backend (a last-wins reader dispatches a different method per backend on
|
|
21
21
|
* the same request).
|
|
22
|
-
* - **`GET /api/facts/:hash`** (gated on `capabilities.
|
|
22
|
+
* - **`GET /api/facts/:hash`** (gated on `capabilities.fact_serving_admin_hash`) —
|
|
23
23
|
* strict query schema: unknown keys and a malformed `acting` are 400
|
|
24
24
|
* `invalid_query_params` even for an anonymous caller (query precedes the
|
|
25
25
|
* 401 guard), while a duplicated `acting` takes the first occurrence.
|
|
@@ -35,9 +35,7 @@ import { describe, test, assert } from 'vitest';
|
|
|
35
35
|
import { account_verify_action_spec } from '../../auth/account_action_specs.js';
|
|
36
36
|
import { ERROR_INVALID_QUERY_PARAMS } from '../../http/error_schemas.js';
|
|
37
37
|
import { test_if } from './capabilities.js';
|
|
38
|
-
import { SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
39
|
-
/** The status route every spine serves (bundled into the account family). */
|
|
40
|
-
const STATUS_PATH = '/api/account/status';
|
|
38
|
+
import { SPINE_ACCOUNT_STATUS_PATH, SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
41
39
|
/** A well-formed blake3 hash that references no stored fact. */
|
|
42
40
|
const MISSING_HASH = `blake3:${'a'.repeat(64)}`;
|
|
43
41
|
export const describe_query_shape_cross_tests = (options) => {
|
|
@@ -51,7 +49,7 @@ export const describe_query_shape_cross_tests = (options) => {
|
|
|
51
49
|
// persona; last-wins would fall through to `actor: null`, and a
|
|
52
50
|
// derived-serde extractor would 400 before the handler ran.
|
|
53
51
|
const nil = '00000000-0000-0000-0000-000000000000';
|
|
54
|
-
const res = await fixture.transport(`${
|
|
52
|
+
const res = await fixture.transport(`${SPINE_ACCOUNT_STATUS_PATH}?acting=${fixture.actor.id}&acting=${nil}`, { method: 'GET', headers: fixture.create_session_headers() });
|
|
55
53
|
assert.strictEqual(res.status, 200);
|
|
56
54
|
const body = (await res.json());
|
|
57
55
|
assert.strictEqual(body.actor?.id, fixture.actor.id, 'first acting occurrence must win');
|
|
@@ -60,7 +58,7 @@ export const describe_query_shape_cross_tests = (options) => {
|
|
|
60
58
|
const fixture = await setup_test();
|
|
61
59
|
// The keeper has one actor: an omitted selector resolves it, while a
|
|
62
60
|
// malformed one answers account-only.
|
|
63
|
-
const omitted = await fixture.transport(
|
|
61
|
+
const omitted = await fixture.transport(SPINE_ACCOUNT_STATUS_PATH, {
|
|
64
62
|
method: 'GET',
|
|
65
63
|
headers: fixture.create_session_headers()
|
|
66
64
|
});
|
|
@@ -68,7 +66,7 @@ export const describe_query_shape_cross_tests = (options) => {
|
|
|
68
66
|
const omitted_body = (await omitted.json());
|
|
69
67
|
assert.strictEqual(omitted_body.actor?.id, fixture.actor.id);
|
|
70
68
|
for (const raw of ['not-a-uuid', '', `"${fixture.actor.id}"`]) {
|
|
71
|
-
const res = await fixture.transport(`${
|
|
69
|
+
const res = await fixture.transport(`${SPINE_ACCOUNT_STATUS_PATH}?acting=${encodeURIComponent(raw)}`, {
|
|
72
70
|
method: 'GET',
|
|
73
71
|
headers: fixture.create_session_headers()
|
|
74
72
|
});
|
|
@@ -83,14 +81,14 @@ export const describe_query_shape_cross_tests = (options) => {
|
|
|
83
81
|
// The status route has no query schema — an anonymous prober with a
|
|
84
82
|
// junk query must hear the same 401 as a clean one (a pre-handler
|
|
85
83
|
// query reject here is a backend fingerprint).
|
|
86
|
-
const dup = await anon(`${
|
|
84
|
+
const dup = await anon(`${SPINE_ACCOUNT_STATUS_PATH}?acting=a&acting=b`, { method: 'GET' });
|
|
87
85
|
assert.strictEqual(dup.status, 401);
|
|
88
|
-
const unknown = await anon(`${
|
|
86
|
+
const unknown = await anon(`${SPINE_ACCOUNT_STATUS_PATH}?foo=1&bar=2`, { method: 'GET' });
|
|
89
87
|
assert.strictEqual(unknown.status, 401);
|
|
90
88
|
});
|
|
91
89
|
test_if(capabilities.account_status, 'status: unknown query keys are ignored for an authed caller (no strict schema)', async () => {
|
|
92
90
|
const fixture = await setup_test();
|
|
93
|
-
const res = await fixture.transport(`${
|
|
91
|
+
const res = await fixture.transport(`${SPINE_ACCOUNT_STATUS_PATH}?foo=1`, {
|
|
94
92
|
method: 'GET',
|
|
95
93
|
headers: fixture.create_session_headers()
|
|
96
94
|
});
|
|
@@ -107,7 +105,7 @@ export const describe_query_shape_cross_tests = (options) => {
|
|
|
107
105
|
assert.strictEqual(body.id, 7, 'first id occurrence must win');
|
|
108
106
|
assert.ok(body.result !== undefined, 'first method occurrence must dispatch');
|
|
109
107
|
});
|
|
110
|
-
test_if(capabilities.
|
|
108
|
+
test_if(capabilities.fact_serving_admin_hash, 'bare-hash facts: unknown query key → 400 invalid_query_params, even anonymous', async () => {
|
|
111
109
|
const fixture = await setup_test();
|
|
112
110
|
// Authed admin (the keeper): the strict query schema refuses the
|
|
113
111
|
// unknown key before the handler can 404 the missing fact.
|
|
@@ -126,7 +124,7 @@ export const describe_query_shape_cross_tests = (options) => {
|
|
|
126
124
|
const anon_clean = await anon(`/api/facts/${MISSING_HASH}`, { method: 'GET' });
|
|
127
125
|
assert.strictEqual(anon_clean.status, 401);
|
|
128
126
|
});
|
|
129
|
-
test_if(capabilities.
|
|
127
|
+
test_if(capabilities.fact_serving_admin_hash, 'bare-hash facts: duplicated acting takes the first occurrence; malformed acting → 400', async () => {
|
|
130
128
|
const fixture = await setup_test();
|
|
131
129
|
// First occurrence is the keeper's real actor; the second is
|
|
132
130
|
// garbage. First-wins passes the query phase and reaches the
|
|
@@ -13,13 +13,13 @@ import '../assert_dev_env.js';
|
|
|
13
13
|
* `/ready` is a plain public REST route — not an RPC method, not one of the six
|
|
14
14
|
* REST auth routes — and it's deliberately **off** the declared spine surface
|
|
15
15
|
* (`create_spine_surface_spec`), like ws/sse/cells/fact-serving. So it needs a
|
|
16
|
-
* bespoke imperative suite (à la `
|
|
16
|
+
* bespoke imperative suite (à la `describe_origin_cross_tests`), gated on
|
|
17
17
|
* `capabilities.ready`, rather than a `conformance_table` row or generic
|
|
18
18
|
* round-trip enumeration. The drift → `503` path stays per-impl unit tests.
|
|
19
19
|
*
|
|
20
20
|
* Runs both legs via the shared `{setup_test, capabilities}` protocol: the
|
|
21
21
|
* in-process leg (`cross_backend/ready_parity.db.test.ts`, plain `gro test`) and
|
|
22
|
-
* the cross-process leg (`cross_backend/
|
|
22
|
+
* the cross-process leg (`cross_backend/spine_suites.cross.test.ts`, the TS spine
|
|
23
23
|
* binaries + Rust `testing_spine_stub` over real HTTP).
|
|
24
24
|
*
|
|
25
25
|
* @module
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import type { BackendCapabilities } from './capabilities.js';
|
|
3
|
-
import type {
|
|
3
|
+
import type { TestAccount } from '../app_server.js';
|
|
4
|
+
import type { TestFixture } from './setup.js';
|
|
4
5
|
/**
|
|
5
6
|
* POST a JSON body to a REST route on `account`'s session, asserting `200`.
|
|
6
7
|
*
|
|
@@ -9,7 +10,7 @@ import type { TestAccountFixture, TestFixture } from './setup.js';
|
|
|
9
10
|
* @param path - the REST route path
|
|
10
11
|
* @param body - the JSON request body
|
|
11
12
|
*/
|
|
12
|
-
export declare const cross_post_as: (fixture: TestFixture, account:
|
|
13
|
+
export declare const cross_post_as: (fixture: TestFixture, account: TestAccount, path: string, body: unknown) => Promise<void>;
|
|
13
14
|
/**
|
|
14
15
|
* Revoke every API token of `account_id` over the keeper's admin session
|
|
15
16
|
* (`admin_token_revoke_all`), asserting success.
|
|
@@ -45,7 +46,7 @@ export interface AccountRevocation {
|
|
|
45
46
|
* `target` must have been created with `DEFAULT_TEST_PASSWORD`, which the
|
|
46
47
|
* password-change row presents.
|
|
47
48
|
*/
|
|
48
|
-
readonly revoke: (fixture: TestFixture, target:
|
|
49
|
+
readonly revoke: (fixture: TestFixture, target: TestAccount) => Promise<void>;
|
|
49
50
|
}
|
|
50
51
|
/** Configuration for `create_account_revocations`. */
|
|
51
52
|
export interface AccountRevocationsOptions {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"revocation_drivers.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/revocation_drivers.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA4B9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"revocation_drivers.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/revocation_drivers.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA4B9B,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAC;AAE7D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C;;;;;;;GAOG;AACH,eAAO,MAAM,aAAa,GACzB,SAAS,WAAW,EACpB,SAAS,WAAW,EACpB,MAAM,MAAM,EACZ,MAAM,OAAO,KACX,OAAO,CAAC,IAAI,CAOd,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,GACxC,SAAS,WAAW,EACpB,UAAU,MAAM,EAChB,YAAY,MAAM,KAChB,OAAO,CAAC,IAAI,CASd,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAC/B,SAAS,WAAW,EACpB,UAAU,MAAM,EAChB,YAAY,MAAM,KAChB,OAAO,CAAC,IAAI,CASd,CAAC;AAEF,2EAA2E;AAC3E,MAAM,WAAW,iBAAiB;IACjC,iEAAiE;IACjE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,4FAA4F;IAC5F,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB;;;;OAIG;IACH,QAAQ,CAAC,MAAM,EAAE,CAAC,OAAO,EAAE,WAAW,EAAE,MAAM,EAAE,WAAW,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9E;AAED,sDAAsD;AACtD,MAAM,WAAW,yBAAyB;IACzC,8EAA8E;IAC9E,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,sEAAsE;IACtE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,8BAA8B;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,uCAAuC;IACvC,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAC/B;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GACtC,SAAS,yBAAyB,KAChC,aAAa,CAAC,iBAAiB,CAiCjC,CAAC"}
|
|
@@ -20,7 +20,7 @@ import '../assert_dev_env.js';
|
|
|
20
20
|
import { assert } from 'vitest';
|
|
21
21
|
import { account_purge_action_spec, admin_token_revoke_all_action_spec } from '../../auth/admin_action_specs.js';
|
|
22
22
|
import { DEFAULT_TEST_PASSWORD } from '../test_credentials.js';
|
|
23
|
-
import { cross_rpc_call } from './
|
|
23
|
+
import { cross_rpc_call } from './cross_rpc.js';
|
|
24
24
|
/**
|
|
25
25
|
* POST a JSON body to a REST route on `account`'s session, asserting `200`.
|
|
26
26
|
*
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
|
-
import type {
|
|
2
|
+
import type { TestAccount } from '../app_server.js';
|
|
3
|
+
import type { TestFixture } from './setup.js';
|
|
3
4
|
/** Options for the role-grant fixtures. */
|
|
4
5
|
export interface RoleGrantFixtureOptions {
|
|
5
6
|
/** The role to offer. Default `ROLE_ADMIN`, the one builtin admin-grantable role. */
|
|
@@ -31,5 +32,5 @@ export declare const create_pending_role_grant_offer: (fixture: TestFixture, to_
|
|
|
31
32
|
* @param options - the role and the RPC path
|
|
32
33
|
* @returns the active grant's id
|
|
33
34
|
*/
|
|
34
|
-
export declare const create_active_role_grant: (fixture: TestFixture, recipient:
|
|
35
|
+
export declare const create_active_role_grant: (fixture: TestFixture, recipient: TestAccount, options?: RoleGrantFixtureOptions) => Promise<string>;
|
|
35
36
|
//# sourceMappingURL=role_grant_cross_helpers.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_cross_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_cross_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAoB9B,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_cross_helpers.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_cross_helpers.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAoB9B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,2CAA2C;AAC3C,MAAM,WAAW,uBAAuB;IACvC,qFAAqF;IACrF,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB,4DAA4D;IAC5D,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;;;;;;;;GASG;AACH,eAAO,MAAM,+BAA+B,GAC3C,SAAS,WAAW,EACpB,eAAe,MAAM,EACrB,kBAAiB,MAAM,CAAC,MAAM,EAAE,MAAM,CAAoC,EAC1E,UAAS,uBAA4B,KACnC,OAAO,CAAC;IAAE,EAAE,EAAE,MAAM,CAAA;CAAE,CAWxB,CAAC;AAEF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,wBAAwB,GACpC,SAAS,WAAW,EACpB,WAAW,WAAW,EACtB,UAAS,uBAA4B,KACnC,OAAO,CAAC,MAAM,CAgBhB,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
import type { RpcPathCrossSuiteOptions } from './setup.js';
|
|
3
|
+
/** Options for `describe_role_grant_offer_lifecycle_cross_tests`. */
|
|
4
|
+
export interface RoleGrantOfferLifecycleCrossTestOptions extends RpcPathCrossSuiteOptions {
|
|
5
|
+
/**
|
|
6
|
+
* The role offered — one the backend's registry holds with the `admin`
|
|
7
|
+
* grant path. Default `SPINE_PARTICIPANT_ROLE`, the spine's own; a backend
|
|
8
|
+
* with only the builtin roles passes `ROLE_ADMIN`.
|
|
9
|
+
*/
|
|
10
|
+
readonly role?: string;
|
|
11
|
+
}
|
|
12
|
+
export declare const describe_role_grant_offer_lifecycle_cross_tests: (options: RoleGrantOfferLifecycleCrossTestOptions) => void;
|
|
13
|
+
//# sourceMappingURL=role_grant_offer_lifecycle.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"role_grant_offer_lifecycle.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_offer_lifecycle.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmD9B,OAAO,KAAK,EAAE,wBAAwB,EAAe,MAAM,YAAY,CAAC;AAMxE,qEAAqE;AACrE,MAAM,WAAW,uCAAwC,SAAQ,wBAAwB;IACxF;;;;OAIG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,eAAO,MAAM,+CAA+C,GAC3D,SAAS,uCAAuC,KAC9C,IAyRF,CAAC"}
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* Cross-backend parity suite for the **role-grant-offer decline and retract
|
|
4
|
+
* refusals** — the error paths beneath the happy-path WS notification suite
|
|
5
|
+
* (`describe_role_grant_offer_notification_ws_tests`), which is the only other
|
|
6
|
+
* cross-process caller of these two verbs.
|
|
7
|
+
*
|
|
8
|
+
* What both spines must agree on, per the handlers
|
|
9
|
+
* (`auth/role_grant_offer_actions.ts` and the queries behind them):
|
|
10
|
+
*
|
|
11
|
+
* - **not yours → 404** `role_grant_offer_not_found`. Decline is scoped to the
|
|
12
|
+
* recipient account and retract to the grantor actor, so an offer the caller
|
|
13
|
+
* is not party to answers the same `not_found` as an id that names no offer
|
|
14
|
+
* (`docs/security.md` §"404-over-403 is the general mask"), and the offer is
|
|
15
|
+
* left pending. The scope check comes before the terminal check, so a
|
|
16
|
+
* stranger learns nothing of an offer's state either.
|
|
17
|
+
* - **already decided → 400** `invalid_request` with reason
|
|
18
|
+
* `role_grant_offer_terminal` — a decline or retract after an accept, a
|
|
19
|
+
* second decline, a second retract, and either one after the other.
|
|
20
|
+
* - **one audit row per success, none per refusal.** A decline writes
|
|
21
|
+
* `role_grant_offer_decline` attributed to the recipient and targeting the
|
|
22
|
+
* grantor, a retract `role_grant_offer_retract` attributed to the grantor
|
|
23
|
+
* and targeting the recipient, each with its event's metadata; the refusals
|
|
24
|
+
* above write nothing. Read through `list_audit_events`, which drains first
|
|
25
|
+
* and parses each row's metadata against its event's schema.
|
|
26
|
+
*
|
|
27
|
+
* Multi-step (each case needs an offer in a given state), so this is an
|
|
28
|
+
* imperative suite; the single-request rows — decline and retract of an id
|
|
29
|
+
* that names no offer — live in the conformance security slate. The verbs are
|
|
30
|
+
* on every spine's standard RPC surface, so the suite is ungated. The keeper
|
|
31
|
+
* is the grantor, so the fixture's keeper must hold `ROLE_ADMIN`
|
|
32
|
+
* (`extra_keeper_roles`), and it offers an admin-grantable role — the
|
|
33
|
+
* `role` option, `SPINE_PARTICIPANT_ROLE` by default; `ROLE_ADMIN` serves a
|
|
34
|
+
* backend with only the builtin roles.
|
|
35
|
+
*
|
|
36
|
+
* @module
|
|
37
|
+
*/
|
|
38
|
+
import { assert, describe, test } from 'vitest';
|
|
39
|
+
import { rpc_call } from '../rpc_helpers.js';
|
|
40
|
+
import { ERROR_ROLE_GRANT_OFFER_NOT_FOUND, ERROR_ROLE_GRANT_OFFER_TERMINAL } from '../../auth/role_grant_offer_action_specs.js';
|
|
41
|
+
import { JSONRPC_INVALID_REQUEST } from '../../http/jsonrpc.js';
|
|
42
|
+
import { JSONRPC_ERROR_CODES } from '../../http/jsonrpc_errors.js';
|
|
43
|
+
import { assert_audit_metadata_fields, list_audit_events } from './audit_cross_helpers.js';
|
|
44
|
+
import { create_pending_role_grant_offer } from './role_grant_cross_helpers.js';
|
|
45
|
+
import { SPINE_PARTICIPANT_ROLE, SPINE_RPC_PATH } from './spine_surface_constants.js';
|
|
46
|
+
/** A well-formed UUID that names no offer. */
|
|
47
|
+
const NIL_UUID = '00000000-0000-0000-0000-000000000000';
|
|
48
|
+
export const describe_role_grant_offer_lifecycle_cross_tests = (options) => {
|
|
49
|
+
const { setup_test } = options;
|
|
50
|
+
const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
|
|
51
|
+
const role = options.role ?? SPINE_PARTICIPANT_ROLE;
|
|
52
|
+
const rpc = (fixture, method, params, headers) => rpc_call({ app: fixture.fresh_transport(), path: rpc_path, method, params, headers });
|
|
53
|
+
/** A pending `role` offer from the keeper to `recipient`. */
|
|
54
|
+
const create_offer = (fixture, recipient) => create_pending_role_grant_offer(fixture, recipient.account.id, undefined, {
|
|
55
|
+
role,
|
|
56
|
+
rpc_path
|
|
57
|
+
});
|
|
58
|
+
const decline = (fixture, offer_id, headers, reason) => rpc(fixture, 'role_grant_offer_decline', reason === undefined ? { offer_id } : { offer_id, reason }, headers);
|
|
59
|
+
const retract = (fixture, offer_id, headers) => rpc(fixture, 'role_grant_offer_retract', { offer_id }, headers);
|
|
60
|
+
const accept = (fixture, offer_id, headers) => rpc(fixture, 'role_grant_offer_accept', { offer_id }, headers);
|
|
61
|
+
const assert_ok = (res, label) => {
|
|
62
|
+
assert.ok(res.ok, `${label} must succeed: ${JSON.stringify(res)}`);
|
|
63
|
+
};
|
|
64
|
+
/** The masked refusal, returned as `{status, error}` for an identity comparison. */
|
|
65
|
+
const assert_not_found = (res, label) => {
|
|
66
|
+
assert.ok(!res.ok, `${label} must be refused: ${JSON.stringify(res)}`);
|
|
67
|
+
assert.strictEqual(res.status, 404, label);
|
|
68
|
+
assert.strictEqual(res.error.code, JSONRPC_ERROR_CODES.not_found, label);
|
|
69
|
+
assert.deepStrictEqual(res.error.data, { reason: ERROR_ROLE_GRANT_OFFER_NOT_FOUND }, label);
|
|
70
|
+
return { status: res.status, error: res.error };
|
|
71
|
+
};
|
|
72
|
+
const assert_terminal = (res, label) => {
|
|
73
|
+
assert.ok(!res.ok, `${label} must be refused: ${JSON.stringify(res)}`);
|
|
74
|
+
assert.strictEqual(res.status, 400, label);
|
|
75
|
+
assert.strictEqual(res.error.code, JSONRPC_INVALID_REQUEST, label);
|
|
76
|
+
assert.deepStrictEqual(res.error.data, { reason: ERROR_ROLE_GRANT_OFFER_TERMINAL }, label);
|
|
77
|
+
};
|
|
78
|
+
/** Whether `offer_id` is still in the recipient's pending inbox. */
|
|
79
|
+
const is_pending = async (fixture, recipient, offer_id) => {
|
|
80
|
+
const listed = await rpc(fixture, 'role_grant_offer_list', {}, recipient.create_session_headers());
|
|
81
|
+
if (!listed.ok)
|
|
82
|
+
assert.fail(`role_grant_offer_list failed: ${JSON.stringify(listed)}`);
|
|
83
|
+
const { offers } = listed.result;
|
|
84
|
+
return offers.some((offer) => offer.id === offer_id);
|
|
85
|
+
};
|
|
86
|
+
describe('role_grant_offer decline + retract refusals', () => {
|
|
87
|
+
test('a decline by anyone but the recipient → 404, identical to a missing offer; the offer stays pending', async () => {
|
|
88
|
+
const fixture = await setup_test();
|
|
89
|
+
const recipient = await fixture.create_account({ username: 'lifecycle_decline_to' });
|
|
90
|
+
const stranger = await fixture.create_account({ username: 'lifecycle_decline_by' });
|
|
91
|
+
const offer = await create_offer(fixture, recipient);
|
|
92
|
+
const missing = assert_not_found(await decline(fixture, NIL_UUID, stranger.create_session_headers()), 'a decline of a missing offer');
|
|
93
|
+
const by_stranger = assert_not_found(await decline(fixture, offer.id, stranger.create_session_headers()), 'a stranger declining');
|
|
94
|
+
assert.deepStrictEqual(by_stranger, missing, 'a stranger declining a real offer must hear what a missing offer answers');
|
|
95
|
+
// the grantor is a party to the offer, but not its recipient
|
|
96
|
+
assert_not_found(await decline(fixture, offer.id, fixture.create_session_headers()), 'the grantor declining its own offer');
|
|
97
|
+
assert.ok(await is_pending(fixture, recipient, offer.id), 'the refusals leave it pending');
|
|
98
|
+
assert_ok(await decline(fixture, offer.id, recipient.create_session_headers()), 'decline');
|
|
99
|
+
});
|
|
100
|
+
test('a retract by anyone but the grantor → 404, identical to a missing offer; the offer stays pending', async () => {
|
|
101
|
+
const fixture = await setup_test();
|
|
102
|
+
const recipient = await fixture.create_account({ username: 'lifecycle_retract_to' });
|
|
103
|
+
const stranger = await fixture.create_account({ username: 'lifecycle_retract_by' });
|
|
104
|
+
const offer = await create_offer(fixture, recipient);
|
|
105
|
+
const missing = assert_not_found(await retract(fixture, NIL_UUID, stranger.create_session_headers()), 'a retract of a missing offer');
|
|
106
|
+
const by_stranger = assert_not_found(await retract(fixture, offer.id, stranger.create_session_headers()), 'a stranger retracting');
|
|
107
|
+
assert.deepStrictEqual(by_stranger, missing, 'a stranger retracting a real offer must hear what a missing offer answers');
|
|
108
|
+
assert_not_found(await retract(fixture, offer.id, recipient.create_session_headers()), 'the recipient retracting');
|
|
109
|
+
assert.ok(await is_pending(fixture, recipient, offer.id), 'the refusals leave it pending');
|
|
110
|
+
assert_ok(await retract(fixture, offer.id, fixture.create_session_headers()), 'retract');
|
|
111
|
+
});
|
|
112
|
+
test('a decline or retract after accept → 400 role_grant_offer_terminal; a stranger still gets 404', async () => {
|
|
113
|
+
const fixture = await setup_test();
|
|
114
|
+
const recipient = await fixture.create_account({ username: 'lifecycle_accepted' });
|
|
115
|
+
const stranger = await fixture.create_account({ username: 'lifecycle_after_accept' });
|
|
116
|
+
const offer = await create_offer(fixture, recipient);
|
|
117
|
+
assert_ok(await accept(fixture, offer.id, recipient.create_session_headers()), 'accept');
|
|
118
|
+
assert_terminal(await decline(fixture, offer.id, recipient.create_session_headers()), 'a decline after accept');
|
|
119
|
+
assert_terminal(await retract(fixture, offer.id, fixture.create_session_headers()), 'a retract after accept');
|
|
120
|
+
// the scope check runs before the terminal check, so a non-party
|
|
121
|
+
// learns nothing of the offer's state
|
|
122
|
+
assert_not_found(await decline(fixture, offer.id, stranger.create_session_headers()), 'a stranger declining an accepted offer');
|
|
123
|
+
assert_not_found(await retract(fixture, offer.id, stranger.create_session_headers()), 'a stranger retracting an accepted offer');
|
|
124
|
+
});
|
|
125
|
+
test('a second decline, a second retract, and either after the other → 400 role_grant_offer_terminal', async () => {
|
|
126
|
+
const fixture = await setup_test();
|
|
127
|
+
const recipient = await fixture.create_account({ username: 'lifecycle_twice' });
|
|
128
|
+
const recipient_headers = recipient.create_session_headers();
|
|
129
|
+
const grantor_headers = fixture.create_session_headers();
|
|
130
|
+
const declined = await create_offer(fixture, recipient);
|
|
131
|
+
assert_ok(await decline(fixture, declined.id, recipient_headers), 'the first decline');
|
|
132
|
+
assert_terminal(await decline(fixture, declined.id, recipient_headers), 'a second decline');
|
|
133
|
+
assert_terminal(await retract(fixture, declined.id, grantor_headers), 'a retract after decline');
|
|
134
|
+
const retracted = await create_offer(fixture, recipient);
|
|
135
|
+
assert_ok(await retract(fixture, retracted.id, grantor_headers), 'the first retract');
|
|
136
|
+
assert_terminal(await retract(fixture, retracted.id, grantor_headers), 'a second retract');
|
|
137
|
+
assert_terminal(await decline(fixture, retracted.id, recipient_headers), 'a decline after retract');
|
|
138
|
+
});
|
|
139
|
+
test('a decline and a retract each write one audit row; their refusals write none', async () => {
|
|
140
|
+
const fixture = await setup_test();
|
|
141
|
+
const recipient = await fixture.create_account({ username: 'lifecycle_audited' });
|
|
142
|
+
const stranger = await fixture.create_account({ username: 'lifecycle_unaudited' });
|
|
143
|
+
const recipient_headers = recipient.create_session_headers();
|
|
144
|
+
const grantor_headers = fixture.create_session_headers();
|
|
145
|
+
const with_reason = await create_offer(fixture, recipient);
|
|
146
|
+
assert_ok(await decline(fixture, with_reason.id, recipient_headers, 'not now'), 'decline');
|
|
147
|
+
const without_reason = await create_offer(fixture, recipient);
|
|
148
|
+
assert_ok(await decline(fixture, without_reason.id, recipient_headers), 'decline');
|
|
149
|
+
const retracted = await create_offer(fixture, recipient);
|
|
150
|
+
assert_ok(await retract(fixture, retracted.id, grantor_headers), 'retract');
|
|
151
|
+
const read = async () => ({
|
|
152
|
+
declines: await list_audit_events(fixture, { event_type: 'role_grant_offer_decline' }, rpc_path),
|
|
153
|
+
retracts: await list_audit_events(fixture, { event_type: 'role_grant_offer_retract' }, rpc_path)
|
|
154
|
+
});
|
|
155
|
+
const before = await read();
|
|
156
|
+
const decline_row = (offer_id) => {
|
|
157
|
+
const rows = before.declines.filter((e) => e.metadata?.offer_id === offer_id);
|
|
158
|
+
assert.strictEqual(rows.length, 1, `one role_grant_offer_decline row for ${offer_id}`);
|
|
159
|
+
return rows[0];
|
|
160
|
+
};
|
|
161
|
+
for (const [offer_id, reason] of [
|
|
162
|
+
[with_reason.id, 'not now'],
|
|
163
|
+
[without_reason.id, undefined]
|
|
164
|
+
]) {
|
|
165
|
+
const row = decline_row(offer_id);
|
|
166
|
+
assert.strictEqual(row.outcome, 'success');
|
|
167
|
+
// a decline is the recipient's act, addressed to the grantor
|
|
168
|
+
assert.strictEqual(row.account_id, recipient.account.id);
|
|
169
|
+
assert.strictEqual(row.actor_id, recipient.actor.id);
|
|
170
|
+
assert.strictEqual(row.target_account_id, fixture.account.id);
|
|
171
|
+
assert.strictEqual(row.target_actor_id, fixture.actor.id);
|
|
172
|
+
// a decline without a reason records none — absent, not null
|
|
173
|
+
assert_audit_metadata_fields(fixture, row.metadata, reason === undefined
|
|
174
|
+
? { offer_id, role, scope_id: null }
|
|
175
|
+
: { offer_id, role, scope_id: null, reason }, reason === undefined ? ['reason'] : []);
|
|
176
|
+
}
|
|
177
|
+
assert.strictEqual(before.retracts.length, 1, 'one role_grant_offer_retract row');
|
|
178
|
+
const retract_row = before.retracts[0];
|
|
179
|
+
assert.strictEqual(retract_row.outcome, 'success');
|
|
180
|
+
// a retract is the grantor's act, addressed to the recipient; the
|
|
181
|
+
// offer named no actor, so no target actor
|
|
182
|
+
assert.strictEqual(retract_row.account_id, fixture.account.id);
|
|
183
|
+
assert.strictEqual(retract_row.actor_id, fixture.actor.id);
|
|
184
|
+
assert.strictEqual(retract_row.target_account_id, recipient.account.id);
|
|
185
|
+
assert.strictEqual(retract_row.target_actor_id, null);
|
|
186
|
+
assert_audit_metadata_fields(fixture, retract_row.metadata, {
|
|
187
|
+
offer_id: retracted.id,
|
|
188
|
+
role,
|
|
189
|
+
scope_id: null
|
|
190
|
+
});
|
|
191
|
+
// every refusal: not a party, then already decided
|
|
192
|
+
const pending = await create_offer(fixture, recipient);
|
|
193
|
+
assert_not_found(await decline(fixture, pending.id, stranger.create_session_headers()), 'a stranger declining');
|
|
194
|
+
assert_not_found(await retract(fixture, pending.id, stranger.create_session_headers()), 'a stranger retracting');
|
|
195
|
+
assert_terminal(await decline(fixture, with_reason.id, recipient_headers), 'a re-decline');
|
|
196
|
+
assert_terminal(await retract(fixture, retracted.id, grantor_headers), 'a re-retract');
|
|
197
|
+
assert_terminal(await decline(fixture, retracted.id, recipient_headers), 'a decline after retract');
|
|
198
|
+
const after = await read();
|
|
199
|
+
assert.strictEqual(after.declines.length, before.declines.length, 'no decline row added');
|
|
200
|
+
assert.strictEqual(after.retracts.length, before.retracts.length, 'no retract row added');
|
|
201
|
+
});
|
|
202
|
+
});
|
|
203
|
+
};
|
|
@@ -1,16 +1,13 @@
|
|
|
1
1
|
import '../assert_dev_env.js';
|
|
2
2
|
import { type BackendCapabilities } from './capabilities.js';
|
|
3
3
|
import type { SetupTest } from './setup.js';
|
|
4
|
+
import { type WsEndpointTarget } from './ws_endpoint.js';
|
|
4
5
|
/** Configuration for `describe_role_grant_offer_notification_ws_tests`. */
|
|
5
|
-
export interface RoleGrantOfferNotificationWsTestOptions {
|
|
6
|
+
export interface RoleGrantOfferNotificationWsTestOptions extends WsEndpointTarget {
|
|
6
7
|
/** Per-test fixture producer (`default_cross_process_setup(handle, ...)`). */
|
|
7
8
|
readonly setup_test: SetupTest;
|
|
8
9
|
/** Backend capability flags; every case gates on `capabilities.ws`. */
|
|
9
10
|
readonly capabilities: BackendCapabilities;
|
|
10
|
-
/** Base URL the backend is reachable at (e.g. `http://localhost:1178`). */
|
|
11
|
-
readonly base_url: string;
|
|
12
|
-
/** WebSocket endpoint path on the backend (e.g. `/api/ws`). */
|
|
13
|
-
readonly ws_path: string;
|
|
14
11
|
/**
|
|
15
12
|
* The roles `ws_path` gates its upgrade on (its `required_roles`), or
|
|
16
13
|
* omitted / `[]` for an ungated endpoint. Declare them for a backend whose
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"role_grant_offer_notification_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_offer_notification_ws.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"role_grant_offer_notification_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/role_grant_offer_notification_ws.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkF9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AAKtE,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAmB,KAAK,gBAAgB,EAAyB,MAAM,kBAAkB,CAAC;AAKjG,2EAA2E;AAC3E,MAAM,WAAW,uCAAwC,SAAQ,gBAAgB;IAChF,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,uEAAuE;IACvE,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,iBAAiB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,+CAA+C,GAC3D,SAAS,uCAAuC,KAC9C,IAgbF,CAAC"}
|