@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,897 @@
|
|
|
1
|
+
import '../assert_dev_env.js';
|
|
2
|
+
/**
|
|
3
|
+
* One call that runs every shipped single-backend cross suite a backend's
|
|
4
|
+
* capabilities admit — `describe_spine_cross_suites(handle, options)`.
|
|
5
|
+
*
|
|
6
|
+
* A consumer's cross project otherwise wires one file per upstream suite, so a
|
|
7
|
+
* suite added here never reaches it unless someone adds the file. The one-call
|
|
8
|
+
* form runs `SUITE_NAMES` — the closed registry of shipped suites, each named by
|
|
9
|
+
* its module — and builds every suite's options from the handle: the paths,
|
|
10
|
+
* cookie name and capabilities from `handle.config` (`to_cross_suite_options`),
|
|
11
|
+
* the daemon token from the handle, and each suite's `setup_test` through
|
|
12
|
+
* `default_cross_process_setup` with whatever seeding that suite needs (the
|
|
13
|
+
* multi-actor keeper, the offer grantor, the cell role holder). Each suite
|
|
14
|
+
* gates its own cases on the capabilities, so a backend runs what it mounts.
|
|
15
|
+
*
|
|
16
|
+
* **Options are additive.**
|
|
17
|
+
*
|
|
18
|
+
* - `exclude` names a suite with the reason it can't run on this backend; the
|
|
19
|
+
* reason is required and shows in the run as a skipped test, so a skip is
|
|
20
|
+
* stated in code and visible in the report. `testing_backdoor` can't be
|
|
21
|
+
* excluded, by suite or by case, nor its `capabilities.testing_backdoors`
|
|
22
|
+
* changed: it checks the declaration the backdoor gating reads.
|
|
23
|
+
* - `exclude_cases` names single tests of a suite that does run, each by its
|
|
24
|
+
* name path (describe titles and test name, joined with `' > '`) with a
|
|
25
|
+
* required reason; one `beforeEach` skips them at run time, so each shows as
|
|
26
|
+
* skipped with its reason, and an entry matching no collected test of its
|
|
27
|
+
* suite fails the closing check test.
|
|
28
|
+
* - `overrides` replaces a suite's derived options, field by field (a shallow
|
|
29
|
+
* merge over what the handle gives) — a role list the backend's WS upgrade
|
|
30
|
+
* requires, an `xfail` for a tracked divergence, a narrower capability set.
|
|
31
|
+
* An entry can be a function of the derived options instead, for a change
|
|
32
|
+
* built on a derived value. A required option the merge leaves `undefined`
|
|
33
|
+
* throws at collection.
|
|
34
|
+
* - `rpc_success_fixtures` / `rpc_skip_methods` / `rpc_input_overrides` go to
|
|
35
|
+
* the standard bundle's RPC round-trip; `rpc_input_overrides` also seeds
|
|
36
|
+
* `rpc_adversarial`'s params cases (each override is the valid base they
|
|
37
|
+
* break one field of). A key naming a method on neither the declared surface
|
|
38
|
+
* nor `live_rpc_endpoints` throws at collection.
|
|
39
|
+
* - `ws_required_roles` names the roles the backend's WS upgrade requires, so the
|
|
40
|
+
* suites that open sockets for accounts they create give them those roles.
|
|
41
|
+
* - `suite_roles` names the roles the role-shaped suites use when the backend's
|
|
42
|
+
* registry lacks the spine's own (`cell_editor`, `participant`); the call
|
|
43
|
+
* seeds the holder each needs.
|
|
44
|
+
*
|
|
45
|
+
* **Inputs the handle doesn't carry.** The declared surface (`surface_source`,
|
|
46
|
+
* `session_options`, `rpc_endpoints`, `roles`) is a required option. Paths and
|
|
47
|
+
* roles that name what the reference spines mount (the role-gated WS endpoint
|
|
48
|
+
* and its roles) default to the `SPINE_*` constants, and a suite whose
|
|
49
|
+
* capability flag is off never reaches them. Anything else a suite needs comes
|
|
50
|
+
* from the handle when it can (`session_cookie_codec`'s `cookie_keys` from the
|
|
51
|
+
* config's `SECRET_FUZ_COOKIE_KEYS`), else from `overrides`; when neither has
|
|
52
|
+
* it, the call throws naming the suite — provide it or exclude the suite.
|
|
53
|
+
*
|
|
54
|
+
* **Backdoors.** The backend's `capabilities.testing_backdoors` must declare
|
|
55
|
+
* `SPINE_SUITES_REQUIRED_BACKDOORS` (the per-test reset and the audit
|
|
56
|
+
* barrier), and `_testing_put_fact` when it declares `fact_serving`; the call
|
|
57
|
+
* throws otherwise. A suite every case of which drives another backdoor
|
|
58
|
+
* (`credential_deadline`, `origin_conformance`, `ws_outbound_overflow`) skips
|
|
59
|
+
* itself on a backend not declaring it, shown like an exclude with a reason
|
|
60
|
+
* naming the backdoor — so an exclude, override or `exclude_cases` entry for
|
|
61
|
+
* it is refused as dead.
|
|
62
|
+
*
|
|
63
|
+
* **Not in the one call** (`STANDALONE_CROSS_SUITES`, with the reasons): the
|
|
64
|
+
* suites that need two handles (the differential), a backend spawned with live
|
|
65
|
+
* limiters (`login_security`, `ws_action_rate_limit` — the standard suites would
|
|
66
|
+
* exhaust them), and the conformance runner, which runs the consumer's own
|
|
67
|
+
* cases. Each keeps its own entry point.
|
|
68
|
+
*
|
|
69
|
+
* Suites are registered without a wrapping `describe`, so their test names are
|
|
70
|
+
* the same as when each is called on its own — and an `exclude_cases` path is
|
|
71
|
+
* the name the reporter prints after the file and any `describe` wrapping the
|
|
72
|
+
* call.
|
|
73
|
+
*
|
|
74
|
+
* @module
|
|
75
|
+
*/
|
|
76
|
+
import { beforeAll, beforeEach, describe, test, TestRunner } from 'vitest';
|
|
77
|
+
import { ROLE_KEEPER, ROLE_ADMIN } from '../../auth/role_schema.js';
|
|
78
|
+
import { resolve_rpc_endpoints_for_setup } from '../rpc_helpers.js';
|
|
79
|
+
import { describe_account_lifecycle_cross_tests } from './account_lifecycle.js';
|
|
80
|
+
import { describe_actor_lookup_cross_tests } from './actor_lookup.js';
|
|
81
|
+
import { describe_app_settings_cross_tests } from './app_settings.js';
|
|
82
|
+
import { describe_body_size_cross_tests } from './body_size.js';
|
|
83
|
+
import { describe_body_size_smuggling_cross_tests } from './body_size_smuggling.js';
|
|
84
|
+
import { describe_cell_crud_cross_tests } from './cell_crud.js';
|
|
85
|
+
import { describe_cell_gated_create_cross_tests } from './cell_gated_create.js';
|
|
86
|
+
import { CELL_EDITOR_ROLE, CELL_ROLE_HOLDER_USERNAME, describe_cell_grant_role_cross_tests } from './cell_grant_role.js';
|
|
87
|
+
import { describe_cell_moderate_cross_tests } from './cell_moderate.js';
|
|
88
|
+
import { describe_cell_ref_admission_cross_tests } from './cell_ref_admission.js';
|
|
89
|
+
import { describe_cell_relations_cross_tests } from './cell_relations.js';
|
|
90
|
+
import { describe_cookie_attributes_cross_tests } from './cookie_attributes.js';
|
|
91
|
+
import { describe_credential_deadline_cross_tests } from './credential_deadline.js';
|
|
92
|
+
import { describe_credential_header_robustness_cross_tests } from './credential_header_robustness.js';
|
|
93
|
+
import { describe_fact_serving_cross_tests } from './fact_serving.js';
|
|
94
|
+
import { describe_identity_parity_cross_tests } from './identity_parity.js';
|
|
95
|
+
import { describe_origin_cross_tests } from './origin.js';
|
|
96
|
+
import { describe_origin_conformance_cross_tests } from './origin_conformance.js';
|
|
97
|
+
import { describe_origin_scope_cross_tests } from './origin_scope.js';
|
|
98
|
+
import { describe_peer_ping_ws_tests } from './peer_ping_ws.js';
|
|
99
|
+
import { describe_query_shape_cross_tests } from './query_shape.js';
|
|
100
|
+
import { describe_ready_cross_tests } from './ready.js';
|
|
101
|
+
import { describe_role_grant_offer_enumeration_cross_tests, OFFER_GRANTOR_USERNAME } from './role_grant_offer_enumeration.js';
|
|
102
|
+
import { describe_role_grant_offer_lifecycle_cross_tests } from './role_grant_offer_lifecycle.js';
|
|
103
|
+
import { describe_role_grant_offer_notification_ws_tests } from './role_grant_offer_notification_ws.js';
|
|
104
|
+
import { describe_role_grant_participation_cross_tests } from './role_grant_participation.js';
|
|
105
|
+
import { describe_role_grant_role_grammar_cross_tests } from './role_grant_role_grammar.js';
|
|
106
|
+
import { describe_rpc_adversarial_cross_tests } from './rpc_adversarial.js';
|
|
107
|
+
import { describe_session_cap_cross_tests } from './session_cap.js';
|
|
108
|
+
import { describe_session_cookie_codec_cross_tests } from './session_cookie_codec.js';
|
|
109
|
+
import { default_cross_process_setup } from './setup.js';
|
|
110
|
+
import { describe_simple_request_cross_tests } from './simple_request.js';
|
|
111
|
+
import { SPINE_PARTICIPANT_ROLE, SPINE_ROLE_GATED_WS_PATH, SPINE_ROLE_GATED_WS_ROLES } from './spine_surface_constants.js';
|
|
112
|
+
import { describe_cross_process_sse_tests } from './sse_round_trip.js';
|
|
113
|
+
import { describe_standard_cross_process_tests } from './standard.js';
|
|
114
|
+
import { describe_testing_backdoor_cross_tests } from './testing_backdoor.js';
|
|
115
|
+
import { describe_token_lifetime_cross_tests } from './token_lifetime.js';
|
|
116
|
+
import { describe_token_scope_surface_cross_tests } from './token_scope_surface.js';
|
|
117
|
+
import { describe_transport_composition_cross_tests } from './transport_composition.js';
|
|
118
|
+
import { describe_ws_connection_cap_cross_tests } from './ws_connection_cap.js';
|
|
119
|
+
import { describe_ws_inbound_dispatch_cross_tests } from './ws_inbound_dispatch.js';
|
|
120
|
+
import { describe_ws_outbound_overflow_cross_tests } from './ws_outbound_overflow.js';
|
|
121
|
+
import { describe_ws_role_gated_cross_tests } from './ws_role_gated.js';
|
|
122
|
+
import { describe_cross_process_ws_tests } from './ws_round_trip.js';
|
|
123
|
+
/**
|
|
124
|
+
* Every shipped suite `describe_spine_cross_suites` runs, each named by its
|
|
125
|
+
* module under `testing/cross_backend/` (`ws_round_trip` is
|
|
126
|
+
* `describe_cross_process_ws_tests`, `standard` the standard bundle). Closed: a
|
|
127
|
+
* guard test holds it, with `STANDALONE_CROSS_SUITES`, to every module that
|
|
128
|
+
* exports a `describe_*` suite, so a new suite fails until it is registered.
|
|
129
|
+
*/
|
|
130
|
+
export const SUITE_NAMES = [
|
|
131
|
+
'account_lifecycle',
|
|
132
|
+
'actor_lookup',
|
|
133
|
+
'app_settings',
|
|
134
|
+
'body_size',
|
|
135
|
+
'body_size_smuggling',
|
|
136
|
+
'cell_crud',
|
|
137
|
+
'cell_gated_create',
|
|
138
|
+
'cell_grant_role',
|
|
139
|
+
'cell_moderate',
|
|
140
|
+
'cell_ref_admission',
|
|
141
|
+
'cell_relations',
|
|
142
|
+
'cookie_attributes',
|
|
143
|
+
'credential_deadline',
|
|
144
|
+
'credential_header_robustness',
|
|
145
|
+
'fact_serving',
|
|
146
|
+
'identity_parity',
|
|
147
|
+
'origin',
|
|
148
|
+
'origin_conformance',
|
|
149
|
+
'origin_scope',
|
|
150
|
+
'peer_ping_ws',
|
|
151
|
+
'query_shape',
|
|
152
|
+
'ready',
|
|
153
|
+
'role_grant_offer_enumeration',
|
|
154
|
+
'role_grant_offer_lifecycle',
|
|
155
|
+
'role_grant_offer_notification_ws',
|
|
156
|
+
'role_grant_participation',
|
|
157
|
+
'role_grant_role_grammar',
|
|
158
|
+
'rpc_adversarial',
|
|
159
|
+
'session_cap',
|
|
160
|
+
'session_cookie_codec',
|
|
161
|
+
'simple_request',
|
|
162
|
+
'sse_round_trip',
|
|
163
|
+
'standard',
|
|
164
|
+
'testing_backdoor',
|
|
165
|
+
'token_lifetime',
|
|
166
|
+
'token_scope_surface',
|
|
167
|
+
'transport_composition',
|
|
168
|
+
'ws_connection_cap',
|
|
169
|
+
'ws_inbound_dispatch',
|
|
170
|
+
'ws_outbound_overflow',
|
|
171
|
+
'ws_role_gated',
|
|
172
|
+
'ws_round_trip'
|
|
173
|
+
];
|
|
174
|
+
/**
|
|
175
|
+
* The shipped suite modules `describe_spine_cross_suites` leaves out, each with
|
|
176
|
+
* why. Their entry points stay as they are; a consumer calls them beside the
|
|
177
|
+
* one call.
|
|
178
|
+
*/
|
|
179
|
+
export const STANDALONE_CROSS_SUITES = {
|
|
180
|
+
differential: 'compares two backends: it needs both handles of a dual-spawn project, not one backend',
|
|
181
|
+
login_security: 'needs a backend spawned with the login limiters enabled, which the standard suites would exhaust',
|
|
182
|
+
ws_action_rate_limit: 'needs a backend spawned with the action limiters enabled, which the standard suites would exhaust',
|
|
183
|
+
conformance_table: "a runner over the consumer's own cases, not a suite"
|
|
184
|
+
};
|
|
185
|
+
/**
|
|
186
|
+
* The backdoors `describe_spine_cross_suites` requires of every backend, since
|
|
187
|
+
* the suites share them: `_testing_reset` is every `setup_test`'s per-test
|
|
188
|
+
* reset, and `_testing_drain_effects` the barrier every audit read waits on. A
|
|
189
|
+
* backend whose `capabilities.testing_backdoors` lacks one fails at collection.
|
|
190
|
+
*/
|
|
191
|
+
export const SPINE_SUITES_REQUIRED_BACKDOORS = Object.freeze([
|
|
192
|
+
'_testing_reset',
|
|
193
|
+
'_testing_drain_effects'
|
|
194
|
+
]);
|
|
195
|
+
/**
|
|
196
|
+
* The suite options a handle's `config` supplies — the fields most cross
|
|
197
|
+
* suites take beside `setup_test`. For a suite called on its own:
|
|
198
|
+
* `describe_ready_cross_tests({setup_test, ...to_cross_suite_options(handle)})`
|
|
199
|
+
* passes extra fields the suite's type doesn't list, so destructure what it
|
|
200
|
+
* takes instead when calling it with an object literal.
|
|
201
|
+
*/
|
|
202
|
+
export const to_cross_suite_options = (handle) => {
|
|
203
|
+
const { capabilities, base_url, rpc_path, ws_path, sse_path, cookie_name } = handle.config;
|
|
204
|
+
return { capabilities, base_url, rpc_path, ws_path, sse_path, cookie_name };
|
|
205
|
+
};
|
|
206
|
+
/** The capability-gated RPC-path shape the cell suites and several others share. */
|
|
207
|
+
const rpc_gated = (c, seed) => ({
|
|
208
|
+
setup_test: c.setup(seed),
|
|
209
|
+
capabilities: c.config.capabilities,
|
|
210
|
+
rpc_path: c.config.rpc_path
|
|
211
|
+
});
|
|
212
|
+
const SUITE_ENTRIES = {
|
|
213
|
+
account_lifecycle: {
|
|
214
|
+
derive: (c) => rpc_gated(c),
|
|
215
|
+
describe: describe_account_lifecycle_cross_tests,
|
|
216
|
+
required: { setup_test: true, capabilities: true }
|
|
217
|
+
},
|
|
218
|
+
actor_lookup: {
|
|
219
|
+
derive: (c) => ({ setup_test: c.setup(), rpc_path: c.config.rpc_path }),
|
|
220
|
+
describe: describe_actor_lookup_cross_tests,
|
|
221
|
+
required: { setup_test: true }
|
|
222
|
+
},
|
|
223
|
+
app_settings: {
|
|
224
|
+
derive: (c) => ({ setup_test: c.setup(), rpc_path: c.config.rpc_path }),
|
|
225
|
+
describe: describe_app_settings_cross_tests,
|
|
226
|
+
required: { setup_test: true }
|
|
227
|
+
},
|
|
228
|
+
body_size: {
|
|
229
|
+
derive: (c) => ({
|
|
230
|
+
setup_test: c.setup(),
|
|
231
|
+
rpc_path: c.config.rpc_path,
|
|
232
|
+
base_url: c.config.base_url,
|
|
233
|
+
rpc_max_body_bytes: c.options.rpc_max_body_bytes
|
|
234
|
+
}),
|
|
235
|
+
describe: describe_body_size_cross_tests,
|
|
236
|
+
required: { setup_test: true, base_url: true }
|
|
237
|
+
},
|
|
238
|
+
body_size_smuggling: {
|
|
239
|
+
derive: (c) => ({
|
|
240
|
+
base_url: c.config.base_url,
|
|
241
|
+
rpc_path: c.config.rpc_path,
|
|
242
|
+
rpc_max_body_bytes: c.options.rpc_max_body_bytes
|
|
243
|
+
}),
|
|
244
|
+
describe: describe_body_size_smuggling_cross_tests,
|
|
245
|
+
required: { base_url: true }
|
|
246
|
+
},
|
|
247
|
+
cell_crud: {
|
|
248
|
+
derive: (c) => rpc_gated(c),
|
|
249
|
+
describe: describe_cell_crud_cross_tests,
|
|
250
|
+
required: { setup_test: true, capabilities: true }
|
|
251
|
+
},
|
|
252
|
+
cell_gated_create: {
|
|
253
|
+
derive: (c) => rpc_gated(c),
|
|
254
|
+
describe: describe_cell_gated_create_cross_tests,
|
|
255
|
+
required: { setup_test: true, capabilities: true }
|
|
256
|
+
},
|
|
257
|
+
cell_grant_role: {
|
|
258
|
+
// the holder is seeded at reset: `CELL_EDITOR_ROLE` has no grant path
|
|
259
|
+
derive: (c) => {
|
|
260
|
+
const role = c.options.suite_roles?.cell_grant ?? CELL_EDITOR_ROLE;
|
|
261
|
+
return {
|
|
262
|
+
...rpc_gated(c, {
|
|
263
|
+
extra_accounts: [{ username: CELL_ROLE_HOLDER_USERNAME, roles: [role] }]
|
|
264
|
+
}),
|
|
265
|
+
role
|
|
266
|
+
};
|
|
267
|
+
},
|
|
268
|
+
describe: describe_cell_grant_role_cross_tests,
|
|
269
|
+
required: { setup_test: true, capabilities: true }
|
|
270
|
+
},
|
|
271
|
+
cell_moderate: {
|
|
272
|
+
derive: (c) => rpc_gated(c),
|
|
273
|
+
describe: describe_cell_moderate_cross_tests,
|
|
274
|
+
required: { setup_test: true, capabilities: true }
|
|
275
|
+
},
|
|
276
|
+
cell_ref_admission: {
|
|
277
|
+
derive: (c) => rpc_gated(c),
|
|
278
|
+
describe: describe_cell_ref_admission_cross_tests,
|
|
279
|
+
required: { setup_test: true, capabilities: true }
|
|
280
|
+
},
|
|
281
|
+
cell_relations: {
|
|
282
|
+
derive: (c) => rpc_gated(c),
|
|
283
|
+
describe: describe_cell_relations_cross_tests,
|
|
284
|
+
required: { setup_test: true, capabilities: true }
|
|
285
|
+
},
|
|
286
|
+
cookie_attributes: {
|
|
287
|
+
derive: (c) => ({ setup_test: c.setup(), cookie_name: c.config.cookie_name }),
|
|
288
|
+
describe: describe_cookie_attributes_cross_tests,
|
|
289
|
+
required: { setup_test: true, cookie_name: true }
|
|
290
|
+
},
|
|
291
|
+
credential_deadline: {
|
|
292
|
+
derive: (c) => ({
|
|
293
|
+
setup_test: c.setup(),
|
|
294
|
+
capabilities: c.config.capabilities,
|
|
295
|
+
base_url: c.config.base_url,
|
|
296
|
+
ws_path: c.config.ws_path,
|
|
297
|
+
sse_path: c.config.sse_path,
|
|
298
|
+
rpc_path: c.config.rpc_path,
|
|
299
|
+
ws_account_roles: c.options.ws_required_roles
|
|
300
|
+
}),
|
|
301
|
+
describe: describe_credential_deadline_cross_tests,
|
|
302
|
+
required: { setup_test: true, capabilities: true, base_url: true, ws_path: true },
|
|
303
|
+
backdoors: ['_testing_shorten_credential_expiry']
|
|
304
|
+
},
|
|
305
|
+
credential_header_robustness: {
|
|
306
|
+
derive: (c) => ({
|
|
307
|
+
base_url: c.config.base_url,
|
|
308
|
+
rpc_path: c.config.rpc_path,
|
|
309
|
+
daemon_token: c.handle.daemon_token
|
|
310
|
+
}),
|
|
311
|
+
describe: describe_credential_header_robustness_cross_tests,
|
|
312
|
+
required: { base_url: true, daemon_token: true }
|
|
313
|
+
},
|
|
314
|
+
fact_serving: {
|
|
315
|
+
// the multi-actor setup reaches the fallthrough a single-actor keeper can't
|
|
316
|
+
derive: (c) => ({
|
|
317
|
+
...rpc_gated(c),
|
|
318
|
+
setup_test_multi_actor: c.setup({ extra_actors: ['second_persona'] })
|
|
319
|
+
}),
|
|
320
|
+
describe: describe_fact_serving_cross_tests,
|
|
321
|
+
required: { setup_test: true, capabilities: true }
|
|
322
|
+
},
|
|
323
|
+
identity_parity: {
|
|
324
|
+
derive: (c) => ({ setup_test: c.setup() }),
|
|
325
|
+
describe: describe_identity_parity_cross_tests,
|
|
326
|
+
required: { setup_test: true }
|
|
327
|
+
},
|
|
328
|
+
origin: {
|
|
329
|
+
derive: (c) => ({ setup_test: c.setup(), rpc_path: c.config.rpc_path }),
|
|
330
|
+
describe: describe_origin_cross_tests,
|
|
331
|
+
required: { setup_test: true }
|
|
332
|
+
},
|
|
333
|
+
origin_conformance: {
|
|
334
|
+
derive: (c) => ({ setup_test: c.setup(), rpc_path: c.config.rpc_path }),
|
|
335
|
+
describe: describe_origin_conformance_cross_tests,
|
|
336
|
+
required: { setup_test: true },
|
|
337
|
+
backdoors: ['_testing_origin_check']
|
|
338
|
+
},
|
|
339
|
+
origin_scope: {
|
|
340
|
+
derive: (c) => ({
|
|
341
|
+
setup_test: c.setup(),
|
|
342
|
+
capabilities: c.config.capabilities,
|
|
343
|
+
base_url: c.config.base_url
|
|
344
|
+
}),
|
|
345
|
+
describe: describe_origin_scope_cross_tests,
|
|
346
|
+
required: { setup_test: true, capabilities: true }
|
|
347
|
+
},
|
|
348
|
+
peer_ping_ws: {
|
|
349
|
+
derive: (c) => ({
|
|
350
|
+
setup_test: c.setup(),
|
|
351
|
+
capabilities: c.config.capabilities,
|
|
352
|
+
base_url: c.config.base_url,
|
|
353
|
+
ws_path: c.config.ws_path
|
|
354
|
+
}),
|
|
355
|
+
describe: describe_peer_ping_ws_tests,
|
|
356
|
+
required: { setup_test: true, capabilities: true, base_url: true, ws_path: true }
|
|
357
|
+
},
|
|
358
|
+
query_shape: {
|
|
359
|
+
derive: (c) => rpc_gated(c),
|
|
360
|
+
describe: describe_query_shape_cross_tests,
|
|
361
|
+
required: { setup_test: true, capabilities: true }
|
|
362
|
+
},
|
|
363
|
+
ready: {
|
|
364
|
+
derive: (c) => ({ setup_test: c.setup(), capabilities: c.config.capabilities }),
|
|
365
|
+
describe: describe_ready_cross_tests,
|
|
366
|
+
required: { setup_test: true, capabilities: true }
|
|
367
|
+
},
|
|
368
|
+
role_grant_offer_enumeration: {
|
|
369
|
+
// a sibling actor for the wrong-persona arm, and a single-actor admin
|
|
370
|
+
// grantor (the keeper can't offer to itself)
|
|
371
|
+
derive: (c) => ({
|
|
372
|
+
setup_test: c.setup({
|
|
373
|
+
extra_actors: ['offer_sibling'],
|
|
374
|
+
extra_accounts: [{ username: OFFER_GRANTOR_USERNAME, roles: [ROLE_ADMIN] }]
|
|
375
|
+
}),
|
|
376
|
+
rpc_path: c.config.rpc_path
|
|
377
|
+
}),
|
|
378
|
+
describe: describe_role_grant_offer_enumeration_cross_tests,
|
|
379
|
+
required: { setup_test: true }
|
|
380
|
+
},
|
|
381
|
+
role_grant_offer_lifecycle: {
|
|
382
|
+
derive: (c) => ({
|
|
383
|
+
setup_test: c.setup(),
|
|
384
|
+
rpc_path: c.config.rpc_path,
|
|
385
|
+
role: c.options.suite_roles?.conferral
|
|
386
|
+
}),
|
|
387
|
+
describe: describe_role_grant_offer_lifecycle_cross_tests,
|
|
388
|
+
required: { setup_test: true }
|
|
389
|
+
},
|
|
390
|
+
role_grant_offer_notification_ws: {
|
|
391
|
+
derive: (c) => ({
|
|
392
|
+
setup_test: c.setup(),
|
|
393
|
+
capabilities: c.config.capabilities,
|
|
394
|
+
base_url: c.config.base_url,
|
|
395
|
+
ws_path: c.config.ws_path,
|
|
396
|
+
ws_required_roles: c.options.ws_required_roles
|
|
397
|
+
}),
|
|
398
|
+
describe: describe_role_grant_offer_notification_ws_tests,
|
|
399
|
+
required: { setup_test: true, capabilities: true, base_url: true, ws_path: true }
|
|
400
|
+
},
|
|
401
|
+
role_grant_participation: {
|
|
402
|
+
derive: (c) => ({
|
|
403
|
+
setup_test: c.setup(),
|
|
404
|
+
rpc_path: c.config.rpc_path,
|
|
405
|
+
role: c.options.suite_roles?.conferral
|
|
406
|
+
}),
|
|
407
|
+
describe: describe_role_grant_participation_cross_tests,
|
|
408
|
+
required: { setup_test: true }
|
|
409
|
+
},
|
|
410
|
+
role_grant_role_grammar: {
|
|
411
|
+
derive: (c) => ({ setup_test: c.setup(), rpc_path: c.config.rpc_path }),
|
|
412
|
+
describe: describe_role_grant_role_grammar_cross_tests,
|
|
413
|
+
required: { setup_test: true }
|
|
414
|
+
},
|
|
415
|
+
rpc_adversarial: {
|
|
416
|
+
derive: (c) => ({
|
|
417
|
+
setup_test: c.setup(),
|
|
418
|
+
surface_source: c.options.surface_source,
|
|
419
|
+
live_rpc_endpoints: c.options.live_rpc_endpoints &&
|
|
420
|
+
resolve_rpc_endpoints_for_setup(c.options.live_rpc_endpoints, c.options.session_options),
|
|
421
|
+
rpc_path: c.config.rpc_path,
|
|
422
|
+
input_overrides: c.options.rpc_input_overrides
|
|
423
|
+
}),
|
|
424
|
+
describe: describe_rpc_adversarial_cross_tests,
|
|
425
|
+
required: { setup_test: true, surface_source: true }
|
|
426
|
+
},
|
|
427
|
+
session_cap: {
|
|
428
|
+
derive: (c) => ({
|
|
429
|
+
setup_test: c.setup(),
|
|
430
|
+
ws: c.ws,
|
|
431
|
+
ws_account_roles: c.options.ws_required_roles
|
|
432
|
+
}),
|
|
433
|
+
describe: describe_session_cap_cross_tests,
|
|
434
|
+
required: { setup_test: true }
|
|
435
|
+
},
|
|
436
|
+
session_cookie_codec: {
|
|
437
|
+
derive: (c) => ({
|
|
438
|
+
setup_test: c.setup(),
|
|
439
|
+
cookie_name: c.config.cookie_name,
|
|
440
|
+
cookie_keys: c.handle.config.env.SECRET_FUZ_COOKIE_KEYS ?? '',
|
|
441
|
+
rpc_path: c.config.rpc_path
|
|
442
|
+
}),
|
|
443
|
+
describe: describe_session_cookie_codec_cross_tests,
|
|
444
|
+
required: { setup_test: true, cookie_name: true, cookie_keys: true }
|
|
445
|
+
},
|
|
446
|
+
simple_request: {
|
|
447
|
+
derive: (c) => ({ setup_test: c.setup(), rpc_path: c.config.rpc_path }),
|
|
448
|
+
describe: describe_simple_request_cross_tests,
|
|
449
|
+
required: { setup_test: true }
|
|
450
|
+
},
|
|
451
|
+
sse_round_trip: {
|
|
452
|
+
derive: (c) => ({
|
|
453
|
+
setup_test: c.setup(),
|
|
454
|
+
capabilities: c.config.capabilities,
|
|
455
|
+
base_url: c.config.base_url,
|
|
456
|
+
sse_path: c.config.sse_path,
|
|
457
|
+
rpc_path: c.config.rpc_path
|
|
458
|
+
}),
|
|
459
|
+
describe: describe_cross_process_sse_tests,
|
|
460
|
+
required: { setup_test: true, capabilities: true, base_url: true }
|
|
461
|
+
},
|
|
462
|
+
standard: {
|
|
463
|
+
// a keeper-only account for the admin suite's keeper ≠ admin probe —
|
|
464
|
+
// `ROLE_KEEPER` is bootstrap-only, so it is seeded at reset
|
|
465
|
+
derive: (c) => ({
|
|
466
|
+
setup_test: c.setup({
|
|
467
|
+
extra_accounts: [{ username: 'non_admin_keeper', roles: [ROLE_KEEPER] }]
|
|
468
|
+
}),
|
|
469
|
+
surface_source: c.options.surface_source,
|
|
470
|
+
capabilities: c.config.capabilities,
|
|
471
|
+
session_options: c.options.session_options,
|
|
472
|
+
rpc_endpoints: c.options.rpc_endpoints,
|
|
473
|
+
roles: c.options.roles,
|
|
474
|
+
rpc_success_fixtures: c.options.rpc_success_fixtures,
|
|
475
|
+
rpc_skip_methods: c.options.rpc_skip_methods,
|
|
476
|
+
rpc_input_overrides: c.options.rpc_input_overrides
|
|
477
|
+
}),
|
|
478
|
+
describe: describe_standard_cross_process_tests,
|
|
479
|
+
required: {
|
|
480
|
+
setup_test: true,
|
|
481
|
+
surface_source: true,
|
|
482
|
+
session_options: true,
|
|
483
|
+
rpc_endpoints: true,
|
|
484
|
+
capabilities: true
|
|
485
|
+
}
|
|
486
|
+
},
|
|
487
|
+
testing_backdoor: {
|
|
488
|
+
derive: (c) => ({ ...rpc_gated(c), ws: c.ws }),
|
|
489
|
+
describe: describe_testing_backdoor_cross_tests,
|
|
490
|
+
required: { setup_test: true, capabilities: true }
|
|
491
|
+
},
|
|
492
|
+
token_lifetime: {
|
|
493
|
+
derive: (c) => ({ setup_test: c.setup(), rpc_path: c.config.rpc_path }),
|
|
494
|
+
describe: describe_token_lifetime_cross_tests,
|
|
495
|
+
required: { setup_test: true }
|
|
496
|
+
},
|
|
497
|
+
token_scope_surface: {
|
|
498
|
+
derive: (c) => ({ ...rpc_gated(c), sse_path: c.config.sse_path }),
|
|
499
|
+
describe: describe_token_scope_surface_cross_tests,
|
|
500
|
+
required: { setup_test: true, capabilities: true }
|
|
501
|
+
},
|
|
502
|
+
transport_composition: {
|
|
503
|
+
derive: (c) => ({
|
|
504
|
+
...rpc_gated(c),
|
|
505
|
+
base_url: c.config.base_url,
|
|
506
|
+
ws_path: c.config.ws_path,
|
|
507
|
+
sse_path: c.config.sse_path,
|
|
508
|
+
multi_actor: { setup_test: c.setup({ extra_actors: ['keeper_second_persona'] }) },
|
|
509
|
+
rpc_max_body_bytes: c.options.rpc_max_body_bytes
|
|
510
|
+
}),
|
|
511
|
+
describe: describe_transport_composition_cross_tests,
|
|
512
|
+
required: { setup_test: true, capabilities: true, base_url: true }
|
|
513
|
+
},
|
|
514
|
+
ws_connection_cap: {
|
|
515
|
+
derive: (c) => ({
|
|
516
|
+
setup_test: c.setup(),
|
|
517
|
+
capabilities: c.config.capabilities,
|
|
518
|
+
base_url: c.config.base_url,
|
|
519
|
+
ws_path: c.config.ws_path
|
|
520
|
+
}),
|
|
521
|
+
describe: describe_ws_connection_cap_cross_tests,
|
|
522
|
+
required: { setup_test: true, capabilities: true, base_url: true, ws_path: true }
|
|
523
|
+
},
|
|
524
|
+
ws_inbound_dispatch: {
|
|
525
|
+
derive: (c) => ({
|
|
526
|
+
setup_test: c.setup(),
|
|
527
|
+
capabilities: c.config.capabilities,
|
|
528
|
+
base_url: c.config.base_url,
|
|
529
|
+
ws_path: c.config.ws_path
|
|
530
|
+
}),
|
|
531
|
+
describe: describe_ws_inbound_dispatch_cross_tests,
|
|
532
|
+
required: { setup_test: true, capabilities: true, base_url: true, ws_path: true }
|
|
533
|
+
},
|
|
534
|
+
ws_outbound_overflow: {
|
|
535
|
+
derive: (c) => ({
|
|
536
|
+
setup_test: c.setup(),
|
|
537
|
+
capabilities: c.config.capabilities,
|
|
538
|
+
base_url: c.config.base_url,
|
|
539
|
+
ws_path: c.config.ws_path,
|
|
540
|
+
rpc_path: c.config.rpc_path
|
|
541
|
+
}),
|
|
542
|
+
describe: describe_ws_outbound_overflow_cross_tests,
|
|
543
|
+
required: { setup_test: true, capabilities: true, base_url: true, ws_path: true },
|
|
544
|
+
backdoors: ['_testing_flood_notifications']
|
|
545
|
+
},
|
|
546
|
+
ws_role_gated: {
|
|
547
|
+
// the reference spines' role-gated endpoint; a backend mounting its own
|
|
548
|
+
// overrides these, and one without it states `ws_role_gated: false`
|
|
549
|
+
derive: (c) => ({
|
|
550
|
+
setup_test: c.setup(),
|
|
551
|
+
capabilities: c.config.capabilities,
|
|
552
|
+
base_url: c.config.base_url,
|
|
553
|
+
ws_path: c.config.ws_path,
|
|
554
|
+
gated_ws_path: SPINE_ROLE_GATED_WS_PATH,
|
|
555
|
+
gated_roles: SPINE_ROLE_GATED_WS_ROLES,
|
|
556
|
+
scoped_role: SPINE_PARTICIPANT_ROLE
|
|
557
|
+
}),
|
|
558
|
+
describe: describe_ws_role_gated_cross_tests,
|
|
559
|
+
required: {
|
|
560
|
+
setup_test: true,
|
|
561
|
+
capabilities: true,
|
|
562
|
+
base_url: true,
|
|
563
|
+
ws_path: true,
|
|
564
|
+
gated_ws_path: true,
|
|
565
|
+
gated_roles: true,
|
|
566
|
+
scoped_role: true
|
|
567
|
+
}
|
|
568
|
+
},
|
|
569
|
+
ws_round_trip: {
|
|
570
|
+
derive: (c) => ({
|
|
571
|
+
setup_test: c.setup(),
|
|
572
|
+
capabilities: c.config.capabilities,
|
|
573
|
+
base_url: c.config.base_url,
|
|
574
|
+
ws_path: c.config.ws_path,
|
|
575
|
+
rpc_path: c.config.rpc_path,
|
|
576
|
+
ws_account_roles: c.options.ws_required_roles,
|
|
577
|
+
max_message_bytes: c.options.ws_max_message_bytes
|
|
578
|
+
}),
|
|
579
|
+
describe: describe_cross_process_ws_tests,
|
|
580
|
+
required: { setup_test: true, capabilities: true, base_url: true, ws_path: true }
|
|
581
|
+
}
|
|
582
|
+
};
|
|
583
|
+
/** Concatenate a suite's seeding onto the base setup options. */
|
|
584
|
+
const merge_setup_options = (base, seed) => ({
|
|
585
|
+
...base,
|
|
586
|
+
...seed,
|
|
587
|
+
extra_keeper_roles: [...(base?.extra_keeper_roles ?? []), ...(seed?.extra_keeper_roles ?? [])],
|
|
588
|
+
extra_accounts: [...(base?.extra_accounts ?? []), ...(seed?.extra_accounts ?? [])],
|
|
589
|
+
extra_actors: [...(base?.extra_actors ?? []), ...(seed?.extra_actors ?? [])]
|
|
590
|
+
});
|
|
591
|
+
/**
|
|
592
|
+
* The backdoors every case of `name` drives beyond
|
|
593
|
+
* `SPINE_SUITES_REQUIRED_BACKDOORS` — a backend not declaring one skips the
|
|
594
|
+
* suite.
|
|
595
|
+
*
|
|
596
|
+
* @param name - the suite
|
|
597
|
+
* @returns its `SuiteEntry.backdoors`, empty for a suite needing none
|
|
598
|
+
* @internal
|
|
599
|
+
*/
|
|
600
|
+
export const get_spine_suite_backdoors = (name) => SUITE_ENTRIES[name].backdoors ?? [];
|
|
601
|
+
const is_suite_name = (name) => SUITE_NAMES.includes(name);
|
|
602
|
+
/**
|
|
603
|
+
* Each suite whose backdoors `declared` lacks, with the generated reason it is
|
|
604
|
+
* skipped — the suites whose every case drives a backdoor (`SuiteEntry.backdoors`).
|
|
605
|
+
*/
|
|
606
|
+
const to_backdoor_skips = (declared) => {
|
|
607
|
+
const skips = new Map();
|
|
608
|
+
for (const name of SUITE_NAMES) {
|
|
609
|
+
const missing = get_spine_suite_backdoors(name).filter((m) => !declared.includes(m));
|
|
610
|
+
if (missing.length > 0) {
|
|
611
|
+
skips.set(name, `needs ${missing.map((m) => `\`${m}\``).join(', ')}, which capabilities.testing_backdoors doesn't declare`);
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
return skips;
|
|
615
|
+
};
|
|
616
|
+
/**
|
|
617
|
+
* Refuse an exclude with an empty reason, an unknown suite name, a suite both
|
|
618
|
+
* excluded and overridden, a backend missing a backdoor the suites share, an
|
|
619
|
+
* exclude or override of a suite that skips itself for an undeclared
|
|
620
|
+
* backdoor, any exclusion of `testing_backdoor`, and an `exclude_cases` entry
|
|
621
|
+
* naming a suite the call doesn't run or carrying no reason — before anything
|
|
622
|
+
* is registered.
|
|
623
|
+
*/
|
|
624
|
+
const validate_spine_cross_suites_options = (options, declared, backdoor_skips, capabilities) => {
|
|
625
|
+
if (options.rpc_input_overrides?.size) {
|
|
626
|
+
// a key the round-trip (the declared surface) and `rpc_adversarial` (that
|
|
627
|
+
// and the live mount) both lack would be ignored silently — a typo
|
|
628
|
+
const methods = new Set();
|
|
629
|
+
for (const endpoint of options.surface_source.surface.rpc_endpoints) {
|
|
630
|
+
for (const method of endpoint.methods)
|
|
631
|
+
methods.add(method.name);
|
|
632
|
+
}
|
|
633
|
+
if (options.live_rpc_endpoints) {
|
|
634
|
+
const live = resolve_rpc_endpoints_for_setup(options.live_rpc_endpoints, options.session_options);
|
|
635
|
+
for (const endpoint of live) {
|
|
636
|
+
for (const action of endpoint.actions)
|
|
637
|
+
methods.add(action.spec.method);
|
|
638
|
+
}
|
|
639
|
+
}
|
|
640
|
+
for (const method of options.rpc_input_overrides.keys()) {
|
|
641
|
+
if (!methods.has(method)) {
|
|
642
|
+
throw new Error(`describe_spine_cross_suites: rpc_input_overrides names '${method}', ` +
|
|
643
|
+
`a method on neither the declared surface nor the live mount`);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
for (const [name, reason] of Object.entries(options.exclude ?? {})) {
|
|
648
|
+
if (!is_suite_name(name)) {
|
|
649
|
+
throw new Error(`describe_spine_cross_suites: exclude names unknown suite '${name}'`);
|
|
650
|
+
}
|
|
651
|
+
if (typeof reason !== 'string' || reason.trim() === '') {
|
|
652
|
+
throw new Error(`describe_spine_cross_suites: exclude '${name}' needs a reason`);
|
|
653
|
+
}
|
|
654
|
+
if (options.overrides?.[name] !== undefined) {
|
|
655
|
+
throw new Error(`describe_spine_cross_suites: '${name}' is both excluded and overridden`);
|
|
656
|
+
}
|
|
657
|
+
if (name === 'testing_backdoor') {
|
|
658
|
+
throw new Error("describe_spine_cross_suites: 'testing_backdoor' can't be excluded — it checks the " +
|
|
659
|
+
'capabilities.testing_backdoors declaration the other suites gate on');
|
|
660
|
+
}
|
|
661
|
+
const skip = backdoor_skips.get(name);
|
|
662
|
+
if (skip !== undefined) {
|
|
663
|
+
throw new Error(`describe_spine_cross_suites: exclude '${name}' is redundant — the suite skips itself (${skip})`);
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
for (const name of Object.keys(options.overrides ?? {})) {
|
|
667
|
+
if (!is_suite_name(name)) {
|
|
668
|
+
throw new Error(`describe_spine_cross_suites: overrides names unknown suite '${name}'`);
|
|
669
|
+
}
|
|
670
|
+
const skip = backdoor_skips.get(name);
|
|
671
|
+
if (skip !== undefined) {
|
|
672
|
+
throw new Error(`describe_spine_cross_suites: overrides names '${name}', which skips itself (${skip})`);
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
for (const [name, cases] of Object.entries(options.exclude_cases ?? {})) {
|
|
676
|
+
if (!is_suite_name(name)) {
|
|
677
|
+
throw new Error(`describe_spine_cross_suites: exclude_cases names unknown suite '${name}'`);
|
|
678
|
+
}
|
|
679
|
+
if (options.exclude?.[name] !== undefined) {
|
|
680
|
+
throw new Error(`describe_spine_cross_suites: '${name}' is excluded, so its exclude_cases entries name nothing`);
|
|
681
|
+
}
|
|
682
|
+
if (name === 'testing_backdoor') {
|
|
683
|
+
throw new Error("describe_spine_cross_suites: exclude_cases can't name 'testing_backdoor' — it checks the " +
|
|
684
|
+
'capabilities.testing_backdoors declaration the other suites gate on, and a declared ' +
|
|
685
|
+
'backdoor failing its credential row is a bug, not a case to skip');
|
|
686
|
+
}
|
|
687
|
+
const skip = backdoor_skips.get(name);
|
|
688
|
+
if (skip !== undefined) {
|
|
689
|
+
throw new Error(`describe_spine_cross_suites: exclude_cases names '${name}', which skips itself (${skip})`);
|
|
690
|
+
}
|
|
691
|
+
const entries = Object.entries(cases ?? {});
|
|
692
|
+
if (entries.length === 0) {
|
|
693
|
+
throw new Error(`describe_spine_cross_suites: exclude_cases '${name}' names no test`);
|
|
694
|
+
}
|
|
695
|
+
for (const [test_name, reason] of entries) {
|
|
696
|
+
if (typeof reason !== 'string' || reason.trim() === '') {
|
|
697
|
+
throw new Error(`describe_spine_cross_suites: exclude_cases '${name}' › '${test_name}' needs a reason`);
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
const missing = SPINE_SUITES_REQUIRED_BACKDOORS.filter((m) => !declared.includes(m));
|
|
702
|
+
if (missing.length > 0) {
|
|
703
|
+
throw new Error(`describe_spine_cross_suites: capabilities.testing_backdoors must declare ${missing.join(', ')} ` +
|
|
704
|
+
'— every suite resets through _testing_reset and reads audit rows behind _testing_drain_effects');
|
|
705
|
+
}
|
|
706
|
+
if (capabilities.fact_serving && !declared.includes('_testing_put_fact')) {
|
|
707
|
+
throw new Error('describe_spine_cross_suites: capabilities.fact_serving needs _testing_put_fact in ' +
|
|
708
|
+
'capabilities.testing_backdoors — the fact suites seed through it');
|
|
709
|
+
}
|
|
710
|
+
};
|
|
711
|
+
const run_suite = (name, context) => {
|
|
712
|
+
const entry = SUITE_ENTRIES[name];
|
|
713
|
+
const derived = entry.derive(context);
|
|
714
|
+
const override = context.options.overrides?.[name];
|
|
715
|
+
const patch = typeof override === 'function' ? override(derived) : override;
|
|
716
|
+
const suite_options = { ...derived, ...patch };
|
|
717
|
+
for (const field of Object.keys(entry.required)) {
|
|
718
|
+
const value = suite_options[field];
|
|
719
|
+
if (value === undefined || value === '') {
|
|
720
|
+
throw new Error(`describe_spine_cross_suites: '${name}' needs \`${field}\`, which neither the handle ` +
|
|
721
|
+
`nor the override gives — pass \`overrides.${name}\` with it or exclude '${name}' with a reason`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
// the backdoor suite checks the declaration the gating read, so an override
|
|
725
|
+
// can't hand it another; it reads nothing else of `capabilities`, so a copy
|
|
726
|
+
// keeping the list passes
|
|
727
|
+
if (name === 'testing_backdoor') {
|
|
728
|
+
const checked = suite_options.capabilities
|
|
729
|
+
.testing_backdoors;
|
|
730
|
+
const declared = context.config.capabilities.testing_backdoors;
|
|
731
|
+
// the same entries, each as often, in any order
|
|
732
|
+
if ([...checked].sort().join(' ') !== [...declared].sort().join(' ')) {
|
|
733
|
+
throw new Error("describe_spine_cross_suites: overrides can't change 'testing_backdoor' " +
|
|
734
|
+
'capabilities.testing_backdoors — it checks the declaration the other suites gate on');
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
entry.describe(suite_options);
|
|
738
|
+
};
|
|
739
|
+
/**
|
|
740
|
+
* Where `task` sits below `root`: its name path (its describe titles and its
|
|
741
|
+
* name) and its top-level ancestor, one of `root`'s own tasks — or `null`
|
|
742
|
+
* outside `root`.
|
|
743
|
+
*/
|
|
744
|
+
const locate_task = (task, root) => {
|
|
745
|
+
const path = [];
|
|
746
|
+
let current = task;
|
|
747
|
+
for (;;) {
|
|
748
|
+
path.unshift(current.name);
|
|
749
|
+
// a test or describe at a file's top level has no `suite`, only its `file`
|
|
750
|
+
const parent = current.suite ?? current.file;
|
|
751
|
+
if (parent === root)
|
|
752
|
+
return { path, top: current };
|
|
753
|
+
if (!parent || parent === current)
|
|
754
|
+
return null;
|
|
755
|
+
current = parent;
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
/** Every test under `task`, as name paths starting with `prefix`. */
|
|
759
|
+
const collect_test_paths = (task, prefix, out) => {
|
|
760
|
+
const path = [...prefix, task.name];
|
|
761
|
+
if (task.type === 'suite') {
|
|
762
|
+
for (const child of task.tasks)
|
|
763
|
+
collect_test_paths(child, path, out);
|
|
764
|
+
}
|
|
765
|
+
else {
|
|
766
|
+
out.add(path.join(' > '));
|
|
767
|
+
}
|
|
768
|
+
};
|
|
769
|
+
/**
|
|
770
|
+
* Check every `exclude_cases` entry against the test paths each suite
|
|
771
|
+
* collected — the static half of the case exclusions, which the closing test
|
|
772
|
+
* `register_spine_case_exclusions` registers runs.
|
|
773
|
+
*
|
|
774
|
+
* @param exclude_cases - the entries, by suite
|
|
775
|
+
* @param collected - each run suite's collected test paths
|
|
776
|
+
* @throws Error - naming every entry whose suite collected no test at its path
|
|
777
|
+
* @internal
|
|
778
|
+
*/
|
|
779
|
+
export const check_spine_case_exclusions = (exclude_cases, collected) => {
|
|
780
|
+
const dead = [];
|
|
781
|
+
for (const [name, cases] of Object.entries(exclude_cases)) {
|
|
782
|
+
const paths = collected.get(name);
|
|
783
|
+
for (const test_name of Object.keys(cases ?? {})) {
|
|
784
|
+
if (!paths?.has(test_name))
|
|
785
|
+
dead.push(`${name} › ${test_name}`);
|
|
786
|
+
}
|
|
787
|
+
}
|
|
788
|
+
if (dead.length > 0) {
|
|
789
|
+
throw new Error(`exclude_cases entries naming no collected test of their suite (a renamed or removed ` +
|
|
790
|
+
`test, or a path missing a describe title): ${dead.join('; ')}`);
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
/**
|
|
794
|
+
* Skip the `exclude_cases` tests, and fail on an entry no collected test has.
|
|
795
|
+
* `describe_spine_cross_suites` calls it after registering the suites, in the
|
|
796
|
+
* same suite.
|
|
797
|
+
*
|
|
798
|
+
* One `beforeEach` on the calling suite: it finds the test's top-level task
|
|
799
|
+
* among the ranges the suites registered, and calls `ctx.skip(reason)` when
|
|
800
|
+
* that suite's entries name the test's path — so the suite files stay as they
|
|
801
|
+
* are, and the test shows as skipped with its reason. A closing test holds
|
|
802
|
+
* every entry to a test its suite collected (`check_spine_case_exclusions`;
|
|
803
|
+
* the paths are read at collection, so a filtered run still checks them).
|
|
804
|
+
* A skipped test's own `beforeEach` hooks don't run, but its `afterEach`
|
|
805
|
+
* hooks and its suite's `beforeAll` still do.
|
|
806
|
+
*
|
|
807
|
+
* @param exclude_cases - the entries, by suite
|
|
808
|
+
* @param ranges - each run suite's top-level tasks in the calling suite
|
|
809
|
+
* @internal
|
|
810
|
+
*/
|
|
811
|
+
export const register_spine_case_exclusions = (exclude_cases, ranges) => {
|
|
812
|
+
let root;
|
|
813
|
+
const collected = new Map();
|
|
814
|
+
const suite_of = (index) => ranges.find((r) => index >= r.start && index < r.end)?.name;
|
|
815
|
+
// vitest passes the suite a hook is registered on as the second argument,
|
|
816
|
+
// and requires the first be destructured
|
|
817
|
+
// eslint-disable-next-line no-empty-pattern
|
|
818
|
+
beforeAll(({}, suite) => {
|
|
819
|
+
root = suite;
|
|
820
|
+
for (const range of ranges) {
|
|
821
|
+
const paths = new Set();
|
|
822
|
+
for (const task of suite.tasks.slice(range.start, range.end)) {
|
|
823
|
+
collect_test_paths(task, [], paths);
|
|
824
|
+
}
|
|
825
|
+
collected.set(range.name, paths);
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
beforeEach((ctx) => {
|
|
829
|
+
if (!root)
|
|
830
|
+
return;
|
|
831
|
+
const located = locate_task(ctx.task, root);
|
|
832
|
+
if (!located)
|
|
833
|
+
return;
|
|
834
|
+
const name = suite_of(root.tasks.indexOf(located.top));
|
|
835
|
+
if (name === undefined)
|
|
836
|
+
return;
|
|
837
|
+
const reason = exclude_cases[name]?.[located.path.join(' > ')];
|
|
838
|
+
if (reason !== undefined)
|
|
839
|
+
ctx.skip(`excluded on this backend: ${reason}`);
|
|
840
|
+
});
|
|
841
|
+
test('spine cross suites: every exclude_cases entry names a collected test', () => {
|
|
842
|
+
check_spine_case_exclusions(exclude_cases, collected);
|
|
843
|
+
});
|
|
844
|
+
};
|
|
845
|
+
/**
|
|
846
|
+
* Run every shipped single-backend cross suite against one spawned backend —
|
|
847
|
+
* see the module doc for what the options add and what stays outside.
|
|
848
|
+
*
|
|
849
|
+
* @param handle - the backend, as `inject_backend_handle()` returns it
|
|
850
|
+
* @param options - the declared surface, plus excludes, case exclusions and overrides
|
|
851
|
+
* @throws Error - on an exclude with no reason, an unknown suite name, a suite
|
|
852
|
+
* both excluded and overridden, an excluded `testing_backdoor` (or one of
|
|
853
|
+
* its cases, or an override changing its `capabilities.testing_backdoors`), an exclude,
|
|
854
|
+
* override or `exclude_cases` entry for a suite that skips itself or isn't
|
|
855
|
+
* run, a backend not declaring `SPINE_SUITES_REQUIRED_BACKDOORS` (or
|
|
856
|
+
* `_testing_put_fact` with `fact_serving`), or a suite missing an input the
|
|
857
|
+
* handle doesn't carry
|
|
858
|
+
*/
|
|
859
|
+
export const describe_spine_cross_suites = (handle, options) => {
|
|
860
|
+
const config = to_cross_suite_options(handle);
|
|
861
|
+
const declared = config.capabilities.testing_backdoors;
|
|
862
|
+
const backdoor_skips = to_backdoor_skips(declared);
|
|
863
|
+
validate_spine_cross_suites_options(options, declared, backdoor_skips, config.capabilities);
|
|
864
|
+
const context = {
|
|
865
|
+
handle,
|
|
866
|
+
config,
|
|
867
|
+
options,
|
|
868
|
+
setup: (seed) => default_cross_process_setup(handle, merge_setup_options(options.setup, seed)),
|
|
869
|
+
ws: config.capabilities.ws ? { base_url: config.base_url, ws_path: config.ws_path } : undefined
|
|
870
|
+
};
|
|
871
|
+
// each suite's top-level tasks, by index, so the case exclusions know which
|
|
872
|
+
// suite a test belongs to
|
|
873
|
+
const collector = TestRunner.getCurrentSuite();
|
|
874
|
+
const ranges = [];
|
|
875
|
+
const skipped = Object.entries(options.exclude ?? {});
|
|
876
|
+
for (const name of SUITE_NAMES) {
|
|
877
|
+
if (options.exclude?.[name] !== undefined)
|
|
878
|
+
continue;
|
|
879
|
+
const skip = backdoor_skips.get(name);
|
|
880
|
+
if (skip !== undefined) {
|
|
881
|
+
skipped.push([name, skip]);
|
|
882
|
+
continue;
|
|
883
|
+
}
|
|
884
|
+
const start = collector.tasks.length;
|
|
885
|
+
run_suite(name, context);
|
|
886
|
+
ranges.push({ name, start, end: collector.tasks.length });
|
|
887
|
+
}
|
|
888
|
+
if (Object.keys(options.exclude_cases ?? {}).length > 0) {
|
|
889
|
+
register_spine_case_exclusions(options.exclude_cases, ranges);
|
|
890
|
+
}
|
|
891
|
+
if (skipped.length > 0) {
|
|
892
|
+
describe('spine cross suites excluded on this backend', () => {
|
|
893
|
+
for (const [name, reason] of skipped)
|
|
894
|
+
test.skip(`${name}: ${reason}`, () => { });
|
|
895
|
+
});
|
|
896
|
+
}
|
|
897
|
+
};
|