@tellescope/sdk 1.256.13 → 1.256.15

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 (352) hide show
  1. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js +1 -1
  2. package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js.map +1 -1
  3. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts +5 -0
  4. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -0
  5. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js +388 -0
  6. package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -0
  7. package/lib/cjs/tests/api_tests/integrations_redacted.test.js +3 -3
  8. package/lib/cjs/tests/api_tests/integrations_redacted.test.js.map +1 -1
  9. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts +20 -0
  10. package/lib/cjs/tests/api_tests/organization_signup.test.d.ts.map +1 -0
  11. package/lib/cjs/tests/api_tests/organization_signup.test.js +272 -0
  12. package/lib/cjs/tests/api_tests/organization_signup.test.js.map +1 -0
  13. package/lib/cjs/tests/api_tests/purchase_made_trigger.test.d.ts.map +1 -1
  14. package/lib/cjs/tests/api_tests/purchase_made_trigger.test.js +103 -162
  15. package/lib/cjs/tests/api_tests/purchase_made_trigger.test.js.map +1 -1
  16. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts +30 -0
  17. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts.map +1 -0
  18. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js +336 -0
  19. package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js.map +1 -0
  20. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  21. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  22. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js +354 -0
  23. package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  24. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts +46 -0
  25. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts.map +1 -0
  26. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js +389 -0
  27. package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js.map +1 -0
  28. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts +43 -0
  29. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts.map +1 -0
  30. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js +324 -0
  31. package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js.map +1 -0
  32. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts +43 -0
  33. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  34. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js +282 -0
  35. package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js.map +1 -0
  36. package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts +33 -0
  37. package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts.map +1 -0
  38. package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.js +249 -0
  39. package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.js.map +1 -0
  40. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +42 -0
  41. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  42. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +281 -0
  43. package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -0
  44. package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.d.ts +38 -0
  45. package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.d.ts.map +1 -0
  46. package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.js +390 -0
  47. package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.js.map +1 -0
  48. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.d.ts +6 -0
  49. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.d.ts.map +1 -0
  50. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js +1003 -0
  51. package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -0
  52. package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.d.ts +42 -0
  53. package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.d.ts.map +1 -0
  54. package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.js +323 -0
  55. package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.js.map +1 -0
  56. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts +29 -0
  57. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts.map +1 -0
  58. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js +335 -0
  59. package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -0
  60. package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts +32 -0
  61. package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts.map +1 -0
  62. package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js +237 -0
  63. package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js.map +1 -0
  64. package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts +28 -0
  65. package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts.map +1 -0
  66. package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.js +349 -0
  67. package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.js.map +1 -0
  68. package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts +30 -0
  69. package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts.map +1 -0
  70. package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js +314 -0
  71. package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js.map +1 -0
  72. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.d.ts +21 -0
  73. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.d.ts.map +1 -0
  74. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js +362 -0
  75. package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js.map +1 -0
  76. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.d.ts +21 -0
  77. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.d.ts.map +1 -0
  78. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js +366 -0
  79. package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -0
  80. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts +32 -0
  81. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -0
  82. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js +248 -0
  83. package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -0
  84. package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts +19 -0
  85. package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts.map +1 -0
  86. package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.js +138 -0
  87. package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.js.map +1 -0
  88. package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts +24 -0
  89. package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts.map +1 -0
  90. package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js +201 -0
  91. package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js.map +1 -0
  92. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.d.ts +29 -0
  93. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.d.ts.map +1 -0
  94. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js +278 -0
  95. package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -0
  96. package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.d.ts +17 -0
  97. package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.d.ts.map +1 -0
  98. package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.js +155 -0
  99. package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.js.map +1 -0
  100. package/lib/cjs/tests/api_tests/security/prototype-pollution.test.d.ts +2 -0
  101. package/lib/cjs/tests/api_tests/security/prototype-pollution.test.d.ts.map +1 -0
  102. package/lib/cjs/tests/api_tests/security/prototype-pollution.test.js +88 -0
  103. package/lib/cjs/tests/api_tests/security/prototype-pollution.test.js.map +1 -0
  104. package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts +34 -0
  105. package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts.map +1 -0
  106. package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.js +230 -0
  107. package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.js.map +1 -0
  108. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts +2 -0
  109. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -0
  110. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +242 -0
  111. package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -0
  112. package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.d.ts +2 -0
  113. package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.d.ts.map +1 -0
  114. package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.js +148 -0
  115. package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.js.map +1 -0
  116. package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts +45 -0
  117. package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts.map +1 -0
  118. package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.js +388 -0
  119. package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.js.map +1 -0
  120. package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.d.ts +38 -0
  121. package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.d.ts.map +1 -0
  122. package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.js +222 -0
  123. package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.js.map +1 -0
  124. package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  125. package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  126. package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js +354 -0
  127. package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  128. package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts +28 -0
  129. package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts.map +1 -0
  130. package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.js +442 -0
  131. package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.js.map +1 -0
  132. package/lib/cjs/tests/api_tests/security/webhook-timeout.test.d.ts +19 -0
  133. package/lib/cjs/tests/api_tests/security/webhook-timeout.test.d.ts.map +1 -0
  134. package/lib/cjs/tests/api_tests/security/webhook-timeout.test.js +156 -0
  135. package/lib/cjs/tests/api_tests/security/webhook-timeout.test.js.map +1 -0
  136. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +5 -0
  137. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -0
  138. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js +329 -0
  139. package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -0
  140. package/lib/cjs/tests/api_tests/webhook_error_handling.test.js +2 -2
  141. package/lib/cjs/tests/api_tests/webhook_error_handling.test.js.map +1 -1
  142. package/lib/cjs/tests/setup_isolated.d.ts +68 -0
  143. package/lib/cjs/tests/setup_isolated.d.ts.map +1 -0
  144. package/lib/cjs/tests/setup_isolated.js +232 -0
  145. package/lib/cjs/tests/setup_isolated.js.map +1 -0
  146. package/lib/cjs/tests/tests.d.ts.map +1 -1
  147. package/lib/cjs/tests/tests.js +287 -227
  148. package/lib/cjs/tests/tests.js.map +1 -1
  149. package/lib/cjs/tests/unit_tests/do_not_send_address.test.d.ts +2 -0
  150. package/lib/cjs/tests/unit_tests/do_not_send_address.test.d.ts.map +1 -0
  151. package/lib/cjs/tests/unit_tests/do_not_send_address.test.js +89 -0
  152. package/lib/cjs/tests/unit_tests/do_not_send_address.test.js.map +1 -0
  153. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.d.ts +17 -0
  154. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.d.ts.map +1 -0
  155. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.js +140 -0
  156. package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.js.map +1 -0
  157. package/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -0
  158. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js +1 -1
  159. package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js.map +1 -1
  160. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts +5 -0
  161. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -0
  162. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js +383 -0
  163. package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -0
  164. package/lib/esm/tests/api_tests/integrations_redacted.test.js +3 -3
  165. package/lib/esm/tests/api_tests/integrations_redacted.test.js.map +1 -1
  166. package/lib/esm/tests/api_tests/organization_signup.test.d.ts +20 -0
  167. package/lib/esm/tests/api_tests/organization_signup.test.d.ts.map +1 -0
  168. package/lib/esm/tests/api_tests/organization_signup.test.js +264 -0
  169. package/lib/esm/tests/api_tests/organization_signup.test.js.map +1 -0
  170. package/lib/esm/tests/api_tests/purchase_made_trigger.test.d.ts.map +1 -1
  171. package/lib/esm/tests/api_tests/purchase_made_trigger.test.js +104 -163
  172. package/lib/esm/tests/api_tests/purchase_made_trigger.test.js.map +1 -1
  173. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts +30 -0
  174. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts.map +1 -0
  175. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js +332 -0
  176. package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js.map +1 -0
  177. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  178. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  179. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js +350 -0
  180. package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  181. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts +46 -0
  182. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts.map +1 -0
  183. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js +382 -0
  184. package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js.map +1 -0
  185. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts +43 -0
  186. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts.map +1 -0
  187. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js +317 -0
  188. package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js.map +1 -0
  189. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts +43 -0
  190. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  191. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js +277 -0
  192. package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js.map +1 -0
  193. package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts +33 -0
  194. package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts.map +1 -0
  195. package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.js +244 -0
  196. package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.js.map +1 -0
  197. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +42 -0
  198. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
  199. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +276 -0
  200. package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -0
  201. package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.d.ts +38 -0
  202. package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.d.ts.map +1 -0
  203. package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.js +386 -0
  204. package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.js.map +1 -0
  205. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.d.ts +6 -0
  206. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.d.ts.map +1 -0
  207. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js +996 -0
  208. package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -0
  209. package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.d.ts +42 -0
  210. package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.d.ts.map +1 -0
  211. package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.js +316 -0
  212. package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.js.map +1 -0
  213. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts +29 -0
  214. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts.map +1 -0
  215. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js +331 -0
  216. package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -0
  217. package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts +32 -0
  218. package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts.map +1 -0
  219. package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js +233 -0
  220. package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js.map +1 -0
  221. package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts +28 -0
  222. package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts.map +1 -0
  223. package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.js +345 -0
  224. package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.js.map +1 -0
  225. package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts +30 -0
  226. package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts.map +1 -0
  227. package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js +310 -0
  228. package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js.map +1 -0
  229. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.d.ts +21 -0
  230. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.d.ts.map +1 -0
  231. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js +358 -0
  232. package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js.map +1 -0
  233. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.d.ts +21 -0
  234. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.d.ts.map +1 -0
  235. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js +362 -0
  236. package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -0
  237. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts +32 -0
  238. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -0
  239. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js +243 -0
  240. package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -0
  241. package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts +19 -0
  242. package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts.map +1 -0
  243. package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.js +134 -0
  244. package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.js.map +1 -0
  245. package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts +24 -0
  246. package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts.map +1 -0
  247. package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js +194 -0
  248. package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js.map +1 -0
  249. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.d.ts +29 -0
  250. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.d.ts.map +1 -0
  251. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js +271 -0
  252. package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -0
  253. package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.d.ts +17 -0
  254. package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.d.ts.map +1 -0
  255. package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.js +151 -0
  256. package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.js.map +1 -0
  257. package/lib/esm/tests/api_tests/security/prototype-pollution.test.d.ts +2 -0
  258. package/lib/esm/tests/api_tests/security/prototype-pollution.test.d.ts.map +1 -0
  259. package/lib/esm/tests/api_tests/security/prototype-pollution.test.js +84 -0
  260. package/lib/esm/tests/api_tests/security/prototype-pollution.test.js.map +1 -0
  261. package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts +34 -0
  262. package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts.map +1 -0
  263. package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.js +223 -0
  264. package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.js.map +1 -0
  265. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts +2 -0
  266. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -0
  267. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +238 -0
  268. package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -0
  269. package/lib/esm/tests/api_tests/security/sanitize-user-html.test.d.ts +2 -0
  270. package/lib/esm/tests/api_tests/security/sanitize-user-html.test.d.ts.map +1 -0
  271. package/lib/esm/tests/api_tests/security/sanitize-user-html.test.js +144 -0
  272. package/lib/esm/tests/api_tests/security/sanitize-user-html.test.js.map +1 -0
  273. package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts +45 -0
  274. package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts.map +1 -0
  275. package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.js +381 -0
  276. package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.js.map +1 -0
  277. package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.d.ts +38 -0
  278. package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.d.ts.map +1 -0
  279. package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.js +218 -0
  280. package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.js.map +1 -0
  281. package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
  282. package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
  283. package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js +350 -0
  284. package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js.map +1 -0
  285. package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts +28 -0
  286. package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts.map +1 -0
  287. package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.js +435 -0
  288. package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.js.map +1 -0
  289. package/lib/esm/tests/api_tests/security/webhook-timeout.test.d.ts +19 -0
  290. package/lib/esm/tests/api_tests/security/webhook-timeout.test.d.ts.map +1 -0
  291. package/lib/esm/tests/api_tests/security/webhook-timeout.test.js +149 -0
  292. package/lib/esm/tests/api_tests/security/webhook-timeout.test.js.map +1 -0
  293. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +5 -0
  294. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -0
  295. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js +324 -0
  296. package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -0
  297. package/lib/esm/tests/api_tests/webhook_error_handling.test.js +2 -2
  298. package/lib/esm/tests/api_tests/webhook_error_handling.test.js.map +1 -1
  299. package/lib/esm/tests/setup_isolated.d.ts +68 -0
  300. package/lib/esm/tests/setup_isolated.d.ts.map +1 -0
  301. package/lib/esm/tests/setup_isolated.js +221 -0
  302. package/lib/esm/tests/setup_isolated.js.map +1 -0
  303. package/lib/esm/tests/tests.d.ts.map +1 -1
  304. package/lib/esm/tests/tests.js +287 -227
  305. package/lib/esm/tests/tests.js.map +1 -1
  306. package/lib/esm/tests/unit_tests/do_not_send_address.test.d.ts +2 -0
  307. package/lib/esm/tests/unit_tests/do_not_send_address.test.d.ts.map +1 -0
  308. package/lib/esm/tests/unit_tests/do_not_send_address.test.js +85 -0
  309. package/lib/esm/tests/unit_tests/do_not_send_address.test.js.map +1 -0
  310. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.d.ts +17 -0
  311. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.d.ts.map +1 -0
  312. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.js +113 -0
  313. package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.js.map +1 -0
  314. package/lib/tsconfig.tsbuildinfo +1 -1
  315. package/package.json +10 -10
  316. package/src/tests/api_tests/auto_merge_form_submission.test.ts +1 -1
  317. package/src/tests/api_tests/enduser_observations_auto_review.test.ts +358 -0
  318. package/src/tests/api_tests/integrations_redacted.test.ts +1 -1
  319. package/src/tests/api_tests/organization_signup.test.ts +240 -0
  320. package/src/tests/api_tests/purchase_made_trigger.test.ts +29 -41
  321. package/src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts +207 -0
  322. package/src/tests/api_tests/security/{F-0005-ai-conversations-rbac.test.ts → ai-conversations-rbac.test.ts} +14 -14
  323. package/src/tests/api_tests/security/{F-0166-allowed-paths-wildcard-bypass.test.ts → allowed-paths-confinement.test.ts} +11 -11
  324. package/src/tests/api_tests/security/automated-actions-process-gate.test.ts +237 -0
  325. package/src/tests/api_tests/security/booking-info-idor-token-mint.test.ts +224 -0
  326. package/src/tests/api_tests/security/{F-0053-cascade-role-rename-cross-tenant.test.ts → cascade-role-rename-cross-tenant.test.ts} +10 -10
  327. package/src/tests/api_tests/security/{F-0001-data-sync-redaction-bypass.test.ts → data-sync-redaction-bypass.test.ts} +9 -9
  328. package/src/tests/api_tests/security/{F-0006-develop-health-benefit-verification-rbac.test.ts → develop-health-benefit-verification-rbac.test.ts} +7 -8
  329. package/src/tests/api_tests/security/{F-0116-F-0119-enduser-cross-create.test.ts → enduser-cross-create.test.ts} +25 -25
  330. package/src/tests/api_tests/security/{F-0106-F-0110-enduser-write-restrictions.test.ts → enduser-write-restrictions.test.ts} +12 -14
  331. package/src/tests/api_tests/security/form-logic-js-execution.test.ts +183 -0
  332. package/src/tests/api_tests/security/{F-0145-globalunique-leak.test.ts → globalunique-conflict-redaction.test.ts} +5 -6
  333. package/src/tests/api_tests/security/{F-0008-handle-incoming-communication-cross-tenant.test.ts → handle-incoming-communication-cross-tenant.test.ts} +8 -8
  334. package/src/tests/api_tests/security/{F-0007-invite-user-enumeration.test.ts → invite-user-enumeration.test.ts} +8 -8
  335. package/src/tests/api_tests/security/{F-0151-load-inbox-redaction.test.ts → load-inbox-redaction.test.ts} +5 -6
  336. package/src/tests/api_tests/security/{F-0016-prototype-pollution.test.ts → prototype-pollution.test.ts} +4 -4
  337. package/src/tests/api_tests/security/{F-0025-proxy-image-unauth-ssrf.test.ts → proxy-image-unauth-ssrf.test.ts} +11 -12
  338. package/src/tests/api_tests/security/{F-0013-sanitize-user-html-with-iframes.test.ts → sanitize-user-html-with-iframes.test.ts} +5 -5
  339. package/src/tests/api_tests/security/{F-0013-sanitize-user-html.test.ts → sanitize-user-html.test.ts} +4 -4
  340. package/src/tests/api_tests/security/schema-adminonly-enforcement.test.ts +287 -0
  341. package/src/tests/api_tests/security/{F-0076-self-admin-role-assignment.test.ts → self-admin-role-assignment.test.ts} +13 -13
  342. package/src/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.ts +162 -0
  343. package/src/tests/api_tests/security/{F-0015-webhook-outbound-ssrf.test.ts → webhook-outbound-ssrf.test.ts} +13 -14
  344. package/src/tests/api_tests/security/{F-0155-webhook-timeout.test.ts → webhook-timeout.test.ts} +6 -7
  345. package/src/tests/api_tests/tickets_bulk_assign_from_queue.test.ts +279 -0
  346. package/src/tests/api_tests/webhook_error_handling.test.ts +1 -1
  347. package/src/tests/setup_isolated.ts +170 -0
  348. package/src/tests/tests.ts +60 -18
  349. package/src/tests/unit_tests/do_not_send_address.test.ts +60 -0
  350. package/src/tests/unit_tests/openapi_spec_freshness.test.ts +88 -0
  351. package/test_generated.pdf +0 -0
  352. package/src/tests/api_tests/security/readonly-enduser-preview.test.ts +0 -301
