@tellescope/sdk 1.253.1 → 1.254.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/lib/cjs/sdk.d.ts +4 -0
  2. package/lib/cjs/sdk.d.ts.map +1 -1
  3. package/lib/cjs/tests/api_tests/_tmp_bc_verify.test.d.ts.map +1 -0
  4. package/lib/cjs/tests/api_tests/_tmp_blank_status_verify.test.d.ts.map +1 -0
  5. package/lib/cjs/tests/api_tests/_tmp_followup_verify.test.d.ts.map +1 -0
  6. package/lib/cjs/tests/api_tests/_tmp_nonmatch_verify.test.d.ts.map +1 -0
  7. package/lib/cjs/tests/api_tests/ai_decision_summary_config.test.d.ts +16 -0
  8. package/lib/cjs/tests/api_tests/ai_decision_summary_config.test.d.ts.map +1 -0
  9. package/lib/cjs/tests/api_tests/ai_decision_summary_config.test.js +187 -0
  10. package/lib/cjs/tests/api_tests/ai_decision_summary_config.test.js.map +1 -0
  11. package/lib/cjs/tests/api_tests/field_redaction.test.d.ts.map +1 -1
  12. package/lib/cjs/tests/api_tests/field_redaction.test.js +112 -99
  13. package/lib/cjs/tests/api_tests/field_redaction.test.js.map +1 -1
  14. package/lib/cjs/tests/api_tests/journey_error_branching.test.js +7 -2
  15. package/lib/cjs/tests/api_tests/journey_error_branching.test.js.map +1 -1
  16. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.d.ts +6 -0
  17. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -0
  18. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js +443 -0
  19. package/lib/cjs/tests/api_tests/mdi_case_offerings.test.js.map +1 -0
  20. package/lib/cjs/tests/api_tests/resource_access_tags.test.d.ts +22 -0
  21. package/lib/cjs/tests/api_tests/resource_access_tags.test.d.ts.map +1 -0
  22. package/lib/cjs/tests/api_tests/resource_access_tags.test.js +488 -0
  23. package/lib/cjs/tests/api_tests/resource_access_tags.test.js.map +1 -0
  24. package/lib/cjs/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.d.ts +21 -0
  25. package/lib/cjs/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.d.ts.map +1 -0
  26. package/lib/cjs/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.js +362 -0
  27. package/lib/cjs/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.js.map +1 -0
  28. package/lib/cjs/tests/api_tests/security/F-0145-globalunique-leak.test.d.ts +20 -0
  29. package/lib/cjs/tests/api_tests/security/F-0145-globalunique-leak.test.d.ts.map +1 -0
  30. package/lib/cjs/tests/api_tests/security/F-0145-globalunique-leak.test.js +139 -0
  31. package/lib/cjs/tests/api_tests/security/F-0145-globalunique-leak.test.js.map +1 -0
  32. package/lib/cjs/tests/api_tests/security/F-0151-load-inbox-redaction.test.d.ts +18 -0
  33. package/lib/cjs/tests/api_tests/security/F-0151-load-inbox-redaction.test.d.ts.map +1 -0
  34. package/lib/cjs/tests/api_tests/security/F-0151-load-inbox-redaction.test.js +156 -0
  35. package/lib/cjs/tests/api_tests/security/F-0151-load-inbox-redaction.test.js.map +1 -0
  36. package/lib/cjs/tests/api_tests/security/F-0155-webhook-timeout.test.d.ts +20 -0
  37. package/lib/cjs/tests/api_tests/security/F-0155-webhook-timeout.test.d.ts.map +1 -0
  38. package/lib/cjs/tests/api_tests/security/F-0155-webhook-timeout.test.js +157 -0
  39. package/lib/cjs/tests/api_tests/security/F-0155-webhook-timeout.test.js.map +1 -0
  40. package/lib/cjs/tests/api_tests/time_tracks.test.d.ts +8 -0
  41. package/lib/cjs/tests/api_tests/time_tracks.test.d.ts.map +1 -1
  42. package/lib/cjs/tests/api_tests/time_tracks.test.js +638 -1
  43. package/lib/cjs/tests/api_tests/time_tracks.test.js.map +1 -1
  44. package/lib/cjs/tests/setup.d.ts.map +1 -1
  45. package/lib/cjs/tests/setup.js +20 -1
  46. package/lib/cjs/tests/setup.js.map +1 -1
  47. package/lib/cjs/tests/tests.d.ts.map +1 -1
  48. package/lib/cjs/tests/tests.js +606 -178
  49. package/lib/cjs/tests/tests.js.map +1 -1
  50. package/lib/esm/sdk.d.ts +6 -2
  51. package/lib/esm/sdk.d.ts.map +1 -1
  52. package/lib/esm/tests/api_tests/_tmp_bc_verify.test.d.ts +2 -0
  53. package/lib/esm/tests/api_tests/_tmp_bc_verify.test.d.ts.map +1 -0
  54. package/lib/esm/tests/api_tests/_tmp_bc_verify.test.js +120 -0
  55. package/lib/esm/tests/api_tests/_tmp_bc_verify.test.js.map +1 -0
  56. package/lib/esm/tests/api_tests/_tmp_blank_status_verify.test.d.ts +2 -0
  57. package/lib/esm/tests/api_tests/_tmp_blank_status_verify.test.d.ts.map +1 -0
  58. package/lib/esm/tests/api_tests/_tmp_blank_status_verify.test.js +200 -0
  59. package/lib/esm/tests/api_tests/_tmp_blank_status_verify.test.js.map +1 -0
  60. package/lib/esm/tests/api_tests/_tmp_followup_verify.test.d.ts +2 -0
  61. package/lib/esm/tests/api_tests/_tmp_followup_verify.test.d.ts.map +1 -0
  62. package/lib/esm/tests/api_tests/_tmp_followup_verify.test.js +194 -0
  63. package/lib/esm/tests/api_tests/_tmp_followup_verify.test.js.map +1 -0
  64. package/lib/esm/tests/api_tests/_tmp_nonmatch_verify.test.d.ts +2 -0
  65. package/lib/esm/tests/api_tests/_tmp_nonmatch_verify.test.d.ts.map +1 -0
  66. package/lib/esm/tests/api_tests/_tmp_nonmatch_verify.test.js +115 -0
  67. package/lib/esm/tests/api_tests/_tmp_nonmatch_verify.test.js.map +1 -0
  68. package/lib/esm/tests/api_tests/ai_decision_summary_config.test.d.ts +16 -0
  69. package/lib/esm/tests/api_tests/ai_decision_summary_config.test.d.ts.map +1 -0
  70. package/lib/esm/tests/api_tests/ai_decision_summary_config.test.js +183 -0
  71. package/lib/esm/tests/api_tests/ai_decision_summary_config.test.js.map +1 -0
  72. package/lib/esm/tests/api_tests/field_redaction.test.d.ts.map +1 -1
  73. package/lib/esm/tests/api_tests/field_redaction.test.js +112 -99
  74. package/lib/esm/tests/api_tests/field_redaction.test.js.map +1 -1
  75. package/lib/esm/tests/api_tests/journey_error_branching.test.js +7 -2
  76. package/lib/esm/tests/api_tests/journey_error_branching.test.js.map +1 -1
  77. package/lib/esm/tests/api_tests/mdi_case_offerings.test.d.ts +6 -0
  78. package/lib/esm/tests/api_tests/mdi_case_offerings.test.d.ts.map +1 -0
  79. package/lib/esm/tests/api_tests/mdi_case_offerings.test.js +439 -0
  80. package/lib/esm/tests/api_tests/mdi_case_offerings.test.js.map +1 -0
  81. package/lib/esm/tests/api_tests/resource_access_tags.test.d.ts +22 -0
  82. package/lib/esm/tests/api_tests/resource_access_tags.test.d.ts.map +1 -0
  83. package/lib/esm/tests/api_tests/resource_access_tags.test.js +484 -0
  84. package/lib/esm/tests/api_tests/resource_access_tags.test.js.map +1 -0
  85. package/lib/esm/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.d.ts +21 -0
  86. package/lib/esm/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.d.ts.map +1 -0
  87. package/lib/esm/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.js +358 -0
  88. package/lib/esm/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.js.map +1 -0
  89. package/lib/esm/tests/api_tests/security/F-0145-globalunique-leak.test.d.ts +20 -0
  90. package/lib/esm/tests/api_tests/security/F-0145-globalunique-leak.test.d.ts.map +1 -0
  91. package/lib/esm/tests/api_tests/security/F-0145-globalunique-leak.test.js +135 -0
  92. package/lib/esm/tests/api_tests/security/F-0145-globalunique-leak.test.js.map +1 -0
  93. package/lib/esm/tests/api_tests/security/F-0151-load-inbox-redaction.test.d.ts +18 -0
  94. package/lib/esm/tests/api_tests/security/F-0151-load-inbox-redaction.test.d.ts.map +1 -0
  95. package/lib/esm/tests/api_tests/security/F-0151-load-inbox-redaction.test.js +152 -0
  96. package/lib/esm/tests/api_tests/security/F-0151-load-inbox-redaction.test.js.map +1 -0
  97. package/lib/esm/tests/api_tests/security/F-0155-webhook-timeout.test.d.ts +20 -0
  98. package/lib/esm/tests/api_tests/security/F-0155-webhook-timeout.test.d.ts.map +1 -0
  99. package/lib/esm/tests/api_tests/security/F-0155-webhook-timeout.test.js +150 -0
  100. package/lib/esm/tests/api_tests/security/F-0155-webhook-timeout.test.js.map +1 -0
  101. package/lib/esm/tests/api_tests/time_tracks.test.d.ts +8 -0
  102. package/lib/esm/tests/api_tests/time_tracks.test.d.ts.map +1 -1
  103. package/lib/esm/tests/api_tests/time_tracks.test.js +635 -0
  104. package/lib/esm/tests/api_tests/time_tracks.test.js.map +1 -1
  105. package/lib/esm/tests/setup.d.ts.map +1 -1
  106. package/lib/esm/tests/setup.js +20 -1
  107. package/lib/esm/tests/setup.js.map +1 -1
  108. package/lib/esm/tests/tests.d.ts.map +1 -1
  109. package/lib/esm/tests/tests.js +607 -179
  110. package/lib/esm/tests/tests.js.map +1 -1
  111. package/lib/tsconfig.tsbuildinfo +1 -1
  112. package/package.json +10 -10
  113. package/src/tests/api_tests/field_redaction.test.ts +13 -0
  114. package/src/tests/api_tests/journey_error_branching.test.ts +5 -1
  115. package/src/tests/api_tests/mdi_case_offerings.test.ts +414 -0
  116. package/src/tests/api_tests/resource_access_tags.test.ts +303 -0
  117. package/src/tests/api_tests/security/F-0116-F-0119-enduser-cross-create.test.ts +221 -0
  118. package/src/tests/api_tests/security/F-0145-globalunique-leak.test.ts +78 -0
  119. package/src/tests/api_tests/security/F-0151-load-inbox-redaction.test.ts +81 -0
  120. package/src/tests/api_tests/security/F-0155-webhook-timeout.test.ts +85 -0
  121. package/src/tests/api_tests/time_tracks.test.ts +420 -0
  122. package/src/tests/setup.ts +9 -0
  123. package/src/tests/tests.ts +308 -5
  124. package/test_generated.pdf +0 -0
