@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
@@ -1,14 +1,14 @@
1
1
  import '../assert_dev_env.js';
2
2
  import { ROLE_KEEPER } from '../../auth/role_schema.js';
3
- import { query_create_actor } from '../../auth/account_queries.js';
3
+ import { create_test_extra_actor } from '../db_entities.js';
4
4
  import { create_test_app, create_test_account_with_credentials, mint_test_session } from '../app_server.js';
5
5
  import { create_test_app_surface_spec } from '../stubs.js';
6
6
  import { http_transport } from '../rpc_helpers.js';
7
7
  import { in_process_capabilities } from './capabilities.js';
8
- import { build_extra_account_fixture, EXPIRED_SESSION_OFFSET_SECONDS } from './setup.js';
8
+ import { EXPIRED_SESSION_OFFSET_SECONDS, to_test_account } from './setup.js';
9
9
  /**
10
10
  * Wrap a Hono-style app into a `FetchTransport`-shaped object so the
11
- * shared `TestFixtureBase.transport` type holds for both in-process and
11
+ * shared `TestFixture.transport` type holds for both in-process and
12
12
  * cross-process setups. In-process has no real cookie jar — the no-op
13
13
  * `cookies()` returns `[]`; in-process tests build cookies via
14
14
  * `fixture.create_session_headers()` instead.
@@ -60,15 +60,16 @@ export const default_in_process_setup = (options) => async () => {
60
60
  password_value: spec.password_value,
61
61
  roles: [...spec.roles]
62
62
  });
63
- extra_accounts[spec.username] = build_extra_account_fixture(seeded, cookie_name);
63
+ extra_accounts[spec.username] = to_test_account(seeded, cookie_name);
64
64
  }
65
65
  // Seed additional keeper actors directly against the same DB. Mirrors
66
66
  // the cross-process `_testing_reset` `extra_actors` path; no production
67
67
  // wire mints a second actor, so this bootstrap-cradle insert is the
68
- // only way into a multi-actor keeper state.
68
+ // only way into a multi-actor keeper state. Seeded at the wall clock,
69
+ // like the reset, so they sort after the bootstrap actor.
69
70
  const extra_actors = [];
70
71
  for (const name of options.extra_actors ?? []) {
71
- const seeded_actor = await query_create_actor({ db: test_app.backend.deps.db }, test_app.backend.account.id, name);
72
+ const seeded_actor = await create_test_extra_actor(test_app.backend.deps.db, test_app.backend.account.id, name);
72
73
  extra_actors.push({ id: seeded_actor.id, name: seeded_actor.name });
73
74
  }
74
75
  return {
@@ -85,6 +86,7 @@ export const default_in_process_setup = (options) => async () => {
85
86
  create_account: test_app.create_account,
86
87
  extra_accounts,
87
88
  extra_actors,
89
+ reject_unknown_audit_metadata_keys: options.reject_unknown_audit_metadata_keys ?? false,
88
90
  // Forge directly against the live backend's DB + keyring — no wire
89
91
  // hop needed in-process.
90
92
  mint_expired_session: async () => {
@@ -147,7 +149,8 @@ export const default_in_process_suite_options = (options) => ({
147
149
  app_options: options.app_options,
148
150
  roles: [ROLE_KEEPER, ...(options.extra_keeper_roles ?? [])],
149
151
  extra_accounts: options.extra_accounts,
150
- extra_actors: options.extra_actors
152
+ extra_actors: options.extra_actors,
153
+ reject_unknown_audit_metadata_keys: options.reject_unknown_audit_metadata_keys
151
154
  }),
152
155
  surface_source: options.surface_source ??
153
156
  create_test_app_surface_spec({
@@ -1 +1 @@
1
- {"version":3,"file":"login_security.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/login_security.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAsF9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAK5C,mDAAmD;AACnD,MAAM,WAAW,6BAA6B;IAC7C,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAmDD,eAAO,MAAM,mCAAmC,GAC/C,SAAS,6BAA6B,KACpC,IA8XF,CAAC"}
1
+ {"version":3,"file":"login_security.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/login_security.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAsF9B,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAO5C,mDAAmD;AACnD,MAAM,WAAW,6BAA6B;IAC7C,2EAA2E;IAC3E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,kFAAkF;IAClF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,wEAAwE;IACxE,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC;;;;;;OAMG;IACH,QAAQ,CAAC,uBAAuB,CAAC,EAAE,OAAO,CAAC;IAC3C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAmDD,eAAO,MAAM,mCAAmC,GAC/C,SAAS,6BAA6B,KACpC,IA4VF,CAAC"}
@@ -78,9 +78,8 @@ import { describe, test, assert } from 'vitest';
78
78
  import { ERROR_RATE_LIMIT_EXCEEDED } from '../../http/error_schemas.js';
79
79
  import { raw_http_exchange, raw_http_parse_response } from '../transports/raw_http.js';
80
80
  import { default_login_account_rate_limit, default_login_ip_rate_limit } from '../../rate_limiter.js';
81
- import { AuditLogListOutput } from '../../auth/admin_action_specs.js';
82
- import { cross_rpc_call, expect_output } from './cell_cross_helpers.js';
83
- import { SPINE_RPC_PATH } from './spine_surface_constants.js';
81
+ import { list_audit_events } from './audit_cross_helpers.js';
82
+ import { SPINE_ACCOUNT_LOGIN_PATH, SPINE_ACCOUNT_PASSWORD_PATH } from './spine_surface_constants.js';
84
83
  /**
85
84
  * RFC 5737 TEST-NET-3 (`203.0.113.0/24`) documentation addresses — guaranteed
86
85
  * non-routable, so they never collide with a real client IP. One per bucket the
@@ -116,13 +115,13 @@ const XFF_V6_OTHER_64 = '2001:db8:e3:2::1';
116
115
  const XFF_V6_PASSWORD_EXHAUST = '2001:db8:e3:5::1';
117
116
  const XFF_V6_PASSWORD_SAME_64 = '2001:db8:e3:5:8000::2';
118
117
  /** How long a raw exchange waits for the login's answer. */