@@ -0,0 +1,279 @@
1
+ require('source-map-support').install();
2
+
3
+ import { Session } from "../../sdk"
4
+ import {
5
+ assert_checks,
6
+ Check,
7
+ setup_isolated_tenant,
8
+ } from "../setup_isolated"
9
+
10
+ const host = process.env.API_URL || 'http://localhost:8080' as const
11
+
12
+ const RAND = () => Math.random().toString(36).slice(2, 10)
13
+
14
+ /**
15
+ * Coverage for `tickets.bulk_assign` when a `queueId` is supplied — the bulk equivalent of
16
+ * `tickets.assign_from_queue`, added so a Ticket Queue's tickets can be checkbox-selected and handed
17
+ * to one user in a single call instead of one `assign_from_queue` round trip per ticket.
18
+ *
19
+ * Without `queueId` the endpoint only does `$set: { owner }`. That is insufficient for a QUEUED
20
+ * ticket: it would stay in the queue (still pullable by someone else), carry no dequeue provenance,
21
+ * and leave `ticket_queues.count` drifting upward forever. The queue branch therefore also unsets
22
+ * `queueId`, sets `dequeuedAt`/`dequeuedFrom`, and decrements the queue's count — the same
23
+ * bookkeeping `assign_from_queue` does for a single ticket. A/B/C below pin each of those.
24
+ *
25
+ * D is the check that actually justifies the handler's shape. The tickets model declares
26
+ * `constraints.access: [{ type: 'filter', field: 'owner' }]`, which the DB layer turns into
27
+ * `owner === self OR creator === self` for any non-`All` access. A queued ticket has no owner, and
28
+ * was typically created by someone else, so a non-admin queue manager's `DB.tickets.updateMany`
29
+ * matches NOTHING — the call would return 200 having assigned zero tickets, silently. The handler
30
+ * uses `buildOrganizationWideQueries` to get past that, exactly as `assign_from_queue` does, and
31
+ * re-adds the queue-membership check that those queries bypass. D1 proves the escape hatch works for
32
+ * a legitimate queue member; D2 proves it is still gated, and is the check that fails if someone
33
+ * later drops the membership check while keeping the organization-wide queries.
34
+ *
35
+ * A provisioned non-admin gets `tickets: ASSIGNED_AND_DEFAULT_ACCESS` (read/update = 'Assigned'),
36
+ * which is what makes D meaningful. The D tickets deliberately have NO enduserId, so the 'Assigned'
37
+ * role filter's other branch (enduserId in assignedEnduserIds) cannot match either — the ONLY way
38
+ * they can be assigned is through the organization-wide path.
39
+ *
40
+ * E is the regression guard on the pre-existing no-`queueId` behavior, which must be unchanged.
41
+ *
42
+ * Cross-tenant (F) is covered because `buildOrganizationWideQueries` is bound to the caller's
43
+ * businessId: another tenant's queue is invisible even to an admin, who skips the membership check.
44
+ *
45
+ * `ticket_queues.count` is maintained by asynchronous side effects (increment on ticket create) and
46
+ * the decrement here is deliberately fire-and-forget, so every count read polls.
47
+ */
48
+
49
+ // count is maintained by async side effects; give it a moment to settle before reading
50
+ const poll_for = async <T,>(get: () => Promise<T>, done: (v: T) => boolean, attempts=20): Promise<T> => {
51
+ let value = await get()
52
+ for (let i = 0; i < attempts && !done(value); i++) {
53
+ await new Promise(resolve => setTimeout(resolve, 250))
54
+ value = await get()
55
+ }
56
+ return value
57
+ }
58
+
59
+ export const tickets_bulk_assign_from_queue_checks = async (): Promise<Check[]> => {
60
+ const checks: Check[] = []
61
+ const record = (label: string, pass: boolean, detail: string) => checks.push({ label, pass, detail })
62
+
63
+ let tenant: Awaited<ReturnType<typeof setup_isolated_tenant>> | undefined
64
+ let otherTenant: Awaited<ReturnType<typeof setup_isolated_tenant>> | undefined
65
+
66
+ try {
67
+ /* ================= fixtures ================= */
68
+
69
+ // users[1] joins the queue, users[2] deliberately does not (D2)
70
+ ;[tenant, otherTenant] = await Promise.all([
71
+ setup_isolated_tenant({
72
+ name: `BulkAssignQueue ${RAND()}`,
73
+ users: [{ isAdmin: true }, { roles: ['Non-Admin'] }, { roles: ['Non-Admin'] }],
74
+ apiKeys: 0,
75
+ }),
76
+ setup_isolated_tenant({
77
+ name: `BulkAssignQueue other ${RAND()}`,
78
+ users: [{ isAdmin: true }],
79
+ apiKeys: 0,
80
+ }),
81
+ ])
82
+
83
+ const { sdk, sdkNonAdmin } = tenant
84
+ const queueMember = tenant.users[1]
85
+ const outsider = tenant.users[2]
86
+
87
+ // a session for users[2] — setup_isolated_tenant only builds sessions for users[0] and users[1]
88
+ const sdkOutsider = new Session({ host, authToken: outsider.authToken })
89
+ await sdkOutsider.refresh_session()
90
+
91
+ const owner = await sdk.api.users.createOne({
92
+ email: `dontsend-bulk-assign-queue-${RAND()}@tellescope.com`,
93
+ fname: 'BulkAssign', lname: 'QueueOwner',
94
+ verifiedEmail: true,
95
+ })
96
+
97
+ const queue = await sdk.api.ticket_queues.createOne({
98
+ title: `Bulk Assign Queue ${RAND()}`,
99
+ userIds: [owner.id, queueMember.id], // outsider is NOT a member
100
+ })
101
+
102
+ const enduser = await sdk.api.endusers.createOne({ fname: 'BulkAssign', lname: 'QueuePatient' })
103
+
104
+ // A/B/C fixtures — one carries an enduser so care team behavior is exercised alongside dequeue
105
+ const queued1 = await sdk.api.tickets.createOne({ title: 'BAQ 1', enduserId: enduser.id, queueId: queue.id })
106
+ const queued2 = await sdk.api.tickets.createOne({ title: 'BAQ 2', queueId: queue.id })
107
+
108
+ const countBefore = await poll_for(
109
+ () => sdk.api.ticket_queues.getOne(queue.id).then(q => q.count ?? 0),
110
+ c => c === 2,
111
+ )
112
+ record(
113
+ 'setup: queue count reflects both queued tickets',
114
+ countBefore === 2,
115
+ `count=${countBefore} (want 2); queue=${queue.id}`,
116
+ )
117
+
118
+ /* ===== A: the dequeue bookkeeping ===== */
119
+
120
+ const assignResult = await sdk.api.tickets.bulk_assign({
121
+ ids: [queued1.id, queued2.id], userId: owner.id, queueId: queue.id, addToCareTeam: true,
122
+ })
123
+ record(
124
+ 'A1: reports both tickets assigned and none skipped',
125
+ assignResult.assigned === 2 && assignResult.skipped === 0,
126
+ `assigned=${assignResult.assigned} skipped=${assignResult.skipped} (want 2 / 0)`,
127
+ )
128
+
129
+ const after1 = await sdk.api.tickets.getOne(queued1.id)
130
+ record(
131
+ 'A2: ticket left the queue with owner and dequeue provenance set',
132
+ !after1.queueId && after1.owner === owner.id && !!after1.dequeuedAt && after1.dequeuedFrom === queue.id,
133
+ `queueId=${JSON.stringify(after1.queueId)} owner=${after1.owner} dequeuedAt=${JSON.stringify(after1.dequeuedAt)} dequeuedFrom=${after1.dequeuedFrom}`,
134
+ )
135
+
136
+ const countAfter = await poll_for(
137
+ () => sdk.api.ticket_queues.getOne(queue.id).then(q => q.count ?? 0),
138
+ c => c === 0,
139
+ )
140
+ record(
141
+ 'A3: queue count decremented by the number assigned',
142
+ countAfter === 0,
143
+ `count=${countAfter} (want 0, was ${countBefore})`,
144
+ )
145
+
146
+ record(
147
+ 'A4: addToCareTeam adds the new owner to the ticket enduser\'s care team',
148
+ !!(await sdk.api.endusers.getOne(enduser.id)).assignedTo?.includes(owner.id),
149
+ `assignedTo must contain ${owner.id}`,
150
+ )
151
+
152
+ /* ===== B: ids no longer in the queue are skipped, not re-assigned ===== */
153
+
154
+ const replayResult = await sdk.api.tickets.bulk_assign({
155
+ ids: [queued1.id, queued2.id], userId: owner.id, queueId: queue.id,
156
+ })
157
+ record(
158
+ 'B1: already-dequeued ids are reported as skipped',
159
+ replayResult.assigned === 0 && replayResult.skipped === 2,
160
+ `assigned=${replayResult.assigned} skipped=${replayResult.skipped} (want 0 / 2)`,
161
+ )
162
+
163
+ const countAfterReplay = await sdk.api.ticket_queues.getOne(queue.id).then(q => q.count ?? 0)
164
+ record(
165
+ 'B2: queue count is not decremented again when nothing was assigned',
166
+ countAfterReplay === 0,
167
+ `count=${countAfterReplay} (want 0 — a decrement here would drive it negative)`,
168
+ )
169
+
170
+ /* ===== C: the queue scope is honored ===== */
171
+
172
+ const otherQueue = await sdk.api.ticket_queues.createOne({
173
+ title: `Bulk Assign Other Queue ${RAND()}`,
174
+ userIds: [owner.id],
175
+ })
176
+ const inOtherQueue = await sdk.api.tickets.createOne({ title: 'BAQ other queue', queueId: otherQueue.id })
177
+
178
+ const wrongQueueResult = await sdk.api.tickets.bulk_assign({
179
+ ids: [inOtherQueue.id], userId: owner.id, queueId: queue.id,
180
+ })
181
+ const stillInOther = await sdk.api.tickets.getOne(inOtherQueue.id)
182
+ record(
183
+ 'C1: a ticket in a different queue is not assigned when the wrong queueId is passed',
184
+ wrongQueueResult.assigned === 0 && stillInOther.queueId === otherQueue.id && !stillInOther.owner,
185
+ `assigned=${wrongQueueResult.assigned} (want 0); ticket queueId=${stillInOther.queueId} owner=${JSON.stringify(stillInOther.owner)}`,
186
+ )
187
+
188
+ /* ===== D: the organization-wide path, and its gate ===== */
189
+
190
+ // NO enduserId: the 'Assigned' role filter cannot match these by owner, creator, or enduser,
191
+ // so they are assignable only through the organization-wide queries
192
+ const forMember1 = await sdk.api.tickets.createOne({ title: 'BAQ member 1', queueId: queue.id })
193
+ const forMember2 = await sdk.api.tickets.createOne({ title: 'BAQ member 2', queueId: queue.id })
194
+
195
+ const memberResult = await sdkNonAdmin.api.tickets.bulk_assign({
196
+ ids: [forMember1.id, forMember2.id], userId: owner.id, queueId: queue.id,
197
+ }).then(r => r).catch((e: any) => ({ error: e?.message || String(e) } as any))
198
+
199
+ const memberAfter = await sdk.api.tickets.getOne(forMember1.id)
200
+ record(
201
+ 'D1: a non-admin queue member can bulk assign queued tickets they do not own',
202
+ memberResult?.assigned === 2 && !memberAfter.queueId && memberAfter.owner === owner.id,
203
+ `result=${JSON.stringify(memberResult)}; ticket queueId=${JSON.stringify(memberAfter.queueId)} owner=${memberAfter.owner}`
204
+ + ` — 0 assigned here means the handler fell back to the owner-filtered DB`,
205
+ )
206
+
207
+ const forOutsider = await sdk.api.tickets.createOne({ title: 'BAQ outsider', queueId: queue.id })
208
+ const outsiderResult = await sdkOutsider.api.tickets.bulk_assign({
209
+ ids: [forOutsider.id], userId: outsider.id, queueId: queue.id,
210
+ }).then((r: any) => ({ resolved: r })).catch((e: any) => ({ rejected: e?.message || String(e) }))
211
+
212
+ const outsiderAfter = await sdk.api.tickets.getOne(forOutsider.id)
213
+ record(
214
+ 'D2: a non-admin who is not a member of the queue is rejected',
215
+ !!(outsiderResult as any).rejected && outsiderAfter.queueId === queue.id && !outsiderAfter.owner,
216
+ `result=${JSON.stringify(outsiderResult)}; ticket queueId=${outsiderAfter.queueId} owner=${JSON.stringify(outsiderAfter.owner)}`,
217
+ )
218
+
219
+ /* ===== E: regression guard — the no-queueId path is unchanged ===== */
220
+
221
+ const stayQueued = await sdk.api.tickets.createOne({ title: 'BAQ not dequeued', queueId: queue.id })
222
+ const legacyResult = await sdk.api.tickets.bulk_assign({ ids: [stayQueued.id], userId: owner.id })
223
+ const stayedAfter = await sdk.api.tickets.getOne(stayQueued.id)
224
+ record(
225
+ 'E1: without queueId the ticket is assigned but NOT dequeued',
226
+ legacyResult.assigned === 1 && stayedAfter.owner === owner.id
227
+ && stayedAfter.queueId === queue.id && !stayedAfter.dequeuedAt,
228
+ `assigned=${legacyResult.assigned}; owner=${stayedAfter.owner} queueId=${stayedAfter.queueId} dequeuedAt=${JSON.stringify(stayedAfter.dequeuedAt)}`,
229
+ )
230
+
231
+ // a ticket already owned by the target user is still "assigned", not counted as skipped
232
+ const repeatResult = await sdk.api.tickets.bulk_assign({ ids: [stayQueued.id], userId: owner.id })
233
+ record(
234
+ 'E2: re-assigning to the same owner is not reported as skipped',
235
+ repeatResult.assigned === 1 && repeatResult.skipped === 0,
236
+ `assigned=${repeatResult.assigned} skipped=${repeatResult.skipped} (want 1 / 0 — matchedCount, not modifiedCount)`,
237
+ )
238
+
239
+ /* ===== F: another tenant's queue is invisible, even to an admin ===== */
240
+
241
+ const foreignResult = await otherTenant.sdk.api.tickets.bulk_assign({
242
+ ids: [forOutsider.id], userId: otherTenant.users[0].id, queueId: queue.id,
243
+ }).then((r: any) => ({ resolved: r })).catch((e: any) => ({ rejected: e?.message || String(e) }))
244
+
245
+ const foreignAfter = await sdk.api.tickets.getOne(forOutsider.id)
246
+ record(
247
+ 'F1: another tenant cannot assign tickets out of this tenant\'s queue',
248
+ (foreignAfter.queueId === queue.id && !foreignAfter.owner)
249
+ && ((foreignResult as any).rejected || (foreignResult as any).resolved?.assigned === 0),
250
+ `result=${JSON.stringify(foreignResult)}; ticket queueId=${foreignAfter.queueId} owner=${JSON.stringify(foreignAfter.owner)}`,
251
+ )
252
+ } finally {
253
+ // Teardown BEFORE anything asserts — assert calls process.exit, which would strand these tenants.
254
+ for (const t of [tenant, otherTenant]) {
255
+ if (t) { try { await t.teardown() } catch (e) { console.error('teardown failed', e) } }
256
+ }
257
+ }
258
+
259
+ return checks
260
+ }
261
+
262
+ /** Asserting wrapper, for standalone runs and for sequential use in the suite. */
263
+ export const tickets_bulk_assign_from_queue_tests = async () => {
264
+ assert_checks('Tickets bulk assign from queue', await tickets_bulk_assign_from_queue_checks())
265
+ }
266
+
267
+ // Allow running this test file independently
268
+ if (require.main === module) {
269
+ console.log(`🌐 Using API URL: ${host}`)
270
+ tickets_bulk_assign_from_queue_tests()
271
+ .then(() => {
272
+ console.log("✅ Tickets bulk assign from queue test suite completed successfully")
273
+ process.exit(0)
274
+ })
275
+ .catch((error) => {
276
+ console.error("❌ Tickets bulk assign from queue test suite failed:", error)
277
+ process.exit(1)
278
+ })
279
+ }
@@ -133,7 +133,7 @@ export const webhook_error_handling_tests = async ({ sdk, sdkNonAdmin } : { sdk:
133
133
 
134
134
  // Scenario 3 (timeout): skipped — WEBHOOK_TIMEOUT_MS is a constant (15s), not env-overridable,
135
135
  // and a 15s+ non-responding receiver would make this suite unacceptably slow.
136
- // Covered by security/F-0155-webhook-timeout.test.ts via send_automation_webhook.
136
+ // Covered by security/webhook-timeout.test.ts via send_automation_webhook.
137
137
 
138
138
  // Scenario 4: success path unchanged — 200 logged, counter untouched
139
139
  await sdk.api.webhooks.update({ url: `${receiverBase}/success` })
@@ -0,0 +1,170 @@
1
+ import axios from "axios"
2
+
3
+ import { assert, log_header } from "@tellescope/testing"
4
+
5
+ import { Session } from "../sdk"
6
+
7
+ const host = process.env.API_URL || 'http://localhost:8080' as const
8
+
9
+ /**
10
+ * Provisions a throwaway tenant for a test, via the API's /test-support endpoints.
11
+ *
12
+ * Why this exists alongside setup.ts: setup_tests authenticates two hand-seeded users named by
13
+ * TEST_EMAIL / NON_ADMIN_EMAIL against a hardcoded businessId, so running the suite requires a copy
14
+ * of someone's local database, and every test shares one tenant. This helper needs **no environment
15
+ * configuration at all** against a local API — clone, empty Mongo, run.
16
+ *
17
+ * setup.ts is deliberately untouched; existing tests keep the shared org. Use this for new tests.
18
+ *
19
+ * Isolated tests must NOT call sdk.reset_db(): it clears six global throttle namespaces, so it
20
+ * disturbs the rate-limit state of any concurrently running suite even though record data is
21
+ * tenant-scoped.
22
+ */
23
+
24
+ const TEST_SUPPORT_HEADER = 'x-tellescope-test-support'
25
+
26
+ export type IsolatedUser = {
27
+ id: string,
28
+ email: string,
29
+ password: string,
30
+ authToken: string,
31
+ roles: string[],
32
+ }
33
+
34
+ export type IsolatedTenant = {
35
+ businessId: string,
36
+ subdomain: string,
37
+ users: IsolatedUser[],
38
+ apiKeys: string[],
39
+ roles: string[],
40
+ /** Admin session (users[0]) */
41
+ sdk: Session,
42
+ /** Non-admin session (users[1]), when the spec provisioned one */
43
+ sdkNonAdmin: Session,
44
+ teardown: () => Promise<void>,
45
+ }
46
+
47
+ export type IsolatedTenantSpec = {
48
+ name?: string,
49
+ users?: { roles?: string[], isAdmin?: boolean }[],
50
+ apiKeys?: number,
51
+ roles?: { role: string, permissions?: any }[],
52
+ }
53
+
54
+ const post = async (path: string, body: object) => {
55
+ const r = await axios.request({
56
+ method: 'post',
57
+ url: `${host}/test-support${path}`,
58
+ // forwarded only if present — required on staging, unnecessary (and unset) locally
59
+ data: { ...body, ...(process.env.SERVICES_SECRET ? { servicesSecret: process.env.SERVICES_SECRET } : {}) },
60
+ headers: { [TEST_SUPPORT_HEADER]: '1' },
61
+ validateStatus: () => true,
62
+ })
63
+
64
+ if (r.status !== 200) {
65
+ // Surface the server's reason. setup.ts's bare process.exit() on missing credentials turns a
66
+ // misconfiguration into a silent green pass; a provisioning failure must be loud.
67
+ throw new Error(
68
+ `test-support ${path} failed (${r.status}): ${JSON.stringify(r.data)}`
69
+ + (r.status === 404
70
+ ? `\nThe route is not mounted — LOCAL is probably not "true" in the API's environment.`
71
+ : '')
72
+ )
73
+ }
74
+ return r.data
75
+ }
76
+
77
+ export const setup_isolated_tenant = async (spec: IsolatedTenantSpec = {}): Promise<IsolatedTenant> => {
78
+ const tenant = await post('/provision-tenant', spec)
79
+
80
+ const sessionFor = async (user?: IsolatedUser) => {
81
+ if (!user) return new Session({ host })
82
+ const session = new Session({ host, authToken: user.authToken })
83
+ await session.refresh_session()
84
+ return session
85
+ }
86
+
87
+ const [sdk, sdkNonAdmin] = await Promise.all([
88
+ sessionFor(tenant.users[0]),
89
+ sessionFor(tenant.users[1]),
90
+ ])
91
+
92
+ return {
93
+ ...tenant,
94
+ sdk,
95
+ sdkNonAdmin,
96
+ teardown: async () => { await post('/teardown-tenant', { businessId: tenant.businessId }) },
97
+ }
98
+ }
99
+
100
+ /** Sets the pending email-verification code for an unverified user, so a test can drive the real
101
+ * submit_email_verification endpoint. The address must be a `dontsend...@tellescope.com` one. */
102
+ export const set_email_verification_code = async (email: string, code: string) => (
103
+ post('/set-email-verification-code', { email, code })
104
+ )
105
+
106
+ /** Raw access, for tests that need to assert on the response of a rejected call. */
107
+ export const test_support_request = async (path: string, body: object) => {
108
+ const r = await axios.request({
109
+ method: 'post',
110
+ url: `${host}/test-support${path}`,
111
+ data: { ...body, ...(process.env.SERVICES_SECRET ? { servicesSecret: process.env.SERVICES_SECRET } : {}) },
112
+ headers: { [TEST_SUPPORT_HEADER]: '1' },
113
+ validateStatus: () => true,
114
+ })
115
+ return { status: r.status, body: r.data }
116
+ }
117
+
118
+ /* ============================ isolated-test result plumbing ============================ */
119
+
120
+ export type Check = { label: string, pass: boolean, detail: string }
121
+
122
+ /** Prints a suite's evidence table, then asserts. Split out so the assert phase can be deferred until
123
+ * after every concurrent suite has torn down — see run_isolated_in_parallel. */
124
+ export const assert_checks = (name: string, checks: Check[]) => {
125
+ console.log(`\n ${name} check summary:`)
126
+ for (const c of checks) {
127
+ console.log(` ${c.pass ? 'PASS' : 'FAIL'} ${c.label} — ${c.detail}`)
128
+ }
129
+ console.log('')
130
+
131
+ for (const c of checks) {
132
+ assert(c.pass, `${c.label} — ${c.detail}`, c.label)
133
+ }
134
+ }
135
+
136
+ /**
137
+ * Runs isolated test bodies CONCURRENTLY.
138
+ *
139
+ * Why this exists rather than a bare Promise.all: the harness's `assert` calls process.exit(1) on
140
+ * failure, and process.exit does not run pending `finally` blocks. So if one suite asserted while
141
+ * another was still in flight, the other's teardown would never run and its provisioned tenants would
142
+ * be stranded permanently (nothing in the system can delete an organization record). `assert` also
143
+ * exits rather than throwing, so Promise.allSettled cannot recover it either.
144
+ *
145
+ * The contract that makes concurrency safe: each `run` collects Check[] and tears down its OWN tenants
146
+ * in a finally, and never asserts. Nothing here asserts until every suite has settled.
147
+ *
148
+ * Each suite's table prints as one non-yielding burst, so concurrent output stays readable.
149
+ */
150
+ export const run_isolated_in_parallel = async (
151
+ suites: { name: string, run: () => Promise<Check[]> }[],
152
+ ) => {
153
+ log_header(`Isolated (parallel): ${suites.map(s => s.name).join(' | ')}`)
154
+
155
+ const settled = await Promise.allSettled(suites.map(s => s.run()))
156
+
157
+ // Surface an infrastructure failure (a throw, not a failed check) before asserting anything, so the
158
+ // reason isn't buried under another suite's table.
159
+ const rejected = settled.flatMap((r, i) => r.status === 'rejected' ? [{ name: suites[i].name, reason: r.reason }] : [])
160
+ for (const { name, reason } of rejected) {
161
+ console.error(` ${name} threw before producing checks:`, reason)
162
+ }
163
+ if (rejected.length) throw rejected[0].reason
164
+
165
+ for (let i = 0; i < settled.length; i++) {
166
+ const r = settled[i]
167
+ if (r.status !== 'fulfilled') continue
168
+ assert_checks(suites[i].name, r.value)
169
+ }
170
+ }
@@ -112,29 +112,34 @@ import { nutrition_tracking_tests } from "./api_tests/nutrition_tracking.test";
112
112
  import { no_access_permission_checks_tests } from "./api_tests/no_access_permission_checks.test";
113
113
  import { resource_access_tags_tests } from "./api_tests/resource_access_tags.test";
114
114
  import { field_redaction_tests } from "./api_tests/field_redaction.test";
115
- import { data_sync_redaction_bypass_tests } from "./api_tests/security/F-0001-data-sync-redaction-bypass.test";
116
- import { enduser_write_restrictions_tests } from "./api_tests/security/F-0106-F-0110-enduser-write-restrictions.test";
117
- import { ai_conversations_rbac_tests } from "./api_tests/security/F-0005-ai-conversations-rbac.test";
118
- import { develop_health_benefit_verification_rbac_tests } from "./api_tests/security/F-0006-develop-health-benefit-verification-rbac.test";
119
- import { cascade_role_rename_cross_tenant_tests } from "./api_tests/security/F-0053-cascade-role-rename-cross-tenant.test";
120
- import { self_admin_role_assignment_tests } from "./api_tests/security/F-0076-self-admin-role-assignment.test";
121
- import { invite_user_enumeration_tests } from "./api_tests/security/F-0007-invite-user-enumeration.test";
115
+ import { data_sync_redaction_bypass_tests } from "./api_tests/security/data-sync-redaction-bypass.test";
116
+ import { enduser_write_restrictions_tests } from "./api_tests/security/enduser-write-restrictions.test";
117
+ import { ai_conversations_rbac_tests } from "./api_tests/security/ai-conversations-rbac.test";
118
+ import { develop_health_benefit_verification_rbac_tests } from "./api_tests/security/develop-health-benefit-verification-rbac.test";
119
+ import { cascade_role_rename_cross_tenant_tests } from "./api_tests/security/cascade-role-rename-cross-tenant.test";
120
+ import { self_admin_role_assignment_tests } from "./api_tests/security/self-admin-role-assignment.test";
121
+ import { invite_user_enumeration_tests } from "./api_tests/security/invite-user-enumeration.test";
122
122
  import { register_business_id_injection_tests } from "./api_tests/security/register_business_id_injection.test";
123
- import { handle_incoming_communication_cross_tenant_tests } from "./api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test";
124
- import { sanitize_user_html_xss_tests } from "./api_tests/security/F-0013-sanitize-user-html.test";
125
- import { sanitize_user_html_with_iframes_tests } from "./api_tests/security/F-0013-sanitize-user-html-with-iframes.test";
126
- import { prototype_pollution_tests } from "./api_tests/security/F-0016-prototype-pollution.test";
127
- import { enduser_cross_create_tests } from "./api_tests/security/F-0116-F-0119-enduser-cross-create.test";
128
- import { globalunique_leak_tests } from "./api_tests/security/F-0145-globalunique-leak.test";
129
- import { load_inbox_redaction_tests } from "./api_tests/security/F-0151-load-inbox-redaction.test";
123
+ import { handle_incoming_communication_cross_tenant_tests } from "./api_tests/security/handle-incoming-communication-cross-tenant.test";
124
+ import { sanitize_user_html_xss_tests } from "./api_tests/security/sanitize-user-html.test";
125
+ import { sanitize_user_html_with_iframes_tests } from "./api_tests/security/sanitize-user-html-with-iframes.test";
126
+ import { prototype_pollution_tests } from "./api_tests/security/prototype-pollution.test";
127
+ import { enduser_cross_create_tests } from "./api_tests/security/enduser-cross-create.test";
128
+ import { globalunique_leak_tests } from "./api_tests/security/globalunique-conflict-redaction.test";
129
+ import { load_inbox_redaction_tests } from "./api_tests/security/load-inbox-redaction.test";
130
130
  import { suggested_reply_redaction_tests } from "./api_tests/security/suggested-reply-redaction.test";
131
- import { webhook_timeout_tests } from "./api_tests/security/F-0155-webhook-timeout.test";
131
+ import { webhook_timeout_tests } from "./api_tests/security/webhook-timeout.test";
132
132
  import { enduser_ai_summary_trust_gate_tests } from "./api_tests/security/enduser-ai-summary-trust-gate.test";
133
- import { allowed_paths_confinement_tests } from "./api_tests/security/F-0166-allowed-paths-wildcard-bypass.test";
134
- import { proxy_image_ssrf_tests } from "./api_tests/security/F-0025-proxy-image-unauth-ssrf.test";
135
- import { webhook_outbound_ssrf_tests } from "./api_tests/security/F-0015-webhook-outbound-ssrf.test";
133
+ import { allowed_paths_confinement_tests } from "./api_tests/security/allowed-paths-confinement.test";
134
+ import { proxy_image_ssrf_tests } from "./api_tests/security/proxy-image-unauth-ssrf.test";
135
+ import { webhook_outbound_ssrf_tests } from "./api_tests/security/webhook-outbound-ssrf.test";
136
136
  import { public_endpoint_auth_field_lock_tests } from "./api_tests/security/public_endpoint_auth_field_lock.test";
137
137
  import { formsort_webhook_auth_field_lock_tests } from "./api_tests/security/formsort_webhook_auth_field_lock.test";
138
+ import { booking_info_idor_tests } from "./api_tests/security/booking-info-idor-token-mint.test";
139
+ import { automated_actions_process_gate_tests } from "./api_tests/security/automated-actions-process-gate.test";
140
+ import { schema_adminonly_enforcement_tests } from "./api_tests/security/schema-adminonly-enforcement.test";
141
+ import { startlinktoken_escalation_tests } from "./api_tests/security/startlinktoken-enduser-staff-escalation.test";
142
+ import { form_logic_js_execution_checks } from "./api_tests/security/form-logic-js-execution.test";
138
143
  import { bulk_assignment_tests } from "./api_tests/bulk_assignment.test";
139
144
  import { tickets_bulk_assign_care_team_tests } from "./api_tests/tickets_bulk_assign_care_team.test";
140
145
  import { managed_content_enduser_access_tests } from "./api_tests/managed_content_enduser_access.test";
@@ -152,6 +157,13 @@ import { dont_sync_to_elation_form_submission_tests } from "./api_tests/dont_syn
152
157
  import { medication_added_trigger_tests } from "./api_tests/medication_added_trigger.test";
153
158
  import { conditional_logic_medication_unit_tests } from "./unit_tests/conditional_logic_medication.test";
154
159
  import { phone_validation_unit_tests } from "./unit_tests/phone_validation.test";
160
+ import { openapi_spec_freshness_unit_tests } from "./unit_tests/openapi_spec_freshness.test";
161
+ import { do_not_send_address_unit_tests } from "./unit_tests/do_not_send_address.test";
162
+ import { organization_signup_checks } from "./api_tests/organization_signup.test";
163
+ import { add_to_journey_foreign_enduserid_checks } from "./api_tests/security/add-to-journey-foreign-enduserid.test";
164
+ import { tickets_bulk_assign_from_queue_checks } from "./api_tests/tickets_bulk_assign_from_queue.test";
165
+ import { enduser_observations_auto_review_checks } from "./api_tests/enduser_observations_auto_review.test";
166
+ import { run_isolated_in_parallel } from "./setup_isolated";
155
167
  import { elation_user_id_tests } from "./api_tests/elation_user_id.test";
156
168
  import { organization_settings_duplicates_tests } from "./api_tests/organization_settings_duplicates.test";
157
169
  import { stedi_approval_tests } from "./api_tests/stedi_approval.test";
@@ -10893,6 +10905,19 @@ export const form_conditional_logic_tests = async () => {
10893
10905
  run_conditional_form_test({ $and: [{ condition: { [FORM_LOGIC_CALCULATED_FIELDS[0]]: { $exists: true } } }] }, true)
10894
10906
  run_conditional_form_test({ $and: [{ condition: { [FORM_LOGIC_CALCULATED_FIELDS[0]]: { $gt: 25 } } }] }, true)
10895
10907
  run_conditional_form_test({ $and: [{ condition: { [FORM_LOGIC_CALCULATED_FIELDS[0]]: { $lt: 25 } } }] }, false)
10908
+
10909
+ // A $JS(...) condition value was previously compiled and executed with new Function().
10910
+ // These must evaluate to false (parseInt -> NaN -> no match), and must never be executed.
10911
+ // The values are typed `number` on BasicFilter but are strings in stored data, hence the casts.
10912
+ delete (globalThis as any).__F0200
10913
+ run_conditional_form_test({ $and: [{ condition: { '3': { $gt: '$JS(return 0)' as any } } }] }, false, '$gt $JS not evaluated')
10914
+ run_conditional_form_test({ $and: [{ condition: { '3': { $lt: '$JS(return 99999)' as any } } }] }, false, '$lt $JS not evaluated')
10915
+ run_conditional_form_test({ $and: [{ condition: { '3': { $gt: '$JS(globalThis.__F0200 = 1; return 0)' as any } } }] }, false, '$JS side effect not evaluated')
10916
+ assert(
10917
+ (globalThis as any).__F0200 === undefined,
10918
+ 'a $JS(...) condition value was executed',
10919
+ '$JS payload never executed',
10920
+ )
10896
10921
  }
10897
10922
 
10898
10923
  export const enduser_conditional_logic_tests = async () => {
@@ -15363,11 +15388,24 @@ const ip_address_form_tests = async () => {
15363
15388
  await enduser_conditional_logic_tests()
15364
15389
  await conditional_logic_medication_unit_tests()
15365
15390
  await phone_validation_unit_tests()
15391
+ await openapi_spec_freshness_unit_tests()
15392
+ await do_not_send_address_unit_tests()
15366
15393
  await replace_enduser_template_values_tests()
15367
15394
  await safe_json_parse_template_tests()
15368
15395
  await replace_form_field_template_values_tests()
15369
15396
  await mfa_tests()
15370
15397
  await setup_tests(sdk, sdkNonAdmin)
15398
+ // Isolated, tenant-provisioned suites run first and CONCURRENTLY: each provisions its own
15399
+ // throwaway tenant via /test-support, so they cannot collide with each other or with the
15400
+ // shared fixture. Both collect Check[] and tear down before anything asserts — assert calls
15401
+ // process.exit, which would otherwise strand the other suite's tenants.
15402
+ await run_isolated_in_parallel([
15403
+ { name: 'Organization signup', run: organization_signup_checks },
15404
+ { name: 'add_to_journey foreign enduserId', run: add_to_journey_foreign_enduserid_checks },
15405
+ { name: 'Tickets bulk assign from queue', run: tickets_bulk_assign_from_queue_checks },
15406
+ { name: 'Vital auto-review on create', run: enduser_observations_auto_review_checks },
15407
+ { name: '$JS in stored conditional logic', run: form_logic_js_execution_checks },
15408
+ ])
15371
15409
  await embeddables_auth_token_tests({ sdk, sdkNonAdmin })
15372
15410
  await proxy_image_ssrf_tests({ sdk, sdkNonAdmin })
15373
15411
  await webhook_outbound_ssrf_tests({ sdk, sdkNonAdmin })
@@ -15387,6 +15425,10 @@ const ip_address_form_tests = async () => {
15387
15425
  await enduser_write_restrictions_tests({ sdk, sdkNonAdmin })
15388
15426
  await enduser_ai_summary_trust_gate_tests({ sdk, sdkNonAdmin })
15389
15427
  await enduser_cross_create_tests({ sdk, sdkNonAdmin })
15428
+ await booking_info_idor_tests({ sdk, sdkNonAdmin })
15429
+ await automated_actions_process_gate_tests({ sdk, sdkNonAdmin })
15430
+ await schema_adminonly_enforcement_tests({ sdk, sdkNonAdmin })
15431
+ await startlinktoken_escalation_tests({ sdk, sdkNonAdmin })
15390
15432
  await globalunique_leak_tests({ sdk, sdkNonAdmin })
15391
15433
  await load_inbox_redaction_tests({ sdk, sdkNonAdmin })
15392
15434
  await suggested_reply_redaction_tests({ sdk, sdkNonAdmin })
@@ -0,0 +1,60 @@
1
+ require('source-map-support').install();
2
+
3
+ import {
4
+ assert,
5
+ log_header,
6
+ } from "@tellescope/testing"
7
+
8
+ /**
9
+ * The email-suppression predicate from packages/private/api/api/modules/sqs.ts.
10
+ *
11
+ * Mirrored here rather than imported: the API is a private package the SDK does not depend on. The
12
+ * point of the test is to pin the CONTRACT — prefix AND domain both required — because the guard is
13
+ * otherwise unobservable from a test (a suppressed message simply never reaches SQS), and because
14
+ * loosening it in either direction would start suppressing real mail or start sending test mail.
15
+ *
16
+ * If this drifts from sqs.ts, that is the signal to look at why.
17
+ */
18
+ const is_do_not_send_address = (emailAddress?: string) => (
19
+ !!emailAddress && emailAddress.endsWith('@tellescope.com') && emailAddress.startsWith('dontsend')
20
+ )
21
+
22
+ export const do_not_send_address_unit_tests = async () => {
23
+ log_header("Do-not-send address predicate")
24
+
25
+ const cases: [string | undefined, boolean, string][] = [
26
+ ['dontsend@tellescope.com', true, 'exact prefix and domain'],
27
+ ['dontsend-signup-ab12@tellescope.com', true, 'suffixed local part (what provisioning generates)'],
28
+ ['dontsend3@tellescope.com', true, 'numbered variant already used by the suite'],
29
+
30
+ // near misses — both halves are load-bearing
31
+ ['dontsend@example.com', false, 'right prefix, wrong domain: must NOT suppress'],
32
+ ['realuser@tellescope.com', false, 'right domain, wrong prefix: must NOT suppress'],
33
+ ['notdontsend@tellescope.com', false, 'prefix must be at the START, not anywhere'],
34
+ ['dontsend@tellescope.com.evil.com', false, 'domain must be a suffix, not a substring'],
35
+
36
+ [undefined, false, 'undefined is not suppressed (and must not throw)'],
37
+ ['', false, 'empty string is not suppressed'],
38
+ ]
39
+
40
+ for (const [input, expected, why] of cases) {
41
+ assert(
42
+ is_do_not_send_address(input) === expected,
43
+ `${JSON.stringify(input)} should be ${expected ? 'suppressed' : 'sent'} — ${why}`,
44
+ `${JSON.stringify(input)} -> ${expected ? 'suppressed' : 'sent'} (${why})`,
45
+ )
46
+ }
47
+ }
48
+
49
+ // Allow running this test file independently
50
+ if (require.main === module) {
51
+ do_not_send_address_unit_tests()
52
+ .then(() => {
53
+ console.log("✅ Do-not-send address predicate tests completed successfully")
54
+ process.exit(0)
55
+ })
56
+ .catch((error) => {
57
+ console.error("❌ Do-not-send address predicate tests failed:", error)
58
+ process.exit(1)
59
+ })
60
+ }