@@ -83,12 +83,13 @@ import { cross_org_api_key_tests } from "./api_tests/cross_org_api_key.test";
83
83
  import { custom_dashboards_tests } from "./api_tests/custom_dashboards.test";
84
84
  import { message_assignment_trigger_tests } from "./api_tests/message_assignment_trigger.test";
85
85
  import { outbound_chat_sent_trigger_tests } from "./api_tests/outbound_chat_sent_trigger.test";
86
- import { time_tracks_tests, time_tracks_historical_tests, time_tracks_correction_tests, time_tracks_review_tests, time_tracks_lock_tests, time_tracks_edge_case_tests } from "./api_tests/time_tracks.test";
86
+ import { time_tracks_tests, time_tracks_historical_tests, time_tracks_correction_tests, time_tracks_review_tests, time_tracks_lock_tests, time_tracks_edge_case_tests, time_tracks_resubmit_tests, time_tracks_appointment_duration_tests } from "./api_tests/time_tracks.test";
87
87
  import { monthly_availability_restrictions_tests } from "./api_tests/monthly_availability_restrictions.test";
88
88
  import { calendar_event_limits_tests } from "./api_tests/calendar_event_limits.test";
89
89
  import { custom_aggregation_tests } from "./api_tests/custom_aggregation.test";
