@tellescope/sdk 1.245.1 → 1.246.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/lib/cjs/tests/api_tests/account_switcher.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/account_switcher.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/account_switcher.test.js +445 -0
- package/lib/cjs/tests/api_tests/account_switcher.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/beluga_pharmacy_mappings.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/beluga_pharmacy_mappings.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/beluga_pharmacy_mappings.test.js +357 -0
- package/lib/cjs/tests/api_tests/beluga_pharmacy_mappings.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/calendar_event_limits.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/calendar_event_limits.test.js +163 -0
- package/lib/cjs/tests/api_tests/calendar_event_limits.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/calendar_events_bulk_update.test.d.ts +5 -0
- package/lib/cjs/tests/api_tests/calendar_events_bulk_update.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/calendar_events_bulk_update.test.js +483 -0
- package/lib/cjs/tests/api_tests/calendar_events_bulk_update.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/email_utils.test.d.ts +2 -0
- package/lib/cjs/tests/api_tests/email_utils.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/email_utils.test.js +141 -0
- package/lib/cjs/tests/api_tests/email_utils.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.js +268 -0
- package/lib/cjs/tests/api_tests/organization_settings_duplicates.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/time_tracks.test.d.ts +20 -0
- package/lib/cjs/tests/api_tests/time_tracks.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/time_tracks.test.js +692 -20
- package/lib/cjs/tests/api_tests/time_tracks.test.js.map +1 -1
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +157 -122
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/tests/api_tests/account_switcher.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/account_switcher.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/account_switcher.test.js +438 -0
- package/lib/esm/tests/api_tests/account_switcher.test.js.map +1 -0
- package/lib/esm/tests/api_tests/beluga_pharmacy_mappings.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/beluga_pharmacy_mappings.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/beluga_pharmacy_mappings.test.js +353 -0
- package/lib/esm/tests/api_tests/beluga_pharmacy_mappings.test.js.map +1 -0
- package/lib/esm/tests/api_tests/calendar_event_limits.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/calendar_event_limits.test.js +163 -0
- package/lib/esm/tests/api_tests/calendar_event_limits.test.js.map +1 -1
- package/lib/esm/tests/api_tests/calendar_events_bulk_update.test.d.ts +5 -0
- package/lib/esm/tests/api_tests/calendar_events_bulk_update.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/calendar_events_bulk_update.test.js +479 -0
- package/lib/esm/tests/api_tests/calendar_events_bulk_update.test.js.map +1 -0
- package/lib/esm/tests/api_tests/email_utils.test.d.ts +2 -0
- package/lib/esm/tests/api_tests/email_utils.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/email_utils.test.js +137 -0
- package/lib/esm/tests/api_tests/email_utils.test.js.map +1 -0
- package/lib/esm/tests/api_tests/organization_settings_duplicates.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/organization_settings_duplicates.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/organization_settings_duplicates.test.js +264 -0
- package/lib/esm/tests/api_tests/organization_settings_duplicates.test.js.map +1 -0
- package/lib/esm/tests/api_tests/time_tracks.test.d.ts +20 -0
- package/lib/esm/tests/api_tests/time_tracks.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/time_tracks.test.js +687 -20
- package/lib/esm/tests/api_tests/time_tracks.test.js.map +1 -1
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +158 -123
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/tests/api_tests/beluga_pharmacy_mappings.test.ts +351 -0
- package/src/tests/api_tests/calendar_event_limits.test.ts +195 -0
- package/src/tests/api_tests/time_tracks.test.ts +542 -16
- package/src/tests/tests.ts +34 -5
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,351 @@
|
|
|
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
|
+
import { evaluate_conditional_logic } from "@tellescope/utilities"
|
|
10
|
+
import { CompoundFilter, BelugaPharmacyMapping } from "@tellescope/types-models"
|
|
11
|
+
|
|
12
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
13
|
+
|
|
14
|
+
// Helper mimicking resolve_beluga_pharmacy_mapping evaluator
|
|
15
|
+
const evaluate_mapping_conditions = (
|
|
16
|
+
conditions: CompoundFilter<string>,
|
|
17
|
+
responses: { externalId?: string, value?: string }[]
|
|
18
|
+
) => evaluate_conditional_logic(conditions, (key, value) => {
|
|
19
|
+
const responseValue = responses.find(r => r.externalId === key)?.value
|
|
20
|
+
|
|
21
|
+
if (typeof value === 'string') {
|
|
22
|
+
return responseValue === value
|
|
23
|
+
}
|
|
24
|
+
if (typeof value === 'object' && value !== null) {
|
|
25
|
+
const operator = Object.keys(value)[0]
|
|
26
|
+
const operand = Object.values(value)[0]
|
|
27
|
+
|
|
28
|
+
if (operator === '$contains') {
|
|
29
|
+
return typeof responseValue === 'string' && responseValue.includes(String(operand))
|
|
30
|
+
}
|
|
31
|
+
if (operator === '$doesNotContain') {
|
|
32
|
+
return typeof responseValue === 'string' && !responseValue.includes(String(operand))
|
|
33
|
+
}
|
|
34
|
+
if (operator === '$exists') {
|
|
35
|
+
return operand ? responseValue !== undefined : responseValue === undefined
|
|
36
|
+
}
|
|
37
|
+
if (operator === '$ne') {
|
|
38
|
+
return responseValue !== String(operand)
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return false
|
|
43
|
+
})
|
|
44
|
+
|
|
45
|
+
// Helper to resolve first matching mapping
|
|
46
|
+
const resolve_mapping = (
|
|
47
|
+
mappings: BelugaPharmacyMapping[],
|
|
48
|
+
responses: { externalId?: string, value?: string }[]
|
|
49
|
+
): BelugaPharmacyMapping | undefined => {
|
|
50
|
+
for (const mapping of mappings) {
|
|
51
|
+
if (!mapping.conditions) continue
|
|
52
|
+
if (evaluate_mapping_conditions(mapping.conditions, responses)) {
|
|
53
|
+
return mapping
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return undefined
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export const beluga_pharmacy_mappings_tests = async ({ sdk, sdkNonAdmin }: { sdk: Session, sdkNonAdmin: Session }) => {
|
|
60
|
+
log_header("Beluga Pharmacy Mappings Tests")
|
|
61
|
+
|
|
62
|
+
// --- 6a. CRUD Tests ---
|
|
63
|
+
|
|
64
|
+
let testFormId: string | undefined
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
await async_test(
|
|
68
|
+
"Create form with belugaPharmacyMappings",
|
|
69
|
+
async () => {
|
|
70
|
+
const mappings: BelugaPharmacyMapping[] = [
|
|
71
|
+
{
|
|
72
|
+
pharmacyId: "pharmacy-001",
|
|
73
|
+
patientPreference: JSON.stringify([{ name: "Med A", strength: "10mg", quantity: "30", refills: "3", daysSupply: "30", sig: "Take once daily", dispenseUnit: "Tablet", medId: "med-001" }]),
|
|
74
|
+
conditions: { $and: [{ condition: { "state": "CA" } }, { condition: { "med_type": "weightLoss" } }] },
|
|
75
|
+
},
|
|
76
|
+
]
|
|
77
|
+
|
|
78
|
+
const form = await sdk.api.forms.createOne({
|
|
79
|
+
title: 'Beluga Pharmacy Mappings Test Form',
|
|
80
|
+
belugaPharmacyMappings: mappings,
|
|
81
|
+
})
|
|
82
|
+
testFormId = form.id
|
|
83
|
+
|
|
84
|
+
const fetched = await sdk.api.forms.getOne(form.id)
|
|
85
|
+
return (
|
|
86
|
+
fetched.belugaPharmacyMappings?.length === 1
|
|
87
|
+
&& fetched.belugaPharmacyMappings[0].pharmacyId === "pharmacy-001"
|
|
88
|
+
&& !!fetched.belugaPharmacyMappings[0].conditions?.$and
|
|
89
|
+
)
|
|
90
|
+
},
|
|
91
|
+
{ expectedResult: true }
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
await async_test(
|
|
95
|
+
"Update belugaPharmacyMappings on existing form",
|
|
96
|
+
async () => {
|
|
97
|
+
if (!testFormId) throw new Error("No test form")
|
|
98
|
+
|
|
99
|
+
const updatedMappings: BelugaPharmacyMapping[] = [
|
|
100
|
+
{
|
|
101
|
+
pharmacyId: "pharmacy-002",
|
|
102
|
+
patientPreference: JSON.stringify([{ name: "Med B", strength: "20mg", quantity: "60", refills: "2", daysSupply: "30", sig: "Take twice daily", dispenseUnit: "Capsule", medId: "med-002" }]),
|
|
103
|
+
conditions: { condition: { "state": "NY" } },
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
pharmacyId: "pharmacy-003",
|
|
107
|
+
patientPreference: "[]",
|
|
108
|
+
conditions: { $or: [{ condition: { "state": "TX" } }, { condition: { "state": "FL" } }] },
|
|
109
|
+
},
|
|
110
|
+
]
|
|
111
|
+
|
|
112
|
+
await sdk.api.forms.updateOne(testFormId, { belugaPharmacyMappings: updatedMappings }, { replaceObjectFields: true })
|
|
113
|
+
const fetched = await sdk.api.forms.getOne(testFormId)
|
|
114
|
+
return (
|
|
115
|
+
fetched.belugaPharmacyMappings?.length === 2
|
|
116
|
+
&& fetched.belugaPharmacyMappings[0].pharmacyId === "pharmacy-002"
|
|
117
|
+
&& fetched.belugaPharmacyMappings[1].pharmacyId === "pharmacy-003"
|
|
118
|
+
)
|
|
119
|
+
},
|
|
120
|
+
{ expectedResult: true }
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
await async_test(
|
|
124
|
+
"Save with nested CompoundFilter structure",
|
|
125
|
+
async () => {
|
|
126
|
+
if (!testFormId) throw new Error("No test form")
|
|
127
|
+
|
|
128
|
+
const nestedMappings: BelugaPharmacyMapping[] = [
|
|
129
|
+
{
|
|
130
|
+
pharmacyId: "pharmacy-nested",
|
|
131
|
+
patientPreference: "[]",
|
|
132
|
+
conditions: {
|
|
133
|
+
$and: [
|
|
134
|
+
{ $or: [{ condition: { "state": "CA" } }, { condition: { "state": "NY" } }] },
|
|
135
|
+
{ condition: { "med_type": "weightLoss" } },
|
|
136
|
+
]
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
]
|
|
140
|
+
|
|
141
|
+
await sdk.api.forms.updateOne(testFormId, { belugaPharmacyMappings: nestedMappings }, { replaceObjectFields: true })
|
|
142
|
+
const fetched = await sdk.api.forms.getOne(testFormId)
|
|
143
|
+
return (
|
|
144
|
+
fetched.belugaPharmacyMappings?.length === 1
|
|
145
|
+
&& fetched.belugaPharmacyMappings[0].pharmacyId === "pharmacy-nested"
|
|
146
|
+
&& !!fetched.belugaPharmacyMappings[0].conditions?.$and
|
|
147
|
+
)
|
|
148
|
+
},
|
|
149
|
+
{ expectedResult: true }
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
// --- 6b. Conditional logic evaluation tests ---
|
|
153
|
+
|
|
154
|
+
await async_test(
|
|
155
|
+
"Simple equality: condition matches response",
|
|
156
|
+
async () => {
|
|
157
|
+
return evaluate_mapping_conditions(
|
|
158
|
+
{ condition: { "state": "CA" } },
|
|
159
|
+
[{ externalId: "state", value: "CA" }]
|
|
160
|
+
)
|
|
161
|
+
},
|
|
162
|
+
{ expectedResult: true }
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
await async_test(
|
|
166
|
+
"Simple equality miss: condition does not match response",
|
|
167
|
+
async () => {
|
|
168
|
+
return evaluate_mapping_conditions(
|
|
169
|
+
{ condition: { "state": "CA" } },
|
|
170
|
+
[{ externalId: "state", value: "NY" }]
|
|
171
|
+
)
|
|
172
|
+
},
|
|
173
|
+
{ expectedResult: false }
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
await async_test(
|
|
177
|
+
"$and: two conditions, both match",
|
|
178
|
+
async () => {
|
|
179
|
+
return evaluate_mapping_conditions(
|
|
180
|
+
{ $and: [{ condition: { "state": "CA" } }, { condition: { "med_type": "weightLoss" } }] },
|
|
181
|
+
[{ externalId: "state", value: "CA" }, { externalId: "med_type", value: "weightLoss" }]
|
|
182
|
+
)
|
|
183
|
+
},
|
|
184
|
+
{ expectedResult: true }
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
await async_test(
|
|
188
|
+
"$and: two conditions, one misses",
|
|
189
|
+
async () => {
|
|
190
|
+
return evaluate_mapping_conditions(
|
|
191
|
+
{ $and: [{ condition: { "state": "CA" } }, { condition: { "med_type": "weightLoss" } }] },
|
|
192
|
+
[{ externalId: "state", value: "CA" }, { externalId: "med_type", value: "skincare" }]
|
|
193
|
+
)
|
|
194
|
+
},
|
|
195
|
+
{ expectedResult: false }
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
await async_test(
|
|
199
|
+
"$or: two conditions, one matches",
|
|
200
|
+
async () => {
|
|
201
|
+
return evaluate_mapping_conditions(
|
|
202
|
+
{ $or: [{ condition: { "state": "CA" } }, { condition: { "state": "NY" } }] },
|
|
203
|
+
[{ externalId: "state", value: "NY" }]
|
|
204
|
+
)
|
|
205
|
+
},
|
|
206
|
+
{ expectedResult: true }
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
await async_test(
|
|
210
|
+
"$or: two conditions, neither matches",
|
|
211
|
+
async () => {
|
|
212
|
+
return evaluate_mapping_conditions(
|
|
213
|
+
{ $or: [{ condition: { "state": "CA" } }, { condition: { "state": "NY" } }] },
|
|
214
|
+
[{ externalId: "state", value: "TX" }]
|
|
215
|
+
)
|
|
216
|
+
},
|
|
217
|
+
{ expectedResult: false }
|
|
218
|
+
)
|
|
219
|
+
|
|
220
|
+
await async_test(
|
|
221
|
+
"$contains: value includes substring",
|
|
222
|
+
async () => {
|
|
223
|
+
return evaluate_mapping_conditions(
|
|
224
|
+
{ condition: { "meds": { $contains: "GLP" } as any } },
|
|
225
|
+
[{ externalId: "meds", value: "GLP-1 agonist" }]
|
|
226
|
+
)
|
|
227
|
+
},
|
|
228
|
+
{ expectedResult: true }
|
|
229
|
+
)
|
|
230
|
+
|
|
231
|
+
await async_test(
|
|
232
|
+
"$ne: value does not equal",
|
|
233
|
+
async () => {
|
|
234
|
+
return evaluate_mapping_conditions(
|
|
235
|
+
{ condition: { "state": { $ne: "CA" } as any } },
|
|
236
|
+
[{ externalId: "state", value: "NY" }]
|
|
237
|
+
)
|
|
238
|
+
},
|
|
239
|
+
{ expectedResult: true }
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
await async_test(
|
|
243
|
+
"$exists: field present",
|
|
244
|
+
async () => {
|
|
245
|
+
return evaluate_mapping_conditions(
|
|
246
|
+
{ condition: { "pharmacyOverride": { $exists: true } as any } },
|
|
247
|
+
[{ externalId: "pharmacyOverride", value: "some-value" }]
|
|
248
|
+
)
|
|
249
|
+
},
|
|
250
|
+
{ expectedResult: true }
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
await async_test(
|
|
254
|
+
"$exists: field absent",
|
|
255
|
+
async () => {
|
|
256
|
+
return evaluate_mapping_conditions(
|
|
257
|
+
{ condition: { "pharmacyOverride": { $exists: true } as any } },
|
|
258
|
+
[{ externalId: "other_field", value: "some-value" }]
|
|
259
|
+
)
|
|
260
|
+
},
|
|
261
|
+
{ expectedResult: false }
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
await async_test(
|
|
265
|
+
"Nested compound: $and with $or",
|
|
266
|
+
async () => {
|
|
267
|
+
return evaluate_mapping_conditions(
|
|
268
|
+
{
|
|
269
|
+
$and: [
|
|
270
|
+
{ $or: [{ condition: { "state": "CA" } }, { condition: { "state": "NY" } }] },
|
|
271
|
+
{ condition: { "med_type": "weightLoss" } },
|
|
272
|
+
]
|
|
273
|
+
},
|
|
274
|
+
[{ externalId: "state", value: "NY" }, { externalId: "med_type", value: "weightLoss" }]
|
|
275
|
+
)
|
|
276
|
+
},
|
|
277
|
+
{ expectedResult: true }
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
await async_test(
|
|
281
|
+
"First-match-wins: multiple mappings, first matching returned",
|
|
282
|
+
async () => {
|
|
283
|
+
const mappings: BelugaPharmacyMapping[] = [
|
|
284
|
+
{
|
|
285
|
+
pharmacyId: "pharmacy-first",
|
|
286
|
+
patientPreference: "[]",
|
|
287
|
+
conditions: { condition: { "state": "CA" } },
|
|
288
|
+
},
|
|
289
|
+
{
|
|
290
|
+
pharmacyId: "pharmacy-second",
|
|
291
|
+
patientPreference: "[]",
|
|
292
|
+
conditions: { condition: { "state": "CA" } },
|
|
293
|
+
},
|
|
294
|
+
]
|
|
295
|
+
|
|
296
|
+
const result = resolve_mapping(
|
|
297
|
+
mappings,
|
|
298
|
+
[{ externalId: "state", value: "CA" }]
|
|
299
|
+
)
|
|
300
|
+
return result?.pharmacyId === "pharmacy-first"
|
|
301
|
+
},
|
|
302
|
+
{ expectedResult: true }
|
|
303
|
+
)
|
|
304
|
+
|
|
305
|
+
await async_test(
|
|
306
|
+
"No match: no mappings match, returns undefined",
|
|
307
|
+
async () => {
|
|
308
|
+
const mappings: BelugaPharmacyMapping[] = [
|
|
309
|
+
{
|
|
310
|
+
pharmacyId: "pharmacy-ca",
|
|
311
|
+
patientPreference: "[]",
|
|
312
|
+
conditions: { condition: { "state": "CA" } },
|
|
313
|
+
},
|
|
314
|
+
]
|
|
315
|
+
|
|
316
|
+
const result = resolve_mapping(
|
|
317
|
+
mappings,
|
|
318
|
+
[{ externalId: "state", value: "TX" }]
|
|
319
|
+
)
|
|
320
|
+
return result === undefined
|
|
321
|
+
},
|
|
322
|
+
{ expectedResult: true }
|
|
323
|
+
)
|
|
324
|
+
} finally {
|
|
325
|
+
// Cleanup
|
|
326
|
+
if (testFormId) {
|
|
327
|
+
await sdk.api.forms.deleteOne(testFormId).catch(console.error)
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
if (require.main === module) {
|
|
333
|
+
console.log(`Using API URL: ${host}`)
|
|
334
|
+
const sdk = new Session({ host })
|
|
335
|
+
const sdkNonAdmin = new Session({ host })
|
|
336
|
+
|
|
337
|
+
const runTests = async () => {
|
|
338
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
339
|
+
await beluga_pharmacy_mappings_tests({ sdk, sdkNonAdmin })
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
runTests()
|
|
343
|
+
.then(() => {
|
|
344
|
+
console.log("Beluga pharmacy mappings test suite completed successfully")
|
|
345
|
+
process.exit(0)
|
|
346
|
+
})
|
|
347
|
+
.catch((error) => {
|
|
348
|
+
console.error("Beluga pharmacy mappings test suite failed:", error)
|
|
349
|
+
process.exit(1)
|
|
350
|
+
})
|
|
351
|
+
}
|
|
@@ -685,6 +685,201 @@ export const calendar_event_limits_unit_tests = () => {
|
|
|
685
685
|
}
|
|
686
686
|
})
|
|
687
687
|
|
|
688
|
+
// === otherTemplateIds (OR logic) tests ===
|
|
689
|
+
|
|
690
|
+
const TEMPLATE_B = "templateB456"
|
|
691
|
+
const TEMPLATE_C = "templateC789"
|
|
692
|
+
|
|
693
|
+
// Test 21: otherTemplateIds - booking a secondary template should count toward the limit
|
|
694
|
+
tests.push({
|
|
695
|
+
name: 'otherTemplateIds - secondary template counts toward limit',
|
|
696
|
+
fn: () => {
|
|
697
|
+
const oct1 = new Date('2025-10-01T18:00:00.000Z').getTime()
|
|
698
|
+
|
|
699
|
+
const limits: CalendarEventLimit[] = [{
|
|
700
|
+
templateId: TEMPLATE_ID,
|
|
701
|
+
otherTemplateIds: [TEMPLATE_B],
|
|
702
|
+
period: 1,
|
|
703
|
+
limit: 1,
|
|
704
|
+
}]
|
|
705
|
+
|
|
706
|
+
const existingEvents = [
|
|
707
|
+
createEvent(oct1, TEMPLATE_ID), // 1 event of primary template
|
|
708
|
+
]
|
|
709
|
+
|
|
710
|
+
// Booking template B should be blocked because the shared limit is reached
|
|
711
|
+
const result = slot_violates_calendar_event_limits({
|
|
712
|
+
slotStartTimeInMS: oct1,
|
|
713
|
+
templateId: TEMPLATE_B,
|
|
714
|
+
userId: USER_ID,
|
|
715
|
+
calendarEventLimits: limits,
|
|
716
|
+
existingEvents,
|
|
717
|
+
timezone: 'America/New_York',
|
|
718
|
+
})
|
|
719
|
+
|
|
720
|
+
assertEqual(result, true, 'Should block secondary template when shared limit reached')
|
|
721
|
+
}
|
|
722
|
+
})
|
|
723
|
+
|
|
724
|
+
// Test 22: otherTemplateIds - events of secondary template count toward primary booking
|
|
725
|
+
tests.push({
|
|
726
|
+
name: 'otherTemplateIds - secondary template events count when booking primary',
|
|
727
|
+
fn: () => {
|
|
728
|
+
const oct1 = new Date('2025-10-01T18:00:00.000Z').getTime()
|
|
729
|
+
|
|
730
|
+
const limits: CalendarEventLimit[] = [{
|
|
731
|
+
templateId: TEMPLATE_ID,
|
|
732
|
+
otherTemplateIds: [TEMPLATE_B],
|
|
733
|
+
period: 1,
|
|
734
|
+
limit: 1,
|
|
735
|
+
}]
|
|
736
|
+
|
|
737
|
+
const existingEvents = [
|
|
738
|
+
createEvent(oct1, TEMPLATE_B), // 1 event of secondary template
|
|
739
|
+
]
|
|
740
|
+
|
|
741
|
+
// Booking primary template should be blocked because a secondary template event used the limit
|
|
742
|
+
const result = slot_violates_calendar_event_limits({
|
|
743
|
+
slotStartTimeInMS: oct1,
|
|
744
|
+
templateId: TEMPLATE_ID,
|
|
745
|
+
userId: USER_ID,
|
|
746
|
+
calendarEventLimits: limits,
|
|
747
|
+
existingEvents,
|
|
748
|
+
timezone: 'America/New_York',
|
|
749
|
+
})
|
|
750
|
+
|
|
751
|
+
assertEqual(result, true, 'Should block primary template when secondary events fill limit')
|
|
752
|
+
}
|
|
753
|
+
})
|
|
754
|
+
|
|
755
|
+
// Test 23: otherTemplateIds - mixed events from both templates count together
|
|
756
|
+
tests.push({
|
|
757
|
+
name: 'otherTemplateIds - mixed events count toward shared limit',
|
|
758
|
+
fn: () => {
|
|
759
|
+
const oct1_9am = new Date('2025-10-01T13:00:00.000Z').getTime()
|
|
760
|
+
const oct1_2pm = new Date('2025-10-01T18:00:00.000Z').getTime()
|
|
761
|
+
const oct1_5pm = new Date('2025-10-01T21:00:00.000Z').getTime()
|
|
762
|
+
|
|
763
|
+
const limits: CalendarEventLimit[] = [{
|
|
764
|
+
templateId: TEMPLATE_ID,
|
|
765
|
+
otherTemplateIds: [TEMPLATE_B],
|
|
766
|
+
period: 1,
|
|
767
|
+
limit: 2,
|
|
768
|
+
}]
|
|
769
|
+
|
|
770
|
+
const existingEvents = [
|
|
771
|
+
createEvent(oct1_9am, TEMPLATE_ID), // 1 primary
|
|
772
|
+
createEvent(oct1_2pm, TEMPLATE_B), // 1 secondary
|
|
773
|
+
]
|
|
774
|
+
|
|
775
|
+
// Both count toward limit of 2, so a 3rd should be blocked
|
|
776
|
+
const result = slot_violates_calendar_event_limits({
|
|
777
|
+
slotStartTimeInMS: oct1_5pm,
|
|
778
|
+
templateId: TEMPLATE_ID,
|
|
779
|
+
userId: USER_ID,
|
|
780
|
+
calendarEventLimits: limits,
|
|
781
|
+
existingEvents,
|
|
782
|
+
timezone: 'America/New_York',
|
|
783
|
+
})
|
|
784
|
+
|
|
785
|
+
assertEqual(result, true, 'Should block when mixed events reach shared limit')
|
|
786
|
+
}
|
|
787
|
+
})
|
|
788
|
+
|
|
789
|
+
// Test 24: otherTemplateIds - unrelated template should NOT be affected
|
|
790
|
+
tests.push({
|
|
791
|
+
name: 'otherTemplateIds - unrelated template not affected by shared limit',
|
|
792
|
+
fn: () => {
|
|
793
|
+
const oct1 = new Date('2025-10-01T18:00:00.000Z').getTime()
|
|
794
|
+
|
|
795
|
+
const limits: CalendarEventLimit[] = [{
|
|
796
|
+
templateId: TEMPLATE_ID,
|
|
797
|
+
otherTemplateIds: [TEMPLATE_B],
|
|
798
|
+
period: 1,
|
|
799
|
+
limit: 1,
|
|
800
|
+
}]
|
|
801
|
+
|
|
802
|
+
const existingEvents = [
|
|
803
|
+
createEvent(oct1, TEMPLATE_ID), // Limit reached for A+B group
|
|
804
|
+
]
|
|
805
|
+
|
|
806
|
+
// Template C is not part of the group, should be allowed
|
|
807
|
+
const result = slot_violates_calendar_event_limits({
|
|
808
|
+
slotStartTimeInMS: oct1,
|
|
809
|
+
templateId: TEMPLATE_C,
|
|
810
|
+
userId: USER_ID,
|
|
811
|
+
calendarEventLimits: limits,
|
|
812
|
+
existingEvents,
|
|
813
|
+
timezone: 'America/New_York',
|
|
814
|
+
})
|
|
815
|
+
|
|
816
|
+
assertEqual(result, false, 'Should allow unrelated template even when shared limit reached')
|
|
817
|
+
}
|
|
818
|
+
})
|
|
819
|
+
|
|
820
|
+
// Test 25: otherTemplateIds - under limit should allow
|
|
821
|
+
tests.push({
|
|
822
|
+
name: 'otherTemplateIds - under shared limit should allow',
|
|
823
|
+
fn: () => {
|
|
824
|
+
const oct1_9am = new Date('2025-10-01T13:00:00.000Z').getTime()
|
|
825
|
+
const oct1_2pm = new Date('2025-10-01T18:00:00.000Z').getTime()
|
|
826
|
+
|
|
827
|
+
const limits: CalendarEventLimit[] = [{
|
|
828
|
+
templateId: TEMPLATE_ID,
|
|
829
|
+
otherTemplateIds: [TEMPLATE_B],
|
|
830
|
+
period: 1,
|
|
831
|
+
limit: 3,
|
|
832
|
+
}]
|
|
833
|
+
|
|
834
|
+
const existingEvents = [
|
|
835
|
+
createEvent(oct1_9am, TEMPLATE_ID), // 1 event
|
|
836
|
+
createEvent(oct1_9am, TEMPLATE_B), // 1 event, total = 2
|
|
837
|
+
]
|
|
838
|
+
|
|
839
|
+
// Under limit of 3, should allow
|
|
840
|
+
const result = slot_violates_calendar_event_limits({
|
|
841
|
+
slotStartTimeInMS: oct1_2pm,
|
|
842
|
+
templateId: TEMPLATE_B,
|
|
843
|
+
userId: USER_ID,
|
|
844
|
+
calendarEventLimits: limits,
|
|
845
|
+
existingEvents,
|
|
846
|
+
timezone: 'America/New_York',
|
|
847
|
+
})
|
|
848
|
+
|
|
849
|
+
assertEqual(result, false, 'Should allow when under shared limit')
|
|
850
|
+
}
|
|
851
|
+
})
|
|
852
|
+
|
|
853
|
+
// Test 26: No otherTemplateIds - backward compatibility
|
|
854
|
+
tests.push({
|
|
855
|
+
name: 'No otherTemplateIds - backward compatible single-template behavior',
|
|
856
|
+
fn: () => {
|
|
857
|
+
const oct1 = new Date('2025-10-01T18:00:00.000Z').getTime()
|
|
858
|
+
|
|
859
|
+
const limits: CalendarEventLimit[] = [{
|
|
860
|
+
templateId: TEMPLATE_ID,
|
|
861
|
+
period: 1,
|
|
862
|
+
limit: 1,
|
|
863
|
+
}]
|
|
864
|
+
|
|
865
|
+
const existingEvents = [
|
|
866
|
+
createEvent(oct1, TEMPLATE_B), // Event of different template
|
|
867
|
+
]
|
|
868
|
+
|
|
869
|
+
// Without otherTemplateIds, template B events should NOT count toward template A's limit
|
|
870
|
+
const result = slot_violates_calendar_event_limits({
|
|
871
|
+
slotStartTimeInMS: oct1,
|
|
872
|
+
templateId: TEMPLATE_ID,
|
|
873
|
+
userId: USER_ID,
|
|
874
|
+
calendarEventLimits: limits,
|
|
875
|
+
existingEvents,
|
|
876
|
+
timezone: 'America/New_York',
|
|
877
|
+
})
|
|
878
|
+
|
|
879
|
+
assertEqual(result, false, 'Should not count other template events without otherTemplateIds')
|
|
880
|
+
}
|
|
881
|
+
})
|
|
882
|
+
|
|
688
883
|
return tests
|
|
689
884
|
}
|
|
690
885
|
|