@fuzdev/fuz_app 0.146.1 → 0.148.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 (313) hide show
  1. package/dist/actions/CLAUDE.md +6 -4
  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/perform_action.js +1 -1
  6. package/dist/auth/CLAUDE.md +1 -1
  7. package/dist/auth/bootstrap_account.d.ts +2 -1
  8. package/dist/auth/bootstrap_account.d.ts.map +1 -1
  9. package/dist/auth/bootstrap_account.js +17 -6
  10. package/dist/auth/cell_action_specs.d.ts +3 -1
  11. package/dist/auth/cell_action_specs.d.ts.map +1 -1
  12. package/dist/auth/cell_action_specs.js +5 -5
  13. package/dist/auth/cell_actions.d.ts.map +1 -1
  14. package/dist/auth/cell_actions.js +2 -3
  15. package/dist/auth/cell_data_schema.d.ts +1 -1
  16. package/dist/auth/cell_data_schema.js +1 -1
  17. package/dist/auth/cell_field_action_specs.d.ts +4 -3
  18. package/dist/auth/cell_field_action_specs.d.ts.map +1 -1
  19. package/dist/auth/cell_field_action_specs.js +5 -6
  20. package/dist/auth/cell_field_actions.d.ts.map +1 -1
  21. package/dist/auth/cell_field_actions.js +8 -1
  22. package/dist/auth/cell_item_action_specs.d.ts +4 -2
  23. package/dist/auth/cell_item_action_specs.d.ts.map +1 -1
  24. package/dist/auth/cell_item_action_specs.js +5 -5
  25. package/dist/auth/cell_item_actions.d.ts.map +1 -1
  26. package/dist/auth/cell_item_actions.js +8 -1
  27. package/dist/auth/keyring.d.ts +35 -38
  28. package/dist/auth/keyring.d.ts.map +1 -1
  29. package/dist/auth/keyring.js +43 -50
  30. package/dist/auth/password_argon2.d.ts.map +1 -1
  31. package/dist/auth/password_argon2.js +11 -8
  32. package/dist/auth/role_grant_offer_action_specs.d.ts +2 -0
  33. package/dist/auth/role_grant_offer_action_specs.d.ts.map +1 -1
  34. package/dist/auth/role_grant_offer_action_specs.js +3 -1
  35. package/dist/auth/session_cookie.d.ts.map +1 -1
  36. package/dist/auth/session_cookie.js +5 -2
  37. package/dist/db/CLAUDE.md +18 -16
  38. package/dist/db/cell_queries.d.ts +3 -2
  39. package/dist/db/cell_queries.d.ts.map +1 -1
  40. package/dist/db/cell_queries.js +3 -2
  41. package/dist/db/fact_disk_storage.d.ts +15 -15
  42. package/dist/db/fact_disk_storage.d.ts.map +1 -1
  43. package/dist/db/fact_disk_storage.js +22 -28
  44. package/dist/db/fact_queries.d.ts +2 -3
  45. package/dist/db/fact_queries.d.ts.map +1 -1
  46. package/dist/db/fact_queries.js +2 -3
  47. package/dist/db/fact_store.d.ts +20 -18
  48. package/dist/db/fact_store.d.ts.map +1 -1
  49. package/dist/db/fact_store.js +30 -44
  50. package/dist/db/file_fact_url.d.ts +19 -4
  51. package/dist/db/file_fact_url.d.ts.map +1 -1
  52. package/dist/db/file_fact_url.js +19 -4
  53. package/dist/primitive_schemas.d.ts +1 -1
  54. package/dist/primitive_schemas.d.ts.map +1 -1
  55. package/dist/primitive_schemas.js +2 -1
  56. package/dist/schema_meta.d.ts +20 -0
  57. package/dist/schema_meta.d.ts.map +1 -1
  58. package/dist/schema_meta.js +7 -1
  59. package/dist/server/env.d.ts.map +1 -1
  60. package/dist/server/env.js +2 -2
  61. package/dist/server/serve_fact_route.d.ts +9 -5
  62. package/dist/server/serve_fact_route.d.ts.map +1 -1
  63. package/dist/server/serve_fact_route.js +15 -7
  64. package/dist/testing/CLAUDE.md +737 -114
  65. package/dist/testing/admin_integration.d.ts.map +1 -1
  66. package/dist/testing/admin_integration.js +73 -5
  67. package/dist/testing/adversarial_input.d.ts +0 -47
  68. package/dist/testing/adversarial_input.d.ts.map +1 -1
  69. package/dist/testing/adversarial_input.js +4 -340
  70. package/dist/testing/adversarial_input_cases.d.ts +64 -0
  71. package/dist/testing/adversarial_input_cases.d.ts.map +1 -0
  72. package/dist/testing/adversarial_input_cases.js +348 -0
  73. package/dist/testing/app_server.d.ts.map +1 -1
  74. package/dist/testing/app_server.js +2 -2
  75. package/dist/testing/audit_completeness.d.ts.map +1 -1
  76. package/dist/testing/audit_completeness.js +24 -20
  77. package/dist/testing/audit_drift_guard.d.ts +27 -1
  78. package/dist/testing/audit_drift_guard.d.ts.map +1 -1
  79. package/dist/testing/audit_drift_guard.js +50 -0
  80. package/dist/testing/cross_backend/account_lifecycle.d.ts.map +1 -1
  81. package/dist/testing/cross_backend/account_lifecycle.js +29 -12
  82. package/dist/testing/cross_backend/actor_lookup.d.ts.map +1 -1
  83. package/dist/testing/cross_backend/actor_lookup.js +7 -25
  84. package/dist/testing/cross_backend/app_settings.d.ts.map +1 -1
  85. package/dist/testing/cross_backend/app_settings.js +4 -6
  86. package/dist/testing/cross_backend/audit_cross_helpers.d.ts +51 -0
  87. package/dist/testing/cross_backend/audit_cross_helpers.d.ts.map +1 -0
  88. package/dist/testing/cross_backend/audit_cross_helpers.js +78 -0
  89. package/dist/testing/cross_backend/backend_config.d.ts +2 -1
  90. package/dist/testing/cross_backend/backend_config.d.ts.map +1 -1
  91. package/dist/testing/cross_backend/body_size.d.ts +6 -0
  92. package/dist/testing/cross_backend/body_size.d.ts.map +1 -1
  93. package/dist/testing/cross_backend/body_size.js +26 -44
  94. package/dist/testing/cross_backend/body_size_smuggling.d.ts +5 -0
  95. package/dist/testing/cross_backend/body_size_smuggling.d.ts.map +1 -1
  96. package/dist/testing/cross_backend/body_size_smuggling.js +7 -14
  97. package/dist/testing/cross_backend/bootstrap_backend.d.ts +11 -1
  98. package/dist/testing/cross_backend/bootstrap_backend.d.ts.map +1 -1
  99. package/dist/testing/cross_backend/bootstrap_backend.js +2 -1
  100. package/dist/testing/cross_backend/build_test_backend_paths.d.ts +3 -1
  101. package/dist/testing/cross_backend/build_test_backend_paths.d.ts.map +1 -1
  102. package/dist/testing/cross_backend/capabilities.d.ts +55 -59
  103. package/dist/testing/cross_backend/capabilities.d.ts.map +1 -1
  104. package/dist/testing/cross_backend/capabilities.js +20 -23
  105. package/dist/testing/cross_backend/cargo_binary.d.ts +70 -0
  106. package/dist/testing/cross_backend/cargo_binary.d.ts.map +1 -0
  107. package/dist/testing/cross_backend/cargo_binary.js +148 -0
  108. package/dist/testing/cross_backend/cell_crud.d.ts.map +1 -1
  109. package/dist/testing/cross_backend/cell_crud.js +57 -1
  110. package/dist/testing/cross_backend/cell_gated_create.d.ts +0 -60
  111. package/dist/testing/cross_backend/cell_gated_create.d.ts.map +1 -1
  112. package/dist/testing/cross_backend/cell_gated_create.js +9 -487
  113. package/dist/testing/cross_backend/cell_grant_role.d.ts +14 -2
  114. package/dist/testing/cross_backend/cell_grant_role.d.ts.map +1 -1
  115. package/dist/testing/cross_backend/cell_grant_role.js +22 -12
  116. package/dist/testing/cross_backend/cell_moderate.d.ts +66 -0
  117. package/dist/testing/cross_backend/cell_moderate.d.ts.map +1 -0
  118. package/dist/testing/cross_backend/cell_moderate.js +511 -0
  119. package/dist/testing/cross_backend/cell_ref_admission.d.ts.map +1 -1
  120. package/dist/testing/cross_backend/cell_ref_admission.js +1 -1
  121. package/dist/testing/cross_backend/cell_relations.d.ts.map +1 -1
  122. package/dist/testing/cross_backend/cell_relations.js +40 -19
  123. package/dist/testing/cross_backend/conformance_case.d.ts +43 -4
  124. package/dist/testing/cross_backend/conformance_case.d.ts.map +1 -1
  125. package/dist/testing/cross_backend/conformance_case.js +29 -1
  126. package/dist/testing/cross_backend/conformance_rpc_auth.d.ts +93 -0
  127. package/dist/testing/cross_backend/conformance_rpc_auth.d.ts.map +1 -0
  128. package/dist/testing/cross_backend/conformance_rpc_auth.js +125 -0
  129. package/dist/testing/cross_backend/conformance_table.d.ts +144 -4
  130. package/dist/testing/cross_backend/conformance_table.d.ts.map +1 -1
  131. package/dist/testing/cross_backend/conformance_table.js +297 -65
  132. package/dist/testing/cross_backend/cookie_attributes.d.ts.map +1 -1
  133. package/dist/testing/cross_backend/cookie_attributes.js +3 -2
  134. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts +38 -5
  135. package/dist/testing/cross_backend/create_cross_backend_global_setup.d.ts.map +1 -1
  136. package/dist/testing/cross_backend/create_cross_backend_global_setup.js +24 -3
  137. package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts +5 -5
  138. package/dist/testing/cross_backend/create_dual_spawn_global_setup.d.ts.map +1 -1
  139. package/dist/testing/cross_backend/credential_deadline.d.ts +10 -6
  140. package/dist/testing/cross_backend/credential_deadline.d.ts.map +1 -1
  141. package/dist/testing/cross_backend/credential_deadline.js +32 -31
  142. package/dist/testing/cross_backend/credential_header_robustness.d.ts.map +1 -1
  143. package/dist/testing/cross_backend/credential_header_robustness.js +4 -6
  144. package/dist/testing/cross_backend/{cell_cross_helpers.d.ts → cross_rpc.d.ts} +12 -2
  145. package/dist/testing/cross_backend/cross_rpc.d.ts.map +1 -0
  146. package/dist/testing/cross_backend/{cell_cross_helpers.js → cross_rpc.js} +31 -7
  147. package/dist/testing/cross_backend/default_backend_configs.d.ts +56 -61
  148. package/dist/testing/cross_backend/default_backend_configs.d.ts.map +1 -1
  149. package/dist/testing/cross_backend/default_backend_configs.js +97 -91
  150. package/dist/testing/cross_backend/default_spine_surface.d.ts.map +1 -1
  151. package/dist/testing/cross_backend/default_spine_surface.js +2 -2
  152. package/dist/testing/cross_backend/differential.d.ts +126 -0
  153. package/dist/testing/cross_backend/differential.d.ts.map +1 -0
  154. package/dist/testing/cross_backend/differential.js +201 -0
  155. package/dist/testing/cross_backend/differential_normalize.d.ts +150 -0
  156. package/dist/testing/cross_backend/differential_normalize.d.ts.map +1 -0
  157. package/dist/testing/cross_backend/differential_normalize.js +153 -0
  158. package/dist/testing/cross_backend/fact_serving.d.ts +5 -0
  159. package/dist/testing/cross_backend/fact_serving.d.ts.map +1 -1
  160. package/dist/testing/cross_backend/fact_serving.js +41 -7
  161. package/dist/testing/cross_backend/full_spine_mount.d.ts +15 -0
  162. package/dist/testing/cross_backend/full_spine_mount.d.ts.map +1 -1
  163. package/dist/testing/cross_backend/full_spine_mount.js +55 -1
  164. package/dist/testing/cross_backend/identity_parity.d.ts.map +1 -1
  165. package/dist/testing/cross_backend/identity_parity.js +6 -5
  166. package/dist/testing/cross_backend/in_process_setup.d.ts +12 -2
  167. package/dist/testing/cross_backend/in_process_setup.d.ts.map +1 -1
  168. package/dist/testing/cross_backend/in_process_setup.js +10 -7
  169. package/dist/testing/cross_backend/login_security.d.ts.map +1 -1
  170. package/dist/testing/cross_backend/login_security.js +13 -18
  171. package/dist/testing/cross_backend/origin.js +1 -1
  172. package/dist/testing/cross_backend/origin_conformance.js +1 -1
  173. package/dist/testing/cross_backend/origin_scope.d.ts +6 -0
  174. package/dist/testing/cross_backend/origin_scope.d.ts.map +1 -1
  175. package/dist/testing/cross_backend/origin_scope.js +13 -12
  176. package/dist/testing/cross_backend/peer_ping_ws.d.ts +2 -5
  177. package/dist/testing/cross_backend/peer_ping_ws.d.ts.map +1 -1
  178. package/dist/testing/cross_backend/peer_ping_ws.js +14 -24
  179. package/dist/testing/cross_backend/postgres_database.d.ts +85 -0
  180. package/dist/testing/cross_backend/postgres_database.d.ts.map +1 -0
  181. package/dist/testing/cross_backend/postgres_database.js +140 -0
  182. package/dist/testing/cross_backend/provided_context.d.ts +28 -0
  183. package/dist/testing/cross_backend/provided_context.d.ts.map +1 -0
  184. package/dist/testing/cross_backend/provided_context.js +35 -0
  185. package/dist/testing/cross_backend/query_shape.d.ts.map +1 -1
  186. package/dist/testing/cross_backend/query_shape.js +10 -12
  187. package/dist/testing/cross_backend/ready.js +2 -2
  188. package/dist/testing/cross_backend/revocation_drivers.d.ts +4 -3
  189. package/dist/testing/cross_backend/revocation_drivers.d.ts.map +1 -1
  190. package/dist/testing/cross_backend/revocation_drivers.js +1 -1
  191. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts +3 -2
  192. package/dist/testing/cross_backend/role_grant_cross_helpers.d.ts.map +1 -1
  193. package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts +13 -0
  194. package/dist/testing/cross_backend/role_grant_offer_lifecycle.d.ts.map +1 -0
  195. package/dist/testing/cross_backend/role_grant_offer_lifecycle.js +203 -0
  196. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts +2 -5
  197. package/dist/testing/cross_backend/role_grant_offer_notification_ws.d.ts.map +1 -1
  198. package/dist/testing/cross_backend/role_grant_offer_notification_ws.js +203 -111
  199. package/dist/testing/cross_backend/role_grant_participation.d.ts +13 -0
  200. package/dist/testing/cross_backend/role_grant_participation.d.ts.map +1 -1
  201. package/dist/testing/cross_backend/role_grant_participation.js +32 -21
  202. package/dist/testing/cross_backend/role_grant_role_grammar.d.ts.map +1 -1
  203. package/dist/testing/cross_backend/role_grant_role_grammar.js +6 -9
  204. package/dist/testing/cross_backend/rpc_adversarial.d.ts +38 -0
  205. package/dist/testing/cross_backend/rpc_adversarial.d.ts.map +1 -0
  206. package/dist/testing/cross_backend/rpc_adversarial.js +292 -0
  207. package/dist/testing/cross_backend/rpc_success_fixtures.d.ts +24 -0
  208. package/dist/testing/cross_backend/rpc_success_fixtures.d.ts.map +1 -0
  209. package/dist/testing/cross_backend/rpc_success_fixtures.js +124 -0
  210. package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts +101 -0
  211. package/dist/testing/cross_backend/rust_spine_consumer_surface.d.ts.map +1 -0
  212. package/dist/testing/cross_backend/rust_spine_consumer_surface.js +113 -0
  213. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts +10 -0
  214. package/dist/testing/cross_backend/rust_spine_stub_backend_config.d.ts.map +1 -1
  215. package/dist/testing/cross_backend/rust_spine_stub_backend_config.js +27 -18
  216. package/dist/testing/cross_backend/session_cap.d.ts +9 -8
  217. package/dist/testing/cross_backend/session_cap.d.ts.map +1 -1
  218. package/dist/testing/cross_backend/session_cap.js +9 -4
  219. package/dist/testing/cross_backend/session_cookie_codec.d.ts +19 -0
  220. package/dist/testing/cross_backend/session_cookie_codec.d.ts.map +1 -0
  221. package/dist/testing/cross_backend/session_cookie_codec.js +128 -0
  222. package/dist/testing/cross_backend/setup.d.ts +56 -54
  223. package/dist/testing/cross_backend/setup.d.ts.map +1 -1
  224. package/dist/testing/cross_backend/setup.js +29 -29
  225. package/dist/testing/cross_backend/simple_request.d.ts.map +1 -1
  226. package/dist/testing/cross_backend/simple_request.js +4 -4
  227. package/dist/testing/cross_backend/spawn_backend.d.ts +59 -10
  228. package/dist/testing/cross_backend/spawn_backend.d.ts.map +1 -1
  229. package/dist/testing/cross_backend/spawn_backend.js +219 -60
  230. package/dist/testing/cross_backend/spine_suites.d.ts +231 -0
  231. package/dist/testing/cross_backend/spine_suites.d.ts.map +1 -0
  232. package/dist/testing/cross_backend/spine_suites.js +626 -0
  233. package/dist/testing/cross_backend/spine_surface_constants.d.ts +29 -0
  234. package/dist/testing/cross_backend/spine_surface_constants.d.ts.map +1 -1
  235. package/dist/testing/cross_backend/spine_surface_constants.js +29 -0
  236. package/dist/testing/cross_backend/sse_round_trip.d.ts.map +1 -1
  237. package/dist/testing/cross_backend/sse_round_trip.js +3 -4
  238. package/dist/testing/cross_backend/standard.d.ts +18 -1
  239. package/dist/testing/cross_backend/standard.d.ts.map +1 -1
  240. package/dist/testing/cross_backend/standard.js +2 -0
  241. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts +24 -0
  242. package/dist/testing/cross_backend/test_cell_gated_create_authorize.d.ts.map +1 -1
  243. package/dist/testing/cross_backend/test_cell_gated_create_authorize.js +15 -0
  244. package/dist/testing/cross_backend/testing_backdoor.d.ts +2 -8
  245. package/dist/testing/cross_backend/testing_backdoor.d.ts.map +1 -1
  246. package/dist/testing/cross_backend/testing_backdoor.js +7 -10
  247. package/dist/testing/cross_backend/testing_reset_actions.d.ts +10 -5
  248. package/dist/testing/cross_backend/testing_reset_actions.d.ts.map +1 -1
  249. package/dist/testing/cross_backend/testing_reset_actions.js +21 -12
  250. package/dist/testing/cross_backend/testing_server_bun.js +1 -1
  251. package/dist/testing/cross_backend/token_lifetime.js +1 -1
  252. package/dist/testing/cross_backend/token_scope_surface.d.ts.map +1 -1
  253. package/dist/testing/cross_backend/token_scope_surface.js +9 -13
  254. package/dist/testing/cross_backend/transport_composition.d.ts +26 -2
  255. package/dist/testing/cross_backend/transport_composition.d.ts.map +1 -1
  256. package/dist/testing/cross_backend/transport_composition.js +81 -51
  257. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts +8 -0
  258. package/dist/testing/cross_backend/ts_spine_backend_config.d.ts.map +1 -1
  259. package/dist/testing/cross_backend/ts_spine_backend_config.js +17 -10
  260. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts +2 -5
  261. package/dist/testing/cross_backend/ws_action_rate_limit.d.ts.map +1 -1
  262. package/dist/testing/cross_backend/ws_action_rate_limit.js +8 -14
  263. package/dist/testing/cross_backend/ws_connection_cap.d.ts +4 -9
  264. package/dist/testing/cross_backend/ws_connection_cap.d.ts.map +1 -1
  265. package/dist/testing/cross_backend/ws_connection_cap.js +11 -12
  266. package/dist/testing/cross_backend/ws_endpoint.d.ts +52 -0
  267. package/dist/testing/cross_backend/ws_endpoint.d.ts.map +1 -0
  268. package/dist/testing/cross_backend/ws_endpoint.js +54 -0
  269. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts +2 -5
  270. package/dist/testing/cross_backend/ws_inbound_dispatch.d.ts.map +1 -1
  271. package/dist/testing/cross_backend/ws_inbound_dispatch.js +9 -15
  272. package/dist/testing/cross_backend/ws_outbound_overflow.d.ts +2 -7
  273. package/dist/testing/cross_backend/ws_outbound_overflow.d.ts.map +1 -1
  274. package/dist/testing/cross_backend/ws_outbound_overflow.js +5 -9
  275. package/dist/testing/cross_backend/ws_role_gated.d.ts +4 -5
  276. package/dist/testing/cross_backend/ws_role_gated.d.ts.map +1 -1
  277. package/dist/testing/cross_backend/ws_role_gated.js +12 -21
  278. package/dist/testing/cross_backend/ws_round_trip.d.ts +12 -9
  279. package/dist/testing/cross_backend/ws_round_trip.d.ts.map +1 -1
  280. package/dist/testing/cross_backend/ws_round_trip.js +29 -34
  281. package/dist/testing/db_entities.d.ts +16 -3
  282. package/dist/testing/db_entities.d.ts.map +1 -1
  283. package/dist/testing/db_entities.js +28 -5
  284. package/dist/testing/integration_helpers.d.ts +61 -2
  285. package/dist/testing/integration_helpers.d.ts.map +1 -1
  286. package/dist/testing/integration_helpers.js +75 -18
  287. package/dist/testing/round_trip.d.ts +17 -0
  288. package/dist/testing/round_trip.d.ts.map +1 -1
  289. package/dist/testing/round_trip.js +67 -24
  290. package/dist/testing/rpc_attack_surface.d.ts.map +1 -1
  291. package/dist/testing/rpc_attack_surface.js +13 -138
  292. package/dist/testing/rpc_envelope_cases.d.ts +111 -0
  293. package/dist/testing/rpc_envelope_cases.d.ts.map +1 -0
  294. package/dist/testing/rpc_envelope_cases.js +240 -0
  295. package/dist/testing/rpc_helpers.d.ts +31 -0
  296. package/dist/testing/rpc_helpers.d.ts.map +1 -1
  297. package/dist/testing/rpc_helpers.js +35 -14
  298. package/dist/testing/rpc_round_trip.d.ts +62 -6
  299. package/dist/testing/rpc_round_trip.d.ts.map +1 -1
  300. package/dist/testing/rpc_round_trip.js +151 -39
  301. package/dist/testing/schema_generators.d.ts.map +1 -1
  302. package/dist/testing/schema_generators.js +22 -2
  303. package/dist/testing/standard.d.ts +26 -0
  304. package/dist/testing/standard.d.ts.map +1 -1
  305. package/dist/testing/standard.js +4 -1
  306. package/dist/testing/transports/raw_http.d.ts +21 -0
  307. package/dist/testing/transports/raw_http.d.ts.map +1 -1
  308. package/dist/testing/transports/raw_http.js +30 -0
  309. package/package.json +10 -8
  310. package/dist/testing/cross_backend/actor_search.d.ts +0 -6
  311. package/dist/testing/cross_backend/actor_search.d.ts.map +0 -1
  312. package/dist/testing/cross_backend/actor_search.js +0 -90
  313. package/dist/testing/cross_backend/cell_cross_helpers.d.ts.map +0 -1