90
90
  import { chats_analytics_tests } from "./api_tests/chats_analytics.test";
91
91
  import { no_access_permission_checks_tests } from "./api_tests/no_access_permission_checks.test";
92
+ import { resource_access_tags_tests } from "./api_tests/resource_access_tags.test";
92
93
  import { field_redaction_tests } from "./api_tests/field_redaction.test";
93
94
  import { data_sync_redaction_bypass_tests } from "./api_tests/security/F-0001-data-sync-redaction-bypass.test";
94
95
  import { enduser_write_restrictions_tests } from "./api_tests/security/F-0106-F-0110-enduser-write-restrictions.test";
@@ -99,6 +100,10 @@ import { invite_user_enumeration_tests } from "./api_tests/security/F-0007-invit
99
100
  import { handle_incoming_communication_cross_tenant_tests } from "./api_tests/security/F-0008-handle-incoming-communication-cross-tenant.test";
100
101
  import { sanitize_user_html_xss_tests } from "./api_tests/security/F-0013-sanitize-user-html.test";
101
102
  import { prototype_pollution_tests } from "./api_tests/security/F-0016-prototype-pollution.test";
103
+ import { enduser_cross_create_tests } from "./api_tests/security/F-0116-F-0119-enduser-cross-create.test";
104
+ import { globalunique_leak_tests } from "./api_tests/security/F-0145-globalunique-leak.test";
105
+ import { load_inbox_redaction_tests } from "./api_tests/security/F-0151-load-inbox-redaction.test";
106
+ import { webhook_timeout_tests } from "./api_tests/security/F-0155-webhook-timeout.test";
102
107
  import { bulk_assignment_tests } from "./api_tests/bulk_assignment.test";
103
108
  import { managed_content_enduser_access_tests } from "./api_tests/managed_content_enduser_access.test";
