@tellescope/sdk 1.246.2 → 1.247.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,201 @@
1
+ require('source-map-support').install();
2
+
3
+ import { Session } from "../../sdk"
4
+ import {
5
+ async_test,
6
+ log_header,
7
+ } from "@tellescope/testing"
8
+ import { setup_tests } from "../setup"
9
+
10
+ const host = process.env.API_URL || 'http://localhost:8080' as const
11
+
12
+ export const organization_settings_duplicates_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
13
+ log_header("Organization Settings Duplicate Validation Tests")
14
+
15
+ const orgId = sdk.userInfo.businessId
16
+
17
+ // === A. replaceObjectFields: false (merge/push behavior) ===
18
+
19
+ // A1. Duplicate tags via merge
20
+ await sdk.api.organizations.updateOne(orgId, {
21
+ settings: { endusers: { tags: ['tag1', 'tag2'] } }
22
+ }, { replaceObjectFields: true })
23
+
24
+ await async_test(
25
+ "Merge tags rejects duplicates (tag2 appears in both old and new)",
26
+ () => sdk.api.organizations.updateOne(orgId, {
27
+ settings: { endusers: { tags: ['tag2', 'tag3'] } }
28
+ }),
29
+ { shouldError: true, onError: (e: { message: string }) => e.message.includes('Duplicate value in settings.endusers.tags') }
30
+ )
31
+
32
+ // A2. Duplicate customFields via merge
33
+ await sdk.api.organizations.updateOne(orgId, {
34
+ settings: { endusers: { customFields: [{ type: 'Text' as const, field: 'myField', info: {} }] } }
35
+ }, { replaceObjectFields: true })
36
+
37
+ await async_test(
38
+ "Merge customFields rejects duplicate field name",
39
+ () => sdk.api.organizations.updateOne(orgId, {
40
+ settings: { endusers: { customFields: [{ type: 'Text' as const, field: 'myField', info: {} }] } }
41
+ }),
42
+ { shouldError: true, onError: (e: { message: string }) => e.message.includes('Duplicate field in settings.endusers.customFields') }
43
+ )
44
+
45
+ // A3. Duplicate builtinFields via merge
46
+ await sdk.api.organizations.updateOne(orgId, {
47
+ settings: { endusers: { builtinFields: [{ field: 'fname', label: 'First Name' }] } }
48
+ }, { replaceObjectFields: true })
49
+
50
+ await async_test(
51
+ "Merge builtinFields rejects duplicate field name",
52
+ () => sdk.api.organizations.updateOne(orgId, {
53
+ settings: { endusers: { builtinFields: [{ field: 'fname', label: 'First Name Copy' }] } }
54
+ }),
55
+ { shouldError: true, onError: (e: { message: string }) => e.message.includes('Duplicate field in settings.endusers.builtinFields') }
56
+ )
57
+
58
+ // A4. Duplicate dontRecordCallsToPhone via merge
59
+ await sdk.api.organizations.updateOne(orgId, {
60
+ settings: { endusers: { dontRecordCallsToPhone: ['+15551234567'] } }
61
+ }, { replaceObjectFields: true })
62
+
63
+ await async_test(
64
+ "Merge dontRecordCallsToPhone rejects duplicates",
65
+ () => sdk.api.organizations.updateOne(orgId, {
66
+ settings: { endusers: { dontRecordCallsToPhone: ['+15551234567'] } }
67
+ }),
68
+ { shouldError: true, onError: (e: { message: string }) => e.message.includes('Duplicate value in settings.endusers.dontRecordCallsToPhone') }
69
+ )
70
+
71
+ // A5. Duplicate cancelReasons via merge
72
+ await sdk.api.organizations.updateOne(orgId, {
73
+ settings: { calendar: { cancelReasons: ['No show'] } }
74
+ }, { replaceObjectFields: true })
75
+
76
+ await async_test(
77
+ "Merge cancelReasons rejects duplicates",
78
+ () => sdk.api.organizations.updateOne(orgId, {
79
+ settings: { calendar: { cancelReasons: ['No show'] } }
80
+ }),
81
+ { shouldError: true, onError: (e: { message: string }) => e.message.includes('Duplicate value in settings.calendar.cancelReasons') }
82
+ )
83
+
84
+ // === B. replaceObjectFields: true (full replacement) ===
85
+
86
+ // B1. Replace that grows the array with dupes should be rejected
87
+ await sdk.api.organizations.updateOne(orgId, {
88
+ settings: { endusers: { tags: ['tag1'] } }
89
+ }, { replaceObjectFields: true })
90
+
91
+ await async_test(
92
+ "Replace tags rejects duplicates when array grows",
93
+ () => sdk.api.organizations.updateOne(orgId, {
94
+ settings: { endusers: { tags: ['tag1', 'tag1', 'tag2'] } }
95
+ }, { replaceObjectFields: true }),
96
+ { shouldError: true, onError: (e: { message: string }) => e.message.includes('Duplicate value in settings.endusers.tags') }
97
+ )
98
+
99
+ // B2. Replace with dupes that shrinks the array should be allowed
100
+ await sdk.api.organizations.updateOne(orgId, {
101
+ settings: { endusers: { tags: ['a', 'b', 'c'] } }
102
+ }, { replaceObjectFields: true })
103
+
104
+ await async_test(
105
+ "Replace with dupes that shrinks array succeeds",
106
+ () => sdk.api.organizations.updateOne(orgId, {
107
+ settings: { endusers: { tags: ['a', 'a'] } }
108
+ }, { replaceObjectFields: true }),
109
+ { shouldError: false, onResult: () => true }
110
+ )
111
+
112
+ // B3. Replace with unique values always succeeds
113
+ await async_test(
114
+ "Replace tags succeeds with unique values",
115
+ () => sdk.api.organizations.updateOne(orgId, {
116
+ settings: { endusers: { tags: ['tag1', 'tag2'] } }
117
+ }, { replaceObjectFields: true }),
118
+ { shouldError: false, onResult: () => true }
119
+ )
120
+
121
+ // === C. Non-duplicate updates still succeed ===
122
+
123
+ // C1. Set initial tags then add different tags via merge
124
+ await sdk.api.organizations.updateOne(orgId, {
125
+ settings: { endusers: { tags: ['tagA', 'tagB'] } }
126
+ }, { replaceObjectFields: true })
127
+
128
+ await async_test(
129
+ "Merge with unique new tags succeeds",
130
+ () => sdk.api.organizations.updateOne(orgId, {
131
+ settings: { endusers: { tags: ['tagC', 'tagD'] } }
132
+ }),
133
+ { shouldError: false, onResult: () => true }
134
+ )
135
+
136
+ // C2. Replace with unique values
137
+ await async_test(
138
+ "Replace customFields with unique values succeeds",
139
+ () => sdk.api.organizations.updateOne(orgId, {
140
+ settings: { endusers: { customFields: [
141
+ { type: 'Text' as const, field: 'field1', info: {} },
142
+ { type: 'Text' as const, field: 'field2', info: {} },
143
+ ] } }
144
+ }, { replaceObjectFields: true }),
145
+ { shouldError: false, onResult: () => true }
146
+ )
147
+
148
+ // C3. Updating another settings field preserves pre-existing duplicates
149
+ // First set tags to a longer array, then shrink to a dupe array (shrinking is allowed)
150
+ await sdk.api.organizations.updateOne(orgId, {
151
+ settings: { endusers: { tags: ['dupeTag', 'otherTag', 'anotherTag'] } }
152
+ }, { replaceObjectFields: true })
153
+ await sdk.api.organizations.updateOne(orgId, {
154
+ settings: { endusers: { tags: ['dupeTag', 'dupeTag'] } }
155
+ }, { replaceObjectFields: true })
156
+
157
+ await async_test(
158
+ "Updating cancelReasons succeeds even when tags has pre-existing duplicates",
159
+ () => sdk.api.organizations.updateOne(orgId, {
160
+ settings: { calendar: { cancelReasons: ['new reason'] } }
161
+ }, { replaceObjectFields: true }),
162
+ { shouldError: false, onResult: () => true }
163
+ )
164
+
165
+ // Clean up settings to avoid affecting other tests
166
+ await sdk.api.organizations.updateOne(orgId, {
167
+ settings: {
168
+ endusers: {
169
+ tags: [],
170
+ customFields: [],
171
+ builtinFields: [],
172
+ dontRecordCallsToPhone: [],
173
+ },
174
+ calendar: {
175
+ cancelReasons: [],
176
+ },
177
+ }
178
+ }, { replaceObjectFields: true })
179
+ }
180
+
181
+ // Allow running this test file independently
182
+ if (require.main === module) {
183
+ console.log(`Using API URL: ${host}`)
184
+ const sdk = new Session({ host })
185
+ const sdkNonAdmin = new Session({ host })
186
+
187
+ const runTests = async () => {
188
+ await setup_tests(sdk, sdkNonAdmin)
189
+ await organization_settings_duplicates_tests({ sdk, sdkNonAdmin })
190
+ }
191
+
192
+ runTests()
193
+ .then(() => {
194
+ console.log("✅ Organization settings duplicate validation tests completed successfully")
195
+ process.exit(0)
196
+ })
197
+ .catch((error) => {
198
+ console.error("❌ Organization settings duplicate validation tests failed:", error)
199
+ process.exit(1)
200
+ })
201
+ }
@@ -37,6 +37,7 @@ import {
37
37
  import { Session, APIQuery, EnduserSession } from "../sdk"
38
38
  import { enduser_observations_acknowledge_tests } from "./api_tests/enduser_observations_acknowledge.test"
39
39
  import { get_some_projection_tests } from "./api_tests/get_some_projection.test"
40
+ import { mdb_sort_tests } from "./api_tests/mdb_sort.test"
40
41
  import { create_user_notifications_trigger_tests } from "./api_tests/create_user_notifications_trigger.test"
41
42
  import { inbox_thread_assignment_updates_tests } from "./api_tests/inbox_thread_assignment_updates.test"
42
43
  import { inbox_thread_draft_scheduled_tests } from "./api_tests/inbox_thread_draft_scheduled.test"
@@ -88,6 +89,8 @@ import { load_team_chat_tests } from "./api_tests/load_team_chat.test";
88
89
  import { form_started_trigger_tests } from "./api_tests/form_started_trigger.test";
89
90
  import { medication_added_trigger_tests } from "./api_tests/medication_added_trigger.test";
90
91
  import { elation_user_id_tests } from "./api_tests/elation_user_id.test";
92
+ import { organization_settings_duplicates_tests } from "./api_tests/organization_settings_duplicates.test";
93
+ import { calendar_events_bulk_update_tests } from "./api_tests/calendar_events_bulk_update.test";
91
94
  import { openloop_webhooks_tests } from "./api_tests/openloop_webhooks.test";
92
95
  import { beluga_pharmacy_mappings_tests } from "./api_tests/beluga_pharmacy_mappings.test";
93
96
 
@@ -14123,6 +14126,8 @@ const ip_address_form_tests = async () => {
14123
14126
  await replace_enduser_template_values_tests()
14124
14127
  await mfa_tests()
14125
14128
  await setup_tests(sdk, sdkNonAdmin)
14129
+ await mdb_sort_tests({ sdk, sdkNonAdmin })
14130
+ await organization_settings_duplicates_tests({ sdk, sdkNonAdmin })
14126
14131
  await search_tests()
14127
14132
  await time_tracks_tests({ sdk, sdkNonAdmin })
14128
14133
  await time_tracks_historical_tests({ sdk, sdkNonAdmin })
@@ -14170,6 +14175,7 @@ const ip_address_form_tests = async () => {
14170
14175
  await rate_limit_tests()
14171
14176
  await ip_address_form_tests()
14172
14177
  await bulk_update_tests()
14178
+ await calendar_events_bulk_update_tests({ sdk })
14173
14179
  await cancel_upcoming_appointments_journey_action_test()
14174
14180
  await multi_tenant_tests() // should come right after setup tests
14175
14181
  await sync_tests_with_access_tags() // should come directly after setup to avoid extra sync values
Binary file