@@ -0,0 +1,113 @@
1
+ import '../assert_dev_env.js';
2
+ import { is_protocol_action_method } from '../../actions/action_codegen.js';
3
+ import { account_status_route_shape, create_account_route_shapes } from '../../auth/account_route_schema.js';
4
+ import { create_signup_route_shape } from '../../auth/signup_route_schema.js';
5
+ import { create_standard_rpc_actions } from '../../auth/standard_rpc_actions.js';
6
+ import { create_health_route_spec } from '../../http/common_routes.js';
7
+ import { prefix_route_specs } from '../../http/route_spec.js';
8
+ import { create_test_app_surface_spec, stub_handler, stub_mw } from '../stubs.js';
9
+ import { SPINE_ACCOUNT_PATH_PREFIX, SPINE_RPC_PATH } from './spine_surface_constants.js';
10
+ /**
11
+ * The routes `fuz_auth::account_router` mounts, relative to `/api/account`. An
12
+ * allowlist rather than every account route shape: `/verify` is a TS-only
13
+ * route the Rust router doesn't serve, so declaring it would have the suites
14
+ * probe a 404. A shape fuz_app renames or drops fails the surface build; one
15
+ * it adds stays off the surface until the Rust router mounts it and this list
16
+ * names it.
17
+ */
18
+ export const ACCOUNT_ROUTER_PATHS = ['/status', '/login', '/logout', '/password'];
19
+ /** Surface generation never invokes handlers — see the module doc. */
20
+ const noop_action_handler = (async () => undefined);
21
+ const to_route_spec = (shape) => ({
22
+ ...shape,
23
+ handler: stub_handler
24
+ });
25
+ /**
26
+ * The RPC endpoints a Rust spine consumer mounts: the standard admin /
27
+ * role-grant-offer / account actions, the extra families, then the domain
28
+ * specs — what `describe_spine_cross_suites` takes as `rpc_endpoints`.
29
+ *
30
+ * @param options - the consumer's action specs, extras and roles
31
+ * @returns the endpoints, as a factory of the stub context
32
+ * @throws Error when an `exclude_methods` entry names no domain spec
33
+ */
34
+ export const create_rust_spine_consumer_rpc_endpoints = (options) => {
35
+ const { domain_action_specs, extra_rpc_actions, roles, exclude_methods = [], rpc_path = SPINE_RPC_PATH } = options;
36
+ const excluded = new Set(exclude_methods);
37
+ for (const method of excluded) {
38
+ if (!domain_action_specs.some((spec) => spec.method === method)) {
39
+ throw new Error(`create_rust_spine_consumer_rpc_endpoints: exclude_methods names '${method}', ` +
40
+ 'which is not among the domain action specs');
41
+ }
42
+ }
43
+ const domain_actions = domain_action_specs
44
+ .filter((spec) => spec.kind === 'request_response')
45
+ .filter((spec) => !is_protocol_action_method(spec.method) && !excluded.has(spec.method))
46
+ .map((spec) => ({ spec, handler: noop_action_handler }));
47
+ return (ctx) => [
48
+ {
49
+ path: rpc_path,
50
+ actions: [
51
+ ...create_standard_rpc_actions(ctx.deps, roles ? { roles } : {}),
52
+ ...(extra_rpc_actions?.(ctx) ?? []),
53
+ ...domain_actions
54
+ ]
55
+ }
56
+ ];
57
+ };
58
+ /**
59
+ * The spine routes a Rust spine consumer serves, outside the RPC endpoint:
60
+ * `/health`, the `ACCOUNT_ROUTER_PATHS` under `/api/account`, and signup
61
+ * beside them (bootstrap is added by `create_test_app_surface_spec`).
62
+ */
63
+ const create_spine_consumer_route_specs = (ctx) => {
64
+ const shapes = [
65
+ account_status_route_shape,
66
+ ...create_account_route_shapes({
67
+ login_account_rate_limited: ctx.login_account_rate_limiter !== null
68
+ })
69
+ ];
70
+ const account_router_shapes = ACCOUNT_ROUTER_PATHS.map((path) => {
71
+ const shape = shapes.find((candidate) => candidate.path === path);
72
+ if (!shape)
73
+ throw new Error(`fuz_app's account route shapes have no '${path}'`);
74
+ return shape;
75
+ });
76
+ return [
77
+ create_health_route_spec(),
78
+ ...prefix_route_specs(SPINE_ACCOUNT_PATH_PREFIX, [
79
+ ...account_router_shapes,
80
+ create_signup_route_shape({
81
+ signup_account_rate_limited: ctx.signup_account_rate_limiter !== null
82
+ })
83
+ ].map(to_route_spec))
84
+ ];
85
+ };
86
+ /**
87
+ * Build the declared surface of a Rust spine consumer for its cross suites,
88
+ * on `create_test_app_surface_spec`: the spine routes (health, the account
89
+ * router's paths, signup, and bootstrap as a `surface_only` shape), the RPC
90
+ * endpoint from `create_rust_spine_consumer_rpc_endpoints`, the daemon-token
91
+ * layer every consumer test binary wires, and — with `host_gate` — the
92
+ * `host_validation` layer at the head of the middleware list.
93
+ *
94
+ * @param options - the consumer's session config, action specs, extras and roles
95
+ * @returns the surface spec, for `describe_spine_cross_suites`'s `surface_source`
96
+ * @throws Error when an account route shape the Rust router mounts is missing,
97
+ * or an `exclude_methods` entry names no domain spec
98
+ */
99
+ export const create_rust_spine_consumer_surface_spec = (options) => create_test_app_surface_spec({
100
+ session_options: options.session_options,
101
+ create_route_specs: create_spine_consumer_route_specs,
102
+ rpc_endpoints: create_rust_spine_consumer_rpc_endpoints(options),
103
+ // the binaries mount `bootstrap_router`; `surface_only` declares its shape
104
+ bootstrap: { mode: 'surface_only' },
105
+ // consumer test binaries wire a daemon-token state (production ones don't)
106
+ daemon_token: true,
107
+ transform_middleware: options.host_gate
108
+ ? (specs) => [
109
+ { name: 'host_validation', path: '*', handler: stub_mw },
110
+ ...specs
111
+ ]
112
+ : undefined
113
+ });
@@ -18,6 +18,8 @@ export declare const RUST_SPINE_STUB_DATABASE_URL_ENV = "FUZ_TESTING_RUST_SPINE_
18
18
  export declare const RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV = "FUZ_RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH";