104
109
  import { managed_content_file_access_tests } from "./api_tests/managed_content_file_access.test";
@@ -115,6 +120,7 @@ import { organization_settings_duplicates_tests } from "./api_tests/organization
115
120
  import { calendar_events_bulk_update_tests } from "./api_tests/calendar_events_bulk_update.test";
116
121
  import { openloop_webhooks_tests } from "./api_tests/openloop_webhooks.test";
117
122
  import { beluga_pharmacy_mappings_tests } from "./api_tests/beluga_pharmacy_mappings.test";
123
+ import { mdi_case_offerings_tests } from "./api_tests/mdi_case_offerings.test";
118
124
  import { beluga_manual_sync_tests } from "./api_tests/beluga_manual_sync.test";
119
125
  import { mdi_webhooks_tests } from "./api_tests/mdi_webhooks.test";
120
126
  import { account_switcher_tests } from "./api_tests/account_switcher.test";
@@ -3713,6 +3719,290 @@ const order_status_equals_tests = async () => {
3713
3719
  ])
3714
3720
  }
3715
3721
 
3722
+ // Covers the "blank status matches any order status" behavior (Order Status Equals acting as "Order Status Changed").
3723
+ // Kept separate from order_status_equals_tests because a blank-status trigger would break that test's exact tag-count assertions.
3724
+ const order_status_blank_tests = async () => {
3725
+ log_header("Automation Trigger Tests (Order Status Equals - blank status)")
3726
+
3727
+ // A and A2 both fire on any status for BlankSource; the only difference is omitted vs. empty-string status.
3728
+ const A = await sdk.api.automation_triggers.createOne({
3729
+ event: { type: 'Order Status Equals', info: { source: 'BlankSource' } }, // status omitted entirely
3730
+ action: { type: 'Add Tags', info: { tags: ['AnyOmit'] }},
3731
+ status: 'Active',
3732
+ title: "Blank status (omitted)"
3733
+ })
3734
+ const A2 = await sdk.api.automation_triggers.createOne({
3735
+ event: { type: 'Order Status Equals', info: { source: 'BlankSource', status: '' } }, // explicit empty string
3736
+ action: { type: 'Add Tags', info: { tags: ['AnyEmpty'] }},
3737
+ status: 'Active',
3738
+ title: "Blank status (empty string)"
3739
+ })
3740
+ const B = await sdk.api.automation_triggers.createOne({
3741
+ event: { type: 'Order Status Equals', info: { source: 'BlankSource', status: '', skus: ['MATCH-SKU'] } },
3742
+ action: { type: 'Add Tags', info: { tags: ['BlankWithSku'] }},
3743
+ status: 'Active',
3744
+ title: "Blank status with SKU filter"
3745
+ })
3746
+ const C = await sdk.api.automation_triggers.createOne({
3747
+ event: { type: 'Order Status Equals', info: { source: 'OtherSource' } }, // blank status, different source
3748
+ action: { type: 'Add Tags', info: { tags: ['WrongSource'] }},
3749
+ status: 'Active',
3750
+ title: "Blank status, other source"
3751
+ })
3752
+ const D = await sdk.api.automation_triggers.createOne({
3753
+ event: { type: 'Order Status Equals', info: { source: 'BlankSource', status: 'Shipped' } }, // status-specific (regression)
3754
+ action: { type: 'Add Tags', info: { tags: ['ShippedOnly'] }},
3755
+ status: 'Active',
3756
+ title: "Status-specific (regression)"
3757
+ })
3758
+ // BA/BD exercise the separate bulk-create code path (createSome of 2+); kept on their own source so the
3759
+ // pre-existing "bulk ignores sku/protocol filters" divergence doesn't affect these assertions.
3760
+ const BA = await sdk.api.automation_triggers.createOne({
3761
+ event: { type: 'Order Status Equals', info: { source: 'BulkSource' } }, // blank status
3762
+ action: { type: 'Add Tags', info: { tags: ['BulkAny'] }},
3763
+ status: 'Active',
3764
+ title: "Bulk blank status"
3765
+ })
3766
+ const BD = await sdk.api.automation_triggers.createOne({
3767
+ event: { type: 'Order Status Equals', info: { source: 'BulkSource', status: 'Shipped' } }, // status-specific
3768
+ action: { type: 'Add Tags', info: { tags: ['BulkShipped'] }},
3769
+ status: 'Active',
3770
+ title: "Bulk status-specific"
3771
+ })
3772
+ // BD2's status matches NO order in the bulk batch below; backwards-compat: it must never fire (the query's $in
3773
+ // branch excludes it entirely, exactly as before the blank-status change).
3774
+ const BD2 = await sdk.api.automation_triggers.createOne({
3775
+ event: { type: 'Order Status Equals', info: { source: 'BulkSource', status: 'NeverInBatch' } },
3776
+ action: { type: 'Add Tags', info: { tags: ['BulkNever'] }},
3777
+ status: 'Active',
3778
+ title: "Bulk status-specific (absent from batch)"
3779
+ })
3780
+ // P: blank status composed with a protocol filter (proves blank works with non-SKU filters + the no-protocol branch)
3781
+ const P = await sdk.api.automation_triggers.createOne({
3782
+ event: { type: 'Order Status Equals', info: { source: 'BlankSource', status: '', protocols: ['Protocol-A'] } },
3783
+ action: { type: 'Add Tags', info: { tags: ['BlankProtocol'] }},
3784
+ status: 'Active',
3785
+ title: "Blank status with protocol filter"
3786
+ })
3787
+
3788
+ // Case 1: validation accepts triggers with no status / empty status (A and A2 created above without error)
3789
+ await async_test(
3790
+ "Trigger with omitted status was created",
3791
+ () => Promise.resolve(A),
3792
+ { onResult: t => !!t.id && t.event.type === 'Order Status Equals' && !t.event.info.status }
3793
+ )
3794
+ await async_test(
3795
+ "Trigger with empty-string status was created",
3796
+ () => Promise.resolve(A2),
3797
+ { onResult: t => !!t.id && t.event.type === 'Order Status Equals' && t.event.info.status === '' }
3798
+ )
3799
+
3800
+ // Case 2: blank status fires on an arbitrary status
3801
+ const e2 = await sdk.api.endusers.createOne({})
3802
+ await sdk.api.enduser_orders.createOne({ status: 'StatusOne', source: 'BlankSource', title: 'Title', externalId: 'blank-1', enduserId: e2.id })
3803
+ await wait(undefined, 500) // allow triggers to happen
3804
+ await async_test(
3805
+ "Blank status fires on arbitrary status (no other tags)",
3806
+ () => sdk.api.endusers.getOne(e2.id),
3807
+ { onResult: e => !!(
3808
+ e.tags?.includes('AnyOmit')
3809
+ && e.tags?.includes('AnyEmpty')
3810
+ && !e.tags?.includes('BlankWithSku')
3811
+ && !e.tags?.includes('WrongSource')
3812
+ && !e.tags?.includes('ShippedOnly')
3813
+ ) }
3814
+ )
3815
+
3816
+ // Case 3: blank status fires on a totally different status value (status-value-independent)
3817
+ const e3 = await sdk.api.endusers.createOne({})
3818
+ await sdk.api.enduser_orders.createOne({ status: 'totally-arbitrary-xyz', source: 'BlankSource', title: 'Title', externalId: 'blank-2', enduserId: e3.id })
3819
+ await wait(undefined, 500) // allow triggers to happen
3820
+ await async_test(
3821
+ "Blank status fires on a different arbitrary status",
3822
+ () => sdk.api.endusers.getOne(e3.id),
3823
+ { onResult: e => !!(
3824
+ e.tags?.includes('AnyOmit')
3825
+ && e.tags?.includes('AnyEmpty')
3826
+ ) }
3827
+ )
3828
+
3829
+ // Case 4: source isolation - blank trigger does not fire for a different source
3830
+ const e4 = await sdk.api.endusers.createOne({})
3831
+ await sdk.api.enduser_orders.createOne({ status: 'StatusOne', source: 'DifferentSource', title: 'Title', externalId: 'blank-3', enduserId: e4.id })
3832
+ await wait(undefined, 500) // allow triggers to happen
3833
+ await async_test(
3834
+ "Blank status is still scoped to its source",
3835
+ () => sdk.api.endusers.getOne(e4.id),
3836
+ { onResult: e => !!(
3837
+ !e.tags?.includes('AnyOmit')
3838
+ && !e.tags?.includes('AnyEmpty')
3839
+ && !e.tags?.includes('WrongSource') // C only matches OtherSource
3840
+ ) }
3841
+ )
3842
+
3843
+ // Case 5a: other filters still enforced with blank status (sku mismatch)
3844
+ const e5 = await sdk.api.endusers.createOne({})
3845
+ await sdk.api.enduser_orders.createOne({ status: 'whatever', source: 'BlankSource', title: 'Title', externalId: 'blank-4', enduserId: e5.id, sku: 'NOPE' })
3846
+ await wait(undefined, 500) // allow triggers to happen
3847
+ await async_test(
3848
+ "Blank status + SKU filter does not fire on SKU mismatch",
3849
+ () => sdk.api.endusers.getOne(e5.id),
3850
+ { onResult: e => !!(
3851
+ e.tags?.includes('AnyOmit')
3852
+ && e.tags?.includes('AnyEmpty')
3853
+ && !e.tags?.includes('BlankWithSku')
3854
+ ) }
3855
+ )
3856
+
3857
+ // Case 5b: other filters still enforced with blank status (sku match)
3858
+ const e6 = await sdk.api.endusers.createOne({})
3859
+ await sdk.api.enduser_orders.createOne({ status: 'whatever', source: 'BlankSource', title: 'Title', externalId: 'blank-5', enduserId: e6.id, sku: 'MATCH-SKU' })
3860
+ await wait(undefined, 500) // allow triggers to happen
3861
+ await async_test(
3862
+ "Blank status + SKU filter fires on SKU match",
3863
+ () => sdk.api.endusers.getOne(e6.id),
3864
+ { onResult: e => !!(
3865
+ e.tags?.includes('AnyOmit')
3866
+ && e.tags?.includes('AnyEmpty')
3867
+ && e.tags?.includes('BlankWithSku')
3868
+ ) }
3869
+ )
3870
+
3871
+ // Case 6: update path + regression coexistence with a status-specific trigger
3872
+ const e7 = await sdk.api.endusers.createOne({})
3873
+ const o7 = await sdk.api.enduser_orders.createOne({ status: 'Created', source: 'BlankSource', title: 'Title', externalId: 'blank-6', enduserId: e7.id })
3874
+ await wait(undefined, 500) // allow triggers to happen
3875
+ await async_test(
3876
+ "Blank fires on create; status-specific (Shipped) does not yet",
3877
+ () => sdk.api.endusers.getOne(e7.id),
3878
+ { onResult: e => !!(
3879
+ e.tags?.includes('AnyOmit')
3880
+ && e.tags?.includes('AnyEmpty')
3881
+ && !e.tags?.includes('ShippedOnly')
3882
+ ) }
3883
+ )
3884
+ // update to a NON-matching status: blank fires again, but the status-specific trigger must stay un-fired
3885
+ await sdk.api.enduser_orders.updateOne(o7.id, { status: 'Delivered', externalId: 'blank-6-delivered' })
3886
+ await wait(undefined, 500) // allow triggers to happen
3887
+ await async_test(
3888
+ "Update to non-matching status: blank fires, status-specific (Shipped) does NOT fire",
3889
+ () => sdk.api.endusers.getOne(e7.id),
3890
+ { onResult: e => !!(
3891
+ e.tags?.includes('AnyOmit') // proves the update event was processed
3892
+ && e.tags?.includes('AnyEmpty')
3893
+ && !e.tags?.includes('ShippedOnly') // 'Delivered' must not trigger the 'Shipped' trigger
3894
+ ) }
3895
+ )
3896
+ // now update to the matching status: status-specific trigger fires
3897
+ await sdk.api.enduser_orders.updateOne(o7.id, { status: 'Shipped', externalId: 'blank-6-shipped' })
3898
+ await wait(undefined, 500) // allow triggers to happen
3899
+ await async_test(
3900
+ "Blank fires on update too; status-specific (Shipped) now fires",
3901
+ () => sdk.api.endusers.getOne(e7.id),
3902
+ { onResult: e => !!(
3903
+ e.tags?.includes('AnyOmit')
3904
+ && e.tags?.includes('AnyEmpty')
3905
+ && e.tags?.includes('ShippedOnly')
3906
+ ) }
3907
+ )
3908
+
3909
+ // Case 7: bulk create path (createSome of 2+) must honor blank status + status-specific selectivity.
3910
+ // This exercises a SEPARATE matching code path (handle_orders_create bulk branch) from single creates.
3911
+ const eb1 = await sdk.api.endusers.createOne({})
3912
+ const eb2 = await sdk.api.endusers.createOne({})
3913
+ const eb3 = await sdk.api.endusers.createOne({})
3914
+ await sdk.api.enduser_orders.createSome([
3915
+ { status: 'StatusA', source: 'BulkSource', title: 'Title', externalId: 'bulk-1', enduserId: eb1.id },
3916
+ { status: 'Shipped', source: 'BulkSource', title: 'Title', externalId: 'bulk-2', enduserId: eb2.id },
3917
+ { status: 'StatusB', source: 'BulkSource', title: 'Title', externalId: 'bulk-3', enduserId: eb3.id },
3918
+ ])
3919
+ await wait(undefined, 500) // allow triggers to happen
3920
+ await async_test(
3921
+ "Bulk create: blank fires on non-Shipped order; status-specific (matching + absent) do not",
3922
+ () => sdk.api.endusers.getOne(eb1.id),
3923
+ { onResult: e => !!(e.tags?.includes('BulkAny') && !e.tags?.includes('BulkShipped') && !e.tags?.includes('BulkNever')) }
3924
+ )
3925
+ await async_test(
3926
+ "Bulk create: blank + matching status-specific fire on the Shipped order; absent-status one does not",
3927
+ () => sdk.api.endusers.getOne(eb2.id),
3928
+ { onResult: e => !!(e.tags?.includes('BulkAny') && e.tags?.includes('BulkShipped') && !e.tags?.includes('BulkNever')) }
3929
+ )
3930
+ await async_test(
3931
+ "Bulk create: blank fires on the other non-Shipped order; status-specific (matching + absent) do not",
3932
+ () => sdk.api.endusers.getOne(eb3.id),
3933
+ { onResult: e => !!(e.tags?.includes('BulkAny') && !e.tags?.includes('BulkShipped') && !e.tags?.includes('BulkNever')) }
3934
+ )
3935
+
3936
+ // Case 8: an order update that does NOT change status must not run the handler (true "status changed" semantic).
3937
+ const e8 = await sdk.api.endusers.createOne({})
3938
+ const o8 = await sdk.api.enduser_orders.createOne({ status: 'Shipped', source: 'BlankSource', title: 'Title', externalId: 'noChange-1', enduserId: e8.id })
3939
+ await wait(undefined, 500) // allow triggers to happen
3940
+ await async_test(
3941
+ "Setup: status-specific (Shipped) fired on create",
3942
+ () => sdk.api.endusers.getOne(e8.id),
3943
+ { onResult: e => !!(e.tags?.includes('AnyOmit') && e.tags?.includes('ShippedOnly')) }
3944
+ )
3945
+ // clear tags (replaceObjectFields forces the empty array to actually replace)
3946
+ await sdk.api.endusers.updateOne(e8.id, { tags: [] }, { replaceObjectFields: true })
3947
+ // sanity-check the clear actually took, so a later failure points at re-firing rather than a broken clear
3948
+ await async_test(
3949
+ "Setup: tags cleared before non-status update",
3950
+ () => sdk.api.endusers.getOne(e8.id),
3951
+ { onResult: e => !e.tags?.length }
3952
+ )
3953
+ // update a non-status field (status stays 'Shipped'); handler should early-return and re-add nothing.
3954
+ // If it wrongly fired, the blank triggers A/A2 would re-add AnyOmit/AnyEmpty, so empty tags proves non-firing.
3955
+ await sdk.api.enduser_orders.updateOne(o8.id, { tracking: 'tracking-123', externalId: 'noChange-1b' })
3956
+ await wait(undefined, 500) // allow triggers to happen
3957
+ await async_test(
3958
+ "Update with unchanged status does not fire any trigger",
3959
+ () => sdk.api.endusers.getOne(e8.id),
3960
+ { onResult: e => !e.tags?.length }
3961
+ )
3962
+
3963
+ // Case 9: protocol filter with blank status - order without a protocol must not fire (the order.protocol ? : false branch)
3964
+ const e9 = await sdk.api.endusers.createOne({})
3965
+ await sdk.api.enduser_orders.createOne({ status: 'whatever', source: 'BlankSource', title: 'Title', externalId: 'proto-1', enduserId: e9.id })
3966
+ await wait(undefined, 500) // allow triggers to happen
3967
+ await async_test(
3968
+ "Blank + protocol filter does not fire when order has no protocol",
3969
+ () => sdk.api.endusers.getOne(e9.id),
3970
+ { onResult: e => !!(e.tags?.includes('AnyOmit') && !e.tags?.includes('BlankProtocol')) }
3971
+ )
3972
+ const e10 = await sdk.api.endusers.createOne({})
3973
+ await sdk.api.enduser_orders.createOne({ status: 'whatever', source: 'BlankSource', title: 'Title', externalId: 'proto-2', enduserId: e10.id, protocol: 'Protocol-A' })
3974
+ await wait(undefined, 500) // allow triggers to happen
3975
+ await async_test(
3976
+ "Blank + protocol filter fires when order protocol matches",
3977
+ () => sdk.api.endusers.getOne(e10.id),
3978
+ { onResult: e => !!(e.tags?.includes('AnyOmit') && e.tags?.includes('BlankProtocol')) }
3979
+ )
3980
+
3981
+ await Promise.all([
3982
+ sdk.api.automation_triggers.deleteOne(A.id),
3983
+ sdk.api.automation_triggers.deleteOne(A2.id),
3984
+ sdk.api.automation_triggers.deleteOne(B.id),
3985
+ sdk.api.automation_triggers.deleteOne(C.id),
3986
+ sdk.api.automation_triggers.deleteOne(D.id),
3987
+ sdk.api.automation_triggers.deleteOne(BA.id),
3988
+ sdk.api.automation_triggers.deleteOne(BD.id),
3989
+ sdk.api.automation_triggers.deleteOne(BD2.id),
3990
+ sdk.api.automation_triggers.deleteOne(P.id),
3991
+ sdk.api.endusers.deleteOne(e2.id),
3992
+ sdk.api.endusers.deleteOne(e3.id),
3993
+ sdk.api.endusers.deleteOne(e4.id),
3994
+ sdk.api.endusers.deleteOne(e5.id),
3995
+ sdk.api.endusers.deleteOne(e6.id),
3996
+ sdk.api.endusers.deleteOne(e7.id),
3997
+ sdk.api.endusers.deleteOne(eb1.id),
3998
+ sdk.api.endusers.deleteOne(eb2.id),
3999
+ sdk.api.endusers.deleteOne(eb3.id),
4000
+ sdk.api.endusers.deleteOne(e8.id),
4001
+ sdk.api.endusers.deleteOne(e9.id),
4002
+ sdk.api.endusers.deleteOne(e10.id),
4003
+ ])
4004
+ }
4005
+
3716
4006
  const tag_added_tests = async () => {
3717
4007
  log_header("Automation Trigger Tests (Tag Added)")
3718
4008
 
@@ -5305,6 +5595,7 @@ const automation_trigger_tests = async () => {
5305
5595
  await assign_care_team_tests()
5306
5596
  await push_forms_to_portal_group_completion_tests({ sdk, sdkNonAdmin })
5307
5597
  await order_status_equals_tests()
5598
+ await order_status_blank_tests()
5308
5599
  await set_fields_order_templates_tests({ sdk, sdkNonAdmin })
5309
5600
  await medication_added_trigger_tests({ sdk, sdkNonAdmin })
5310
5601
  await appointment_cancelled_tests()
@@ -8862,8 +9153,11 @@ export const switch_to_related_contacts_tests = async () => {
8862
9153
  () => pollForResults(
8863
9154
  () => sdk.api.endusers.getOne(parent.id),
8864
9155
  e => !!e.tags?.includes('Success'),
8865
- 50,
8866
- 200,
9156
+ // switchToRelatedContact (cycle 1) then the afterAction addEnduserTags (cycle 2) each wait a
9157
+ // full worker poll cycle (>= 8s, see worker pollingDelaySeconds), so ~16s worst case. Poll for
9158
+ // 30s (500ms x 60) to cover two cycles plus buffer; the old 10s window timed out flakily.
9159
+ 500,
9160
+ 60,
8867
9161
  ),
8868
9162
  passOnAnyResult
8869
9163
  )
@@ -14395,7 +14689,7 @@ const ip_address_form_tests = async () => {
14395
14689
  })
14396
14690
 
14397
14691
  await wait(undefined, 500) // wait for IP to be set
14398
- async_test(
14692
+ await async_test(
14399
14693
  'IP Set on Enduser creation',
14400
14694
  () => sdk.api.endusers.getOne(enduserId),
14401
14695
  { onResult: result => !!result.fields?.IP }
@@ -14414,7 +14708,8 @@ const ip_address_form_tests = async () => {
14414
14708
  lname: 'test',
14415
14709
  })
14416
14710
 
14417
- async_test(
14711
+ await wait(undefined, 500) // wait for IP to be re-set
14712
+ await async_test(
14418
14713
  'IP Set on update',
14419
14714
  () => sdk.api.endusers.getOne(enduserId),
14420
14715
  { onResult: result => !!result.fields?.IP && result.fields?.otherField === 'Set' && result.id === enduserId }
@@ -14547,9 +14842,15 @@ const ip_address_form_tests = async () => {
14547
14842
  await replace_form_field_template_values_tests()
14548
14843
  await mfa_tests()
14549
14844
  await setup_tests(sdk, sdkNonAdmin)
14845
+ await resource_access_tags_tests({ sdk, sdkNonAdmin })
14550
14846
  await beluga_manual_sync_tests({ sdk, sdkNonAdmin })
14551
14847
  await beluga_pharmacy_mappings_tests({ sdk, sdkNonAdmin })
14848
+ await mdi_case_offerings_tests({ sdk, sdkNonAdmin })
14552
14849
  await enduser_write_restrictions_tests({ sdk, sdkNonAdmin })
14850
+ await enduser_cross_create_tests({ sdk, sdkNonAdmin })
14851
+ await globalunique_leak_tests({ sdk, sdkNonAdmin })
14852
+ await load_inbox_redaction_tests({ sdk, sdkNonAdmin })
14853
+ await webhook_timeout_tests({ sdk, sdkNonAdmin })
14553
14854
  await user_portal_settings_tests({ sdk, sdkNonAdmin })
14554
14855
  await automation_trigger_tests()
14555
14856
  await invite_user_enumeration_tests({ sdk, sdkNonAdmin })
@@ -14586,6 +14887,8 @@ const ip_address_form_tests = async () => {
14586
14887
  await time_tracks_review_tests({ sdk, sdkNonAdmin })
14587
14888
  await time_tracks_lock_tests({ sdk, sdkNonAdmin })
14588
14889
  await time_tracks_edge_case_tests({ sdk, sdkNonAdmin })
14890
+ await time_tracks_resubmit_tests({ sdk, sdkNonAdmin })
14891
+ await time_tracks_appointment_duration_tests({ sdk, sdkNonAdmin })
14589
14892
  await calendar_event_limits_tests({ sdk, sdkNonAdmin })
14590
14893
  await get_some_projection_tests({ sdk, sdkNonAdmin })
14591
14894
  await elation_user_id_tests({ sdk, sdkNonAdmin })
Binary file