119
- const RAW_READ_TIMEOUT_MS = 5000;
118
+ const LOGIN_RAW_READ_TIMEOUT_MS = 5000;
120
119
  /** A non-routable, non-existent identifier — login resolves to the not-found path (records the bucket, returns 401). */
121
120
  const PROBE_PASSWORD = 'login_security_wrong_password';
122
121
  export const describe_login_security_cross_tests = (options) => {
123
122
  const { setup_test } = options;
124
- const login_path = options.login_path ?? '/api/account/login';
125
- const password_path = options.password_path ?? '/api/account/password';
123
+ const login_path = options.login_path ?? SPINE_ACCOUNT_LOGIN_PATH;
124
+ const password_path = options.password_path ?? SPINE_ACCOUNT_PASSWORD_PATH;
126
125
  const concurrent_transactions = options.concurrent_transactions ?? false;
127
126
  const base_url = options.base_url ?? null;
128
127
  /** Per-IP cap shared by both impls (TS `default_login_ip_rate_limit`, Rust `DEFAULT_LOGIN_IP_RATE_LIMIT`). */
@@ -214,10 +213,8 @@ export const describe_login_security_cross_tests = (options) => {
214
213
  // that stops keys on the (loopback) connection IP → 401.
215
214
  const stopped = await attempt(fixture, `${XFF_MALFORMED_HOP}, ${XFF_PORT_SUFFIXED_HOP}`, 'probe_malformed_hop');
216
215
  assert.strictEqual(stopped.status, 401, 'the walk must stop at the malformed hop, not reach the entry left of it');
217
- const drained = await cross_rpc_call(fixture.fresh_transport({ origin: null }), SPINE_RPC_PATH, '_testing_drain_effects', undefined, fixture.create_daemon_token_headers());
218
- assert.ok(drained.ok, `_testing_drain_effects failed: ${JSON.stringify(drained.error)}`);
219
- const listed = expect_output(await cross_rpc_call(fixture.transport, SPINE_RPC_PATH, 'audit_log_list', { event_type: 'login', outcome: 'failure' }, fixture.create_session_headers()), AuditLogListOutput);
220
- const ips = listed.events.map((e) => e.ip);
216
+ const events = await list_audit_events(fixture, { event_type: 'login', outcome: 'failure' });
217
+ const ips = events.map((e) => e.ip);
221
218
  assert.ok(ips.some((ip) => ip === '127.0.0.1' || ip === '::1'), `the stopped walk must key on the loopback connection IP: ${JSON.stringify(ips)}`);
222
219
  assert.ok(!ips.includes(XFF_PORT_SUFFIXED_HOP), `a malformed entry is never a client IP: ${JSON.stringify(ips)}`);
223
220
  });
