@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.
- package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js +1 -1
- package/lib/cjs/tests/api_tests/auto_merge_form_submission.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js +388 -0
- package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/integrations_redacted.test.js +3 -3
- package/lib/cjs/tests/api_tests/integrations_redacted.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/organization_signup.test.d.ts +20 -0
- package/lib/cjs/tests/api_tests/organization_signup.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/organization_signup.test.js +272 -0
- package/lib/cjs/tests/api_tests/organization_signup.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/purchase_made_trigger.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/purchase_made_trigger.test.js +103 -162
- package/lib/cjs/tests/api_tests/purchase_made_trigger.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts +30 -0
- package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js +336 -0
- package/lib/cjs/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js +354 -0
- package/lib/cjs/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts +46 -0
- package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js +389 -0
- package/lib/cjs/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts +43 -0
- package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js +324 -0
- package/lib/cjs/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts +43 -0
- package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js +282 -0
- package/lib/cjs/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts +33 -0
- package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.js +249 -0
- package/lib/cjs/tests/api_tests/security/F-0200-form-logic-js-execution.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +42 -0
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +281 -0
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.d.ts +38 -0
- package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.js +390 -0
- package/lib/cjs/tests/api_tests/security/ai-conversations-rbac.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js +1003 -0
- package/lib/cjs/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.d.ts +42 -0
- package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.js +323 -0
- package/lib/cjs/tests/api_tests/security/automated-actions-process-gate.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts +29 -0
- package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js +335 -0
- package/lib/cjs/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts +32 -0
- package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js +237 -0
- package/lib/cjs/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts +28 -0
- package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.js +349 -0
- package/lib/cjs/tests/api_tests/security/data-sync-redaction-bypass.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts +30 -0
- package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js +314 -0
- package/lib/cjs/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.d.ts +21 -0
- package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js +362 -0
- package/lib/cjs/tests/api_tests/security/enduser-cross-create.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.d.ts +21 -0
- package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js +366 -0
- package/lib/cjs/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts +32 -0
- package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js +248 -0
- package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts +19 -0
- package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.js +138 -0
- package/lib/cjs/tests/api_tests/security/globalunique-conflict-redaction.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts +24 -0
- package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js +201 -0
- package/lib/cjs/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.d.ts +29 -0
- package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js +278 -0
- package/lib/cjs/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.d.ts +17 -0
- package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.js +155 -0
- package/lib/cjs/tests/api_tests/security/load-inbox-redaction.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/prototype-pollution.test.d.ts +2 -0
- package/lib/cjs/tests/api_tests/security/prototype-pollution.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/prototype-pollution.test.js +88 -0
- package/lib/cjs/tests/api_tests/security/prototype-pollution.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts +34 -0
- package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.js +230 -0
- package/lib/cjs/tests/api_tests/security/proxy-image-unauth-ssrf.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts +2 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +242 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.d.ts +2 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.js +148 -0
- package/lib/cjs/tests/api_tests/security/sanitize-user-html.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts +45 -0
- package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.js +388 -0
- package/lib/cjs/tests/api_tests/security/schema-adminonly-enforcement.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.d.ts +38 -0
- package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.js +222 -0
- package/lib/cjs/tests/api_tests/security/self-admin-role-assignment.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js +354 -0
- package/lib/cjs/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts +28 -0
- package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.js +442 -0
- package/lib/cjs/tests/api_tests/security/webhook-outbound-ssrf.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/webhook-timeout.test.d.ts +19 -0
- package/lib/cjs/tests/api_tests/security/webhook-timeout.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/security/webhook-timeout.test.js +156 -0
- package/lib/cjs/tests/api_tests/security/webhook-timeout.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js +329 -0
- package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/webhook_error_handling.test.js +2 -2
- package/lib/cjs/tests/api_tests/webhook_error_handling.test.js.map +1 -1
- package/lib/cjs/tests/setup_isolated.d.ts +68 -0
- package/lib/cjs/tests/setup_isolated.d.ts.map +1 -0
- package/lib/cjs/tests/setup_isolated.js +232 -0
- package/lib/cjs/tests/setup_isolated.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +287 -227
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/cjs/tests/unit_tests/do_not_send_address.test.d.ts +2 -0
- package/lib/cjs/tests/unit_tests/do_not_send_address.test.d.ts.map +1 -0
- package/lib/cjs/tests/unit_tests/do_not_send_address.test.js +89 -0
- package/lib/cjs/tests/unit_tests/do_not_send_address.test.js.map +1 -0
- package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.d.ts +17 -0
- package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.d.ts.map +1 -0
- package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.js +140 -0
- package/lib/cjs/tests/unit_tests/openapi_spec_freshness.test.js.map +1 -0
- package/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -0
- package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js +1 -1
- package/lib/esm/tests/api_tests/auto_merge_form_submission.test.js.map +1 -1
- package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js +383 -0
- package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -0
- package/lib/esm/tests/api_tests/integrations_redacted.test.js +3 -3
- package/lib/esm/tests/api_tests/integrations_redacted.test.js.map +1 -1
- package/lib/esm/tests/api_tests/organization_signup.test.d.ts +20 -0
- package/lib/esm/tests/api_tests/organization_signup.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/organization_signup.test.js +264 -0
- package/lib/esm/tests/api_tests/organization_signup.test.js.map +1 -0
- package/lib/esm/tests/api_tests/purchase_made_trigger.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/purchase_made_trigger.test.js +104 -163
- package/lib/esm/tests/api_tests/purchase_made_trigger.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts +30 -0
- package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js +332 -0
- package/lib/esm/tests/api_tests/security/F-0179-booking-info-idor-token-mint.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js +350 -0
- package/lib/esm/tests/api_tests/security/F-0185-startlinktoken-enduser-staff-escalation.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts +46 -0
- package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js +382 -0
- package/lib/esm/tests/api_tests/security/F-0191-schema-adminonly-enforcement.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts +43 -0
- package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js +317 -0
- package/lib/esm/tests/api_tests/security/F-0192-automated-actions-process-gate.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts +43 -0
- package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js +277 -0
- package/lib/esm/tests/api_tests/security/F-0196-add-to-journey-foreign-enduserid.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts +33 -0
- package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.js +244 -0
- package/lib/esm/tests/api_tests/security/F-0200-form-logic-js-execution.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +42 -0
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +276 -0
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.d.ts +38 -0
- package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.js +386 -0
- package/lib/esm/tests/api_tests/security/ai-conversations-rbac.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js +996 -0
- package/lib/esm/tests/api_tests/security/allowed-paths-confinement.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.d.ts +42 -0
- package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.js +316 -0
- package/lib/esm/tests/api_tests/security/automated-actions-process-gate.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts +29 -0
- package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js +331 -0
- package/lib/esm/tests/api_tests/security/booking-info-idor-token-mint.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts +32 -0
- package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js +233 -0
- package/lib/esm/tests/api_tests/security/cascade-role-rename-cross-tenant.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts +28 -0
- package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.js +345 -0
- package/lib/esm/tests/api_tests/security/data-sync-redaction-bypass.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts +30 -0
- package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js +310 -0
- package/lib/esm/tests/api_tests/security/develop-health-benefit-verification-rbac.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/enduser-cross-create.test.d.ts +21 -0
- package/lib/esm/tests/api_tests/security/enduser-cross-create.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js +358 -0
- package/lib/esm/tests/api_tests/security/enduser-cross-create.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.d.ts +21 -0
- package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js +362 -0
- package/lib/esm/tests/api_tests/security/enduser-write-restrictions.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts +32 -0
- package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js +243 -0
- package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts +19 -0
- package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.js +134 -0
- package/lib/esm/tests/api_tests/security/globalunique-conflict-redaction.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts +24 -0
- package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js +194 -0
- package/lib/esm/tests/api_tests/security/handle-incoming-communication-cross-tenant.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.d.ts +29 -0
- package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js +271 -0
- package/lib/esm/tests/api_tests/security/invite-user-enumeration.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.d.ts +17 -0
- package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.js +151 -0
- package/lib/esm/tests/api_tests/security/load-inbox-redaction.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/prototype-pollution.test.d.ts +2 -0
- package/lib/esm/tests/api_tests/security/prototype-pollution.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/prototype-pollution.test.js +84 -0
- package/lib/esm/tests/api_tests/security/prototype-pollution.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts +34 -0
- package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.js +223 -0
- package/lib/esm/tests/api_tests/security/proxy-image-unauth-ssrf.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts +2 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js +238 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html-with-iframes.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html.test.d.ts +2 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html.test.js +144 -0
- package/lib/esm/tests/api_tests/security/sanitize-user-html.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts +45 -0
- package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.js +381 -0
- package/lib/esm/tests/api_tests/security/schema-adminonly-enforcement.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.d.ts +38 -0
- package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.js +218 -0
- package/lib/esm/tests/api_tests/security/self-admin-role-assignment.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js +350 -0
- package/lib/esm/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts +28 -0
- package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.js +435 -0
- package/lib/esm/tests/api_tests/security/webhook-outbound-ssrf.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/webhook-timeout.test.d.ts +19 -0
- package/lib/esm/tests/api_tests/security/webhook-timeout.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/security/webhook-timeout.test.js +149 -0
- package/lib/esm/tests/api_tests/security/webhook-timeout.test.js.map +1 -0
- package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js +324 -0
- package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -0
- package/lib/esm/tests/api_tests/webhook_error_handling.test.js +2 -2
- package/lib/esm/tests/api_tests/webhook_error_handling.test.js.map +1 -1
- package/lib/esm/tests/setup_isolated.d.ts +68 -0
- package/lib/esm/tests/setup_isolated.d.ts.map +1 -0
- package/lib/esm/tests/setup_isolated.js +221 -0
- package/lib/esm/tests/setup_isolated.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +287 -227
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/esm/tests/unit_tests/do_not_send_address.test.d.ts +2 -0
- package/lib/esm/tests/unit_tests/do_not_send_address.test.d.ts.map +1 -0
- package/lib/esm/tests/unit_tests/do_not_send_address.test.js +85 -0
- package/lib/esm/tests/unit_tests/do_not_send_address.test.js.map +1 -0
- package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.d.ts +17 -0
- package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.d.ts.map +1 -0
- package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.js +113 -0
- package/lib/esm/tests/unit_tests/openapi_spec_freshness.test.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/tests/api_tests/auto_merge_form_submission.test.ts +1 -1
- package/src/tests/api_tests/enduser_observations_auto_review.test.ts +358 -0
- package/src/tests/api_tests/integrations_redacted.test.ts +1 -1
- package/src/tests/api_tests/organization_signup.test.ts +240 -0
- package/src/tests/api_tests/purchase_made_trigger.test.ts +29 -41
- package/src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts +207 -0
- package/src/tests/api_tests/security/{F-0005-ai-conversations-rbac.test.ts → ai-conversations-rbac.test.ts} +14 -14
- package/src/tests/api_tests/security/{F-0166-allowed-paths-wildcard-bypass.test.ts → allowed-paths-confinement.test.ts} +11 -11
- package/src/tests/api_tests/security/automated-actions-process-gate.test.ts +237 -0
- package/src/tests/api_tests/security/booking-info-idor-token-mint.test.ts +224 -0
- package/src/tests/api_tests/security/{F-0053-cascade-role-rename-cross-tenant.test.ts → cascade-role-rename-cross-tenant.test.ts} +10 -10
- package/src/tests/api_tests/security/{F-0001-data-sync-redaction-bypass.test.ts → data-sync-redaction-bypass.test.ts} +9 -9
- package/src/tests/api_tests/security/{F-0006-develop-health-benefit-verification-rbac.test.ts → develop-health-benefit-verification-rbac.test.ts} +7 -8
- package/src/tests/api_tests/security/{F-0116-F-0119-enduser-cross-create.test.ts → enduser-cross-create.test.ts} +25 -25
- package/src/tests/api_tests/security/{F-0106-F-0110-enduser-write-restrictions.test.ts → enduser-write-restrictions.test.ts} +12 -14
- package/src/tests/api_tests/security/form-logic-js-execution.test.ts +183 -0
- package/src/tests/api_tests/security/{F-0145-globalunique-leak.test.ts → globalunique-conflict-redaction.test.ts} +5 -6
- package/src/tests/api_tests/security/{F-0008-handle-incoming-communication-cross-tenant.test.ts → handle-incoming-communication-cross-tenant.test.ts} +8 -8
- package/src/tests/api_tests/security/{F-0007-invite-user-enumeration.test.ts → invite-user-enumeration.test.ts} +8 -8
- package/src/tests/api_tests/security/{F-0151-load-inbox-redaction.test.ts → load-inbox-redaction.test.ts} +5 -6
- package/src/tests/api_tests/security/{F-0016-prototype-pollution.test.ts → prototype-pollution.test.ts} +4 -4
- package/src/tests/api_tests/security/{F-0025-proxy-image-unauth-ssrf.test.ts → proxy-image-unauth-ssrf.test.ts} +11 -12
- package/src/tests/api_tests/security/{F-0013-sanitize-user-html-with-iframes.test.ts → sanitize-user-html-with-iframes.test.ts} +5 -5
- package/src/tests/api_tests/security/{F-0013-sanitize-user-html.test.ts → sanitize-user-html.test.ts} +4 -4
- package/src/tests/api_tests/security/schema-adminonly-enforcement.test.ts +287 -0
- package/src/tests/api_tests/security/{F-0076-self-admin-role-assignment.test.ts → self-admin-role-assignment.test.ts} +13 -13
- package/src/tests/api_tests/security/startlinktoken-enduser-staff-escalation.test.ts +162 -0
- package/src/tests/api_tests/security/{F-0015-webhook-outbound-ssrf.test.ts → webhook-outbound-ssrf.test.ts} +13 -14
- package/src/tests/api_tests/security/{F-0155-webhook-timeout.test.ts → webhook-timeout.test.ts} +6 -7
- package/src/tests/api_tests/tickets_bulk_assign_from_queue.test.ts +279 -0
- package/src/tests/api_tests/webhook_error_handling.test.ts +1 -1
- package/src/tests/setup_isolated.ts +170 -0
- package/src/tests/tests.ts +60 -18
- package/src/tests/unit_tests/do_not_send_address.test.ts +60 -0
- package/src/tests/unit_tests/openapi_spec_freshness.test.ts +88 -0
- package/test_generated.pdf +0 -0
- 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/
|
|
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
|
+
}
|
package/src/tests/tests.ts
CHANGED
|
@@ -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/
|
|
116
|
-
import { enduser_write_restrictions_tests } from "./api_tests/security/
|
|
117
|
-
import { ai_conversations_rbac_tests } from "./api_tests/security/
|
|
118
|
-
import { develop_health_benefit_verification_rbac_tests } from "./api_tests/security/
|
|
119
|
-
import { cascade_role_rename_cross_tenant_tests } from "./api_tests/security/
|
|
120
|
-
import { self_admin_role_assignment_tests } from "./api_tests/security/
|
|
121
|
-
import { invite_user_enumeration_tests } from "./api_tests/security/
|
|
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/
|
|
124
|
-
import { sanitize_user_html_xss_tests } from "./api_tests/security/
|
|
125
|
-
import { sanitize_user_html_with_iframes_tests } from "./api_tests/security/
|
|
126
|
-
import { prototype_pollution_tests } from "./api_tests/security/
|
|
127
|
-
import { enduser_cross_create_tests } from "./api_tests/security/
|
|
128
|
-
import { globalunique_leak_tests } from "./api_tests/security/
|
|
129
|
-
import { load_inbox_redaction_tests } from "./api_tests/security/
|
|
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/
|
|
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/
|
|
134
|
-
import { proxy_image_ssrf_tests } from "./api_tests/security/
|
|
135
|
-
import { webhook_outbound_ssrf_tests } from "./api_tests/security/
|
|
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
|
+
}
|