19
19
  /** Default listening port — slots beside zzz's 1175/1176; matches the binary's `DEFAULT_PORT`. */
20
20
  export declare const RUST_SPINE_STUB_DEFAULT_PORT = 1177;
21
+ /** The preset's default backend name — its spawn label and temp directory. */
22
+ export declare const RUST_SPINE_STUB_DEFAULT_NAME = "spine_stub";
21
23
  /** Default Postgres database — real PG (PGlite isn't reachable from `tokio-postgres`). */
22
24
  export declare const RUST_SPINE_STUB_DEFAULT_DATABASE_URL = "postgres://localhost/fuz_app_test_rust_spine_stub";
23
25
  export interface SpineStubBackendConfigOptions {
@@ -28,6 +30,14 @@ export interface SpineStubBackendConfigOptions {
28
30
  * then `RUST_SPINE_STUB_DEFAULT_DATABASE_URL`.
29
31
  */
30
32
  readonly database_url?: string;
33
+ /**
34
+ * Backend name — labels the spawn in errors and logs, and names its
35
+ * directory under `os.tmpdir()` (`build_test_backend_paths`), which holds
36
+ * the bootstrap token, the daemon token and the fact store. Default
37
+ * `RUST_SPINE_STUB_DEFAULT_NAME`. Give each stub spawned alongside another its own name, so
38
+ * those files don't collide.
39
+ */
40
+ readonly name?: string;
31
41
  /**
32
42
  * Prebuilt binary path. Overrides the `FUZ_TESTING_RUST_SPINE_STUB_BIN` env
33
43
  * var. When neither is set the preset throws.
@@ -1 +1 @@
1
- {"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA0C9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAYzD,uGAAuG;AACvG,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,6CAA6C,CAAC;AAE3F;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,6CAA6C,CAAC;AAuBnG,kGAAkG;AAClG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,sDACG,CAAC;AAErD,MAAM,WAAW,6BAA6B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aAmDF,CAAC"}
1
+ {"version":3,"file":"rust_spine_stub_backend_config.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/rust_spine_stub_backend_config.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAwC9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAazD,uGAAuG;AACvG,eAAO,MAAM,uBAAuB,oCAAoC,CAAC;AAEzE;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC,6CAA6C,CAAC;AAE3F;;;;;GAKG;AACH,eAAO,MAAM,wCAAwC,6CAA6C,CAAC;AAiCnG,kGAAkG;AAClG,eAAO,MAAM,4BAA4B,OAAO,CAAC;AAEjD,8EAA8E;AAC9E,eAAO,MAAM,4BAA4B,eAAe,CAAC;AAEzD,0FAA0F;AAC1F,eAAO,MAAM,oCAAoC,sDACG,CAAC;AAErD,MAAM,WAAW,6BAA6B;IAC7C,8DAA8D;IAC9D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B;;;;;;OAMG;IACH,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IACvB;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;;;;OAOG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;;;OAMG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC;;;;;;OAMG;IACH,QAAQ,CAAC,wBAAwB,CAAC,EAAE,OAAO,CAAC;IAC5C;;;;;OAKG;IACH,QAAQ,CAAC,8BAA8B,CAAC,EAAE,MAAM,CAAC;CACjD;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,8BAA8B,GAC1C,UAAS,6BAAkC,KACzC,aAmDF,CAAC"}
@@ -13,17 +13,15 @@ import '../assert_dev_env.js';
13
13
  * path in. `FUZ_TESTING_RUST_SPINE_STUB_BIN` (or the `binary_path` option) must
14
14
  * point at a prebuilt binary; the preset throws a clear error when neither
15
15
  * is set rather than guessing. Build once with
16
- * `cargo build -p testing_spine_stub --release` and point the env var at
17
- * the resulting `target/release/testing_spine_stub`; operators / CI cache
18
- * the binary across runs for fast spawns.
16
+ * `cargo build -p testing_spine_stub --profile cross-test` (the workspace's
17
+ * non-LTO test profile; `--release` works too, slower to rebuild) and point
18
+ * the env var at the resulting `target/cross-test/testing_spine_stub`;
19
+ * operators / CI cache the binary across runs for fast spawns.
19
20
  *
20
21
  * **Operator setup** — the target Postgres database must exist before the
21
- * harness runs (the harness never issues `CREATE DATABASE`, to avoid
22
- * forcing a `CREATEDB` grant on the test role):
23
- *
24
- * ```bash
25
- * createdb fuz_app_test_rust_spine_stub 2>/dev/null || true
26
- * ```
22
+ * stub boots. The harness creates it only when a setup asks
23
+ * (`ensure_database_for_url`, or `create_cross_backend_global_setup`'s
24
+ * `ensure_databases`), so a test role needs no `CREATEDB` grant otherwise.
27
25
  *
28
26
  * `FUZ_TESTING_RUST_SPINE_STUB_DATABASE_URL` points the stub at another
29
27
  * server (e.g. a scratch cluster on a spare port).
@@ -58,7 +56,11 @@ export const RUST_SPINE_STUB_DATABASE_URL_ENV = 'FUZ_TESTING_RUST_SPINE_STUB_DAT
58
56
  export const RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV = 'FUZ_RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH';
59
57
  /**
60
58
  * Capabilities for the Rust `testing_spine_stub` — `rust_default_capabilities`
61
- * plus `sse` (the stub serves `GET /api/admin/audit/stream` over the spine
59
+ * with every `MountFlag` stated, all `true`: `ws` (it mounts `/api/ws`), `peer_request` (its registry carries
60
+ * `peer/ping` on both transports), `cell_crud` + `cell_relations` (it mounts the
61
+ * full cell surface over the `fuz_cell` namespace), `fact_serving` (the
62
+ * cell-gated serve route, the `fuz_facts` namespace and `_testing_put_fact`),
63
+ * `fact_serving_admin_hash` (the admin bare-hash route), `sse` (the stub serves `GET /api/admin/audit/stream` over the spine
62
64
  * `fuz_realtime::SseRegistry` + audit listener), `ready` (it live-mounts
63
65
  * `/ready` over the env-supplied fixture path), `ws_account_actions` (it
64
66
  * serves one action registry on `/api/rpc` and `/api/ws`), `cell_gated_create`
@@ -70,7 +72,13 @@ export const RUST_SPINE_STUB_EXPECTED_SCHEMA_PATH_ENV = 'FUZ_RUST_SPINE_STUB_EXP
70
72
  */
71
73
  const rust_spine_stub_capabilities = Object.freeze({
72
74
  ...rust_default_capabilities,
75
+ ws: true,
76
+ peer_request: true,
73
77
  sse: true,
78
+ cell_crud: true,
79
+ cell_relations: true,
80
+ fact_serving: true,
81
+ fact_serving_admin_hash: true,
74
82
  ready: true,
75
83
  ws_account_actions: true,
76
84
  cell_gated_create: true,
@@ -78,6 +86,8 @@ const rust_spine_stub_capabilities = Object.freeze({
78
86
  });
79
87
  /** Default listening port — slots beside zzz's 1175/1176; matches the binary's `DEFAULT_PORT`. */
80
88
  export const RUST_SPINE_STUB_DEFAULT_PORT = 1177;
89
+ /** The preset's default backend name — its spawn label and temp directory. */
90
+ export const RUST_SPINE_STUB_DEFAULT_NAME = 'spine_stub';
81
91
  /** Default Postgres database — real PG (PGlite isn't reachable from `tokio-postgres`). */
82
92
  export const RUST_SPINE_STUB_DEFAULT_DATABASE_URL = 'postgres://localhost/fuz_app_test_rust_spine_stub';
83
93
  /**
@@ -93,13 +103,12 @@ export const RUST_SPINE_STUB_DEFAULT_DATABASE_URL = 'postgres://localhost/fuz_ap
93
103
  */
94
104
  export const rust_spine_stub_backend_config = (options = {}) => {
95
105
  const { port = RUST_SPINE_STUB_DEFAULT_PORT, database_url = process.env[RUST_SPINE_STUB_DATABASE_URL_ENV] ||
96
- RUST_SPINE_STUB_DEFAULT_DATABASE_URL, binary_path = process.env[RUST_SPINE_STUB_BIN_ENV], enable_login_rate_limit, trusted_proxies } = options;
106
+ RUST_SPINE_STUB_DEFAULT_DATABASE_URL, binary_path = process.env[RUST_SPINE_STUB_BIN_ENV], enable_login_rate_limit, trusted_proxies, name = RUST_SPINE_STUB_DEFAULT_NAME } = options;
97
107
  if (!binary_path) {
98
108
  throw new Error(`rust_spine_stub_backend_config: no binary path — set ${RUST_SPINE_STUB_BIN_ENV} to a prebuilt ` +
99
- '`testing_spine_stub` binary (build it with `cargo build -p testing_spine_stub --release`) ' +
109
+ '`testing_spine_stub` binary (build it with `cargo build -p testing_spine_stub --profile cross-test`) ' +
100
110
  'or pass `binary_path`.');
101
111
  }
102
- const name = 'spine_stub';
103
112
  const paths = build_test_backend_paths(name);
104
113
  return make_default_rust_backend_config({
105
114
  name,
@@ -113,12 +122,12 @@ export const rust_spine_stub_backend_config = (options = {}) => {
113
122
  port_env_var: 'FUZ_RUST_SPINE_STUB_PORT',
114
123
  rust_log: 'info,testing_spine_stub=info',
115
124
  paths,
125
+ // The binary writes its daemon-token JSON to
126
+ // `{FUZ_RUST_SPINE_STUB_DIR}/run/daemon_token`; the builder anchors the
127
+ // dir to `paths.root`, which makes that `paths.daemon_token_path`, the
128
+ // file `spawn_backend` reads after the health probe.
129
+ daemon_token_dir_env: 'FUZ_RUST_SPINE_STUB_DIR',
116
130
  extra_env: {
117
- // The binary writes its daemon-token JSON to
118
- // `{FUZ_RUST_SPINE_STUB_DIR}/run/daemon_token`; anchoring the dir to
119
- // `paths.root` makes that equal `paths.daemon_token_path`, which
120
- // `spawn_backend` reads after the health probe.
121
- FUZ_RUST_SPINE_STUB_DIR: paths.root,
122
131
  // Absolute path to the committed spine `expected_schema.json` — the
123
132
  // stub's `/ready` gate introspects the live DB against it. The SAME
124
133
  // file the TS spine reads, so the two backends share one cross-impl
@@ -1,5 +1,6 @@
1
1
  import '../assert_dev_env.js';
2
2
  import type { SetupTest } from './setup.js';
3
+ import type { WsEndpointTarget } from './ws_endpoint.js';
3
4
  /** Options for the session-cap parity suite. */
4
5
  export interface SessionCapCrossTestOptions {
5
6
  /** Per-test fixture producer (cross-process only — see the module doc). */
@@ -19,14 +20,14 @@ export interface SessionCapCrossTestOptions {
19
20
  * session's socket is closed. Pass it when the backend has a WS transport
20
21
  * (`capabilities.ws`); omitted, that case is skipped.
21
22
  */
22
- readonly ws?: {
23
- /** Base URL the backend is reachable at (e.g. `http://localhost:1178`). */
24
- readonly base_url: string;
25
- /** WebSocket endpoint path on the backend (e.g. `/api/ws`). */
26
- readonly ws_path: string;
27
- /** Origin for the upgrade. Defaults to `base_url`. */
28
- readonly origin?: string;
29
- };
23
+ readonly ws?: WsEndpointTarget;
24
+ /**
25
+ * The roles the account whose socket the `ws` case watches is created with, for a
26
+ * backend whose WS upgrade requires a role (an admin-gated `/api/ws`
27
+ * refuses a roleless account's upgrade). Each must be admin-grantable — the
28
+ * fixture offers it. Default none.
29
+ */
30
+ readonly ws_account_roles?: ReadonlyArray<string>;
30
31
  }
31
32
  export declare const describe_session_cap_cross_tests: (options: SessionCapCrossTestOptions) => void;
32
33
  //# sourceMappingURL=session_cap.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"session_cap.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/session_cap.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA0D9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,gDAAgD;AAChD,MAAM,WAAW,0BAA0B;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE;QACb,2EAA2E;QAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC1B,+DAA+D;QAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;QACzB,sDAAsD;QACtD,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC;KACzB,CAAC;CACF;AAED,eAAO,MAAM,gCAAgC,GAAI,SAAS,0BAA0B,KAAG,IA4FtF,CAAC"}
1
+ {"version":3,"file":"session_cap.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/session_cap.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA0D9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE5C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAEzD,gDAAgD;AAChD,MAAM,WAAW,0BAA0B;IAC1C,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B;;;;OAIG;IACH,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,2DAA2D;IAC3D,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B;;;;OAIG;IACH,QAAQ,CAAC,EAAE,CAAC,EAAE,gBAAgB,CAAC;IAC/B;;;;;OAKG;IACH,QAAQ,CAAC,gBAAgB,CAAC,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CAClD;AAED,eAAO,MAAM,gCAAgC,GAAI,SAAS,0BAA0B,KAAG,IAgGtF,CAAC"}
@@ -52,11 +52,12 @@ import { DEFAULT_MAX_SESSIONS } from '../../auth/account_route_schema.js';
52
52
  import { DEFAULT_TEST_PASSWORD } from '../test_credentials.js';
53
53
  import { create_ws_transport } from '../transports/ws_transport.js';
54
54
  import { test_if } from './capabilities.js';
55
+ import { SPINE_ACCOUNT_LOGIN_PATH, SPINE_ACCOUNT_STATUS_PATH } from './spine_surface_constants.js';
55
56
  export const describe_session_cap_cross_tests = (options) => {
56
- const { setup_test, ws } = options;
57
+ const { setup_test, ws, ws_account_roles = [] } = options;
57
58
  const max_sessions = options.max_sessions ?? DEFAULT_MAX_SESSIONS;
58
- const login_path = options.login_path ?? '/api/account/login';
59
- const status_path = options.status_path ?? '/api/account/status';
59
+ const login_path = options.login_path ?? SPINE_ACCOUNT_LOGIN_PATH;
60
+ const status_path = options.status_path ?? SPINE_ACCOUNT_STATUS_PATH;
60
61
  // Fresh-keeper-per-test wipes the DB between tests, so a literal username
61
62
  // never collides (see `setup.ts`).
62
63
  const username = 'session_cap_user';
@@ -100,7 +101,11 @@ export const describe_session_cap_cross_tests = (options) => {
100
101
  // held before it (`create_account` seeds one on some cradles).
101
102
  test_if(ws !== undefined, 'a session the cap evicts has its open socket closed', async () => {
102
103
  const fixture = await setup_test();
103
- await fixture.create_account({ username, password_value: DEFAULT_TEST_PASSWORD });
104
+ await fixture.create_account({
105
+ username,
106
+ password_value: DEFAULT_TEST_PASSWORD,
107
+ roles: [...ws_account_roles]
108
+ });
104
109
  const victim = await login(fixture, 'the login whose socket is watched');
105
110
  const client = await create_ws_transport({
106
111
  base_url: ws.base_url,
@@ -0,0 +1,19 @@
1
+ import '../assert_dev_env.js';
2
+ import type { SetupTest } from './setup.js';
3
+ /** Options for the session cookie codec parity suite. */
4
+ export interface SessionCookieCodecCrossTestOptions {
5
+ /** Per-test fixture producer (cross-process). */
6
+ readonly setup_test: SetupTest;
7
+ /** The spine's session cookie name (`handle.config.cookie_name`). */
8
+ readonly cookie_name: string;
9
+ /**
10
+ * The backend's `SECRET_FUZ_COOKIE_KEYS` value
11
+ * (`handle.config.env.SECRET_FUZ_COOKIE_KEYS`) — the suite signs with its
12
+ * primary key.
13
+ */
14
+ readonly cookie_keys: string;
15
+ /** JSON-RPC endpoint path. Default `SPINE_RPC_PATH`. */
16
+ readonly rpc_path?: string;
17
+ }
18
+ export declare const describe_session_cookie_codec_cross_tests: (options: SessionCookieCodecCrossTestOptions) => void;
19
+ //# sourceMappingURL=session_cookie_codec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session_cookie_codec.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/session_cookie_codec.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkC9B,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAGzD,yDAAyD;AACzD,MAAM,WAAW,kCAAkC;IAClD,iDAAiD;IACjD,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,qEAAqE;IACrE,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B;;;;OAIG;IACH,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,wDAAwD;IACxD,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;CAC3B;AA2BD,eAAO,MAAM,yCAAyC,GACrD,SAAS,kCAAkC,KACzC,IAsGF,CAAC"}
@@ -0,0 +1,128 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Cross-backend parity suite for the **signed session cookie codec** over real
4
+ * HTTP: which spellings of a correctly signed cookie each spine accepts.
5
+ *
6
+ * The cookie is `${token}:${expires_at}.${signature}`, the signature the padded
7
+ * standard base64 of an HMAC-SHA256 MAC. Both spines must accept exactly one
8
+ * spelling of a given signed payload, or a cookie that one spine refuses would
9
+ * authenticate on the other. The suite holds the test keyring, so it signs
10
+ * payloads itself and sends variants a lenient decoder would take:
11
+ *
12
+ * - **signature spelling** — the last data character bumped so the trailing
13
+ * bits are non-zero (`atob` decodes the same MAC), and the `=` padding
14
+ * dropped. Both must be refused.
15
+ * - **`expires_at` spelling** — `+N`, ` N`, `N.0`, and an all-digit value past
16
+ * `u64::MAX` (the Rust spine parses it as `u64`), each signed with the real
17
+ * key. All must be refused. A leading-zero `0N` reads as `N` on both, so it
18
+ * still authenticates — the bound is on the value, not the digit count.
19
+ *
20
+ * The positive controls run first: re-signing the keeper cookie's payload
21
+ * reproduces the cookie byte for byte (the suite's keyring is the backend's),
22
+ * and that cookie authenticates. Authentication is probed with the
23
+ * `account_verify` RPC action. The per-impl unit tests cover each codec
24
+ * directly; this suite is what catches the two drifting apart.
25
+ *
26
+ * @module
27
+ */
28
+ import { describe, test, assert } from 'vitest';
29
+ import { create_keyring } from '../../auth/keyring.js';
30
+ import { ERROR_AUTHENTICATION_REQUIRED } from '../../http/error_schemas.js';
31
+ import { cross_rpc_call, error_reason } from './cross_rpc.js';
32
+ import { SPINE_RPC_PATH } from './spine_surface_constants.js';
33
+ /** The base64 alphabet, in index order. */
34
+ const BASE64_ALPHABET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
35
+ /** `u64::MAX + 1`, the smallest all-digit `expires_at` the Rust spine cannot parse. */
36
+ const U64_MAX_PLUS_ONE = '18446744073709551616';
37
+ /** Split a signed cookie value on its last `.` into payload and signature. */
38
+ const split_signed = (signed) => {
39
+ const dot = signed.lastIndexOf('.');
40
+ assert.ok(dot !== -1, `signed cookie value has no '.' separator: ${signed}`);
41
+ return { payload: signed.slice(0, dot), signature: signed.slice(dot + 1) };
42
+ };
43
+ /**
44
+ * Bump the signature's last data character (index 42, which carries the MAC's
45
+ * final 4 bits and 2 zero padding bits) to the next base64 character. That sets
46
+ * a padding bit, which `atob` ignores, so the decoded MAC is unchanged.
47
+ */
48
+ const bump_trailing_bits = (signature) => {
49
+ const last_data = signature[42];
50
+ const index = BASE64_ALPHABET.indexOf(last_data);
51
+ assert.ok(index !== -1 && index % 4 === 0, `unexpected final signature character: ${last_data}`);
52
+ return signature.slice(0, 42) + BASE64_ALPHABET[index + 1] + signature.slice(43);
53
+ };
54
+ export const describe_session_cookie_codec_cross_tests = (options) => {
55
+ const { setup_test, cookie_name, cookie_keys } = options;
56
+ const rpc_path = options.rpc_path ?? SPINE_RPC_PATH;
57
+ const keyring_result = create_keyring(cookie_keys);
58
+ if (!keyring_result.ok) {
59
+ throw new Error(`cookie_keys is not a valid keyring: ${keyring_result.errors.join('; ')}`);
60
+ }
61
+ const keyring = keyring_result.keyring;
62
+ /** The keeper's signed cookie value, with its verified `token` and `expires_at`. */
63
+ const keeper_cookie = async (fixture) => {
64
+ const header = fixture.create_session_headers().cookie;
65
+ assert.ok(header, 'the keeper session headers carry a cookie');
66
+ const prefix = `${cookie_name}=`;
67
+ assert.ok(header.startsWith(prefix), `the keeper cookie is named '${cookie_name}'`);
68
+ const signed = header.slice(prefix.length);
69
+ const verified = await keyring.verify(signed);
70
+ assert.ok(verified, 'the keeper cookie verifies under cookie_keys');
71
+ const sep = verified.value.lastIndexOf(':');
72
+ assert.ok(sep !== -1, 'the keeper cookie payload is `token:expires_at`');
73
+ return {
74
+ signed,
75
+ token: verified.value.slice(0, sep),
76
+ expires_at: verified.value.slice(sep + 1)
77
+ };
78
+ };
79
+ const verify_with = (fixture, signed) => cross_rpc_call(fixture.fresh_transport(), rpc_path, 'account_verify', undefined, {
80
+ cookie: `${cookie_name}=${signed}`
81
+ });
82
+ const assert_unauthenticated = async (fixture, signed, label) => {
83
+ const result = await verify_with(fixture, signed);
84
+ assert.strictEqual(result.ok, false, `${label}: must not authenticate`);
85
+ assert.strictEqual(error_reason(result), ERROR_AUTHENTICATION_REQUIRED, label);
86
+ };
87
+ describe('session cookie codec parity', () => {
88
+ test('controls: re-signing the keeper payload reproduces its cookie, which authenticates', async () => {
89
+ const fixture = await setup_test();
90
+ const { signed, token, expires_at } = await keeper_cookie(fixture);
91
+ assert.strictEqual(await keyring.sign(`${token}:${expires_at}`), signed, 'the suite keyring signs as the backend does');
92
+ const result = await verify_with(fixture, signed);
93
+ assert.ok(result.ok, `the keeper cookie authenticates: ${JSON.stringify(result.error)}`);
94
+ });
95
+ test('a signature with non-zero trailing bits is refused', async () => {
96
+ const fixture = await setup_test();
97
+ const { payload, signature } = split_signed((await keeper_cookie(fixture)).signed);
98
+ await assert_unauthenticated(fixture, `${payload}.${bump_trailing_bits(signature)}`, 'non-zero trailing bits');
99
+ });
100
+ test('an unpadded signature is refused', async () => {
101
+ const fixture = await setup_test();
102
+ const { payload, signature } = split_signed((await keeper_cookie(fixture)).signed);
103
+ assert.ok(signature.endsWith('='), 'the canonical signature is padded');
104
+ await assert_unauthenticated(fixture, `${payload}.${signature.slice(0, -1)}`, 'unpadded');
105
+ });
106
+ test('a non-canonical expires_at is refused even when signed', async () => {
107
+ const fixture = await setup_test();
108
+ const { token, expires_at } = await keeper_cookie(fixture);
109
+ const variants = [
110
+ ['plus sign', `+${expires_at}`],
111
+ ['leading space', ` ${expires_at}`],
112
+ ['decimal', `${expires_at}.0`],
113
+ ['past u64::MAX', U64_MAX_PLUS_ONE]
114
+ ];
115
+ for (const [label, value] of variants) {
116
+ const signed = await keyring.sign(`${token}:${value}`);
117
+ await assert_unauthenticated(fixture, signed, `expires_at ${label} (${JSON.stringify(value)})`);
118
+ }
119
+ });
120
+ test('a leading-zero expires_at reads as its value and authenticates', async () => {
121
+ const fixture = await setup_test();
122
+ const { token, expires_at } = await keeper_cookie(fixture);
123
+ const signed = await keyring.sign(`${token}:0${expires_at}`);
124
+ const result = await verify_with(fixture, signed);
125
+ assert.ok(result.ok, `a leading zero authenticates: ${JSON.stringify(result.error)}`);
126
+ });
127
+ });
128
+ };
@@ -26,14 +26,6 @@ export interface CreateTestAccountOptions {
26
26
  */
27
27
  readonly email?: string;
28
28
  }
29
- /**
30
- * Shape returned by `TestFixture.create_account`. Aliased to the
31
- * existing `TestAccount` interface from `testing/app_server.ts` — same shape,
32
- * stable name on the cross-backend testing surface so call sites read
33
- * `fixture.create_account(...)` returning `TestAccountFixture` without
34
- * crossing module boundaries.
35
- */
36
- export type TestAccountFixture = TestAccount;
37
29
  /**
38
30
  * Spec for a bootstrap-time secondary account seeded alongside the
39
31
  * keeper by `_testing_reset` (cross-process) or `create_test_app`
@@ -52,32 +44,21 @@ export interface ExtraAccountSpec {
52
44
  readonly password_value?: string;
53
45
  readonly roles: ReadonlyArray<string>;
54
46
  }
55
- /** Bootstrap-time-seeded secondary account exposed on the fixture. */
56
- export interface ExtraAccountFixture {
57
- readonly account: {
58
- readonly id: Uuid;
59
- readonly username: string;
60
- };
61
- readonly actor: {
62
- readonly id: Uuid;
63
- };
64
- readonly api_token: string;
65
- readonly session_cookie: string;
66
- readonly create_session_headers: (extra?: Record<string, string>) => Record<string, string>;
67
- readonly create_bearer_headers: (extra?: Record<string, string>) => Record<string, string>;
68
- }
69
47
  /**
70
- * Build an `ExtraAccountFixture` from a seeded `{account, actor,
71
- * api_token, session_cookie}` bundle and the session cookie name.
48
+ * Build a `TestAccount` from a seeded `{account, actor, api_token,
49
+ * session_cookie}` bundle and the session cookie name — the header builders
50
+ * close over its credentials.
51
+ *
52
+ * Every fixture path that hands out a secondary account builds it here: the
53
+ * bootstrap-time `extra_accounts` (in-process via
54
+ * `create_test_account_with_credentials`, cross-process from the
55
+ * `_testing_reset` output) and the cross-process `fixture.create_account`.
72
56
  *
73
- * Same shape produced by either path that seeds bootstrap-time
74
- * secondaries: in-process via `create_test_account_with_credentials`
75
- * against the live backend's DB, or cross-process via the
76
- * `_testing_reset` RPC's `extra_accounts` output. Both call this
77
- * helper so the fixture-side header builders + field plumbing stays
78
- * in one place.
57
+ * @param seeded - the account's identity and credentials
58
+ * @param cookie_name - the backend's session cookie name
59
+ * @returns the account with its header builders
79
60
  */
80
- export declare const build_extra_account_fixture: (seeded: {
61
+ export declare const to_test_account: (seeded: {
81
62
  account: {
82
63
  id: Uuid;
83
64
  username: string;
@@ -87,10 +68,18 @@ export declare const build_extra_account_fixture: (seeded: {
87
68
  };
88
69
  api_token: string;
89
70
  session_cookie: string;
90
- }, cookie_name: string) => ExtraAccountFixture;
71
+ }, cookie_name: string) => TestAccount;
91
72
  /**
92
- * Fields shared by every `TestFixture` regardless of transport. The
93
- * discriminated union below adds in-process-only fields conditionally.
73
+ * The per-test bundle returned by `SetupTest`. Every Tier 1 suite body
74
+ * reads exclusively from this shape — no `test_app.backend.*` reads remain
75
+ * in the suite bodies.
76
+ *
77
+ * Transport-agnostic: in-process and cross-process producers return the
78
+ * same shape. Behaviors that once needed raw backend access (keyring for
79
+ * forging cookies) are reached through wire-shaped seams instead —
80
+ * `mint_expired_session()` mints over the `_testing_mint_session` channel
81
+ * cross-process and directly in-process, so suite bodies never branch on
82
+ * the transport.
94
83
  *
95
84
  * **Keeper ≠ admin.** `fixture.account` / `fixture.actor` refer to the
96
85
  * **fresh keeper** seeded per test. The keeper account holds
@@ -102,7 +91,7 @@ export declare const build_extra_account_fixture: (seeded: {
102
91
  * via `extra_accounts: [{username, roles: [ROLE_KEEPER]}]` and read it
103
92
  * from `fixture.extra_accounts[username]`.
104
93
  */
105
- export interface TestFixtureBase {
94
+ export interface TestFixture {
106
95
  /**
107
96
  * Transport for this test's HTTP requests. Typed as `FetchTransport`
108
97
  * so cross-process tests can call `transport.cookies()` for WS upgrade
@@ -159,14 +148,14 @@ export interface TestFixtureBase {
159
148
  * cross-process: goes through the consumer-supplied DB-admin
160
149
  * channel.
161
150
  */
162
- readonly create_account: (options?: CreateTestAccountOptions) => Promise<TestAccountFixture>;
151
+ readonly create_account: (options?: CreateTestAccountOptions) => Promise<TestAccount>;
163
152
  /**
164
153
  * Bootstrap-time-seeded secondaries, keyed by their declared
165
154
  * `username`. Populated from the `extra_accounts` option passed to
166
155
  * `default_in_process_setup` / `default_cross_process_setup`. Empty
167
156
  * for suites that don't declare any.
168
157
  */
169
- readonly extra_accounts: Readonly<Record<string, ExtraAccountFixture>>;
158
+ readonly extra_accounts: Readonly<Record<string, TestAccount>>;
170
159
  /**
171
160
  * Additional actors seeded on the **keeper** account (beyond its single
172
161
  * bootstrap `actor`), in declaration order. Populated from the
@@ -192,23 +181,18 @@ export interface TestFixtureBase {
192
181
  * RPC over the keeper's daemon-token channel (the driver has no keyring).
193
182
  */
194
183
  readonly mint_expired_session: () => Promise<string>;
184
+ /**
185
+ * Whether the suites' audit reads refuse metadata keys an event type
186
+ * doesn't declare — the setup's `reject_unknown_audit_metadata_keys`.
187
+ * `list_audit_events` and the admin suite's readers pass it to
188
+ * `assert_audit_event_metadata`.
189
+ */
190
+ readonly reject_unknown_audit_metadata_keys?: boolean;
195
191
  }
196
192
  /**
197
- * The per-test bundle returned by `SetupTest`. Every Tier 1 suite body
198
- * reads exclusively from this shape — no `test_app.backend.*` reads remain
199
- * in the suite bodies.
200
- *
201
- * Transport-agnostic: in-process and cross-process producers return the
202
- * same shape. Behaviors that once needed raw backend access (keyring for
203
- * forging cookies) are reached through wire-shaped seams instead —
204
- * `mint_expired_session()` mints over the `_testing_mint_session` channel
205
- * cross-process and directly in-process, so suite bodies never branch on
206
- * the transport.
207
- */
208
- export type TestFixture = TestFixtureBase;
209
- /**
210
- * Per-test fixture-producing function. Invoked once inside every
211
- * `test()` body. The implementation captures factory inputs (in-process)
193
+ * Fixture-producing function. Invoked once inside every `test()` body by
194
+ * default; a suite whose cases share no state past the reset may call it once
195
+ * in `beforeAll` instead. The implementation captures factory inputs (in-process)
212
196
  * or a long-running backend handle (cross-process) and creates
213
197
  * a fresh per-test bundle on each call.
214
198
  */
@@ -244,7 +228,7 @@ export interface CapabilityGatedCrossSuiteOptions extends CrossSuiteOptions {
244
228
  /**
245
229
  * `CrossSuiteOptions` plus the RPC endpoint path the suite dispatches
246
230
  * against — the shape every JSON-RPC-driven imperative suite takes (cell
247
- * verbs, origin, body-size, actor lookup/search, account lifecycle, app
231
+ * verbs, origin, body-size, actor lookup, account lifecycle, app
248
232
  * settings, testing backdoor). Suites alias this under a self-documenting
249
233
  * per-suite name (e.g. `OriginCrossTestOptions = RpcPathCrossSuiteOptions`).
250
234
  */
@@ -396,9 +380,27 @@ export interface CrossProcessSetupOptions {
396
380
  * single bootstrap actor — exposed on `fixture.extra_actors`. Declare
397
381
  * to put the keeper into a multi-actor state so the `actor_required` /
398
382
  * explicit-`acting` branches are reachable cross-process. See
399
- * `TestFixtureBase.extra_actors`.
383
+ * `TestFixture.extra_actors`.
400
384
  */
401
385
  readonly extra_actors?: ReadonlyArray<string>;
386
+ /**
387
+ * Hold every audit row the fixture's readers parse to a closed world:
388
+ * metadata keys its event type doesn't declare are refused, not just
389
+ * mistyped or missing ones (`assert_audit_event_metadata`'s
390
+ * `reject_unknown_keys`). Off by default, since the builtin schemas are
391
+ * open for consumer-added fields; a backend's own spine suite turns it on
392
+ * so neither spine can add an undeclared key. Exposed on
393
+ * `fixture.reject_unknown_audit_metadata_keys`.
394
+ */
395
+ readonly reject_unknown_audit_metadata_keys?: boolean;
396
+ /**
397
+ * REST signup route `fixture.create_account` mints through. Default
398
+ * `SPINE_ACCOUNT_SIGNUP_PATH`; set it for a backend mounting the account
399
+ * routes under another prefix.
400
+ */
401
+ readonly signup_path?: string;
402
+ /** REST login route `fixture.create_account` logs in through. Default `SPINE_ACCOUNT_LOGIN_PATH`. */
403
+ readonly login_path?: string;
402
404
  }
403
405
  /**
404
406
  * Capture a backend's schema snapshot over the `_testing_schema_snapshot`