@@ -233,13 +230,11 @@ export const describe_login_security_cross_tests = (options) => {
233
230
  // the next /64 has its own
234
231
  const other = await attempt(fixture, XFF_V6_OTHER_64, 'probe_v6');
235
232
  assert.strictEqual(other.status, 401, 'the next /64 must keep its own budget');
236
- // The limiter keys the /64; the audit trail keeps the address. Drain
237
- // the fire-and-forget audit writes, then read this test's login rows
238
- // (`_testing_reset` wiped the log at setup).
239
- const drained = await cross_rpc_call(fixture.fresh_transport({ origin: null }), SPINE_RPC_PATH, '_testing_drain_effects', undefined, fixture.create_daemon_token_headers());
240
- assert.ok(drained.ok, `_testing_drain_effects failed: ${JSON.stringify(drained.error)}`);
241
- const listed = expect_output(await cross_rpc_call(fixture.transport, SPINE_RPC_PATH, 'audit_log_list', { event_type: 'login', outcome: 'failure' }, fixture.create_session_headers()), AuditLogListOutput);
242
- const ips = listed.events.map((e) => e.ip);
233
+ // The limiter keys the /64; the audit trail keeps the address. Read
234
+ // this test's login rows behind the drain barrier (`_testing_reset`
235
+ // wiped the log at setup).
236
+ const events = await list_audit_events(fixture, { event_type: 'login', outcome: 'failure' });
237
+ const ips = events.map((e) => e.ip);
243
238
  assert.ok(ips.includes(XFF_V6_EXHAUST), `audit ips: ${JSON.stringify(ips)}`);
244
239
  assert.ok(ips.includes(XFF_V6_OTHER_64), `audit ips: ${JSON.stringify(ips)}`);
245
240
  assert.ok(ips.every((ip) => ip === null || !ip.includes('/')), `no audit row may carry a rate-limit key: ${JSON.stringify(ips)}`);
@@ -320,7 +315,7 @@ export const describe_login_security_cross_tests = (options) => {
320
315
  `\r\n` +
321
316
  body;
322
317
  const { received } = await raw_http_exchange(base_url, request, {
323
- read_timeout_ms: RAW_READ_TIMEOUT_MS,
318
+ read_timeout_ms: LOGIN_RAW_READ_TIMEOUT_MS,
324
319
  until: 'response'
325
320
  });
326
321
  const response = raw_http_parse_response(received);
@@ -16,7 +16,7 @@ import '../assert_dev_env.js';
16
16
  *
17
17
  * Runs both legs via the shared `setup_test` protocol: the
18
18
  * in-process leg (`auth/origin_parity.db.test.ts`, plain `gro test`) and the
19
- * cross-process leg (`cross_backend/origin.cross.test.ts`, the TS spine
19
+ * cross-process leg (`cross_backend/spine_suites.cross.test.ts`, the TS spine
20
20
  * binaries + Rust `testing_spine_stub` over real HTTP). Origin middleware is
21
21
  * on every spine, so the suite is ungated.
22
22
  *
@@ -24,7 +24,7 @@ import { readFileSync } from 'node:fs';
24
24
  import { assert, describe, test } from 'vitest';
25
25
  import { z } from 'zod';
26
26
  import { AllowedOriginsErrorKind } from '../../http/origin.js';
27
- import { cross_rpc_call } from './cell_cross_helpers.js';
27
+ import { cross_rpc_call } from './cross_rpc.js';
28
28
  import { SPINE_RPC_PATH } from './spine_surface_constants.js';
29
29
  import { TestingOriginCheckOutput, testing_origin_check_action_spec } from './testing_origin_check.js';
30
30
  /** One expected refusal of a parse row. */
@@ -11,6 +11,12 @@ export interface OriginScopeCrossTestOptions extends CapabilityGatedCrossSuiteOp
11
11
  * default) sends the whole body through the fixture's in-process transport.
12
12
  */
13
13
  readonly base_url?: string | null;
14
+ /**
15
+ * The status the backend's fallback answers an unrouted `GET` with, once
16
+ * the origin check has passed it. Default `404`; a backend serving an SPA
17
+ * shell from its fallback passes `200`.
18
+ */
19
+ readonly unrouted_status?: number;
14
20
  }
15
21
  export declare const describe_origin_scope_cross_tests: (options: OriginScopeCrossTestOptions) => void;
16
22
  //# sourceMappingURL=origin_scope.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"origin_scope.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/origin_scope.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AA4C9B,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,YAAY,CAAC;AAcnE,iDAAiD;AACjD,MAAM,WAAW,2BAA4B,SAAQ,gCAAgC;IACpF,oFAAoF;IACpF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAClC;AAED,eAAO,MAAM,iCAAiC,GAAI,SAAS,2BAA2B,KAAG,IAmGxF,CAAC"}
1
+ {"version":3,"file":"origin_scope.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/origin_scope.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAmD9B,OAAO,KAAK,EAAE,gCAAgC,EAAE,MAAM,YAAY,CAAC;AASnE,iDAAiD;AACjD,MAAM,WAAW,2BAA4B,SAAQ,gCAAgC;IACpF,oFAAoF;IACpF,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B;;;;;OAKG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC;;;;OAIG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,eAAO,MAAM,iCAAiC,GAAI,SAAS,2BAA2B,KAAG,IAwGxF,CAAC"}
@@ -20,8 +20,11 @@ import '../assert_dev_env.js';
20
20
  *
21
21
  * And on a path neither server routes, which reaches each spine's fallback:
22
22
  *
23
- * - **foreign `Origin` → 403 `forbidden_origin`**, **absent → 404** — the
24
- * fallback is behind the check, not attached after it.
23
+ * - **foreign `Origin` → 403 `forbidden_origin`**, **absent → the fallback's
24
+ * own answer** — the fallback is behind the check, not attached after it.
25
+ * The absent row pins that the check let the request through to the
26
+ * fallback: `404` by default, or `unrouted_status` for a backend whose
27
+ * fallback serves an SPA shell (`200`).
25
28
  * - **an over-cap `POST` with a declared `Content-Length` and a foreign
26
29
  * `Origin` → 413 `payload_too_large`** — the body cap comes ahead of the
27
30
  * origin check on the fallback too (413-first holds for a declared length).
@@ -30,27 +33,25 @@ import '../assert_dev_env.js';
30
33
  * server advertises; the unrouted rows run on every backend.
31
34
  * Runs both legs via the shared `{setup_test, capabilities}` protocol: the
32
35
  * in-process leg (`cross_backend/origin_scope_parity.db.test.ts`, plain `gro
33
- * test`) and the cross-process leg (`cross_backend/origin_scope.cross.test.ts`,
36
+ * test`) and the cross-process leg (`cross_backend/spine_suites.cross.test.ts`,
34
37
  * the TS spine binaries + Rust `testing_spine_stub` over real HTTP).
35
38
  *
36
39
  * @module
37
40
  */
38
41
  import { describe, assert, test } from 'vitest';
39
42
  import { ERROR_FORBIDDEN_ORIGIN, ERROR_PAYLOAD_TOO_LARGE } from '../../http/error_schemas.js';
40
- import { raw_http_exchange, raw_http_parse_response } from '../transports/raw_http.js';
43
+ import { raw_http_exchange, raw_http_parse_response, RAW_HTTP_READ_TIMEOUT_MS } from '../transports/raw_http.js';
41
44
  import { test_if } from './capabilities.js';
45
+ import { SPINE_BODY_LIMIT_DEFAULT_BYTES } from './spine_surface_constants.js';
42
46
  /** An origin no spine test server allows. */
43
47
  const FOREIGN_ORIGIN = 'https://evil.example';
44
48
  /** A path no spine test server routes — it reaches the fallback. */
45
49
  const UNROUTED_PATH = '/origin-scope-unrouted';
46
- /** The shared 1 MiB default cap both spines ship, as in `body_size.ts`. */
47
- const BODY_LIMIT_DEFAULT_BYTES = 1024 * 1024;
48
- /** How long a raw exchange waits for the refusal. */
49
- const RAW_READ_TIMEOUT_MS = 2000;
50
50
  export const describe_origin_scope_cross_tests = (options) => {
51
51
  const { setup_test, capabilities } = options;
52
52
  const probe_path = options.probe_path ?? '/ready';
53
53
  const base_url = options.base_url ?? null;
54
+ const unrouted_status = options.unrouted_status ?? 404;
54
55
  describe('origin check scope parity', () => {
55
56
  test_if(capabilities.ready, `a foreign Origin on ${probe_path} → 403 forbidden_origin (the check covers every path)`, async () => {
56
57
  const fixture = await setup_test();
@@ -86,15 +87,15 @@ export const describe_origin_scope_cross_tests = (options) => {
86
87
  const body = (await res.json().catch(() => undefined));
87
88
  assert.strictEqual(body?.error, ERROR_FORBIDDEN_ORIGIN);
88
89
  });
89
- test('an absent Origin on an unrouted path → 404', async () => {
90
+ test(`an absent Origin on an unrouted path → passes to the fallback (${unrouted_status})`, async () => {
90
91
  const fixture = await setup_test();
91
92
  const res = await fixture.fresh_transport({ origin: null })(UNROUTED_PATH, {
92
93
  method: 'GET'
93
94
  });
94
- assert.strictEqual(res.status, 404, 'the fallback answers its own 404 past the check');
95
+ assert.strictEqual(res.status, unrouted_status, 'a request with no Origin must pass the check and reach the fallback');
95
96
  });
96
97
  test('an over-cap POST to an unrouted path with a foreign Origin → 413 payload_too_large', async () => {
97
- const over = BODY_LIMIT_DEFAULT_BYTES + 1;
98
+ const over = SPINE_BODY_LIMIT_DEFAULT_BYTES + 1;
98
99
  let status;
99
100
  let text;
100
101
  if (base_url === null) {
@@ -117,7 +118,7 @@ export const describe_origin_scope_cross_tests = (options) => {
117
118
  `content-length: ${over}\r\n` +
118
119
  `\r\n`;
119
120
  const { received } = await raw_http_exchange(base_url, request, {
120
- read_timeout_ms: RAW_READ_TIMEOUT_MS,
121
+ read_timeout_ms: RAW_HTTP_READ_TIMEOUT_MS,
121
122
  until: 'response'
122
123
  });
123
124
  const response = raw_http_parse_response(received);
@@ -1,16 +1,13 @@
1
1
  import '../assert_dev_env.js';
2
2
  import { type BackendCapabilities } from './capabilities.js';
3
3
  import type { SetupTest } from './setup.js';
4
+ import { type WsEndpointTarget } from './ws_endpoint.js';
4
5
  /** Configuration for `describe_peer_ping_ws_tests`. */
5
- export interface PeerPingWsTestOptions {
6
+ export interface PeerPingWsTestOptions extends WsEndpointTarget {
6
7
  /** Per-test fixture producer (`default_cross_process_setup(handle, ...)`). */
7
8
  readonly setup_test: SetupTest;
8
9
  /** Backend capability flags; every case gates on `capabilities.peer_request`. */
9
10
  readonly capabilities: BackendCapabilities;
10
- /** Base URL the backend is reachable at (e.g. `http://localhost:1177`). */
11
- readonly base_url: string;
12
- /** WebSocket endpoint path on the backend (e.g. `/api/ws`). */
13
- readonly ws_path: string;
14
11
  }
15
12
  /**
16
13
  * Register the server-initiated `peer/ping` suite — a positive round-trip
@@ -1 +1 @@
1
- {"version":3,"file":"peer_ping_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/peer_ping_ws.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAkD9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAuB5C,uDAAuD;AACvD,MAAM,WAAW,qBAAqB;IACrC,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;IAC3C,2EAA2E;IAC3E,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,+DAA+D;IAC/D,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CACzB;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,qBAAqB,KAAG,IA8O5E,CAAC"}
1
+ {"version":3,"file":"peer_ping_ws.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/peer_ping_ws.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAiD9B,OAAO,EAAE,KAAK,mBAAmB,EAAW,MAAM,mBAAmB,CAAC;AACtE,OAAO,KAAK,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAEzD,OAAO,EAAmB,KAAK,gBAAgB,EAAE,MAAM,kBAAkB,CAAC;AAoB1E,uDAAuD;AACvD,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC9D,8EAA8E;IAC9E,QAAQ,CAAC,UAAU,EAAE,SAAS,CAAC;IAC/B,iFAAiF;IACjF,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;CAC3C;AAED;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,SAAS,qBAAqB,KAAG,IAmO5E,CAAC"}
@@ -36,10 +36,11 @@ import '../assert_dev_env.js';
36
36
  */
37
37
  import { assert, describe } from 'vitest';
38
38
  import { rpc_call } from '../rpc_helpers.js';
39
- import { create_ws_transport } from '../transports/ws_transport.js';
40
39
  import { is_response_for } from '../transports/ws_client.js';
41
40
  import { create_jsonrpc_request, is_jsonrpc_request } from '../../http/jsonrpc_helpers.js';
42
41
  import { test_if } from './capabilities.js';
42
+ import { SPINE_RPC_PATH } from './spine_surface_constants.js';
43
+ import { open_session_ws } from './ws_endpoint.js';
43
44
  // The wire literals below are deliberate independent copies of the
44
45
  // producers' constants (`actions/peer_ping.ts` on TS, `REASON_PEER_*` /
45
46
  // `PEER_PING_METHOD` on Rust) — parity by test, not codegen. The harness
@@ -52,8 +53,6 @@ const PEER_PING_METHOD = 'peer/ping';
52
53
  const REASON_PEER_TIMEOUT = 'peer_timeout';
53
54
  const REASON_PEER_PING_INVALID_REPLY = 'peer_ping_invalid_reply';
54
55
  const REASON_PEER_NO_TRANSPORT = 'peer_no_transport';
55
- /** JSON-RPC endpoint path — matches the spine's `/api/rpc` (and the forge's). */
56
- const RPC_PATH = '/api/rpc';
57
56
  /** A reply payload that satisfies the Rust `PingResponse` shape. */
58
57
  const valid_reply = (nonce) => ({ nonce, protocol_version: 1 });
59
58
  /**
@@ -64,19 +63,10 @@ const valid_reply = (nonce) => ({ nonce, protocol_version: 1 });
64
63
  * the HTTP no-transport path). Gated on `capabilities.peer_request`.
65
64
  */
66
65
  export const describe_peer_ping_ws_tests = (options) => {
67
- const { setup_test, capabilities, base_url, ws_path } = options;
66
+ const { setup_test, capabilities } = options;
68
67
  // -- shared helpers -------------------------------------------------------
69
- /** Open a WS transport for a session cookie, with an optional responder. */
70
- const open_ws = (cookie, on_request) => {
71
- assert.ok(cookie, 'expected a session cookie for the WS upgrade');
72
- return create_ws_transport({
73
- base_url,
74
- ws_path,
75
- cookies: [cookie],
76
- origin: base_url,
77
- on_request
78
- });
79
- };
68
+ /** Open a WS transport on the fixture's session, with an optional responder. */
69
+ const open_ws = (fixture, on_request) => open_session_ws(options, fixture, { admitted: false, on_request });
80
70
  /** A responder that echoes a valid `PingResponse` for every `peer/ping`. */
81
71
  const echo_responder = (req) => {
82
72
  const nonce = req.params?.nonce ?? 0;
@@ -96,7 +86,7 @@ export const describe_peer_ping_ws_tests = (options) => {
96
86
  describe('peer/ping server-initiated request (cross-process)', () => {
97
87
  test_if(capabilities.peer_request, 'a client peer/ping round-trips: server pings back, validates the echo, returns it', async () => {
98
88
  const fixture = await setup_test();
99
- const ws = await open_ws(fixture.create_session_headers().cookie, echo_responder);
89
+ const ws = await open_ws(fixture, echo_responder);
100
90
  try {
101
91
  const result = await ws.request(1, PEER_PING_METHOD, { nonce: 42 }, 8000);
102
92
  assert.strictEqual(result.nonce, 42, 'the server echoes the issued nonce back');
@@ -109,7 +99,7 @@ export const describe_peer_ping_ws_tests = (options) => {
109
99
  test_if(capabilities.peer_request, 'a never-replying client yields a server-side Timeout (the liveness signal)', async () => {
110
100
  const fixture = await setup_test();
111
101
  // Responder swallows the ping (returns nothing) — the half-open peer.
112
- const ws = await open_ws(fixture.create_session_headers().cookie, () => undefined);
102
+ const ws = await open_ws(fixture, () => undefined);
113
103
  try {
114
104
  // Short server-side deadline so the negative runs fast.
115
105
  const frame = await ping_raw(ws, 2, { nonce: 1, timeout_ms: 300 });
@@ -124,7 +114,7 @@ export const describe_peer_ping_ws_tests = (options) => {
124
114
  const fixture = await setup_test();
125
115
  // Reply that fails `PingResponse` validation (nonce wrong type,
126
116
  // protocol_version missing).
127
- const ws = await open_ws(fixture.create_session_headers().cookie, () => ({
117
+ const ws = await open_ws(fixture, () => ({
128
118
  result: { nonce: 'not-a-number' }
129
119
  }));
130
120
  try {
@@ -141,7 +131,7 @@ export const describe_peer_ping_ws_tests = (options) => {
141
131
  // and return only the declared shape.
142
132
  test_if(capabilities.peer_request, 'a reply with an undeclared key is accepted and the key is dropped', async () => {
143
133
  const fixture = await setup_test();
144
- const ws = await open_ws(fixture.create_session_headers().cookie, (req) => {
134
+ const ws = await open_ws(fixture, (req) => {
145
135
  const nonce = req.params?.nonce ?? 0;
146
136
  return { result: { ...valid_reply(nonce), added_later: true } };
147
137
  });
@@ -156,7 +146,7 @@ export const describe_peer_ping_ws_tests = (options) => {
156
146
  });
157
147
  test_if(capabilities.peer_request, 'a client JSON-RPC error reply surfaces as the action error (ClientError forwarded)', async () => {
158
148
  const fixture = await setup_test();
159
- const ws = await open_ws(fixture.create_session_headers().cookie, () => ({
149
+ const ws = await open_ws(fixture, () => ({
160
150
  error: { code: -32603, message: 'client refused', data: { reason: 'client_says_no' } }
161
151
  }));
162
152
  try {
@@ -171,7 +161,7 @@ export const describe_peer_ping_ws_tests = (options) => {
171
161
  });
172
162
  test_if(capabilities.peer_request, 'an unsolicited response is dropped: it resolves nothing and the socket survives', async () => {
173
163
  const fixture = await setup_test();
174
- const ws = await open_ws(fixture.create_session_headers().cookie, echo_responder);
164
+ const ws = await open_ws(fixture, echo_responder);
175
165
  try {
176
166
  // Inject a reply for an id the server never issued on this socket.
177
167
  await ws.send({ jsonrpc: '2.0', id: 's999999', result: valid_reply(7) });
@@ -189,8 +179,8 @@ export const describe_peer_ping_ws_tests = (options) => {
189
179
  // A has NO responder, so the inbound server-initiated request is
190
180
  // surfaced as a message A can observe (to learn its server id);
191
181
  // A never answers it.
192
- const ws_a = await open_ws(fixture.create_session_headers().cookie);
193
- const ws_b = await open_ws(fixture.create_session_headers().cookie, echo_responder);
182
+ const ws_a = await open_ws(fixture);
183
+ const ws_b = await open_ws(fixture, echo_responder);
194
184
  try {
195
185
  // A invokes peer/ping with a short server-side deadline; don't
196
186
  // await yet — observe the server→client request first.
@@ -217,7 +207,7 @@ export const describe_peer_ping_ws_tests = (options) => {
217
207
  const fixture = await setup_test();
218
208
  const res = await rpc_call({
219
209
  app: fixture.transport,
220
- path: RPC_PATH,
210
+ path: SPINE_RPC_PATH,
221
211
  method: PEER_PING_METHOD,
222
212
  params: { nonce: 1 },
223
213
  headers: fixture.create_session_headers()
@@ -0,0 +1,85 @@
1
+ import '../assert_dev_env.js';
2
+ /** The URL protocols a Postgres connection string carries. */
3
+ export declare const POSTGRES_URL_PROTOCOLS: ReadonlySet<string>;
4
+ /**
5
+ * Whether `url` parses as a `postgres:` / `postgresql:` URL. `URL.canParse`
6
+ * alone admits a scheme-less `localhost:5432/db`, which it reads as protocol
7
+ * `localhost:`.
8
+ *
9
+ * @param url - the candidate connection string
10
+ */
11
+ export declare const is_postgres_url: (url: string) => boolean;
12
+ /**
13
+ * `database_url` with its database replaced by `database` — how a setup that
14
+ * spawns a second backend lands on the same server under a database nothing
15
+ * else uses.
16
+ *
17
+ * @param database_url - a `postgres://` URL on the target server
18
+ * @param database - the database name to put in it
19
+ * @returns the URL naming `database` on the same server
20
+ */
21
+ export declare const with_database_name: (database_url: string, database: string) => string;
22
+ /**
23
+ * The URL `createdb` connects through to create a database on the server
24
+ * `server_url` names — the same URL with its database replaced by `postgres`.
25
+ *
26
+ * @param server_url - a `postgres://` URL on the target server
27
+ * @returns the URL of that server's `postgres` maintenance database
28
+ */
29
+ export declare const to_maintenance_database_url: (server_url: string) => string;
30
+ /**
31
+ * A database URL fit to print: the password, when there is one, replaced —
32
+ * in the userinfo and in the `password` query parameter `pg` and libpq also
33
+ * take. The whole URL rather than its host, which is empty for a socket URL
34
+ * that names its server in the query string.
35
+ *
36
+ * @param database_url - a `postgres://` URL
37
+ * @returns the URL with any password replaced by `REDACTED`
38
+ */
39
+ export declare const redact_database_url: (database_url: string) => string;
40
+ /**
41
+ * Whether `createdb`'s stderr is its refusal of a database that already
42
+ * exists — Postgres's `database "<name>" already exists`, which `createdb`
43
+ * prints after `database creation failed: ERROR:`.
44
+ *
45
+ * @param stderr - what `createdb` wrote to stderr
46
+ * @param database - the database it was asked to create
47
+ */
48
+ export declare const is_createdb_already_exists: (stderr: string, database: string) => boolean;
49
+ /** Dependencies of `ensure_database` — the warning a failed `createdb` is reported with. */
50
+ export interface EnsureDatabaseDeps {
51
+ readonly warn: (message: string) => void;
52
+ }
53
+ /**
54
+ * Create a Postgres database when it is missing, with `createdb`.
55
+ *
56
+ * Which server it lands on depends on `server_url`. Given one, the database is
57
+ * created on the server that URL names: `createdb` connects through the URL
58
+ * with its database swapped for the `postgres` maintenance database, so host,
59
+ * port, role and query parameters all come from the URL, and a password goes
60
+ * to `createdb` in its environment, never its arguments. Without one,
61
+ * `createdb` reads the libpq environment (`PGHOST` / `PGPORT` / `PGUSER`).
62
+ *
63
+ * Idempotent and never throws: a database that already exists is the quiet
64
+ * success case (`is_createdb_already_exists`). Any other failure — no
65
+ * `createdb` on PATH, no server, a role that may not create databases — is
66
+ * reported through `deps.warn` with `createdb`'s own stderr, and whatever
67
+ * connects next reports a database that is really missing.
68
+ *
69
+ * @param database - the database name
70
+ * @param server_url - a `postgres://` URL on the server to create it on; its database name is ignored
71
+ * @param deps - where a failure is reported, `console.warn` by default
72
+ * @returns whether the database exists now (created, or already there)
73
+ */
74
+ export declare const ensure_database: (database: string, server_url?: string, deps?: EnsureDatabaseDeps) => boolean;
75
+ /**
76
+ * Create the database a Postgres URL names, on the server it names, when it
77
+ * is missing (`ensure_database`). A URL that is not `postgres:` — `memory://`,
78
+ * `file://` — or that names no database is left alone.
79
+ *
80
+ * @param database_url - the URL a backend connects to
81
+ * @param deps - where a failure is reported, `console.warn` by default
82
+ * @returns whether the URL named a Postgres database to ensure
83
+ */
84
+ export declare const ensure_database_for_url: (database_url: string, deps?: EnsureDatabaseDeps) => boolean;
85
+ //# sourceMappingURL=postgres_database.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres_database.d.ts","sourceRoot":"../src/lib/","sources":["../../../src/lib/testing/cross_backend/postgres_database.ts"],"names":[],"mappings":"AAAA,OAAO,sBAAsB,CAAC;AAgB9B,8DAA8D;AAC9D,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,MAAM,CAAyC,CAAC;AAEjG;;;;;;GAMG;AACH,eAAO,MAAM,eAAe,GAAI,KAAK,MAAM,KAAG,OACyB,CAAC;AAExE;;;;;;;;GAQG;AACH,eAAO,MAAM,kBAAkB,GAAI,cAAc,MAAM,EAAE,UAAU,MAAM,KAAG,MAI3E,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,2BAA2B,GAAI,YAAY,MAAM,KAAG,MACtB,CAAC;AAE5C;;;;;;;;GAQG;AACH,eAAO,MAAM,mBAAmB,GAAI,cAAc,MAAM,KAAG,MAK1D,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,0BAA0B,GAAI,QAAQ,MAAM,EAAE,UAAU,MAAM,KAAG,OACrB,CAAC;AAE1D,4FAA4F;AAC5F,MAAM,WAAW,kBAAkB;IAClC,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;CACzC;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,eAAO,MAAM,eAAe,GAC3B,UAAU,MAAM,EAChB,aAAa,MAAM,EACnB,OAAM,kBAAiD,KACrD,OAyBF,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GACnC,cAAc,MAAM,EACpB,OAAO,kBAAkB,KACvB,OAMF,CAAC"}
@@ -0,0 +1,140 @@
1
+ import '../assert_dev_env.js';
2
+ /**
3
+ * Postgres database helpers for cross-backend global setups — creating the
4
+ * database a backend's URL names, and printing such a URL safely.
5
+ *
6
+ * The harness never creates a database by itself: a setup opts in
7
+ * (`create_cross_backend_global_setup`'s `ensure_databases`, or a call to
8
+ * `ensure_database_for_url`), so a test role without `CREATEDB` is never
9
+ * required by default.
10
+ *
11
+ * @module
12
+ */
13
+ import { execFileSync } from 'node:child_process';
14
+ /** The URL protocols a Postgres connection string carries. */
15
+ export const POSTGRES_URL_PROTOCOLS = new Set(['postgres:', 'postgresql:']);
16
+ /**
17
+ * Whether `url` parses as a `postgres:` / `postgresql:` URL. `URL.canParse`
18
+ * alone admits a scheme-less `localhost:5432/db`, which it reads as protocol
19
+ * `localhost:`.
20
+ *
21
+ * @param url - the candidate connection string
22
+ */
23
+ export const is_postgres_url = (url) => URL.canParse(url) && POSTGRES_URL_PROTOCOLS.has(new URL(url).protocol);
24
+ /**
25
+ * `database_url` with its database replaced by `database` — how a setup that
26
+ * spawns a second backend lands on the same server under a database nothing
27
+ * else uses.
28
+ *
29
+ * @param database_url - a `postgres://` URL on the target server
30
+ * @param database - the database name to put in it
31
+ * @returns the URL naming `database` on the same server
32
+ */
33
+ export const with_database_name = (database_url, database) => {
34
+ const url = new URL(database_url);
35
+ url.pathname = `/${database}`;
36
+ return url.href;
37
+ };
38
+ /**
39
+ * The URL `createdb` connects through to create a database on the server
40
+ * `server_url` names — the same URL with its database replaced by `postgres`.
41
+ *
42
+ * @param server_url - a `postgres://` URL on the target server
43
+ * @returns the URL of that server's `postgres` maintenance database
44
+ */
45
+ export const to_maintenance_database_url = (server_url) => with_database_name(server_url, 'postgres');
46
+ /**
47
+ * A database URL fit to print: the password, when there is one, replaced —
48
+ * in the userinfo and in the `password` query parameter `pg` and libpq also
49
+ * take. The whole URL rather than its host, which is empty for a socket URL
50
+ * that names its server in the query string.
51
+ *
52
+ * @param database_url - a `postgres://` URL
53
+ * @returns the URL with any password replaced by `REDACTED`
54
+ */
55
+ export const redact_database_url = (database_url) => {
56
+ const url = new URL(database_url);
57
+ if (url.password)
58
+ url.password = 'REDACTED';
59
+ if (url.searchParams.has('password'))
60
+ url.searchParams.set('password', 'REDACTED');
61
+ return url.href;
62
+ };
63
+ /**
64
+ * Whether `createdb`'s stderr is its refusal of a database that already
65
+ * exists — Postgres's `database "<name>" already exists`, which `createdb`
66
+ * prints after `database creation failed: ERROR:`.
67
+ *
68
+ * @param stderr - what `createdb` wrote to stderr
69
+ * @param database - the database it was asked to create
70
+ */
71
+ export const is_createdb_already_exists = (stderr, database) => stderr.includes(`database "${database}" already exists`);
72
+ const default_ensure_database_deps = {
73
+ warn: (message) => console.warn(message)
74
+ };
75
+ /**
76
+ * Create a Postgres database when it is missing, with `createdb`.
77
+ *
78
+ * Which server it lands on depends on `server_url`. Given one, the database is
79
+ * created on the server that URL names: `createdb` connects through the URL
80
+ * with its database swapped for the `postgres` maintenance database, so host,
81
+ * port, role and query parameters all come from the URL, and a password goes
82
+ * to `createdb` in its environment, never its arguments. Without one,
83
+ * `createdb` reads the libpq environment (`PGHOST` / `PGPORT` / `PGUSER`).
84
+ *
85
+ * Idempotent and never throws: a database that already exists is the quiet
86
+ * success case (`is_createdb_already_exists`). Any other failure — no
87
+ * `createdb` on PATH, no server, a role that may not create databases — is
88
+ * reported through `deps.warn` with `createdb`'s own stderr, and whatever
89
+ * connects next reports a database that is really missing.
90
+ *
91
+ * @param database - the database name
92
+ * @param server_url - a `postgres://` URL on the server to create it on; its database name is ignored
93
+ * @param deps - where a failure is reported, `console.warn` by default
94
+ * @returns whether the database exists now (created, or already there)
95
+ */
96
+ export const ensure_database = (database, server_url, deps = default_ensure_database_deps) => {
97
+ const args = [database];
98
+ const env = { ...process.env };
99
+ if (server_url !== undefined) {
100
+ const url = new URL(to_maintenance_database_url(server_url));
101
+ // any local user can read the process list
102
+ const password = url.searchParams.get('password') ?? decodeURIComponent(url.password);
103
+ if (password)
104
+ env.PGPASSWORD = password;
105
+ url.password = '';
106
+ url.searchParams.delete('password');
107
+ args.unshift(`--maintenance-db=${url.href}`);
108
+ }
109
+ try {
110
+ execFileSync('createdb', args, { stdio: ['ignore', 'ignore', 'pipe'], env, encoding: 'utf-8' });
111
+ return true;
112
+ }
113
+ catch (error) {
114
+ const failure = error;
115
+ const stderr = (failure.stderr ?? '').trim();
116
+ if (is_createdb_already_exists(stderr, database))
117
+ return true;
118
+ deps.warn(`ensure_database: createdb ${database} failed — ` +
119
+ (failure.code === 'ENOENT' ? 'createdb is not on PATH' : stderr || failure.message));
120
+ return false;
121
+ }
122
+ };
123
+ /**
124
+ * Create the database a Postgres URL names, on the server it names, when it
125
+ * is missing (`ensure_database`). A URL that is not `postgres:` — `memory://`,
126
+ * `file://` — or that names no database is left alone.
127
+ *
128
+ * @param database_url - the URL a backend connects to
129
+ * @param deps - where a failure is reported, `console.warn` by default
130
+ * @returns whether the URL named a Postgres database to ensure
131
+ */
132
+ export const ensure_database_for_url = (database_url, deps) => {
133
+ if (!is_postgres_url(database_url))
134
+ return false;
135
+ const database = decodeURIComponent(new URL(database_url).pathname.slice(1));
136
+ if (!database)
137
+ return false;
138
+ ensure_database(database, database_url, deps);
139
+ return true;
140
+ };