@tellescope/sdk 2.0.0-beta.0 → 2.0.0-beta.2
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/CLAUDE.md +24 -0
- package/env.example +6 -0
- package/lib/cjs/sdk.d.ts +376 -3
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +1 -0
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts +22 -0
- package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.js +506 -0
- package/lib/cjs/tests/api_tests/booking_prevent_upcoming_duplicate.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js +3 -1
- package/lib/cjs/tests/api_tests/enduser_fields_sync.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js +3 -1
- package/lib/cjs/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/form_vital_reading.test.d.ts +7 -0
- package/lib/cjs/tests/api_tests/form_vital_reading.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/form_vital_reading.test.js +461 -0
- package/lib/cjs/tests/api_tests/form_vital_reading.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/journey_statistics_cache.test.d.ts +26 -0
- package/lib/cjs/tests/api_tests/journey_statistics_cache.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/journey_statistics_cache.test.js +232 -0
- package/lib/cjs/tests/api_tests/journey_statistics_cache.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/journey_step_manual_count.test.d.ts +34 -0
- package/lib/cjs/tests/api_tests/journey_step_manual_count.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/journey_step_manual_count.test.js +243 -0
- package/lib/cjs/tests/api_tests/journey_step_manual_count.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/load_threads_care_team.test.js +3 -1
- package/lib/cjs/tests/api_tests/load_threads_care_team.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js +139 -9
- package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/organization_signup.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/organization_signup.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/organization_signup.test.js +3 -1
- package/lib/cjs/tests/api_tests/organization_signup.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.d.ts +7 -0
- package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.js +571 -0
- package/lib/cjs/tests/api_tests/phone_embed_leave_message.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/programs.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/programs.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/programs.test.js +73 -17
- package/lib/cjs/tests/api_tests/programs.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.d.ts +7 -0
- package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.js +733 -0
- package/lib/cjs/tests/api_tests/public_form_case_insensitive_match.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +58 -48
- package/lib/cjs/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js +3 -1
- package/lib/cjs/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.js +3 -1
- package/lib/cjs/tests/api_tests/security/file-link-session-confinement.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js +3 -1
- package/lib/cjs/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js +424 -39
- package/lib/cjs/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/register_business_id_injection.test.js +2 -2
- package/lib/cjs/tests/api_tests/security/register_business_id_injection.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +3 -1
- package/lib/cjs/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/session_for_join_link.test.d.ts +10 -0
- package/lib/cjs/tests/api_tests/session_for_join_link.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/session_for_join_link.test.js +147 -0
- package/lib/cjs/tests/api_tests/session_for_join_link.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js +191 -88
- package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/stripe_checkout_items.test.d.ts +17 -0
- package/lib/cjs/tests/api_tests/stripe_checkout_items.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/stripe_checkout_items.test.js +219 -0
- package/lib/cjs/tests/api_tests/stripe_checkout_items.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.js +396 -0
- package/lib/cjs/tests/api_tests/stripe_multiple_customer_ids.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js +3 -1
- package/lib/cjs/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/totp_mfa.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/totp_mfa.test.js +37 -31
- package/lib/cjs/tests/api_tests/totp_mfa.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js +3 -1
- package/lib/cjs/tests/api_tests/trigger_contains_matching.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js +3 -1
- package/lib/cjs/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts +3 -1
- package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js +3 -1
- package/lib/cjs/tests/api_tests/vital_review_trigger_action.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/vitl_push.test.d.ts +7 -0
- package/lib/cjs/tests/api_tests/vitl_push.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/vitl_push.test.js +341 -0
- package/lib/cjs/tests/api_tests/vitl_push.test.js.map +1 -0
- package/lib/cjs/tests/setup_isolated.d.ts +48 -0
- package/lib/cjs/tests/setup_isolated.d.ts.map +1 -1
- package/lib/cjs/tests/setup_isolated.js +119 -1
- package/lib/cjs/tests/setup_isolated.js.map +1 -1
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +378 -282
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.d.ts +2 -0
- package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.d.ts.map +1 -0
- package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.js +201 -0
- package/lib/cjs/tests/unit_tests/isolated_suite_discovery.test.js.map +1 -0
- package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.d.ts +3 -0
- package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.d.ts.map +1 -0
- package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.js +120 -0
- package/lib/cjs/tests/unit_tests/programs_conditional_logic.test.js.map +1 -0
- package/lib/cjs/tests/webhooks_tests.js +44 -34
- package/lib/cjs/tests/webhooks_tests.js.map +1 -1
- package/lib/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/lib/esm/sdk.d.ts +376 -3
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +1 -0
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts +22 -0
- package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.js +501 -0
- package/lib/esm/tests/api_tests/booking_prevent_upcoming_duplicate.test.js.map +1 -0
- package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/enduser_fields_sync.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/enduser_fields_sync.test.js +2 -0
- package/lib/esm/tests/api_tests/enduser_fields_sync.test.js.map +1 -1
- package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js +2 -0
- package/lib/esm/tests/api_tests/enduser_observations_auto_review.test.js.map +1 -1
- package/lib/esm/tests/api_tests/form_vital_reading.test.d.ts +7 -0
- package/lib/esm/tests/api_tests/form_vital_reading.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/form_vital_reading.test.js +456 -0
- package/lib/esm/tests/api_tests/form_vital_reading.test.js.map +1 -0
- package/lib/esm/tests/api_tests/journey_statistics_cache.test.d.ts +26 -0
- package/lib/esm/tests/api_tests/journey_statistics_cache.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/journey_statistics_cache.test.js +227 -0
- package/lib/esm/tests/api_tests/journey_statistics_cache.test.js.map +1 -0
- package/lib/esm/tests/api_tests/journey_step_manual_count.test.d.ts +34 -0
- package/lib/esm/tests/api_tests/journey_step_manual_count.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/journey_step_manual_count.test.js +238 -0
- package/lib/esm/tests/api_tests/journey_step_manual_count.test.js.map +1 -0
- package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/load_threads_care_team.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/load_threads_care_team.test.js +2 -0
- package/lib/esm/tests/api_tests/load_threads_care_team.test.js.map +1 -1
- package/lib/esm/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/mdi_case_offerings.test.js +139 -9
- package/lib/esm/tests/api_tests/mdi_case_offerings.test.js.map +1 -1
- package/lib/esm/tests/api_tests/organization_signup.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/organization_signup.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/organization_signup.test.js +2 -0
- package/lib/esm/tests/api_tests/organization_signup.test.js.map +1 -1
- package/lib/esm/tests/api_tests/phone_embed_leave_message.test.d.ts +7 -0
- package/lib/esm/tests/api_tests/phone_embed_leave_message.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/phone_embed_leave_message.test.js +566 -0
- package/lib/esm/tests/api_tests/phone_embed_leave_message.test.js.map +1 -0
- package/lib/esm/tests/api_tests/programs.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/programs.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/programs.test.js +72 -16
- package/lib/esm/tests/api_tests/programs.test.js.map +1 -1
- package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.d.ts +7 -0
- package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.js +728 -0
- package/lib/esm/tests/api_tests/public_form_case_insensitive_match.test.js.map +1 -0
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js +57 -47
- package/lib/esm/tests/api_tests/security/add-to-journey-foreign-enduserid.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js +2 -0
- package/lib/esm/tests/api_tests/security/appointment-reschedule-ownership.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.js +2 -0
- package/lib/esm/tests/api_tests/security/file-link-session-confinement.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js +2 -0
- package/lib/esm/tests/api_tests/security/form-logic-js-execution.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js +424 -39
- package/lib/esm/tests/api_tests/security/formsort_webhook_auth_field_lock.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/register_business_id_injection.test.js +2 -2
- package/lib/esm/tests/api_tests/security/register_business_id_injection.test.js.map +1 -1
- package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js +2 -0
- package/lib/esm/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.js.map +1 -1
- package/lib/esm/tests/api_tests/session_for_join_link.test.d.ts +10 -0
- package/lib/esm/tests/api_tests/session_for_join_link.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/session_for_join_link.test.js +142 -0
- package/lib/esm/tests/api_tests/session_for_join_link.test.js.map +1 -0
- package/lib/esm/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/set_fields_order_templates.test.js +191 -88
- package/lib/esm/tests/api_tests/set_fields_order_templates.test.js.map +1 -1
- package/lib/esm/tests/api_tests/stripe_checkout_items.test.d.ts +17 -0
- package/lib/esm/tests/api_tests/stripe_checkout_items.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/stripe_checkout_items.test.js +214 -0
- package/lib/esm/tests/api_tests/stripe_checkout_items.test.js.map +1 -0
- package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.js +388 -0
- package/lib/esm/tests/api_tests/stripe_multiple_customer_ids.test.js.map +1 -0
- package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js +2 -0
- package/lib/esm/tests/api_tests/tickets_bulk_assign_from_queue.test.js.map +1 -1
- package/lib/esm/tests/api_tests/totp_mfa.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/totp_mfa.test.js +37 -31
- package/lib/esm/tests/api_tests/totp_mfa.test.js.map +1 -1
- package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/trigger_contains_matching.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/trigger_contains_matching.test.js +2 -0
- package/lib/esm/tests/api_tests/trigger_contains_matching.test.js.map +1 -1
- package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js +2 -0
- package/lib/esm/tests/api_tests/user_logs_restore_deleted.test.js.map +1 -1
- package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts +3 -1
- package/lib/esm/tests/api_tests/vital_review_trigger_action.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js +2 -0
- package/lib/esm/tests/api_tests/vital_review_trigger_action.test.js.map +1 -1
- package/lib/esm/tests/api_tests/vitl_push.test.d.ts +7 -0
- package/lib/esm/tests/api_tests/vitl_push.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/vitl_push.test.js +333 -0
- package/lib/esm/tests/api_tests/vitl_push.test.js.map +1 -0
- package/lib/esm/tests/setup_isolated.d.ts +48 -0
- package/lib/esm/tests/setup_isolated.d.ts.map +1 -1
- package/lib/esm/tests/setup_isolated.js +114 -0
- package/lib/esm/tests/setup_isolated.js.map +1 -1
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +379 -283
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.d.ts +2 -0
- package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.d.ts.map +1 -0
- package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.js +174 -0
- package/lib/esm/tests/unit_tests/isolated_suite_discovery.test.js.map +1 -0
- package/lib/esm/tests/unit_tests/programs_conditional_logic.test.d.ts +3 -0
- package/lib/esm/tests/unit_tests/programs_conditional_logic.test.d.ts.map +1 -0
- package/lib/esm/tests/unit_tests/programs_conditional_logic.test.js +117 -0
- package/lib/esm/tests/unit_tests/programs_conditional_logic.test.js.map +1 -0
- package/lib/esm/tests/webhooks_tests.js +44 -34
- package/lib/esm/tests/webhooks_tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +12 -11
- package/src/sdk.ts +9 -2
- package/src/tests/api_tests/booking_prevent_upcoming_duplicate.test.ts +541 -0
- package/src/tests/api_tests/enduser_fields_sync.test.ts +154 -0
- package/src/tests/api_tests/enduser_observations_auto_review.test.ts +4 -0
- package/src/tests/api_tests/form_vital_reading.test.ts +393 -0
- package/src/tests/api_tests/journey_statistics_cache.test.ts +237 -0
- package/src/tests/api_tests/journey_step_manual_count.test.ts +220 -0
- package/src/tests/api_tests/load_threads_care_team.test.ts +214 -0
- package/src/tests/api_tests/mdi_case_offerings.test.ts +138 -0
- package/src/tests/api_tests/organization_signup.test.ts +4 -0
- package/src/tests/api_tests/phone_embed_leave_message.test.ts +541 -0
- package/src/tests/api_tests/programs.test.ts +351 -0
- package/src/tests/api_tests/public_form_case_insensitive_match.test.ts +632 -0
- package/src/tests/api_tests/security/add-to-journey-foreign-enduserid.test.ts +37 -26
- package/src/tests/api_tests/security/appointment-reschedule-ownership.test.ts +436 -0
- package/src/tests/api_tests/security/file-link-session-confinement.test.ts +4 -0
- package/src/tests/api_tests/security/form-logic-js-execution.test.ts +4 -0
- package/src/tests/api_tests/security/formsort_webhook_auth_field_lock.test.ts +225 -0
- package/src/tests/api_tests/security/register_business_id_injection.test.ts +2 -2
- package/src/tests/api_tests/security/rich-text-answer-sanitized-on-write.test.ts +4 -0
- package/src/tests/api_tests/session_for_join_link.test.ts +99 -0
- package/src/tests/api_tests/set_fields_order_templates.test.ts +94 -9
- package/src/tests/api_tests/stripe_checkout_items.test.ts +186 -0
- package/src/tests/api_tests/tickets_bulk_assign_from_queue.test.ts +4 -0
- package/src/tests/api_tests/totp_mfa.test.ts +10 -5
- package/src/tests/api_tests/trigger_contains_matching.test.ts +349 -0
- package/src/tests/api_tests/user_logs_restore_deleted.test.ts +419 -0
- package/src/tests/api_tests/vital_review_trigger_action.test.ts +455 -0
- package/src/tests/api_tests/vitl_push.test.ts +323 -0
- package/src/tests/setup_isolated.ts +140 -0
- package/src/tests/tests.ts +126 -20
- package/src/tests/unit_tests/isolated_suite_discovery.test.ts +178 -0
- package/src/tests/unit_tests/programs_conditional_logic.test.ts +189 -0
- package/src/tests/unit_tests/trigger_title_partials.test.ts +174 -0
- package/src/tests/webhooks_tests.ts +6 -1
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import * as fs from "fs"
|
|
4
|
+
import * as path from "path"
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
assert,
|
|
8
|
+
log_header,
|
|
9
|
+
} from "@tellescope/testing"
|
|
10
|
+
|
|
11
|
+
import {
|
|
12
|
+
discover_isolated_suites,
|
|
13
|
+
isolated_suites_root,
|
|
14
|
+
parse_test_parallelism,
|
|
15
|
+
DEFAULT_TEST_PARALLELISM,
|
|
16
|
+
ISOLATED_SUITE_EXPORT,
|
|
17
|
+
} from "../setup_isolated"
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Guard for the auto-discovering dispatcher (run_discovered_isolated_suites).
|
|
21
|
+
*
|
|
22
|
+
* Discovery walks the COMPILED tree (lib/cjs/tests/api_tests), so the failure mode the old
|
|
23
|
+
* hand-maintained arrays did not have is a suite that silently stops running: a `.test.ts` enrolls
|
|
24
|
+
* itself, the build no-ops, and the dispatcher never sees it. Nothing else in the suite would
|
|
25
|
+
* notice. So the first check here compares the enrolled SOURCE files against what discovery
|
|
26
|
+
* actually loaded, in both directions — a missing one means a stale build (`npm run check:build`),
|
|
27
|
+
* an extra one means a compiled orphan of a deleted test.
|
|
28
|
+
*
|
|
29
|
+
* The other two checks protect the dispatcher's own invariants: names are the only handle on a
|
|
30
|
+
* suite in the output, and a suite that is both discovered and awaited by name in tests.ts would
|
|
31
|
+
* provision its tenants and run twice.
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
// compiled location is <sdk>/lib/cjs/tests/unit_tests, so four levels up is the package root
|
|
35
|
+
const SRC_TESTS = path.resolve(__dirname, '..', '..', '..', '..', 'src', 'tests')
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Relative paths of every source file under `root` that enrolls itself, split by whether discovery
|
|
39
|
+
* could ever see it. Discovery only loads `*.test.js` under `api_tests`, so a marker anywhere else
|
|
40
|
+
* is a suite that silently never runs — hence `unreachable`, which is an error rather than a miss.
|
|
41
|
+
*/
|
|
42
|
+
const enrolled_source_files = (root: string) => {
|
|
43
|
+
const discoverable: string[] = []
|
|
44
|
+
const unreachable: string[] = []
|
|
45
|
+
|
|
46
|
+
const walk = (dir: string, prefix: string) => {
|
|
47
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
48
|
+
const relative = prefix ? `${prefix}/${entry.name}` : entry.name
|
|
49
|
+
|
|
50
|
+
if (entry.isDirectory()) { walk(path.join(dir, entry.name), relative); continue }
|
|
51
|
+
if (!entry.name.endsWith('.ts') || entry.name.endsWith('.d.ts')) continue
|
|
52
|
+
|
|
53
|
+
const source = fs.readFileSync(path.join(dir, entry.name), 'utf8')
|
|
54
|
+
if (!new RegExp(`^export const ${ISOLATED_SUITE_EXPORT}\\b`, 'm').test(source)) continue
|
|
55
|
+
|
|
56
|
+
const inApiTests = relative === 'api_tests' || relative.indexOf('api_tests/') === 0
|
|
57
|
+
if (inApiTests && entry.name.endsWith('.test.ts')) {
|
|
58
|
+
discoverable.push(relative.replace(/^api_tests\//, ''))
|
|
59
|
+
} else {
|
|
60
|
+
unreachable.push(relative)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
walk(root, '')
|
|
65
|
+
|
|
66
|
+
return { discoverable: discoverable.sort(), unreachable: unreachable.sort() }
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export const isolated_suite_discovery_unit_tests = async () => {
|
|
70
|
+
log_header("Isolated suite discovery")
|
|
71
|
+
|
|
72
|
+
const discovered = discover_isolated_suites() // throws on a malformed descriptor or a duplicate name
|
|
73
|
+
const discoveredFiles = discovered.map(s => s.file).sort()
|
|
74
|
+
|
|
75
|
+
if (!fs.existsSync(SRC_TESTS)) {
|
|
76
|
+
// Published package, not the monorepo — there is no source tree to compare against.
|
|
77
|
+
console.log(` SKIPPED source comparison: no source tree at ${SRC_TESTS}`)
|
|
78
|
+
} else {
|
|
79
|
+
const { discoverable, unreachable } = enrolled_source_files(SRC_TESTS)
|
|
80
|
+
const enrolled = discoverable.map(f => f.replace(/\.ts$/, '.js'))
|
|
81
|
+
|
|
82
|
+
// Scanning all of src/tests, not just api_tests: an ISOLATED_SUITE outside the discovered tree
|
|
83
|
+
// (wrong directory, or a filename that is not *.test.ts) would never run and nothing else would say so.
|
|
84
|
+
assert(
|
|
85
|
+
unreachable.length === 0,
|
|
86
|
+
`exports ${ISOLATED_SUITE_EXPORT} where discovery can never see it (must be api_tests/**/*.test.ts): ${unreachable.join(', ')}`,
|
|
87
|
+
`every ${ISOLATED_SUITE_EXPORT} in src/tests lives where discovery looks`,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
const missing = enrolled.filter(f => discoveredFiles.indexOf(f) === -1)
|
|
91
|
+
const extra = discoveredFiles.filter(f => enrolled.indexOf(f) === -1)
|
|
92
|
+
|
|
93
|
+
assert(
|
|
94
|
+
missing.length === 0,
|
|
95
|
+
`enrolled in src but NOT discovered (stale lib/cjs? run npm run check:build -- --fix): ${missing.join(', ')}`,
|
|
96
|
+
`every src file exporting ${ISOLATED_SUITE_EXPORT} is discovered (${enrolled.length})`,
|
|
97
|
+
)
|
|
98
|
+
assert(
|
|
99
|
+
extra.length === 0,
|
|
100
|
+
`discovered but not enrolled in any src file (compiled orphan? delete lib/cjs and rebuild): ${extra.join(', ')}`,
|
|
101
|
+
'every discovered suite has a source file',
|
|
102
|
+
)
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
assert(discovered.length > 0, `no isolated suites discovered under ${isolated_suites_root()}`, 'suites were discovered')
|
|
106
|
+
|
|
107
|
+
// Names are the only handle on a suite in the run output and in a failure message.
|
|
108
|
+
const byName: Record<string, string[]> = {}
|
|
109
|
+
for (const s of discovered) {
|
|
110
|
+
byName[s.name] = (byName[s.name] || []).concat(s.file)
|
|
111
|
+
}
|
|
112
|
+
const duplicateNames = Object.keys(byName).filter(n => byName[n].length > 1)
|
|
113
|
+
assert(
|
|
114
|
+
duplicateNames.length === 0,
|
|
115
|
+
`duplicate suite names: ${duplicateNames.map(n => `${JSON.stringify(n)} in ${byName[n].join(' and ')}`).join('; ')}`,
|
|
116
|
+
`suite names are unique (${discovered.length})`,
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
// A discovered suite must not ALSO be awaited by name in tests.ts — that would run it twice, each
|
|
120
|
+
// run provisioning its own tenants (which can never be deleted).
|
|
121
|
+
const testsPath = path.join(SRC_TESTS, 'tests.ts')
|
|
122
|
+
if (!fs.existsSync(testsPath)) {
|
|
123
|
+
console.log(` SKIPPED tests.ts comparison: not found at ${testsPath}`)
|
|
124
|
+
} else {
|
|
125
|
+
const testsSource = fs.readFileSync(testsPath, 'utf8')
|
|
126
|
+
|
|
127
|
+
const doubleRun: string[] = []
|
|
128
|
+
for (const suite of discovered) {
|
|
129
|
+
const loaded = require(path.join(isolated_suites_root(), suite.file))
|
|
130
|
+
for (const exported of Object.keys(loaded)) {
|
|
131
|
+
if (exported === ISOLATED_SUITE_EXPORT) continue // every enrolled file exports this one
|
|
132
|
+
if (new RegExp(`\\bawait\\s+${exported}\\s*\\(`).test(testsSource)) {
|
|
133
|
+
doubleRun.push(`${exported} (api_tests/${suite.file}, discovered as "${suite.name}")`)
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
assert(
|
|
138
|
+
doubleRun.length === 0,
|
|
139
|
+
`awaited directly in tests.ts as well as discovered, so it would run twice: ${doubleRun.join(', ')}`,
|
|
140
|
+
'no discovered suite is also awaited directly in tests.ts',
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// TEST_PARALLELISM parsing: a bad value must fall back to the default, never to "unlimited".
|
|
145
|
+
const parallelismCases: [string | undefined, number][] = [
|
|
146
|
+
[undefined, DEFAULT_TEST_PARALLELISM],
|
|
147
|
+
['', DEFAULT_TEST_PARALLELISM],
|
|
148
|
+
[' ', DEFAULT_TEST_PARALLELISM],
|
|
149
|
+
['abc', DEFAULT_TEST_PARALLELISM],
|
|
150
|
+
['0', DEFAULT_TEST_PARALLELISM],
|
|
151
|
+
['-1', DEFAULT_TEST_PARALLELISM],
|
|
152
|
+
['2.5', DEFAULT_TEST_PARALLELISM],
|
|
153
|
+
['1', 1],
|
|
154
|
+
[' 3 ', 3],
|
|
155
|
+
['16', 16],
|
|
156
|
+
]
|
|
157
|
+
for (const [raw, expected] of parallelismCases) {
|
|
158
|
+
const actual = parse_test_parallelism(raw)
|
|
159
|
+
assert(
|
|
160
|
+
actual === expected,
|
|
161
|
+
`TEST_PARALLELISM=${JSON.stringify(raw)} parsed as ${actual}, expected ${expected}`,
|
|
162
|
+
`TEST_PARALLELISM=${JSON.stringify(raw)} → ${expected}`,
|
|
163
|
+
)
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// Allow running this test file independently
|
|
168
|
+
if (require.main === module) {
|
|
169
|
+
isolated_suite_discovery_unit_tests()
|
|
170
|
+
.then(() => {
|
|
171
|
+
console.log("Isolated suite discovery unit tests completed successfully")
|
|
172
|
+
process.exit(0)
|
|
173
|
+
})
|
|
174
|
+
.catch((error) => {
|
|
175
|
+
console.error("Isolated suite discovery unit tests failed:", error)
|
|
176
|
+
process.exit(1)
|
|
177
|
+
})
|
|
178
|
+
}
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
import { evaluate_conditional_logic_for_enduser_fields, validate_custom_field_references } from "@tellescope/utilities"
|
|
2
|
+
import { Enduser, EnduserProgram } from "@tellescope/types-models"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Unit coverage for the `Programs` key in evaluate_conditional_logic_for_enduser_fields — the V1
|
|
6
|
+
* evaluator that triggers, journeys and phone-tree branches all run on.
|
|
7
|
+
*
|
|
8
|
+
* `{ Programs: { $in: 'CHF' } }` means "has a program named CHF"; adding `$status` means "...whose
|
|
9
|
+
* status is S", bound to that same program entry. Comparison is exact string throughout, matching the
|
|
10
|
+
* shipped Order Status Equals / Vital Update program filters.
|
|
11
|
+
*
|
|
12
|
+
* The round-trip between this and the saved V2 Mongo filter is covered separately, by
|
|
13
|
+
* packages/private/webapp/src/__tests__/programs_condition_filter.test.ts.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
let failures = 0
|
|
17
|
+
let passed = 0
|
|
18
|
+
|
|
19
|
+
const assert = (name: string, actual: boolean, expected: boolean) => {
|
|
20
|
+
if (actual === expected) {
|
|
21
|
+
passed++
|
|
22
|
+
console.log(` ✓ ${name}`)
|
|
23
|
+
} else {
|
|
24
|
+
failures++
|
|
25
|
+
console.error(` ✗ ${name} — expected ${expected}, got ${actual}`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const with_programs = (programs?: EnduserProgram[]) => ({ programs } as Omit<Enduser, 'id'>)
|
|
30
|
+
|
|
31
|
+
const matches = (programs: EnduserProgram[] | undefined, value: Record<string, any>) => (
|
|
32
|
+
evaluate_conditional_logic_for_enduser_fields(
|
|
33
|
+
with_programs(programs) as any,
|
|
34
|
+
{ $and: [{ condition: { Programs: value } }] },
|
|
35
|
+
)
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
const CHF_ACTIVE: EnduserProgram[] = [{ name: 'CHF', status: 'active' }]
|
|
39
|
+
const CHF_COMPLETED: EnduserProgram[] = [{ name: 'CHF', status: 'completed' }]
|
|
40
|
+
const CHF_NO_STATUS: EnduserProgram[] = [{ name: 'CHF' }]
|
|
41
|
+
const TWO: EnduserProgram[] = [{ name: 'CHF', status: 'completed' }, { name: 'Diabetes', status: 'active' }]
|
|
42
|
+
|
|
43
|
+
const run_is_in_tests = () => {
|
|
44
|
+
console.log("\n[Programs / Is In]")
|
|
45
|
+
|
|
46
|
+
assert("matches a patient with that program", matches(CHF_ACTIVE, { $in: 'CHF' }), true)
|
|
47
|
+
assert("does not match a patient with a different program", matches([{ name: 'Diabetes' }], { $in: 'CHF' }), false)
|
|
48
|
+
assert("does not match a patient with no programs", matches([], { $in: 'CHF' }), false)
|
|
49
|
+
assert("does not match a patient whose programs are unset", matches(undefined, { $in: 'CHF' }), false)
|
|
50
|
+
assert("matches one program among several", matches(TWO, { $in: 'Diabetes' }), true)
|
|
51
|
+
|
|
52
|
+
// exact string comparison — deliberately NOT case-insensitive
|
|
53
|
+
assert("name comparison is case sensitive", matches(CHF_ACTIVE, { $in: 'chf' }), false)
|
|
54
|
+
assert("name comparison does not substring match", matches(CHF_ACTIVE, { $in: 'CH' }), false)
|
|
55
|
+
assert("a name with spaces and punctuation matches exactly", matches([{ name: 'Post-Op Care' }], { $in: 'Post-Op Care' }), true)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const run_status_tests = () => {
|
|
59
|
+
console.log("\n[Programs / $status]")
|
|
60
|
+
|
|
61
|
+
assert("name + status both match", matches(CHF_ACTIVE, { $in: 'CHF', $status: 'active' }), true)
|
|
62
|
+
assert("name matches but status does not", matches(CHF_COMPLETED, { $in: 'CHF', $status: 'active' }), false)
|
|
63
|
+
assert("status matches but name does not", matches(CHF_ACTIVE, { $in: 'Diabetes', $status: 'active' }), false)
|
|
64
|
+
assert("a program with no status is not matched by a status condition", matches(CHF_NO_STATUS, { $in: 'CHF', $status: 'active' }), false)
|
|
65
|
+
assert("a blank status means any status", matches(CHF_COMPLETED, { $in: 'CHF', $status: '' }), true)
|
|
66
|
+
assert("an omitted status means any status", matches(CHF_NO_STATUS, { $in: 'CHF' }), true)
|
|
67
|
+
assert("status comparison is case sensitive", matches(CHF_ACTIVE, { $in: 'CHF', $status: 'Active' }), false)
|
|
68
|
+
|
|
69
|
+
// name and status must come from the SAME program, which is the whole point of one paired key
|
|
70
|
+
assert(
|
|
71
|
+
"does not match when the name and the status come from different programs",
|
|
72
|
+
matches(TWO, { $in: 'CHF', $status: 'active' }),
|
|
73
|
+
false,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
// $status serialised FIRST — the generic Object.keys(value)[0] operator dispatch would pick $status
|
|
77
|
+
// here and fall through to false, so the Programs branch must read $in/$nin by name
|
|
78
|
+
assert(
|
|
79
|
+
"$status before $in still evaluates as Is In",
|
|
80
|
+
matches(CHF_ACTIVE, { $status: 'active', $in: 'CHF' }),
|
|
81
|
+
true,
|
|
82
|
+
)
|
|
83
|
+
assert(
|
|
84
|
+
"$status before $nin still evaluates as Is Not In",
|
|
85
|
+
matches(CHF_ACTIVE, { $status: 'active', $nin: 'CHF' }),
|
|
86
|
+
false,
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const run_is_not_in_tests = () => {
|
|
91
|
+
console.log("\n[Programs / Is Not In]")
|
|
92
|
+
|
|
93
|
+
assert("does not match a patient with that program", matches(CHF_ACTIVE, { $nin: 'CHF' }), false)
|
|
94
|
+
assert("matches a patient with a different program", matches([{ name: 'Diabetes' }], { $nin: 'CHF' }), true)
|
|
95
|
+
assert("matches a patient with no programs", matches([], { $nin: 'CHF' }), true)
|
|
96
|
+
assert("matches a patient whose programs are unset", matches(undefined, { $nin: 'CHF' }), true)
|
|
97
|
+
|
|
98
|
+
assert("negates the name+status pair", matches(CHF_ACTIVE, { $nin: 'CHF', $status: 'active' }), false)
|
|
99
|
+
assert("matches when the name is present but the status differs", matches(CHF_COMPLETED, { $nin: 'CHF', $status: 'active' }), true)
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const run_unconfigured_tests = () => {
|
|
103
|
+
console.log("\n[Programs / unconfigured]")
|
|
104
|
+
|
|
105
|
+
// An unfinished condition must match nobody in BOTH directions. This deliberately DIVERGES from
|
|
106
|
+
// Journeys rather than copying it: `{ Journeys: { $nin: '' } }` matches everybody (the blank id is
|
|
107
|
+
// simply absent from enduser.journeys). Failing open on $nin would fire a trigger for every patient.
|
|
108
|
+
assert("a blank name matches nobody under Is In", matches(CHF_ACTIVE, { $in: '' }), false)
|
|
109
|
+
assert("a blank name matches nobody under Is Not In", matches(CHF_ACTIVE, { $nin: '' }), false)
|
|
110
|
+
assert("a blank name matches nobody even with no programs", matches([], { $nin: '' }), false)
|
|
111
|
+
assert("a value with no operator matches nobody", matches(CHF_ACTIVE, { $status: 'active' }), false)
|
|
112
|
+
|
|
113
|
+
// the branch is narrowed to $in/$nin, so any other object condition under a key named 'Programs'
|
|
114
|
+
// falls through to the generic custom-field handler — which is what the V2 translator does too
|
|
115
|
+
assert("a non-$in/$nin operator falls through instead of being swallowed", matches(CHF_ACTIVE, { $isSet: 'Value' }), false)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const run_other_conditions_unaffected = () => {
|
|
119
|
+
console.log("\n[Programs / coexistence with Care Type and Stage]")
|
|
120
|
+
|
|
121
|
+
const enduser = {
|
|
122
|
+
programs: CHF_ACTIVE,
|
|
123
|
+
fields: { 'Care Type': 'RPM', 'Care Stage': 'Onboarding' },
|
|
124
|
+
} as any
|
|
125
|
+
|
|
126
|
+
const evaluate = (conditions: Record<string, any>) => (
|
|
127
|
+
evaluate_conditional_logic_for_enduser_fields(enduser, conditions)
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
assert(
|
|
131
|
+
"an existing Care Type condition is unaffected",
|
|
132
|
+
evaluate({ $and: [{ condition: { 'Care Type': 'RPM' } }] }),
|
|
133
|
+
true,
|
|
134
|
+
)
|
|
135
|
+
assert(
|
|
136
|
+
"Care Stage and Programs can be combined in one filter",
|
|
137
|
+
evaluate({ $and: [{ condition: { 'Care Stage': 'Onboarding' } }, { condition: { Programs: { $in: 'CHF' } } }] }),
|
|
138
|
+
true,
|
|
139
|
+
)
|
|
140
|
+
assert(
|
|
141
|
+
"the combined filter fails when the Programs half fails",
|
|
142
|
+
evaluate({ $and: [{ condition: { 'Care Stage': 'Onboarding' } }, { condition: { Programs: { $in: 'Diabetes' } } }] }),
|
|
143
|
+
false,
|
|
144
|
+
)
|
|
145
|
+
assert(
|
|
146
|
+
"an $or over Care Type and Programs matches on either half",
|
|
147
|
+
evaluate({ $or: [{ condition: { 'Care Type': 'Nope' } }, { condition: { Programs: { $in: 'CHF' } } }] }),
|
|
148
|
+
true,
|
|
149
|
+
)
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const run_custom_field_validation_tests = () => {
|
|
153
|
+
console.log("\n[Programs is a built-in field]")
|
|
154
|
+
|
|
155
|
+
// without 'Programs' in BUILT_IN_FIELDS, the trigger and journey validation hooks flag every program
|
|
156
|
+
// condition as a reference to a custom field that no longer exists
|
|
157
|
+
const conditions = { $and: [{ condition: { Programs: { $in: 'CHF', $status: 'active' } } }] }
|
|
158
|
+
assert(
|
|
159
|
+
"a Programs condition is not reported as a missing custom field",
|
|
160
|
+
validate_custom_field_references(conditions, new Set<string>()).length === 0,
|
|
161
|
+
true,
|
|
162
|
+
)
|
|
163
|
+
assert(
|
|
164
|
+
"a genuinely missing custom field is still reported",
|
|
165
|
+
validate_custom_field_references(
|
|
166
|
+
{ $and: [{ condition: { deletedField: 'x' } }] },
|
|
167
|
+
new Set<string>(),
|
|
168
|
+
).includes('deletedField'),
|
|
169
|
+
true,
|
|
170
|
+
)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const run_all = () => {
|
|
174
|
+
console.log("Running programs_conditional_logic unit tests")
|
|
175
|
+
run_is_in_tests()
|
|
176
|
+
run_status_tests()
|
|
177
|
+
run_is_not_in_tests()
|
|
178
|
+
run_unconfigured_tests()
|
|
179
|
+
run_other_conditions_unaffected()
|
|
180
|
+
run_custom_field_validation_tests()
|
|
181
|
+
console.log(`\nResults: ${passed} passed, ${failures} failed`)
|
|
182
|
+
if (failures > 0) process.exit(1)
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (require.main === module) {
|
|
186
|
+
run_all()
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
export { run_all as programs_conditional_logic_unit_tests }
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { title_matches_partials } from "@tellescope/utilities"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Unit coverage for the shared "contains" matcher behind the Problem Created / Order Created title
|
|
5
|
+
* filters. The backend call sites (enduser_problems.ts, enduser_orders.ts, handle_order_created)
|
|
6
|
+
* all delegate here, so the semantics are pinned in one place:
|
|
7
|
+
*
|
|
8
|
+
* - each list is OR internally, the two lists AND together
|
|
9
|
+
* - the case-sensitive list is `titlePartials`, the case-insensitive one `titlePartialsIgnoreCase`
|
|
10
|
+
* - entries are trimmed, and blank-after-trim entries are dropped rather than matching everything
|
|
11
|
+
* - no list set (or every entry blank) means "leave blank for all"
|
|
12
|
+
* - a missing/empty title never satisfies a non-empty list
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
let failures = 0
|
|
16
|
+
let passed = 0
|
|
17
|
+
|
|
18
|
+
const assert = (name: string, actual: boolean, expected: boolean) => {
|
|
19
|
+
if (actual === expected) {
|
|
20
|
+
passed++
|
|
21
|
+
console.log(` ✓ ${name}`)
|
|
22
|
+
} else {
|
|
23
|
+
failures++
|
|
24
|
+
console.error(` ✗ ${name} — expected ${expected}, got ${actual}`)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const run_unset_tests = () => {
|
|
29
|
+
console.log("\n[title_matches_partials — unset lists mean no filter]")
|
|
30
|
+
|
|
31
|
+
assert("both lists undefined", title_matches_partials('Endometriosis, unspecified'), true)
|
|
32
|
+
assert("both lists empty arrays", title_matches_partials('Endometriosis', [], []), true)
|
|
33
|
+
assert("undefined title with no lists set", title_matches_partials(undefined), true)
|
|
34
|
+
assert("empty title with no lists set", title_matches_partials(''), true)
|
|
35
|
+
assert("null title with no lists set", title_matches_partials(null), true)
|
|
36
|
+
assert("all-blank case-sensitive list counts as unset", title_matches_partials('Common cold', [' ']), true)
|
|
37
|
+
assert("all-blank case-insensitive list counts as unset", title_matches_partials('Common cold', [], ['', ' ']), true)
|
|
38
|
+
assert("all-blank lists on an empty title", title_matches_partials('', [' '], ['\t']), true)
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const run_case_sensitive_tests = () => {
|
|
42
|
+
console.log("\n[title_matches_partials — case sensitive list]")
|
|
43
|
+
|
|
44
|
+
assert("substring match", title_matches_partials('Endometriosis, unspecified', ['Endometriosis']), true)
|
|
45
|
+
assert("prefix fragment match", title_matches_partials('Endometriosis, unspecified', ['Endo']), true)
|
|
46
|
+
assert("mid-string fragment match", title_matches_partials('Chronic Endometriosis pain', ['metrios']), true)
|
|
47
|
+
assert("case mismatch does not match", title_matches_partials('Chronic endometriosis pain', ['Endometriosis']), false)
|
|
48
|
+
assert("uppercase entry does not match lowercase title", title_matches_partials('endometriosis', ['ENDO']), false)
|
|
49
|
+
assert("exact-equal title matches", title_matches_partials('Endometriosis', ['Endometriosis']), true)
|
|
50
|
+
assert("unrelated title does not match", title_matches_partials('Common cold', ['Endo']), false)
|
|
51
|
+
|
|
52
|
+
// OR within the list
|
|
53
|
+
assert("OR within list — second entry matches", title_matches_partials('Type 2 Diabetes', ['Endo', 'Diabetes']), true)
|
|
54
|
+
assert("OR within list — no entry matches", title_matches_partials('Common cold', ['Endo', 'Diabetes']), false)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const run_case_insensitive_tests = () => {
|
|
58
|
+
console.log("\n[title_matches_partials — case insensitive list]")
|
|
59
|
+
|
|
60
|
+
assert("lowercase entry matches capitalized title", title_matches_partials('Semaglutide 0.25mg', [], ['semaglutide']), true)
|
|
61
|
+
assert("lowercase entry matches uppercase title", title_matches_partials('SEMAGLUTIDE injection', [], ['semaglutide']), true)
|
|
62
|
+
assert("uppercase entry matches lowercase title", title_matches_partials('semaglutide injection', [], ['SEMAGLUTIDE']), true)
|
|
63
|
+
assert("mixed-case entry matches", title_matches_partials('Chronic endometriosis pain', [], ['EnDoMeTrIoSiS']), true)
|
|
64
|
+
assert("unrelated title does not match", title_matches_partials('Tirzepatide 5mg', [], ['semaglutide']), false)
|
|
65
|
+
assert("OR within list — second entry matches", title_matches_partials('Tirzepatide 5mg', [], ['sema', 'TIRZ']), true)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const run_trimming_tests = () => {
|
|
69
|
+
console.log("\n[title_matches_partials — trimming and blank entries]")
|
|
70
|
+
|
|
71
|
+
assert("padded entry is trimmed before matching", title_matches_partials('Endometriosis, unspecified', [' Endo ']), true)
|
|
72
|
+
assert("padded case-insensitive entry is trimmed", title_matches_partials('SEMAGLUTIDE injection', [], [' semaglutide ']), true)
|
|
73
|
+
assert(
|
|
74
|
+
"blank sibling entry is ignored, real entry still matches",
|
|
75
|
+
title_matches_partials('Endometriosis', ['Endo', ' ']),
|
|
76
|
+
true,
|
|
77
|
+
)
|
|
78
|
+
assert(
|
|
79
|
+
"blank sibling entry does not make a non-matching title match",
|
|
80
|
+
title_matches_partials('Common cold', ['Endo', ' ']),
|
|
81
|
+
false,
|
|
82
|
+
)
|
|
83
|
+
assert(
|
|
84
|
+
"a list of only blanks counts as unset rather than as a match-everything entry",
|
|
85
|
+
title_matches_partials('Common cold', ['', ' ', '\t']),
|
|
86
|
+
true, // unset — and per the next check it cannot rescue a failing sibling list either
|
|
87
|
+
)
|
|
88
|
+
assert(
|
|
89
|
+
"blank case-sensitive list does not rescue a failing case-insensitive list",
|
|
90
|
+
title_matches_partials('Common cold', [' '], ['semaglutide']),
|
|
91
|
+
false,
|
|
92
|
+
)
|
|
93
|
+
assert(
|
|
94
|
+
"whitespace-only title against a real entry",
|
|
95
|
+
title_matches_partials(' ', ['Endo']),
|
|
96
|
+
false,
|
|
97
|
+
)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const run_both_lists_tests = () => {
|
|
101
|
+
console.log("\n[title_matches_partials — the two lists AND together]")
|
|
102
|
+
|
|
103
|
+
assert(
|
|
104
|
+
"both lists match",
|
|
105
|
+
title_matches_partials('Endometriosis, unspecified', ['Endo'], ['unspecified']),
|
|
106
|
+
true,
|
|
107
|
+
)
|
|
108
|
+
assert(
|
|
109
|
+
"case-sensitive list matches, case-insensitive does not",
|
|
110
|
+
title_matches_partials('Endometriosis, unspecified', ['Endo'], ['diabetes']),
|
|
111
|
+
false,
|
|
112
|
+
)
|
|
113
|
+
assert(
|
|
114
|
+
"case-insensitive list matches, case-sensitive does not",
|
|
115
|
+
title_matches_partials('Endometriosis, unspecified', ['ENDO'], ['endometriosis']),
|
|
116
|
+
false,
|
|
117
|
+
)
|
|
118
|
+
assert(
|
|
119
|
+
"neither list matches",
|
|
120
|
+
title_matches_partials('Common cold', ['Endo'], ['semaglutide']),
|
|
121
|
+
false,
|
|
122
|
+
)
|
|
123
|
+
assert(
|
|
124
|
+
"same fragment in both lists, case differing — case-sensitive one decides",
|
|
125
|
+
title_matches_partials('chronic endometriosis', ['Endometriosis'], ['endometriosis']),
|
|
126
|
+
false,
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const run_missing_title_tests = () => {
|
|
131
|
+
console.log("\n[title_matches_partials — absent or empty title with a filter set]")
|
|
132
|
+
|
|
133
|
+
assert("undefined title vs case-sensitive list", title_matches_partials(undefined, ['Endo']), false)
|
|
134
|
+
assert("undefined title vs case-insensitive list", title_matches_partials(undefined, [], ['endo']), false)
|
|
135
|
+
assert("null title vs a set list", title_matches_partials(null, ['Endo']), false)
|
|
136
|
+
assert("empty-string title vs a set list", title_matches_partials('', ['Endo']), false)
|
|
137
|
+
assert("empty-string title vs both lists set", title_matches_partials('', ['Endo'], ['endo']), false)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
const run_literal_matching_tests = () => {
|
|
141
|
+
console.log("\n[title_matches_partials — entries are literal, never regex]")
|
|
142
|
+
|
|
143
|
+
assert("hyphenated fragment matches literally", title_matches_partials('Diabetes Type-2', ['Type-2']), true)
|
|
144
|
+
assert("dosage fragment matches literally", title_matches_partials('Semaglutide 10mg', ['10mg']), true)
|
|
145
|
+
assert("dot is not a wildcard", title_matches_partials('Semaglutide 0x25mg', ['0.25']), false)
|
|
146
|
+
assert("dot matches itself", title_matches_partials('Semaglutide 0.25mg', ['0.25']), true)
|
|
147
|
+
assert("regex metacharacters are literal", title_matches_partials('Order (urgent)', ['(urgent)']), true)
|
|
148
|
+
assert("regex alternation is literal, not alternation", title_matches_partials('Endometriosis', ['Endo|Diabetes']), false)
|
|
149
|
+
assert("star is literal", title_matches_partials('a*b', ['a*b']), true)
|
|
150
|
+
assert("very long entry simply does not match", title_matches_partials('Endometriosis', ['E'.repeat(250)]), false)
|
|
151
|
+
|
|
152
|
+
// non-string entries from bad stored data must not throw
|
|
153
|
+
assert("non-string entries are ignored, not crashed on", title_matches_partials('Endometriosis', [null as any, 'Endo']), true)
|
|
154
|
+
assert("only non-string entries counts as unset", title_matches_partials('Common cold', [undefined as any]), true)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const run_all = () => {
|
|
158
|
+
console.log("Running trigger_title_partials unit tests")
|
|
159
|
+
run_unset_tests()
|
|
160
|
+
run_case_sensitive_tests()
|
|
161
|
+
run_case_insensitive_tests()
|
|
162
|
+
run_trimming_tests()
|
|
163
|
+
run_both_lists_tests()
|
|
164
|
+
run_missing_title_tests()
|
|
165
|
+
run_literal_matching_tests()
|
|
166
|
+
console.log(`\nResults: ${passed} passed, ${failures} failed`)
|
|
167
|
+
if (failures > 0) process.exit(1)
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (require.main === module) {
|
|
171
|
+
run_all()
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export { run_all as trigger_title_partials_unit_tests }
|
|
@@ -756,8 +756,13 @@ const run_tests = async () => {
|
|
|
756
756
|
log_header("Webhooks Tests")
|
|
757
757
|
await sdk.authenticate(email, password)
|
|
758
758
|
await sdkSub.authenticate(subUserEmail, password)
|
|
759
|
-
await reset_test_tenant(sdk)
|
|
759
|
+
await reset_test_tenant(sdk)
|
|
760
760
|
await nonAdminSdk.authenticate(nonAdminEmail, nonAdminPassword)
|
|
761
|
+
// as in setup_tests: booking cancel/reschedule below would otherwise email the host for real
|
|
762
|
+
await Promise.all([
|
|
763
|
+
sdk.api.users.updateOne(sdk.userInfo.id, { notificationEmailsDisabled: true }),
|
|
764
|
+
sdk.api.users.updateOne(nonAdminSdk.userInfo.id, { notificationEmailsDisabled: true }),
|
|
765
|
+
]).catch(console.error)
|
|
761
766
|
|
|
762
767
|
await async_test(
|
|
763
768
|
'configure webhook is admin only',
|
package/test_generated.pdf
CHANGED
|
Binary file
|