@tellescope/sdk 1.256.0 → 1.256.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.
- package/lib/cjs/tests/api_tests/beluga_trigger_refill.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/beluga_trigger_refill.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/beluga_trigger_refill.test.js +224 -0
- package/lib/cjs/tests/api_tests/beluga_trigger_refill.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/start_ai_conversation.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/start_ai_conversation.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/start_ai_conversation.test.js +351 -0
- package/lib/cjs/tests/api_tests/start_ai_conversation.test.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +201 -185
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/enduser.d.ts +1 -0
- package/lib/esm/enduser.d.ts.map +1 -1
- package/lib/esm/sdk.d.ts +3 -2
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/session.d.ts +1 -0
- package/lib/esm/session.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/beluga_trigger_refill.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/beluga_trigger_refill.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/beluga_trigger_refill.test.js +220 -0
- package/lib/esm/tests/api_tests/beluga_trigger_refill.test.js.map +1 -0
- package/lib/esm/tests/api_tests/start_ai_conversation.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/start_ai_conversation.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/start_ai_conversation.test.js +347 -0
- package/lib/esm/tests/api_tests/start_ai_conversation.test.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +201 -185
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +8 -8
- package/src/tests/api_tests/beluga_trigger_refill.test.ts +152 -0
- package/src/tests/api_tests/security/F-0015-webhook-outbound-ssrf.test.ts +292 -0
- package/src/tests/api_tests/security/F-0025-proxy-image-unauth-ssrf.test.ts +179 -0
- package/src/tests/api_tests/start_ai_conversation.test.ts +256 -0
- package/src/tests/tests.ts +8 -0
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import { log_header, wait, async_test, assert } from "@tellescope/testing"
|
|
5
|
+
import { setup_tests } from "../setup"
|
|
6
|
+
|
|
7
|
+
const host = process.env.API_URL || "http://localhost:8080"
|
|
8
|
+
|
|
9
|
+
const TEST_MARKER = '__TELLESCOPE_TEST_INCOMING_SMS_AUTOMATION__'
|
|
10
|
+
const TEST_PHONE = '+15555555555' // send_sms short-circuits real sends for this number
|
|
11
|
+
|
|
12
|
+
// SMS AI conversations (startAIConversation journey action).
|
|
13
|
+
// Runtime flows (start/supersede/takeover/inbound absorption) require the organization to have an
|
|
14
|
+
// SMS number configured — when it doesn't (typical dev org), those sections assert the
|
|
15
|
+
// configuration error path and are otherwise skipped; config validation runs everywhere.
|
|
16
|
+
export const start_ai_conversation_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
17
|
+
log_header("Start AI Conversation (SMS Agent) Tests")
|
|
18
|
+
|
|
19
|
+
const users = await sdk.api.users.getSome({})
|
|
20
|
+
const senderId = users[0].id
|
|
21
|
+
|
|
22
|
+
const journey = await sdk.api.journeys.createOne({ title: 'SMS Agent Test Journey' })
|
|
23
|
+
const createdEnduserIds: string[] = []
|
|
24
|
+
|
|
25
|
+
try {
|
|
26
|
+
/*************** Config validation round-trip ***************/
|
|
27
|
+
const validInfo = {
|
|
28
|
+
channel: 'SMS' as const,
|
|
29
|
+
senderId,
|
|
30
|
+
initialMessage: 'Hello {{enduser.fname}}! Quick question for you.',
|
|
31
|
+
prompt: 'You are texting on behalf of Test Clinic. Answer briefly.',
|
|
32
|
+
outcomes: [{ value: 'resolved', description: 'The question was answered' }],
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const step = await sdk.api.automation_steps.createOne({
|
|
36
|
+
journeyId: journey.id,
|
|
37
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
38
|
+
action: { type: 'startAIConversation', info: validInfo },
|
|
39
|
+
})
|
|
40
|
+
assert(
|
|
41
|
+
step.action.type === 'startAIConversation' && step.action.info.initialMessage === validInfo.initialMessage,
|
|
42
|
+
'startAIConversation step round-trip failed',
|
|
43
|
+
'startAIConversation step round-trips',
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
const outcomeChild = await sdk.api.automation_steps.createOne({
|
|
47
|
+
journeyId: journey.id,
|
|
48
|
+
events: [{ type: 'onAIConversationOutcome', info: { automationStepId: step.id, outcome: 'resolved' } }],
|
|
49
|
+
action: { type: 'addEnduserTags', info: { tags: ['sms-agent-resolved'] } },
|
|
50
|
+
})
|
|
51
|
+
assert(
|
|
52
|
+
outcomeChild.events[0].type === 'onAIConversationOutcome',
|
|
53
|
+
'onAIConversationOutcome event round-trip failed',
|
|
54
|
+
'onAIConversationOutcome event round-trips',
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
await async_test('startAIConversation without prompt is rejected',
|
|
58
|
+
() => sdk.api.automation_steps.createOne({
|
|
59
|
+
journeyId: journey.id,
|
|
60
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
61
|
+
action: { type: 'startAIConversation', info: { ...validInfo, prompt: undefined } as any },
|
|
62
|
+
}),
|
|
63
|
+
{ shouldError: true, onError: () => true },
|
|
64
|
+
)
|
|
65
|
+
await async_test('startAIConversation without senderId is rejected',
|
|
66
|
+
() => sdk.api.automation_steps.createOne({
|
|
67
|
+
journeyId: journey.id,
|
|
68
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
69
|
+
action: { type: 'startAIConversation', info: { ...validInfo, senderId: undefined } as any },
|
|
70
|
+
}),
|
|
71
|
+
{ shouldError: true, onError: () => true },
|
|
72
|
+
)
|
|
73
|
+
await async_test('startAIConversation without initialMessage is rejected',
|
|
74
|
+
() => sdk.api.automation_steps.createOne({
|
|
75
|
+
journeyId: journey.id,
|
|
76
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
77
|
+
action: { type: 'startAIConversation', info: { ...validInfo, initialMessage: undefined } as any },
|
|
78
|
+
}),
|
|
79
|
+
{ shouldError: true, onError: () => true },
|
|
80
|
+
)
|
|
81
|
+
await async_test('startAIConversation with invalid tool is rejected',
|
|
82
|
+
() => sdk.api.automation_steps.createOne({
|
|
83
|
+
journeyId: journey.id,
|
|
84
|
+
events: [{ type: 'onJourneyStart', info: {} }],
|
|
85
|
+
action: { type: 'startAIConversation', info: { ...validInfo, tools: [{ type: 'Submit Form', info: { formId: 'not-an-id' } }] } as any },
|
|
86
|
+
}),
|
|
87
|
+
{ shouldError: true, onError: () => true },
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
/*************** Runtime: the process endpoint ***************/
|
|
91
|
+
const processFor = async (enduserId: string) => {
|
|
92
|
+
const automatedAction = await sdk.api.automated_actions.createOne({
|
|
93
|
+
enduserId,
|
|
94
|
+
journeyId: journey.id,
|
|
95
|
+
automationStepId: step.id,
|
|
96
|
+
event: { type: 'onJourneyStart', info: {} },
|
|
97
|
+
action: { type: 'startAIConversation', info: validInfo },
|
|
98
|
+
status: 'active',
|
|
99
|
+
processAfter: Date.now(),
|
|
100
|
+
})
|
|
101
|
+
return sdk.api.automated_actions.process({ automatedActionId: automatedAction.id })
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// no-phone error path works regardless of org SMS configuration
|
|
105
|
+
const phonelessEnduser = await sdk.api.endusers.createOne({ fname: 'sms-agent-nophone' })
|
|
106
|
+
createdEnduserIds.push(phonelessEnduser.id)
|
|
107
|
+
const noPhoneResult = await processFor(phonelessEnduser.id)
|
|
108
|
+
assert(
|
|
109
|
+
!noPhoneResult.success && !!noPhoneResult.error,
|
|
110
|
+
'phoneless start should fail with an error',
|
|
111
|
+
'start action errors for a contact without a phone',
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
// resolve the same organization the action does (last organizationId, else businessId) —
|
|
115
|
+
// getSome({})[0] is an arbitrary sub-organization and can be a different record entirely
|
|
116
|
+
const session = sdk.userInfo as { businessId?: string, organizationIds?: string[] }
|
|
117
|
+
const resolvedOrgId = [...(session.organizationIds ?? [])].pop() || session.businessId
|
|
118
|
+
const organization = (await sdk.api.organizations.getSome({})).find(o => o.id === resolvedOrgId)
|
|
119
|
+
const orgHasNumber = !!(organization as any)?.twilioNumber || !!((organization as any)?.twilioNumbers?.length)
|
|
120
|
+
|
|
121
|
+
if (!orgHasNumber) {
|
|
122
|
+
// still assert the wiring: the action processes and reports a configuration error rather than
|
|
123
|
+
// crashing. AI enablement is checked before the SMS number and short-circuits first, and
|
|
124
|
+
// bedrockAIAllowed/creditCount are super-admin-only (unreadable here), so accept either.
|
|
125
|
+
const enduser = await sdk.api.endusers.createOne({ fname: 'sms-agent-nonumber', phone: TEST_PHONE })
|
|
126
|
+
createdEnduserIds.push(enduser.id)
|
|
127
|
+
const result = await processFor(enduser.id)
|
|
128
|
+
assert(
|
|
129
|
+
!result.success && /no sms number configured|ai is not enabled/i.test(result.error || ''),
|
|
130
|
+
`expected a configuration error, got: ${result.error}`,
|
|
131
|
+
'start action reports a configuration error (org has no SMS number — runtime flows skipped)',
|
|
132
|
+
)
|
|
133
|
+
return
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/*************** Runtime flows (org has an SMS number) ***************/
|
|
137
|
+
const enduser = await sdk.api.endusers.createOne({ fname: 'sms-agent-runtime', phone: TEST_PHONE })
|
|
138
|
+
createdEnduserIds.push(enduser.id)
|
|
139
|
+
|
|
140
|
+
const startResult = await processFor(enduser.id)
|
|
141
|
+
assert(startResult.success === true, `start failed: ${startResult.error}`, 'start action succeeds')
|
|
142
|
+
await wait(undefined, 1000)
|
|
143
|
+
|
|
144
|
+
let refreshed = await sdk.api.endusers.getOne(enduser.id)
|
|
145
|
+
const entry = (refreshed.aiConversations ?? []).find(c => c.active)
|
|
146
|
+
assert(!!entry, 'no active aiConversations entry on enduser', 'enduser has an active conversation entry')
|
|
147
|
+
assert(entry?.destination === TEST_PHONE, 'wrong destination on entry', 'conversation entry pairs the contact number')
|
|
148
|
+
|
|
149
|
+
const conversation = await sdk.api.ai_conversations.getOne(entry!.aiConversationId)
|
|
150
|
+
assert(conversation.type === 'sms_agent', 'wrong ai_conversations type', 'ai_conversations doc has type sms_agent')
|
|
151
|
+
|
|
152
|
+
const initialMessages = await sdk.api.sms_messages.getSome({ filter: { enduserId: enduser.id } })
|
|
153
|
+
const outbound = initialMessages.find(m => !m.inbound && m.automationStepId === step.id)
|
|
154
|
+
// exact equality on a merge-field-containing opener guards two regressions: templating must
|
|
155
|
+
// happen in send_sms (raw text on the record), and the agent-reply {{ escaping must NOT be
|
|
156
|
+
// applied to the org-authored initial message (preserveMergeFields)
|
|
157
|
+
assert(
|
|
158
|
+
!!outbound && outbound.message === validInfo.initialMessage,
|
|
159
|
+
'initial SMS record missing or not raw text',
|
|
160
|
+
'initial SMS logged with raw (untemplated) text + automationStepId',
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
// supersede: second start on the same pairing replaces the first
|
|
164
|
+
const secondStart = await processFor(enduser.id)
|
|
165
|
+
assert(secondStart.success === true, `second start failed: ${secondStart.error}`, 'duplicate start succeeds (replace)')
|
|
166
|
+
await wait(undefined, 1000)
|
|
167
|
+
refreshed = await sdk.api.endusers.getOne(enduser.id)
|
|
168
|
+
const activeEntries = (refreshed.aiConversations ?? []).filter(c => c.active)
|
|
169
|
+
assert(activeEntries.length === 1, 'expected exactly one active conversation', 'supersede leaves exactly one active conversation')
|
|
170
|
+
assert(
|
|
171
|
+
activeEntries[0].aiConversationId !== entry!.aiConversationId,
|
|
172
|
+
'active conversation was not replaced',
|
|
173
|
+
'supersede started a fresh conversation',
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
// inbound absorption via the testing hook: STOP ends the conversation
|
|
177
|
+
await sdk.api.sms_messages.createOne({
|
|
178
|
+
enduserId: enduser.id,
|
|
179
|
+
message: `stop${TEST_MARKER}`, // marker is stripped before the AI branch sees the text
|
|
180
|
+
inbound: true,
|
|
181
|
+
logOnly: true,
|
|
182
|
+
phoneNumber: activeEntries[0].source,
|
|
183
|
+
enduserPhoneNumber: TEST_PHONE,
|
|
184
|
+
} as any)
|
|
185
|
+
await wait(undefined, 2000)
|
|
186
|
+
refreshed = await sdk.api.endusers.getOne(enduser.id)
|
|
187
|
+
const stopped = (refreshed.aiConversations ?? []).find(c => c.aiConversationId === activeEntries[0].aiConversationId)
|
|
188
|
+
assert(
|
|
189
|
+
stopped?.active === false && stopped?.endedReason === 'unsubscribed',
|
|
190
|
+
`STOP did not end the conversation (${JSON.stringify(stopped)})`,
|
|
191
|
+
'STOP reply ends the conversation',
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
// staff takeover: start again, then a manual (user-session) SMS on the thread ends it
|
|
195
|
+
const takeoverStart = await processFor(enduser.id)
|
|
196
|
+
assert(takeoverStart.success === true, `takeover-start failed: ${takeoverStart.error}`, 'start (for takeover test) succeeds')
|
|
197
|
+
await wait(undefined, 1000)
|
|
198
|
+
await sdk.api.sms_messages.createOne({
|
|
199
|
+
enduserId: enduser.id,
|
|
200
|
+
message: 'Hi, this is a real person taking over.',
|
|
201
|
+
inbound: false,
|
|
202
|
+
phoneNumber: (await sdk.api.endusers.getOne(enduser.id)).aiConversations?.find(c => c.active)?.source,
|
|
203
|
+
enduserPhoneNumber: TEST_PHONE,
|
|
204
|
+
} as any)
|
|
205
|
+
await wait(undefined, 2000)
|
|
206
|
+
refreshed = await sdk.api.endusers.getOne(enduser.id)
|
|
207
|
+
const takenOver = (refreshed.aiConversations ?? []).find(c => c.endedReason === 'staff-takeover')
|
|
208
|
+
assert(!!takenOver, 'no staff-takeover ended entry found', 'manual staff SMS ends the conversation (takeover)')
|
|
209
|
+
|
|
210
|
+
// no-conversation baseline: marker inbound for a fresh enduser produces zero AI artifacts
|
|
211
|
+
const baselineEnduser = await sdk.api.endusers.createOne({ fname: 'sms-agent-baseline', phone: '+15555555554' })
|
|
212
|
+
createdEnduserIds.push(baselineEnduser.id)
|
|
213
|
+
await sdk.api.sms_messages.createOne({
|
|
214
|
+
enduserId: baselineEnduser.id,
|
|
215
|
+
message: `hello there${TEST_MARKER}`,
|
|
216
|
+
inbound: true,
|
|
217
|
+
logOnly: true,
|
|
218
|
+
phoneNumber: activeEntries[0].source,
|
|
219
|
+
enduserPhoneNumber: '+15555555554',
|
|
220
|
+
} as any)
|
|
221
|
+
await wait(undefined, 2000)
|
|
222
|
+
const baselineRefreshed = await sdk.api.endusers.getOne(baselineEnduser.id)
|
|
223
|
+
const baselineMessages = await sdk.api.sms_messages.getSome({ filter: { enduserId: baselineEnduser.id } })
|
|
224
|
+
assert(
|
|
225
|
+
!(baselineRefreshed.aiConversations ?? []).length && !baselineMessages.find(m => !m.inbound),
|
|
226
|
+
'AI artifacts appeared for an enduser with no conversation',
|
|
227
|
+
'no-conversation inbound produces zero AI artifacts (regression baseline)',
|
|
228
|
+
)
|
|
229
|
+
} finally {
|
|
230
|
+
await sdk.api.journeys.deleteOne(journey.id).catch(() => {})
|
|
231
|
+
for (const id of createdEnduserIds) {
|
|
232
|
+
await sdk.api.endusers.deleteOne(id).catch(() => {})
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Allow running this test file independently
|
|
238
|
+
if (require.main === module) {
|
|
239
|
+
const sdk = new Session({ host })
|
|
240
|
+
const sdkNonAdmin = new Session({ host })
|
|
241
|
+
|
|
242
|
+
const runTests = async () => {
|
|
243
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
244
|
+
await start_ai_conversation_tests({ sdk, sdkNonAdmin })
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
runTests()
|
|
248
|
+
.then(() => {
|
|
249
|
+
console.log("✅ Start AI Conversation test suite completed successfully")
|
|
250
|
+
process.exit(0)
|
|
251
|
+
})
|
|
252
|
+
.catch((error) => {
|
|
253
|
+
console.error("❌ Start AI Conversation test suite failed:", error)
|
|
254
|
+
process.exit(1)
|
|
255
|
+
})
|
|
256
|
+
}
|
package/src/tests/tests.ts
CHANGED
|
@@ -120,6 +120,8 @@ import { load_inbox_redaction_tests } from "./api_tests/security/F-0151-load-inb
|
|
|
120
120
|
import { webhook_timeout_tests } from "./api_tests/security/F-0155-webhook-timeout.test";
|
|
121
121
|
import { enduser_ai_summary_trust_gate_tests } from "./api_tests/security/enduser-ai-summary-trust-gate.test";
|
|
122
122
|
import { allowed_paths_confinement_tests } from "./api_tests/security/F-0166-allowed-paths-wildcard-bypass.test";
|
|
123
|
+
import { proxy_image_ssrf_tests } from "./api_tests/security/F-0025-proxy-image-unauth-ssrf.test";
|
|
124
|
+
import { webhook_outbound_ssrf_tests } from "./api_tests/security/F-0015-webhook-outbound-ssrf.test";
|
|
123
125
|
import { public_endpoint_auth_field_lock_tests } from "./api_tests/security/public_endpoint_auth_field_lock.test";
|
|
124
126
|
import { formsort_webhook_auth_field_lock_tests } from "./api_tests/security/formsort_webhook_auth_field_lock.test";
|
|
125
127
|
import { bulk_assignment_tests } from "./api_tests/bulk_assignment.test";
|
|
@@ -133,6 +135,7 @@ import { load_team_chat_tests } from "./api_tests/load_team_chat.test";
|
|
|
133
135
|
import { form_started_trigger_tests } from "./api_tests/form_started_trigger.test";
|
|
134
136
|
import { formsort_header_auth_tests } from "./api_tests/formsort_header_auth.test";
|
|
135
137
|
import { form_submitted_trigger_tests } from "./api_tests/form_submitted_trigger.test";
|
|
138
|
+
import { start_ai_conversation_tests } from "./api_tests/start_ai_conversation.test";
|
|
136
139
|
import { dont_sync_to_elation_form_submission_tests } from "./api_tests/dont_sync_to_elation_form_submission.test";
|
|
137
140
|
import { medication_added_trigger_tests } from "./api_tests/medication_added_trigger.test";
|
|
138
141
|
import { conditional_logic_medication_unit_tests } from "./unit_tests/conditional_logic_medication.test";
|
|
@@ -144,6 +147,7 @@ import { openloop_webhooks_tests } from "./api_tests/openloop_webhooks.test";
|
|
|
144
147
|
import { beluga_pharmacy_mappings_tests } from "./api_tests/beluga_pharmacy_mappings.test";
|
|
145
148
|
import { mdi_case_offerings_tests } from "./api_tests/mdi_case_offerings.test";
|
|
146
149
|
import { beluga_manual_sync_tests } from "./api_tests/beluga_manual_sync.test";
|
|
150
|
+
import { beluga_trigger_refill_tests } from "./api_tests/beluga_trigger_refill.test";
|
|
147
151
|
import { mdi_webhooks_tests } from "./api_tests/mdi_webhooks.test";
|
|
148
152
|
import { account_switcher_tests } from "./api_tests/account_switcher.test";
|
|
149
153
|
import { totp_mfa_tests } from "./api_tests/totp_mfa.test";
|
|
@@ -15274,6 +15278,8 @@ const ip_address_form_tests = async () => {
|
|
|
15274
15278
|
await replace_form_field_template_values_tests()
|
|
15275
15279
|
await mfa_tests()
|
|
15276
15280
|
await setup_tests(sdk, sdkNonAdmin)
|
|
15281
|
+
await proxy_image_ssrf_tests({ sdk, sdkNonAdmin })
|
|
15282
|
+
await webhook_outbound_ssrf_tests({ sdk, sdkNonAdmin })
|
|
15277
15283
|
await allowed_paths_confinement_tests({ sdk, sdkNonAdmin })
|
|
15278
15284
|
await phone_calls_conference_hold_tests({ sdk, sdkNonAdmin })
|
|
15279
15285
|
await tickets_bulk_assign_care_team_tests({ sdk, sdkNonAdmin })
|
|
@@ -15282,6 +15288,7 @@ const ip_address_form_tests = async () => {
|
|
|
15282
15288
|
await automation_trigger_tests()
|
|
15283
15289
|
await resource_access_tags_tests({ sdk, sdkNonAdmin })
|
|
15284
15290
|
await beluga_manual_sync_tests({ sdk, sdkNonAdmin })
|
|
15291
|
+
await beluga_trigger_refill_tests({ sdk, sdkNonAdmin })
|
|
15285
15292
|
await beluga_pharmacy_mappings_tests({ sdk, sdkNonAdmin })
|
|
15286
15293
|
await mdi_case_offerings_tests({ sdk, sdkNonAdmin })
|
|
15287
15294
|
await enduser_write_restrictions_tests({ sdk, sdkNonAdmin })
|
|
@@ -15320,6 +15327,7 @@ const ip_address_form_tests = async () => {
|
|
|
15320
15327
|
await chats_analytics_tests({ sdk, sdkNonAdmin })
|
|
15321
15328
|
await field_redaction_tests({ sdk, sdkNonAdmin })
|
|
15322
15329
|
await form_submitted_trigger_tests({ sdk, sdkNonAdmin })
|
|
15330
|
+
await start_ai_conversation_tests({ sdk, sdkNonAdmin })
|
|
15323
15331
|
await dont_sync_to_elation_form_submission_tests({ sdk, sdkNonAdmin })
|
|
15324
15332
|
await date_string_validation_tests({ sdk, sdkNonAdmin })
|
|
15325
15333
|
await phone_tree_enduser_condition_tests({ sdk, sdkNonAdmin })
|
package/test_generated.pdf
CHANGED
|
Binary file
|