@tellescope/sdk 1.250.1 → 1.251.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.
- package/lib/cjs/sdk.d.ts +9 -0
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +3 -0
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/tests/api_tests/account_switcher.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/account_switcher.test.js +1700 -306
- package/lib/cjs/tests/api_tests/account_switcher.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.js +28 -15
- package/lib/cjs/tests/api_tests/enduser_cross_access_isolation.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/enduser_login.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/enduser_login.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/enduser_login.test.js +315 -0
- package/lib/cjs/tests/api_tests/enduser_login.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/medication_added_trigger.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/medication_added_trigger.test.js +556 -105
- package/lib/cjs/tests/api_tests/medication_added_trigger.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/outbound_chat_sent_trigger.test.d.ts +7 -0
- package/lib/cjs/tests/api_tests/outbound_chat_sent_trigger.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/outbound_chat_sent_trigger.test.js +436 -0
- package/lib/cjs/tests/api_tests/outbound_chat_sent_trigger.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/push_forms_to_portal_group_completion.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/push_forms_to_portal_group_completion.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/push_forms_to_portal_group_completion.test.js +370 -0
- package/lib/cjs/tests/api_tests/push_forms_to_portal_group_completion.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/set_fields_order_templates.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js +373 -0
- package/lib/cjs/tests/api_tests/set_fields_order_templates.test.js.map +1 -0
- package/lib/cjs/tests/setup.d.ts.map +1 -1
- package/lib/cjs/tests/setup.js +47 -32
- package/lib/cjs/tests/setup.js.map +1 -1
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +190 -161
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/cjs/tests/unit_tests/conditional_logic_medication.test.d.ts +3 -0
- package/lib/cjs/tests/unit_tests/conditional_logic_medication.test.d.ts.map +1 -0
- package/lib/cjs/tests/unit_tests/conditional_logic_medication.test.js +114 -0
- package/lib/cjs/tests/unit_tests/conditional_logic_medication.test.js.map +1 -0
- package/lib/esm/sdk.d.ts +9 -0
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +3 -0
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/tests/api_tests/account_switcher.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/account_switcher.test.js +1702 -305
- package/lib/esm/tests/api_tests/account_switcher.test.js.map +1 -1
- package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.js +28 -15
- package/lib/esm/tests/api_tests/enduser_cross_access_isolation.test.js.map +1 -1
- package/lib/esm/tests/api_tests/enduser_login.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/enduser_login.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/enduser_login.test.js +308 -0
- package/lib/esm/tests/api_tests/enduser_login.test.js.map +1 -0
- package/lib/esm/tests/api_tests/enduser_login_phi_disclosure.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/enduser_login_phi_disclosure.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/enduser_login_phi_disclosure.test.js +268 -0
- package/lib/esm/tests/api_tests/enduser_login_phi_disclosure.test.js.map +1 -0
- package/lib/esm/tests/api_tests/medication_added_trigger.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/medication_added_trigger.test.js +556 -105
- package/lib/esm/tests/api_tests/medication_added_trigger.test.js.map +1 -1
- package/lib/esm/tests/api_tests/outbound_chat_sent_trigger.test.d.ts +7 -0
- package/lib/esm/tests/api_tests/outbound_chat_sent_trigger.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/outbound_chat_sent_trigger.test.js +432 -0
- package/lib/esm/tests/api_tests/outbound_chat_sent_trigger.test.js.map +1 -0
- package/lib/esm/tests/api_tests/push_forms_to_portal_group_completion.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/push_forms_to_portal_group_completion.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/push_forms_to_portal_group_completion.test.js +366 -0
- package/lib/esm/tests/api_tests/push_forms_to_portal_group_completion.test.js.map +1 -0
- package/lib/esm/tests/api_tests/set_fields_order_templates.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/set_fields_order_templates.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/set_fields_order_templates.test.js +369 -0
- package/lib/esm/tests/api_tests/set_fields_order_templates.test.js.map +1 -0
- package/lib/esm/tests/setup.d.ts.map +1 -1
- package/lib/esm/tests/setup.js +47 -32
- package/lib/esm/tests/setup.js.map +1 -1
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +190 -161
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/esm/tests/unit_tests/conditional_logic_medication.test.d.ts +3 -0
- package/lib/esm/tests/unit_tests/conditional_logic_medication.test.d.ts.map +1 -0
- package/lib/esm/tests/unit_tests/conditional_logic_medication.test.js +111 -0
- package/lib/esm/tests/unit_tests/conditional_logic_medication.test.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/sdk.ts +12 -0
- package/src/tests/api_tests/account_switcher.test.ts +1283 -0
- package/src/tests/api_tests/enduser_cross_access_isolation.test.ts +26 -0
- package/src/tests/api_tests/enduser_login.test.ts +215 -0
- package/src/tests/api_tests/medication_added_trigger.test.ts +345 -4
- package/src/tests/api_tests/outbound_chat_sent_trigger.test.ts +339 -0
- package/src/tests/api_tests/push_forms_to_portal_group_completion.test.ts +198 -0
- package/src/tests/api_tests/set_fields_order_templates.test.ts +258 -0
- package/src/tests/setup.ts +8 -1
- package/src/tests/tests.ts +23 -6
- package/src/tests/unit_tests/conditional_logic_medication.test.ts +133 -0
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import {
|
|
5
|
+
assert,
|
|
6
|
+
async_test,
|
|
7
|
+
log_header,
|
|
8
|
+
wait,
|
|
9
|
+
} from "@tellescope/testing"
|
|
10
|
+
import { setup_tests } from "../setup"
|
|
11
|
+
|
|
12
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
13
|
+
|
|
14
|
+
const TRIGGER_TITLE_BLOCK_A = "Order Templates: Block A"
|
|
15
|
+
const TRIGGER_TITLE_BLOCK_B = "Order Templates: Block B"
|
|
16
|
+
|
|
17
|
+
const buildSetFieldsAction = (prefix: string) => ({
|
|
18
|
+
type: 'Set Fields' as const,
|
|
19
|
+
info: {
|
|
20
|
+
fields: [
|
|
21
|
+
{ name: `${prefix}_status`, value: '{{order.status}}', type: 'Custom Value' as const },
|
|
22
|
+
{ name: `${prefix}_tracking`, value: '{{order.tracking}}', type: 'Custom Value' as const },
|
|
23
|
+
{ name: `${prefix}_carrier`, value: '{{order.carrier}}', type: 'Custom Value' as const },
|
|
24
|
+
{ name: `${prefix}_sku`, value: '{{order.sku}}', type: 'Custom Value' as const },
|
|
25
|
+
{ name: `${prefix}_externalId`, value: '{{order.externalId}}', type: 'Custom Value' as const },
|
|
26
|
+
{ name: `${prefix}_id`, value: '{{order.id}}', type: 'Custom Value' as const },
|
|
27
|
+
{ name: `${prefix}_protocol`, value: '{{order.protocol}}', type: 'Custom Value' as const },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
// Block A: Direct EnduserOrder creation path
|
|
33
|
+
const direct_order_creation_block = async ({ sdk }: { sdk: Session }) => {
|
|
34
|
+
log_header("Block A: Direct EnduserOrder creation -> {{order.*}} in Set Fields")
|
|
35
|
+
|
|
36
|
+
const enduser = await sdk.api.endusers.createOne({})
|
|
37
|
+
const trigger = await sdk.api.automation_triggers.createOne({
|
|
38
|
+
title: TRIGGER_TITLE_BLOCK_A,
|
|
39
|
+
status: 'Active',
|
|
40
|
+
event: { type: 'Order Status Equals', info: { source: 'Beluga', status: 'Shipped' } },
|
|
41
|
+
action: buildSetFieldsAction('pharmacy'),
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
let createdOrderId: string | undefined
|
|
45
|
+
let nonMatchingOrderId: string | undefined
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const order = await sdk.api.enduser_orders.createOne({
|
|
49
|
+
enduserId: enduser.id,
|
|
50
|
+
source: 'Beluga',
|
|
51
|
+
status: 'Shipped',
|
|
52
|
+
title: 'Beluga Pharmacy Order',
|
|
53
|
+
externalId: 'EXT-A-123',
|
|
54
|
+
tracking: '1Z-AAA',
|
|
55
|
+
carrier: 'UPS',
|
|
56
|
+
sku: 'SKU-A',
|
|
57
|
+
protocol: 'wl1',
|
|
58
|
+
})
|
|
59
|
+
createdOrderId = order.id
|
|
60
|
+
|
|
61
|
+
await wait(undefined, 250) // allow trigger + Set Fields to run
|
|
62
|
+
|
|
63
|
+
await async_test(
|
|
64
|
+
"Block A: {{order.*}} templates resolve to literal values",
|
|
65
|
+
() => sdk.api.endusers.getOne(enduser.id),
|
|
66
|
+
{ onResult: e => !!(
|
|
67
|
+
e.fields?.pharmacy_status === 'Shipped'
|
|
68
|
+
&& e.fields?.pharmacy_tracking === '1Z-AAA'
|
|
69
|
+
&& e.fields?.pharmacy_carrier === 'UPS'
|
|
70
|
+
&& e.fields?.pharmacy_sku === 'SKU-A'
|
|
71
|
+
&& e.fields?.pharmacy_externalId === 'EXT-A-123'
|
|
72
|
+
&& e.fields?.pharmacy_protocol === 'wl1'
|
|
73
|
+
&& typeof e.fields?.pharmacy_id === 'string'
|
|
74
|
+
&& (e.fields?.pharmacy_id as string).length > 0
|
|
75
|
+
&& (e.fields?.pharmacy_id as string) === order.id
|
|
76
|
+
)}
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
// Negative case: status that doesn't match the trigger should NOT alter fields
|
|
80
|
+
const beforeNonMatch = await sdk.api.endusers.getOne(enduser.id)
|
|
81
|
+
const snapshot = {
|
|
82
|
+
pharmacy_status: beforeNonMatch.fields?.pharmacy_status,
|
|
83
|
+
pharmacy_tracking: beforeNonMatch.fields?.pharmacy_tracking,
|
|
84
|
+
pharmacy_carrier: beforeNonMatch.fields?.pharmacy_carrier,
|
|
85
|
+
pharmacy_sku: beforeNonMatch.fields?.pharmacy_sku,
|
|
86
|
+
pharmacy_externalId: beforeNonMatch.fields?.pharmacy_externalId,
|
|
87
|
+
pharmacy_id: beforeNonMatch.fields?.pharmacy_id,
|
|
88
|
+
pharmacy_protocol: beforeNonMatch.fields?.pharmacy_protocol,
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const nonMatching = await sdk.api.enduser_orders.createOne({
|
|
92
|
+
enduserId: enduser.id,
|
|
93
|
+
source: 'Beluga',
|
|
94
|
+
status: 'In Fulfillment', // does NOT match the trigger
|
|
95
|
+
title: 'Beluga Pharmacy Order (other status)',
|
|
96
|
+
externalId: 'EXT-A-NOMATCH',
|
|
97
|
+
tracking: 'NOPE',
|
|
98
|
+
carrier: 'NoCarrier',
|
|
99
|
+
sku: 'SKU-NOPE',
|
|
100
|
+
protocol: 'nope',
|
|
101
|
+
})
|
|
102
|
+
nonMatchingOrderId = nonMatching.id
|
|
103
|
+
|
|
104
|
+
await wait(undefined, 250)
|
|
105
|
+
|
|
106
|
+
await async_test(
|
|
107
|
+
"Block A: non-matching status leaves fields unchanged",
|
|
108
|
+
() => sdk.api.endusers.getOne(enduser.id),
|
|
109
|
+
{ onResult: e => !!(
|
|
110
|
+
e.fields?.pharmacy_status === snapshot.pharmacy_status
|
|
111
|
+
&& e.fields?.pharmacy_tracking === snapshot.pharmacy_tracking
|
|
112
|
+
&& e.fields?.pharmacy_carrier === snapshot.pharmacy_carrier
|
|
113
|
+
&& e.fields?.pharmacy_sku === snapshot.pharmacy_sku
|
|
114
|
+
&& e.fields?.pharmacy_externalId === snapshot.pharmacy_externalId
|
|
115
|
+
&& e.fields?.pharmacy_id === snapshot.pharmacy_id
|
|
116
|
+
&& e.fields?.pharmacy_protocol === snapshot.pharmacy_protocol
|
|
117
|
+
)}
|
|
118
|
+
)
|
|
119
|
+
} finally {
|
|
120
|
+
if (createdOrderId) await sdk.api.enduser_orders.deleteOne(createdOrderId).catch(console.error)
|
|
121
|
+
if (nonMatchingOrderId) await sdk.api.enduser_orders.deleteOne(nonMatchingOrderId).catch(console.error)
|
|
122
|
+
await sdk.api.automation_triggers.deleteOne(trigger.id).catch(console.error)
|
|
123
|
+
await sdk.api.endusers.deleteOne(enduser.id).catch(console.error)
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Block B: Beluga webhook integration path
|
|
128
|
+
const beluga_webhook_block = async ({ sdk }: { sdk: Session }) => {
|
|
129
|
+
log_header("Block B: Beluga webhook -> {{order.*}} in Set Fields")
|
|
130
|
+
|
|
131
|
+
const webhookUrl = `${host}/v1/webhooks/beluga`
|
|
132
|
+
const externalOrderId = `EXT-B-${Date.now()}`
|
|
133
|
+
|
|
134
|
+
const enduser = await sdk.api.endusers.createOne({})
|
|
135
|
+
const form = await sdk.api.forms.createOne({ title: 'Order Templates Beluga Form' })
|
|
136
|
+
const formResponse = await sdk.api.form_responses.createOne({
|
|
137
|
+
formId: form.id,
|
|
138
|
+
enduserId: enduser.id,
|
|
139
|
+
formTitle: form.title,
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
const trigger = await sdk.api.automation_triggers.createOne({
|
|
143
|
+
title: TRIGGER_TITLE_BLOCK_B,
|
|
144
|
+
status: 'Active',
|
|
145
|
+
event: { type: 'Order Status Equals', info: { source: 'Beluga', status: 'Shipped' } },
|
|
146
|
+
action: buildSetFieldsAction('pharmacy'),
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
const deliveredTrigger = await sdk.api.automation_triggers.createOne({
|
|
150
|
+
title: `${TRIGGER_TITLE_BLOCK_B} (Delivered)`,
|
|
151
|
+
status: 'Active',
|
|
152
|
+
event: { type: 'Order Status Equals', info: { source: 'Beluga', status: 'Delivered' } },
|
|
153
|
+
action: buildSetFieldsAction('pharmacy'),
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
try {
|
|
157
|
+
// Step 1: PHARMACY_ORDER_SHIPPED
|
|
158
|
+
const shippedRes = await fetch(webhookUrl, {
|
|
159
|
+
method: 'POST',
|
|
160
|
+
headers: { 'Content-Type': 'application/json' },
|
|
161
|
+
body: JSON.stringify({
|
|
162
|
+
masterId: `tellescope_${formResponse.id}`,
|
|
163
|
+
event: 'PHARMACY_ORDER_SHIPPED',
|
|
164
|
+
orderId: externalOrderId,
|
|
165
|
+
info: { carrier: 'FedEx', tracking: '7777-BBB' },
|
|
166
|
+
}),
|
|
167
|
+
})
|
|
168
|
+
assert(shippedRes.status === 200, `Beluga webhook (shipped) expected 200, got ${shippedRes.status}`)
|
|
169
|
+
|
|
170
|
+
await wait(undefined, 250) // webhook upsert + trigger + Set Fields
|
|
171
|
+
|
|
172
|
+
await async_test(
|
|
173
|
+
"Block B: Beluga PHARMACY_ORDER_SHIPPED resolves {{order.*}} into enduser fields",
|
|
174
|
+
() => sdk.api.endusers.getOne(enduser.id),
|
|
175
|
+
{ onResult: e => !!(
|
|
176
|
+
e.fields?.pharmacy_status === 'Shipped'
|
|
177
|
+
&& e.fields?.pharmacy_tracking === '7777-BBB'
|
|
178
|
+
&& e.fields?.pharmacy_carrier === 'FedEx'
|
|
179
|
+
&& e.fields?.pharmacy_externalId === externalOrderId
|
|
180
|
+
&& typeof e.fields?.pharmacy_id === 'string'
|
|
181
|
+
&& (e.fields?.pharmacy_id as string).length > 0
|
|
182
|
+
// Webhook does not set sku/protocol -> default branch in helper -> ''
|
|
183
|
+
&& e.fields?.pharmacy_sku === ''
|
|
184
|
+
&& e.fields?.pharmacy_protocol === ''
|
|
185
|
+
)}
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
// Step 2: PHARMACY_ORDER_DELIVERED for the same order
|
|
189
|
+
const deliveredRes = await fetch(webhookUrl, {
|
|
190
|
+
method: 'POST',
|
|
191
|
+
headers: { 'Content-Type': 'application/json' },
|
|
192
|
+
body: JSON.stringify({
|
|
193
|
+
masterId: `tellescope_${formResponse.id}`,
|
|
194
|
+
event: 'PHARMACY_ORDER_DELIVERED',
|
|
195
|
+
orderId: externalOrderId,
|
|
196
|
+
}),
|
|
197
|
+
})
|
|
198
|
+
assert(deliveredRes.status === 200, `Beluga webhook (delivered) expected 200, got ${deliveredRes.status}`)
|
|
199
|
+
|
|
200
|
+
await wait(undefined, 250)
|
|
201
|
+
|
|
202
|
+
await async_test(
|
|
203
|
+
"Block B: PHARMACY_ORDER_DELIVERED flips pharmacy_status to 'Delivered'",
|
|
204
|
+
() => sdk.api.endusers.getOne(enduser.id),
|
|
205
|
+
{ onResult: e => !!(
|
|
206
|
+
e.fields?.pharmacy_status === 'Delivered'
|
|
207
|
+
&& e.fields?.pharmacy_externalId === externalOrderId
|
|
208
|
+
)}
|
|
209
|
+
)
|
|
210
|
+
} finally {
|
|
211
|
+
// Clean up the order created by the webhook
|
|
212
|
+
try {
|
|
213
|
+
const orders = await sdk.api.enduser_orders.getSome({
|
|
214
|
+
filter: { source: 'Beluga', externalId: externalOrderId } as any,
|
|
215
|
+
})
|
|
216
|
+
for (const o of orders) {
|
|
217
|
+
await sdk.api.enduser_orders.deleteOne(o.id).catch(console.error)
|
|
218
|
+
}
|
|
219
|
+
} catch (err) {
|
|
220
|
+
console.error(err)
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
await sdk.api.automation_triggers.deleteOne(trigger.id).catch(console.error)
|
|
224
|
+
await sdk.api.automation_triggers.deleteOne(deliveredTrigger.id).catch(console.error)
|
|
225
|
+
await sdk.api.form_responses.deleteOne(formResponse.id).catch(console.error)
|
|
226
|
+
await sdk.api.forms.deleteOne(form.id).catch(console.error)
|
|
227
|
+
await sdk.api.endusers.deleteOne(enduser.id).catch(console.error)
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
export const set_fields_order_templates_tests = async (
|
|
232
|
+
{ sdk, sdkNonAdmin }: { sdk: Session, sdkNonAdmin: Session }
|
|
233
|
+
) => {
|
|
234
|
+
log_header("Set Fields: {{order.*}} template resolution")
|
|
235
|
+
await direct_order_creation_block({ sdk })
|
|
236
|
+
await beluga_webhook_block({ sdk })
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
if (require.main === module) {
|
|
240
|
+
console.log(`Using API URL: ${host}`)
|
|
241
|
+
const sdk = new Session({ host })
|
|
242
|
+
const sdkNonAdmin = new Session({ host })
|
|
243
|
+
|
|
244
|
+
const runTests = async () => {
|
|
245
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
246
|
+
await set_fields_order_templates_tests({ sdk, sdkNonAdmin })
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
runTests()
|
|
250
|
+
.then(() => {
|
|
251
|
+
console.log("set_fields_order_templates test suite completed successfully")
|
|
252
|
+
process.exit(0)
|
|
253
|
+
})
|
|
254
|
+
.catch((error) => {
|
|
255
|
+
console.error("set_fields_order_templates test suite failed:", error)
|
|
256
|
+
process.exit(1)
|
|
257
|
+
})
|
|
258
|
+
}
|
package/src/tests/setup.ts
CHANGED
|
@@ -37,9 +37,16 @@ export const setup_tests = async (sdk: Session, sdkNonAdmin: Session) => {
|
|
|
37
37
|
// Authenticate the SDKs first
|
|
38
38
|
await sdk.authenticate(email, password)
|
|
39
39
|
await sdkNonAdmin.authenticate(nonAdminEmail, nonAdminPassword)
|
|
40
|
-
|
|
40
|
+
|
|
41
41
|
await async_test('test_authenticated', sdk.test_authenticated, { expectedResult: 'Authenticated!' })
|
|
42
42
|
|
|
43
|
+
// Defensive: clear residual OTP gating from a previously-aborted run of
|
|
44
|
+
// enduser_session_invalidation_tests, which would otherwise cause enduser
|
|
45
|
+
// tokens minted in subsequent tests to be rejected as Unauthenticated.
|
|
46
|
+
await sdk.api.organizations.updateOne(sdk.userInfo.businessId, {
|
|
47
|
+
portalSettings: { authentication: { requireOTP: false, requireOTPAfterPassword: false } },
|
|
48
|
+
}, { replaceObjectFields: true })
|
|
49
|
+
|
|
43
50
|
await async_test(
|
|
44
51
|
'test_authenticated (with API Key)',
|
|
45
52
|
(new Session({ host, apiKey: '3n5q0SCBT_iUvZz-b9BJtX7o7HQUVJ9v132PgHJNJsg.' /* local test key */ })).test_authenticated,
|
package/src/tests/tests.ts
CHANGED
|
@@ -49,6 +49,7 @@ import { purchase_made_trigger_tests } from "./api_tests/purchase_made_trigger.t
|
|
|
49
49
|
import { appointment_rescheduled_trigger_tests } from "./api_tests/appointment_rescheduled_trigger.test"
|
|
50
50
|
import { journey_error_branching_tests } from "./api_tests/journey_error_branching.test"
|
|
51
51
|
import { afteraction_day_of_month_delay_tests } from "./api_tests/afteraction_day_of_month_delay.test"
|
|
52
|
+
import { push_forms_to_portal_group_completion_tests } from "./api_tests/push_forms_to_portal_group_completion.test"
|
|
52
53
|
import { setup_tests } from "./setup"
|
|
53
54
|
import { evaluate_conditional_logic_for_enduser_fields, FORM_LOGIC_CALCULATED_FIELDS, get_care_team_primary, get_flattened_fields, get_next_reminder_timestamp, object_is_empty, replace_enduser_template_values, replace_form_field_template_values, responses_satisfy_conditions, truncate_string, weighted_round_robin, YYYY_MM_DD_to_MM_DD_YYYY } from "@tellescope/utilities"
|
|
54
55
|
import { DEFAULT_OPERATIONS, PLACEHOLDER_ID, ZENDESK_INTEGRATIONS_TITLE, ZOOM_TITLE } from "@tellescope/constants"
|
|
@@ -74,10 +75,12 @@ import {
|
|
|
74
75
|
|
|
75
76
|
import fs from "fs"
|
|
76
77
|
import { load_inbox_data_tests } from "./api_tests/load_inbox_data.test";
|
|
78
|
+
import { enduser_login_tests } from "./api_tests/enduser_login.test";
|
|
77
79
|
import { eom_procedure_codes_tests } from "./api_tests/eom_procedure_codes.test";
|
|
78
80
|
import { cross_org_api_key_tests } from "./api_tests/cross_org_api_key.test";
|
|
79
81
|
import { custom_dashboards_tests } from "./api_tests/custom_dashboards.test";
|
|
80
82
|
import { message_assignment_trigger_tests } from "./api_tests/message_assignment_trigger.test";
|
|
83
|
+
import { outbound_chat_sent_trigger_tests } from "./api_tests/outbound_chat_sent_trigger.test";
|
|
81
84
|
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";
|
|
82
85
|
import { monthly_availability_restrictions_tests } from "./api_tests/monthly_availability_restrictions.test";
|
|
83
86
|
import { calendar_event_limits_tests } from "./api_tests/calendar_event_limits.test";
|
|
@@ -95,11 +98,14 @@ import { load_team_chat_tests } from "./api_tests/load_team_chat.test";
|
|
|
95
98
|
import { form_started_trigger_tests } from "./api_tests/form_started_trigger.test";
|
|
96
99
|
import { form_submitted_trigger_tests } from "./api_tests/form_submitted_trigger.test";
|
|
97
100
|
import { medication_added_trigger_tests } from "./api_tests/medication_added_trigger.test";
|
|
101
|
+
import { conditional_logic_medication_unit_tests } from "./unit_tests/conditional_logic_medication.test";
|
|
98
102
|
import { elation_user_id_tests } from "./api_tests/elation_user_id.test";
|
|
99
103
|
import { organization_settings_duplicates_tests } from "./api_tests/organization_settings_duplicates.test";
|
|
100
104
|
import { calendar_events_bulk_update_tests } from "./api_tests/calendar_events_bulk_update.test";
|
|
101
105
|
import { openloop_webhooks_tests } from "./api_tests/openloop_webhooks.test";
|
|
102
106
|
import { beluga_pharmacy_mappings_tests } from "./api_tests/beluga_pharmacy_mappings.test";
|
|
107
|
+
import { account_switcher_tests } from "./api_tests/account_switcher.test";
|
|
108
|
+
import { set_fields_order_templates_tests } from "./api_tests/set_fields_order_templates.test";
|
|
103
109
|
import { date_string_validation_tests } from "./api_tests/date_string_validation.test";
|
|
104
110
|
import { enduser_session_invalidation_tests } from "./api_tests/enduser_session_invalidation.test";
|
|
105
111
|
import { enduser_cross_access_isolation_tests } from "./api_tests/enduser_cross_access_isolation.test";
|
|
@@ -5087,6 +5093,7 @@ const automation_trigger_tests = async () => {
|
|
|
5087
5093
|
log_header("Automation Trigger Tests")
|
|
5088
5094
|
|
|
5089
5095
|
await order_status_equals_tests()
|
|
5096
|
+
await set_fields_order_templates_tests({ sdk, sdkNonAdmin })
|
|
5090
5097
|
await medication_added_trigger_tests({ sdk, sdkNonAdmin })
|
|
5091
5098
|
await appointment_cancelled_tests()
|
|
5092
5099
|
await set_fields_tests()
|
|
@@ -5095,6 +5102,7 @@ const automation_trigger_tests = async () => {
|
|
|
5095
5102
|
await form_response_set_fields_trigger_tests()
|
|
5096
5103
|
await form_response_set_fields_journey_tests()
|
|
5097
5104
|
await appointment_completed_trigger_tests({ sdk, sdkNonAdmin })
|
|
5105
|
+
await push_forms_to_portal_group_completion_tests({ sdk, sdkNonAdmin })
|
|
5098
5106
|
await trigger_events_api_tests()
|
|
5099
5107
|
await fields_changed_tests()
|
|
5100
5108
|
await field_equals_trigger_tests()
|
|
@@ -7119,16 +7127,18 @@ const merge_enduser_tests = async () => {
|
|
|
7119
7127
|
const stripeCustomerId = 'example_cu_id';
|
|
7120
7128
|
const stripeKey = 'example_stripe_key';
|
|
7121
7129
|
const [source, destination, otherEnduser] = (await sdk.api.endusers.createSome([
|
|
7122
|
-
{
|
|
7123
|
-
email: 'source@tellescope.com', fname: 'source', lname: 'enduser',
|
|
7124
|
-
references: [{ type: '2', id: '2.2' }, { type: '3', id: '3.2' }, { type: '4', id: '4.2'}],
|
|
7130
|
+
{
|
|
7131
|
+
email: 'source@tellescope.com', fname: 'source', lname: 'enduser',
|
|
7132
|
+
references: [{ type: '2', id: '2.2' }, { type: '3', id: '3.2' }, { type: '4', id: '4.2'}],
|
|
7125
7133
|
// @ts-ignore
|
|
7126
7134
|
stripeCustomerId,
|
|
7127
7135
|
stripeKey,
|
|
7128
7136
|
athenaPracticeId: '12345',
|
|
7129
7137
|
athenaDepartmentId: '54321',
|
|
7138
|
+
insurance: { memberId: 'src-member', payerName: 'Source Payer' },
|
|
7139
|
+
insuranceSecondary: { memberId: 'src-secondary' },
|
|
7130
7140
|
},
|
|
7131
|
-
{ email: 'destination@tellescope.com', source: '4', externalId: "4", references: [{ type: '1', id: '1' }, { type: '2', id: '2' }] },
|
|
7141
|
+
{ email: 'destination@tellescope.com', source: '4', externalId: "4", references: [{ type: '1', id: '1' }, { type: '2', id: '2' }], insurance: { memberId: 'dest-member', payerName: 'Dest Payer' } },
|
|
7132
7142
|
{ email: 'other@tellescope.com'},
|
|
7133
7143
|
])).created
|
|
7134
7144
|
|
|
@@ -7177,7 +7187,10 @@ const merge_enduser_tests = async () => {
|
|
|
7177
7187
|
&& e.references?.find(r => r.type === '1')?.id === '1'
|
|
7178
7188
|
&& e.references?.find(r => r.type === '2')?.id === '2'
|
|
7179
7189
|
&& e.references?.find(r => r.type === '3')?.id === '3.2'
|
|
7180
|
-
&& !e.references?.find(r => r.type === '4')?.id
|
|
7190
|
+
&& !e.references?.find(r => r.type === '4')?.id
|
|
7191
|
+
&& e.insurance?.memberId === 'dest-member'
|
|
7192
|
+
&& e.insurance?.payerName === 'Dest Payer'
|
|
7193
|
+
&& e.insuranceSecondary?.memberId === 'src-secondary'
|
|
7181
7194
|
)}
|
|
7182
7195
|
)
|
|
7183
7196
|
|
|
@@ -14303,10 +14316,15 @@ const ip_address_form_tests = async () => {
|
|
|
14303
14316
|
|
|
14304
14317
|
|
|
14305
14318
|
await enduser_conditional_logic_tests()
|
|
14319
|
+
await conditional_logic_medication_unit_tests()
|
|
14306
14320
|
await replace_enduser_template_values_tests()
|
|
14307
14321
|
await replace_form_field_template_values_tests()
|
|
14308
14322
|
await mfa_tests()
|
|
14309
14323
|
await setup_tests(sdk, sdkNonAdmin)
|
|
14324
|
+
await account_switcher_tests({ sdk, sdkNonAdmin })
|
|
14325
|
+
await enduser_login_tests({ sdk, sdkNonAdmin })
|
|
14326
|
+
await outbound_chat_sent_trigger_tests({ sdk })
|
|
14327
|
+
await automation_trigger_tests()
|
|
14310
14328
|
await enduser_cross_access_isolation_tests({ sdk, sdkNonAdmin })
|
|
14311
14329
|
await eom_procedure_codes_tests({ sdk, sdkNonAdmin })
|
|
14312
14330
|
await cross_org_api_key_tests({ sdk, sdkNonAdmin })
|
|
@@ -14317,7 +14335,6 @@ const ip_address_form_tests = async () => {
|
|
|
14317
14335
|
await form_submitted_trigger_tests({ sdk, sdkNonAdmin })
|
|
14318
14336
|
await date_string_validation_tests({ sdk, sdkNonAdmin })
|
|
14319
14337
|
await openloop_webhooks_tests({ sdk, sdkNonAdmin })
|
|
14320
|
-
await automation_trigger_tests()
|
|
14321
14338
|
await integrations_redacted_tests({ sdk, sdkNonAdmin })
|
|
14322
14339
|
await mdb_sort_tests({ sdk, sdkNonAdmin })
|
|
14323
14340
|
await search_tests()
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
import {
|
|
2
|
+
evaluate_conditional_logic_for_medication_title,
|
|
3
|
+
evaluate_string_field_comparison,
|
|
4
|
+
} from "@tellescope/utilities"
|
|
5
|
+
import { CompoundFilter } from "@tellescope/types-models"
|
|
6
|
+
|
|
7
|
+
let failures = 0
|
|
8
|
+
let passed = 0
|
|
9
|
+
|
|
10
|
+
const assert = (name: string, actual: boolean, expected: boolean) => {
|
|
11
|
+
if (actual === expected) {
|
|
12
|
+
passed++
|
|
13
|
+
console.log(` ✓ ${name}`)
|
|
14
|
+
} else {
|
|
15
|
+
failures++
|
|
16
|
+
console.error(` ✗ ${name} — expected ${expected}, got ${actual}`)
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const run_string_comparison_tests = () => {
|
|
21
|
+
console.log("\n[evaluate_string_field_comparison]")
|
|
22
|
+
|
|
23
|
+
// Plain-string equals (implicit operator)
|
|
24
|
+
assert("equals plain string match", evaluate_string_field_comparison('Aspirin', 'Aspirin'), true)
|
|
25
|
+
assert("equals plain string mismatch", evaluate_string_field_comparison('Aspirin', 'Lisinopril'), false)
|
|
26
|
+
assert("equals empty string against empty title", evaluate_string_field_comparison('', ''), true)
|
|
27
|
+
assert("equals empty string against undefined title", evaluate_string_field_comparison(undefined, ''), true)
|
|
28
|
+
|
|
29
|
+
// $ne
|
|
30
|
+
assert("$ne matches when different", evaluate_string_field_comparison('Aspirin', { $ne: 'Lisinopril' }), true)
|
|
31
|
+
assert("$ne fails when equal", evaluate_string_field_comparison('Aspirin', { $ne: 'Aspirin' }), false)
|
|
32
|
+
|
|
33
|
+
// $contains (case sensitive — consistent with enduser/form-response evaluators)
|
|
34
|
+
assert("$contains substring match", evaluate_string_field_comparison('Semaglutide GLP-1', { $contains: 'GLP' }), true)
|
|
35
|
+
assert("$contains case sensitive miss", evaluate_string_field_comparison('Lisinopril 10MG', { $contains: 'mg' }), false)
|
|
36
|
+
assert("$contains case sensitive match", evaluate_string_field_comparison('Lisinopril 10mg', { $contains: 'mg' }), true)
|
|
37
|
+
assert("$contains no match", evaluate_string_field_comparison('Aspirin', { $contains: 'GLP' }), false)
|
|
38
|
+
assert("$contains empty string is always true", evaluate_string_field_comparison('Aspirin', { $contains: '' }), true)
|
|
39
|
+
|
|
40
|
+
// $doesNotContain
|
|
41
|
+
assert("$doesNotContain mismatch true", evaluate_string_field_comparison('Aspirin', { $doesNotContain: 'GLP' }), true)
|
|
42
|
+
assert("$doesNotContain match false", evaluate_string_field_comparison('Semaglutide', { $doesNotContain: 'Sema' }), false)
|
|
43
|
+
assert("$doesNotContain case sensitive (different case = no match)", evaluate_string_field_comparison('Placebo', { $doesNotContain: 'PLACEBO' }), true)
|
|
44
|
+
|
|
45
|
+
// $exists
|
|
46
|
+
assert("$exists:true on present", evaluate_string_field_comparison('Aspirin', { $exists: true }), true)
|
|
47
|
+
assert("$exists:true on empty", evaluate_string_field_comparison('', { $exists: true }), false)
|
|
48
|
+
assert("$exists:true on undefined", evaluate_string_field_comparison(undefined, { $exists: true }), false)
|
|
49
|
+
assert("$exists:false on present", evaluate_string_field_comparison('Aspirin', { $exists: false }), false)
|
|
50
|
+
assert("$exists:false on undefined", evaluate_string_field_comparison(undefined, { $exists: false }), true)
|
|
51
|
+
|
|
52
|
+
// null operator → treated as "no value set"
|
|
53
|
+
assert("null operator on undefined title", evaluate_string_field_comparison(undefined, null), true)
|
|
54
|
+
assert("null operator on present title", evaluate_string_field_comparison('Aspirin', null), false)
|
|
55
|
+
|
|
56
|
+
// Unknown operator → returns true (don't suppress triggers on bad data)
|
|
57
|
+
assert("unknown operator returns true", evaluate_string_field_comparison('Aspirin', { $weirdOp: 'x' } as any), true)
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const run_conditional_logic_tests = () => {
|
|
61
|
+
console.log("\n[evaluate_conditional_logic_for_medication_title]")
|
|
62
|
+
|
|
63
|
+
// Single condition (case-sensitive)
|
|
64
|
+
const c1: CompoundFilter<'title'> = { condition: { title: { $contains: 'GLP' } } }
|
|
65
|
+
assert("single $contains true", evaluate_conditional_logic_for_medication_title('Semaglutide GLP-1', c1), true)
|
|
66
|
+
assert("single $contains false", evaluate_conditional_logic_for_medication_title('Aspirin', c1), false)
|
|
67
|
+
assert("single $contains case-sensitive miss", evaluate_conditional_logic_for_medication_title('Semaglutide glp-1', c1), false)
|
|
68
|
+
|
|
69
|
+
// Plain string default-equals
|
|
70
|
+
const c2: CompoundFilter<'title'> = { condition: { title: 'Aspirin' } }
|
|
71
|
+
assert("plain equals true", evaluate_conditional_logic_for_medication_title('Aspirin', c2), true)
|
|
72
|
+
assert("plain equals false", evaluate_conditional_logic_for_medication_title('Lisinopril', c2), false)
|
|
73
|
+
|
|
74
|
+
// $and (both true)
|
|
75
|
+
const cAnd: CompoundFilter<'title'> = {
|
|
76
|
+
$and: [
|
|
77
|
+
{ condition: { title: { $contains: 'mg' } } },
|
|
78
|
+
{ condition: { title: { $doesNotContain: 'Placebo' } } },
|
|
79
|
+
],
|
|
80
|
+
}
|
|
81
|
+
assert("$and both pass", evaluate_conditional_logic_for_medication_title('Lisinopril 10mg', cAnd), true)
|
|
82
|
+
assert("$and first fails", evaluate_conditional_logic_for_medication_title('Lisinopril', cAnd), false)
|
|
83
|
+
assert("$and second fails", evaluate_conditional_logic_for_medication_title('Placebo 5mg', cAnd), false)
|
|
84
|
+
|
|
85
|
+
// $or
|
|
86
|
+
const cOr: CompoundFilter<'title'> = {
|
|
87
|
+
$or: [
|
|
88
|
+
{ condition: { title: 'Aspirin' } },
|
|
89
|
+
{ condition: { title: { $contains: 'pril' } } },
|
|
90
|
+
],
|
|
91
|
+
}
|
|
92
|
+
assert("$or first matches", evaluate_conditional_logic_for_medication_title('Aspirin', cOr), true)
|
|
93
|
+
assert("$or second matches", evaluate_conditional_logic_for_medication_title('Lisinopril', cOr), true)
|
|
94
|
+
assert("$or neither matches", evaluate_conditional_logic_for_medication_title('Metformin', cOr), false)
|
|
95
|
+
|
|
96
|
+
// Mixed compound: $and containing $or — the canonical "compound" case
|
|
97
|
+
const cMixed: CompoundFilter<'title'> = {
|
|
98
|
+
$and: [
|
|
99
|
+
{ condition: { title: { $contains: 'mg' } } },
|
|
100
|
+
{
|
|
101
|
+
$or: [
|
|
102
|
+
{ condition: { title: { $contains: 'Lisin' } } },
|
|
103
|
+
{ condition: { title: { $contains: 'Metform' } } },
|
|
104
|
+
],
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
}
|
|
108
|
+
assert("$and+$or first or-branch matches", evaluate_conditional_logic_for_medication_title('Lisinopril 10mg', cMixed), true)
|
|
109
|
+
assert("$and+$or second or-branch matches", evaluate_conditional_logic_for_medication_title('Metformin 500mg', cMixed), true)
|
|
110
|
+
assert("$and+$or and-branch fails", evaluate_conditional_logic_for_medication_title('Lisinopril', cMixed), false)
|
|
111
|
+
assert("$and+$or or-branch fails", evaluate_conditional_logic_for_medication_title('Aspirin 81mg', cMixed), false)
|
|
112
|
+
|
|
113
|
+
// Empty / no-op
|
|
114
|
+
assert("empty conditions returns true", evaluate_conditional_logic_for_medication_title('Aspirin', {}), true)
|
|
115
|
+
|
|
116
|
+
// Unknown operator inside condition → returns true (safe)
|
|
117
|
+
const cUnknown: CompoundFilter<'title'> = { condition: { title: { $regex: 'foo' } as any } }
|
|
118
|
+
assert("unknown operator is permissive", evaluate_conditional_logic_for_medication_title('Aspirin', cUnknown), true)
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
const run_all = () => {
|
|
122
|
+
console.log("Running conditional_logic_medication unit tests")
|
|
123
|
+
run_string_comparison_tests()
|
|
124
|
+
run_conditional_logic_tests()
|
|
125
|
+
console.log(`\nResults: ${passed} passed, ${failures} failed`)
|
|
126
|
+
if (failures > 0) process.exit(1)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (require.main === module) {
|
|
130
|
+
run_all()
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export { run_all as conditional_logic_medication_unit_tests }
|
package/test_generated.pdf
CHANGED
|
Binary file
|