@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.
Files changed (303) hide show
  1. package/dist/actions/CLAUDE.md +8 -6
  2. package/dist/actions/action_codegen.d.ts +15 -1
  3. package/dist/actions/action_codegen.d.ts.map +1 -1
  4. package/dist/actions/action_codegen.js +23 -2
  5. package/dist/actions/action_event.d.ts.map +1 -1
  6. package/dist/actions/action_event.js +14 -1
  7. package/dist/actions/perform_action.d.ts +17 -1
  8. package/dist/actions/perform_action.d.ts.map +1 -1
  9. package/dist/actions/perform_action.js +23 -3
  10. package/dist/auth/CLAUDE.md +3 -2
  11. package/dist/auth/account_action_specs.d.ts +12 -0
  12. package/dist/auth/account_action_specs.d.ts.map +1 -1
  13. package/dist/auth/account_action_specs.js +16 -2
  14. package/dist/auth/account_actions.d.ts.map +1 -1
  15. package/dist/auth/account_actions.js +7 -4
  16. package/dist/auth/admin_action_specs.d.ts.map +1 -1
  17. package/dist/auth/admin_action_specs.js +29 -8
  18. package/dist/auth/audit_emitter.d.ts +14 -0
  19. package/dist/auth/audit_emitter.d.ts.map +1 -1
  20. package/dist/auth/audit_emitter.js +5 -1
  21. package/dist/auth/cell_action_specs.d.ts +3 -1
  22. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  23. package/dist/auth/cell_action_specs.js +5 -5
  24. package/dist/auth/cell_actions.d.ts.map +1 -1
  25. package/dist/auth/cell_actions.js +2 -3
  26. package/dist/auth/cell_data_schema.d.ts +1 -1
  27. package/dist/auth/cell_data_schema.js +1 -1
  28. package/dist/auth/cell_field_action_specs.d.ts +4 -3
  29. package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
  30. package/dist/auth/cell_field_action_specs.js +5 -6
  31. package/dist/auth/cell_field_actions.d.ts.map +1 -1
  32. package/dist/auth/cell_field_actions.js +8 -1
  33. package/dist/auth/cell_item_action_specs.d.ts +4 -2
  34. package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
  35. package/dist/auth/cell_item_action_specs.js +5 -5
  36. package/dist/auth/cell_item_actions.d.ts.map +1 -1
  37. package/dist/auth/cell_item_actions.js +8 -1
  38. package/dist/auth/role_grant_offer_action_specs.d.ts +2 -0
  39. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  40. package/dist/auth/role_grant_offer_action_specs.js +8 -4
  41. package/dist/http/CLAUDE.md +14 -2
  42. package/dist/http/error_schemas.d.ts +8 -0
  43. package/dist/http/error_schemas.d.ts.map +1 -1
  44. package/dist/http/error_schemas.js +12 -0
  45. package/dist/http/jsonrpc_errors.d.ts +40 -15
  46. package/dist/http/jsonrpc_errors.d.ts.map +1 -1
  47. package/dist/http/jsonrpc_errors.js +90 -35
  48. package/dist/primitive_schemas.d.ts +1 -1
  49. package/dist/primitive_schemas.d.ts.map +1 -1
  50. package/dist/primitive_schemas.js +2 -1
  51. package/dist/schema_meta.d.ts +80 -0
  52. package/dist/schema_meta.d.ts.map +1 -1
  53. package/dist/schema_meta.js +71 -1
  54. package/dist/testing/CLAUDE.md +853 -146
  55. package/dist/testing/admin_integration.d.ts.map +1 -1
  56. package/dist/testing/admin_integration.js +73 -5
  57. package/dist/testing/adversarial_input.d.ts +0 -47
  58. package/dist/testing/adversarial_input.d.ts.map +1 -1
  59. package/dist/testing/adversarial_input.js +4 -340
  60. package/dist/testing/adversarial_input_cases.d.ts +124 -0
  61. package/dist/testing/adversarial_input_cases.d.ts.map +1 -0
  62. package/dist/testing/adversarial_input_cases.js +393 -0
  63. package/dist/testing/audit_completeness.d.ts.map +1 -1
  64. package/dist/testing/audit_completeness.js +24 -20
  65. package/dist/testing/audit_drift_guard.d.ts +30 -3
  66. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  67. package/dist/testing/audit_drift_guard.js +55 -3
  68. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  69. package/dist/testing/cross_backend/account_lifecycle.js +29 -12
  70. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  71. package/dist/testing/cross_backend/actor_lookup.js +7 -25
  72. package/dist/testing/cross_backend/app_settings.d.ts.map +1 -1
  73. package/dist/testing/cross_backend/app_settings.js +4 -6
  74. package/dist/testing/cross_backend/audit_cross_helpers.d.ts +57 -0
  75. package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -0
  76. package/dist/testing/cross_backend/audit_cross_helpers.js +89 -0
  77. package/dist/testing/cross_backend/backend_config.d.ts +2 -1
  78. package/dist/testing/cross_backend/backend_config.d.ts.map +1 -1
  79. package/dist/testing/cross_backend/body_size.d.ts +6 -0
  80. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  81. package/dist/testing/cross_backend/body_size.js +26 -44
  82. package/dist/testing/cross_backend/body_size_smuggling.d.ts +5 -0
  83. package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
  84. package/dist/testing/cross_backend/body_size_smuggling.js +7 -14
  85. package/dist/testing/cross_backend/build_test_backend_paths.d.ts +3 -1
  86. package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -1
  87. package/dist/testing/cross_backend/capabilities.d.ts +78 -59
  88. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  89. package/dist/testing/cross_backend/capabilities.js +25 -24
  90. package/dist/testing/cross_backend/cargo_binary.d.ts +70 -0
  91. package/dist/testing/cross_backend/cargo_binary.d.ts.map +1 -0
  92. package/dist/testing/cross_backend/cargo_binary.js +148 -0
  93. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  94. package/dist/testing/cross_backend/cell_crud.js +174 -3
  95. package/dist/testing/cross_backend/cell_gated_create.d.ts +0 -60
  96. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  97. package/dist/testing/cross_backend/cell_gated_create.js +9 -487
  98. package/dist/testing/cross_backend/cell_grant_role.d.ts +14 -2
  99. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  100. package/dist/testing/cross_backend/cell_grant_role.js +22 -12
  101. package/dist/testing/cross_backend/cell_moderate.d.ts +66 -0
  102. package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -0
  103. package/dist/testing/cross_backend/cell_moderate.js +511 -0
  104. package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
  105. package/dist/testing/cross_backend/cell_ref_admission.js +1 -1
  106. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  107. package/dist/testing/cross_backend/cell_relations.js +40 -19
  108. package/dist/testing/cross_backend/conformance_case.d.ts +43 -4
  109. package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
  110. package/dist/testing/cross_backend/conformance_case.js +29 -1
  111. package/dist/testing/cross_backend/conformance_rpc_auth.d.ts +93 -0
  112. package/dist/testing/cross_backend/conformance_rpc_auth.d.ts.map +1 -0
  113. package/dist/testing/cross_backend/conformance_rpc_auth.js +125 -0
  114. package/dist/testing/cross_backend/conformance_table.d.ts +144 -4
  115. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  116. package/dist/testing/cross_backend/conformance_table.js +297 -65
  117. package/dist/testing/cross_backend/cookie_attributes.d.ts.map +1 -1
  118. package/dist/testing/cross_backend/cookie_attributes.js +3 -2
  119. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts +38 -5
  120. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts.map +1 -1
  121. package/dist/testing/cross_backend/create_cross_backend_global_setup.js +24 -3
  122. package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts +5 -5
  123. package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts.map +1 -1
  124. package/dist/testing/cross_backend/credential_deadline.d.ts +10 -6
  125. package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -1
  126. package/dist/testing/cross_backend/credential_deadline.js +32 -31
  127. package/dist/testing/cross_backend/credential_header_robustness.d.ts.map +1 -1
  128. package/dist/testing/cross_backend/credential_header_robustness.js +4 -6
  129. package/dist/testing/cross_backend/{cell_cross_helpers.d.ts → cross_rpc.d.ts} +14 -2
  130. package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -0
  131. package/dist/testing/cross_backend/{cell_cross_helpers.js → cross_rpc.js} +31 -7
  132. package/dist/testing/cross_backend/default_backend_configs.d.ts +58 -61
  133. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  134. package/dist/testing/cross_backend/default_backend_configs.js +97 -91
  135. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  136. package/dist/testing/cross_backend/default_spine_surface.js +2 -2
  137. package/dist/testing/cross_backend/differential.d.ts +126 -0
  138. package/dist/testing/cross_backend/differential.d.ts.map +1 -0
  139. package/dist/testing/cross_backend/differential.js +201 -0
  140. package/dist/testing/cross_backend/differential_normalize.d.ts +150 -0
  141. package/dist/testing/cross_backend/differential_normalize.d.ts.map +1 -0
  142. package/dist/testing/cross_backend/differential_normalize.js +153 -0
  143. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  144. package/dist/testing/cross_backend/fact_serving.js +27 -17
  145. package/dist/testing/cross_backend/full_spine_mount.d.ts +15 -0
  146. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  147. package/dist/testing/cross_backend/full_spine_mount.js +55 -1
  148. package/dist/testing/cross_backend/identity_parity.d.ts.map +1 -1
  149. package/dist/testing/cross_backend/identity_parity.js +6 -5
  150. package/dist/testing/cross_backend/in_process_setup.d.ts +12 -2
  151. package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -1
  152. package/dist/testing/cross_backend/in_process_setup.js +10 -7
  153. package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
  154. package/dist/testing/cross_backend/login_security.js +13 -18
  155. package/dist/testing/cross_backend/origin.js +1 -1
  156. package/dist/testing/cross_backend/origin_conformance.js +1 -1
  157. package/dist/testing/cross_backend/origin_scope.d.ts +6 -0
  158. package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -1
  159. package/dist/testing/cross_backend/origin_scope.js +13 -12
  160. package/dist/testing/cross_backend/peer_ping_ws.d.ts +2 -5
  161. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  162. package/dist/testing/cross_backend/peer_ping_ws.js +14 -24
  163. package/dist/testing/cross_backend/postgres_database.d.ts +85 -0
  164. package/dist/testing/cross_backend/postgres_database.d.ts.map +1 -0
  165. package/dist/testing/cross_backend/postgres_database.js +140 -0
  166. package/dist/testing/cross_backend/provided_context.d.ts +28 -0
  167. package/dist/testing/cross_backend/provided_context.d.ts.map +1 -0
  168. package/dist/testing/cross_backend/provided_context.js +35 -0
  169. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
  170. package/dist/testing/cross_backend/query_shape.js +10 -12
  171. package/dist/testing/cross_backend/ready.js +2 -2
  172. package/dist/testing/cross_backend/revocation_drivers.d.ts +4 -3
  173. package/dist/testing/cross_backend/revocation_drivers.d.ts.map +1 -1
  174. package/dist/testing/cross_backend/revocation_drivers.js +1 -1
  175. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +3 -2
  176. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -1
  177. package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts +13 -0
  178. package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -0
  179. package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +203 -0
  180. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +2 -5
  181. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
  182. package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +203 -111
  183. package/dist/testing/cross_backend/role_grant_participation.d.ts +13 -0
  184. package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
  185. package/dist/testing/cross_backend/role_grant_participation.js +32 -21
  186. package/dist/testing/cross_backend/role_grant_role_grammar.d.ts.map +1 -1
  187. package/dist/testing/cross_backend/role_grant_role_grammar.js +6 -9
  188. package/dist/testing/cross_backend/rpc_adversarial.d.ts +47 -0
  189. package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -0
  190. package/dist/testing/cross_backend/rpc_adversarial.js +302 -0
  191. package/dist/testing/cross_backend/rpc_success_fixtures.d.ts +24 -0
  192. package/dist/testing/cross_backend/rpc_success_fixtures.d.ts.map +1 -0
  193. package/dist/testing/cross_backend/rpc_success_fixtures.js +124 -0
  194. package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +101 -0
  195. package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts.map +1 -0
  196. package/dist/testing/cross_backend/rust_spine_consumer_surface.js +113 -0
  197. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +10 -0
  198. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  199. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +33 -21
  200. package/dist/testing/cross_backend/session_cap.d.ts +9 -8
  201. package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
  202. package/dist/testing/cross_backend/session_cap.js +9 -4
  203. package/dist/testing/cross_backend/session_cookie_codec.js +1 -1
  204. package/dist/testing/cross_backend/setup.d.ts +58 -56
  205. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  206. package/dist/testing/cross_backend/setup.js +29 -29
  207. package/dist/testing/cross_backend/simple_request.d.ts.map +1 -1
  208. package/dist/testing/cross_backend/simple_request.js +4 -4
  209. package/dist/testing/cross_backend/spawn_backend.d.ts +59 -10
  210. package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -1
  211. package/dist/testing/cross_backend/spawn_backend.js +219 -60
  212. package/dist/testing/cross_backend/spine_suites.d.ts +311 -0
  213. package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -0
  214. package/dist/testing/cross_backend/spine_suites.js +897 -0
  215. package/dist/testing/cross_backend/spine_surface_constants.d.ts +44 -0
  216. package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
  217. package/dist/testing/cross_backend/spine_surface_constants.js +53 -0
  218. package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
  219. package/dist/testing/cross_backend/sse_round_trip.js +3 -4
  220. package/dist/testing/cross_backend/standard.d.ts +18 -1
  221. package/dist/testing/cross_backend/standard.d.ts.map +1 -1
  222. package/dist/testing/cross_backend/standard.js +2 -0
  223. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +24 -0
  224. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
  225. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +15 -0
  226. package/dist/testing/cross_backend/testing_backdoor.d.ts +29 -12
  227. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  228. package/dist/testing/cross_backend/testing_backdoor.js +143 -51
  229. package/dist/testing/cross_backend/testing_reset_actions.d.ts +30 -14
  230. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  231. package/dist/testing/cross_backend/testing_reset_actions.js +58 -37
  232. package/dist/testing/cross_backend/testing_server_bun.js +1 -1
  233. package/dist/testing/cross_backend/token_lifetime.js +1 -1
  234. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  235. package/dist/testing/cross_backend/token_scope_surface.js +9 -13
  236. package/dist/testing/cross_backend/transport_composition.d.ts +26 -2
  237. package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -1
  238. package/dist/testing/cross_backend/transport_composition.js +81 -51
  239. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +8 -0
  240. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  241. package/dist/testing/cross_backend/ts_spine_backend_config.js +23 -13
  242. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +2 -5
  243. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
  244. package/dist/testing/cross_backend/ws_action_rate_limit.js +8 -14
  245. package/dist/testing/cross_backend/ws_connection_cap.d.ts +4 -9
  246. package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -1
  247. package/dist/testing/cross_backend/ws_connection_cap.js +11 -12
  248. package/dist/testing/cross_backend/ws_endpoint.d.ts +52 -0
  249. package/dist/testing/cross_backend/ws_endpoint.d.ts.map +1 -0
  250. package/dist/testing/cross_backend/ws_endpoint.js +54 -0
  251. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts +2 -5
  252. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts.map +1 -1
  253. package/dist/testing/cross_backend/ws_inbound_dispatch.js +9 -15
  254. package/dist/testing/cross_backend/ws_outbound_overflow.d.ts +2 -7
  255. package/dist/testing/cross_backend/ws_outbound_overflow.d.ts.map +1 -1
  256. package/dist/testing/cross_backend/ws_outbound_overflow.js +5 -9
  257. package/dist/testing/cross_backend/ws_role_gated.d.ts +4 -5
  258. package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -1
  259. package/dist/testing/cross_backend/ws_role_gated.js +12 -21
  260. package/dist/testing/cross_backend/ws_round_trip.d.ts +12 -9
  261. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  262. package/dist/testing/cross_backend/ws_round_trip.js +29 -34
  263. package/dist/testing/db_entities.d.ts +16 -3
  264. package/dist/testing/db_entities.d.ts.map +1 -1
  265. package/dist/testing/db_entities.js +28 -5
  266. package/dist/testing/integration_helpers.d.ts +61 -2
  267. package/dist/testing/integration_helpers.d.ts.map +1 -1
  268. package/dist/testing/integration_helpers.js +75 -18
  269. package/dist/testing/round_trip.d.ts +17 -0
  270. package/dist/testing/round_trip.d.ts.map +1 -1
  271. package/dist/testing/round_trip.js +67 -24
  272. package/dist/testing/rpc_attack_surface.d.ts +10 -0
  273. package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
  274. package/dist/testing/rpc_attack_surface.js +27 -140
  275. package/dist/testing/rpc_envelope_cases.d.ts +114 -0
  276. package/dist/testing/rpc_envelope_cases.d.ts.map +1 -0
  277. package/dist/testing/rpc_envelope_cases.js +243 -0
  278. package/dist/testing/rpc_helpers.d.ts +31 -0
  279. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  280. package/dist/testing/rpc_helpers.js +35 -14
  281. package/dist/testing/rpc_round_trip.d.ts +62 -6
  282. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  283. package/dist/testing/rpc_round_trip.js +151 -39
  284. package/dist/testing/schema_generators.d.ts +99 -2
  285. package/dist/testing/schema_generators.d.ts.map +1 -1
  286. package/dist/testing/schema_generators.js +261 -73
  287. package/dist/testing/standard.d.ts +26 -0
  288. package/dist/testing/standard.d.ts.map +1 -1
  289. package/dist/testing/standard.js +4 -1
  290. package/dist/testing/stubs.d.ts +4 -3
  291. package/dist/testing/stubs.d.ts.map +1 -1
  292. package/dist/testing/stubs.js +6 -4
  293. package/dist/testing/transports/raw_http.d.ts +21 -0
  294. package/dist/testing/transports/raw_http.d.ts.map +1 -1
  295. package/dist/testing/transports/raw_http.js +30 -0
  296. package/dist/zod.d.ts +9 -0
  297. package/dist/zod.d.ts.map +1 -1
  298. package/dist/zod.js +20 -0
  299. package/package.json +6 -4
  300. package/dist/testing/cross_backend/actor_search.d.ts +0 -6
  301. package/dist/testing/cross_backend/actor_search.d.ts.map +0 -1
  302. package/dist/testing/cross_backend/actor_search.js +0 -90
  303. 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
+ };