@tellescope/sdk 1.255.4 → 1.255.5
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/healthie_multi_integration.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/healthie_multi_integration.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/healthie_multi_integration.test.js +316 -0
- package/lib/cjs/tests/api_tests/healthie_multi_integration.test.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +131 -127
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/sdk.d.ts +2 -2
- package/lib/esm/tests/api_tests/healthie_multi_integration.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/healthie_multi_integration.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/healthie_multi_integration.test.js +309 -0
- package/lib/esm/tests/api_tests/healthie_multi_integration.test.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +131 -127
- 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/healthie_multi_integration.test.ts +245 -0
- package/src/tests/tests.ts +2 -0
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import axios from "axios"
|
|
4
|
+
import { Session } from "../../sdk"
|
|
5
|
+
import {
|
|
6
|
+
async_test,
|
|
7
|
+
log_header,
|
|
8
|
+
wait,
|
|
9
|
+
} from "@tellescope/testing"
|
|
10
|
+
import { setup_tests } from "../setup"
|
|
11
|
+
import { BUILT_INS_FOR_SET_FIELDS, HEALTHIE_TITLE } from "@tellescope/constants"
|
|
12
|
+
|
|
13
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
14
|
+
|
|
15
|
+
const TAG = 'clinic-two'
|
|
16
|
+
const FORMSORT_TEST_KEY = '9d4f9dff00f60df2690a16da2cb848f289b447614ad9bef850e54af09a1fbf7a'
|
|
17
|
+
|
|
18
|
+
export const healthie_multi_integration_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
|
|
19
|
+
log_header("Multiple Healthie Integrations Tests")
|
|
20
|
+
|
|
21
|
+
const businessId = sdk.userInfo.businessId
|
|
22
|
+
|
|
23
|
+
let primaryIntegrationId = ''
|
|
24
|
+
let taggedIntegrationId = ''
|
|
25
|
+
let enduserId = ''
|
|
26
|
+
let formsortEnduserId = ''
|
|
27
|
+
let formId = ''
|
|
28
|
+
|
|
29
|
+
try {
|
|
30
|
+
// ── Set Fields support ────────────────────────────────────────────────────────────────────
|
|
31
|
+
if (!BUILT_INS_FOR_SET_FIELDS.includes('healthieIntegrationId')) {
|
|
32
|
+
throw new Error("healthieIntegrationId missing from BUILT_INS_FOR_SET_FIELDS")
|
|
33
|
+
}
|
|
34
|
+
console.log("✅ healthieIntegrationId exposed in BUILT_INS_FOR_SET_FIELDS")
|
|
35
|
+
|
|
36
|
+
// ── Create primary (untagged, sandbox-style key) + additional (tagged, production-style key)
|
|
37
|
+
// via the standard REST create endpoint — the real creation path for additional connections ──
|
|
38
|
+
const primary = await sdk.api.integrations.createOne({
|
|
39
|
+
title: HEALTHIE_TITLE,
|
|
40
|
+
disableEnduserAutoSync: true, // fake keys — avoid auto-sync attempts during the test
|
|
41
|
+
disableTicketAutoSync: true,
|
|
42
|
+
authentication: {
|
|
43
|
+
type: 'apiKey',
|
|
44
|
+
info: { access_token: 'gh_sbox_fake_primary_key', refresh_token: 'unused', scope: '', token_type: 'Bearer', expiry_date: 0 },
|
|
45
|
+
},
|
|
46
|
+
})
|
|
47
|
+
primaryIntegrationId = primary.id
|
|
48
|
+
|
|
49
|
+
const tagged = await sdk.api.integrations.createOne({
|
|
50
|
+
title: HEALTHIE_TITLE,
|
|
51
|
+
tenantId: TAG,
|
|
52
|
+
disableEnduserAutoSync: true,
|
|
53
|
+
disableTicketAutoSync: true,
|
|
54
|
+
authentication: {
|
|
55
|
+
type: 'apiKey',
|
|
56
|
+
info: { access_token: 'fake_production_key', refresh_token: 'unused', scope: '', token_type: 'Bearer', expiry_date: 0 },
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
taggedIntegrationId = tagged.id
|
|
60
|
+
|
|
61
|
+
if (tagged.tenantId !== TAG) {
|
|
62
|
+
throw new Error(`tenantId not persisted on create (got "${tagged.tenantId}")`)
|
|
63
|
+
}
|
|
64
|
+
console.log("✅ tenantId persists via standard integrations create")
|
|
65
|
+
|
|
66
|
+
// ── Per-connection settings: update_settings works against a tagged integration ──
|
|
67
|
+
await async_test(
|
|
68
|
+
"update_settings updates a tagged integration",
|
|
69
|
+
() => sdk.api.integrations.update_settings({ id: taggedIntegrationId, updates: { pushAddedTags: true } }),
|
|
70
|
+
{ onResult: r => r.integration?.pushAddedTags === true },
|
|
71
|
+
)
|
|
72
|
+
await async_test(
|
|
73
|
+
"tagged integration settings persist independently",
|
|
74
|
+
() => sdk.api.integrations.getOne(taggedIntegrationId),
|
|
75
|
+
{ onResult: i => i.pushAddedTags === true && i.tenantId === TAG },
|
|
76
|
+
)
|
|
77
|
+
await async_test(
|
|
78
|
+
"primary integration unaffected by tagged settings update",
|
|
79
|
+
() => sdk.api.integrations.getOne(primaryIntegrationId),
|
|
80
|
+
{ onResult: i => !i.pushAddedTags },
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
// ── Organization.healthieIntegrationIds pushed by create side effect ──
|
|
84
|
+
await wait(undefined, 2000) // side effects are async
|
|
85
|
+
await async_test(
|
|
86
|
+
"organization.healthieIntegrationIds includes tag after create",
|
|
87
|
+
() => sdk.api.organizations.getOne(businessId),
|
|
88
|
+
{ onResult: o => !!(o as any).healthieIntegrationIds?.includes(TAG) },
|
|
89
|
+
)
|
|
90
|
+
|
|
91
|
+
// ── Inbound webhook resolution (help page proves the integration lookup without network) ──
|
|
92
|
+
await async_test(
|
|
93
|
+
"webhook help page resolves primary (sandbox) without query param",
|
|
94
|
+
() => axios.get(`${host}/v1/webhooks/healthie/${businessId}`),
|
|
95
|
+
{ onResult: r => typeof r.data === 'string' && r.data.startsWith('Sandbox') },
|
|
96
|
+
)
|
|
97
|
+
await async_test(
|
|
98
|
+
"webhook help page resolves tagged (production) integration via healthieIntegrationId param",
|
|
99
|
+
() => axios.get(`${host}/v1/webhooks/healthie/${businessId}?healthieIntegrationId=${TAG}`),
|
|
100
|
+
{ onResult: r => typeof r.data === 'string' && r.data.startsWith('Production') },
|
|
101
|
+
)
|
|
102
|
+
await async_test(
|
|
103
|
+
"webhook help page rejects unknown healthieIntegrationId",
|
|
104
|
+
() => axios.get(`${host}/v1/webhooks/healthie/${businessId}?healthieIntegrationId=not-a-real-id`),
|
|
105
|
+
{ shouldError: true, onError: (e: any) => e?.response?.status === 400 },
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
// ── Enduser field + one-org-per-patient lock (relationship constraint) ──
|
|
109
|
+
const enduser = await sdk.api.endusers.createOne({
|
|
110
|
+
email: 'multi-healthie-test@tellescope.com',
|
|
111
|
+
healthieIntegrationId: TAG,
|
|
112
|
+
})
|
|
113
|
+
enduserId = enduser.id
|
|
114
|
+
if (enduser.healthieIntegrationId !== TAG) {
|
|
115
|
+
throw new Error("healthieIntegrationId not persisted on enduser create")
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
await async_test(
|
|
119
|
+
"healthieIntegrationId freely changeable while unlinked",
|
|
120
|
+
() => sdk.api.endusers.updateOne(enduserId, { healthieIntegrationId: '' }),
|
|
121
|
+
{ onResult: e => !e.healthieIntegrationId },
|
|
122
|
+
)
|
|
123
|
+
await async_test(
|
|
124
|
+
"healthieIntegrationId re-set while unlinked",
|
|
125
|
+
() => sdk.api.endusers.updateOne(enduserId, { healthieIntegrationId: TAG }),
|
|
126
|
+
{ onResult: e => e.healthieIntegrationId === TAG },
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
// link the patient to Healthie (reference with a Healthie patient id)
|
|
130
|
+
await sdk.api.endusers.updateOne(enduserId, { references: [{ type: HEALTHIE_TITLE, id: '12345' }] } as any, { replaceObjectFields: true })
|
|
131
|
+
|
|
132
|
+
await async_test(
|
|
133
|
+
"healthieIntegrationId locked once a Healthie patient ID exists",
|
|
134
|
+
() => sdk.api.endusers.updateOne(enduserId, { healthieIntegrationId: 'somewhere-else' }),
|
|
135
|
+
{ shouldError: true, onError: (e: any) => (e?.message || e?.toString() || '').includes('healthieIntegrationId') },
|
|
136
|
+
)
|
|
137
|
+
await async_test(
|
|
138
|
+
"no-op write of the same value still allowed while locked",
|
|
139
|
+
() => sdk.api.endusers.updateOne(enduserId, { healthieIntegrationId: TAG }),
|
|
140
|
+
{ onResult: e => e.healthieIntegrationId === TAG },
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
// unlink → changeable again
|
|
144
|
+
await sdk.api.endusers.updateOne(enduserId, { references: [] } as any, { replaceObjectFields: true })
|
|
145
|
+
await async_test(
|
|
146
|
+
"healthieIntegrationId changeable again after unlinking",
|
|
147
|
+
() => sdk.api.endusers.updateOne(enduserId, { healthieIntegrationId: '' }),
|
|
148
|
+
{ onResult: e => !e.healthieIntegrationId },
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
// ── Formsort mapping ──
|
|
152
|
+
const form = await sdk.api.forms.createOne({ title: "Multi Healthie FormSort" })
|
|
153
|
+
formId = form.id
|
|
154
|
+
|
|
155
|
+
const formsortEmail = 'multi-healthie-formsort@tellescope.com'
|
|
156
|
+
const postToFormsort = (answers: { key: string, value: any }[], responder_uuid: string) => {
|
|
157
|
+
const url = new URL(`${host}/v1/webhooks/formsort/${FORMSORT_TEST_KEY}`)
|
|
158
|
+
url.searchParams.set('formId', form.id)
|
|
159
|
+
return axios.post(url.toString(), { answers, responder_uuid, finalized: true })
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
await postToFormsort([
|
|
163
|
+
{ key: 'email', value: formsortEmail },
|
|
164
|
+
{ key: 'healthieIntegrationId', value: TAG },
|
|
165
|
+
], "multi-healthie-1")
|
|
166
|
+
|
|
167
|
+
await async_test(
|
|
168
|
+
"formsort sets healthieIntegrationId on unlinked enduser",
|
|
169
|
+
() => sdk.api.endusers.getSome({ filter: { email: formsortEmail } }),
|
|
170
|
+
{ onResult: es => {
|
|
171
|
+
const e = es.find(e => e.email === formsortEmail)
|
|
172
|
+
formsortEnduserId = e?.id || ''
|
|
173
|
+
return e?.healthieIntegrationId === TAG
|
|
174
|
+
}},
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
// link the formsort enduser, then attempt a relink via formsort — field change must be dropped
|
|
178
|
+
await sdk.api.endusers.updateOne(formsortEnduserId, { references: [{ type: HEALTHIE_TITLE, id: '6789' }] } as any, { replaceObjectFields: true })
|
|
179
|
+
await postToFormsort([
|
|
180
|
+
{ key: 'email', value: formsortEmail },
|
|
181
|
+
{ key: 'healthieIntegrationId', value: 'somewhere-else' },
|
|
182
|
+
], "multi-healthie-2")
|
|
183
|
+
await wait(undefined, 1000)
|
|
184
|
+
|
|
185
|
+
await async_test(
|
|
186
|
+
"formsort relink attempt on linked enduser is dropped",
|
|
187
|
+
() => sdk.api.endusers.getOne(formsortEnduserId),
|
|
188
|
+
{ onResult: e => e.healthieIntegrationId === TAG },
|
|
189
|
+
)
|
|
190
|
+
await async_test(
|
|
191
|
+
"formsort relink attempt logs a customer-visible background error",
|
|
192
|
+
() => sdk.api.background_errors.getSome(),
|
|
193
|
+
{ onResult: errors => errors.some(e =>
|
|
194
|
+
e.title === "Healthie Integration Change Blocked" && e.enduserId === formsortEnduserId
|
|
195
|
+
)},
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
// ── Removing the primary must not affect the tagged integration; org list recomputes on delete ──
|
|
199
|
+
await sdk.api.integrations.deleteOne(primaryIntegrationId)
|
|
200
|
+
primaryIntegrationId = ''
|
|
201
|
+
await async_test(
|
|
202
|
+
"tagged integration survives primary deletion",
|
|
203
|
+
() => sdk.api.integrations.getOne(taggedIntegrationId),
|
|
204
|
+
{ onResult: i => i.tenantId === TAG },
|
|
205
|
+
)
|
|
206
|
+
|
|
207
|
+
await sdk.api.integrations.deleteOne(taggedIntegrationId)
|
|
208
|
+
taggedIntegrationId = ''
|
|
209
|
+
await wait(undefined, 2000) // delete side effect is async
|
|
210
|
+
await async_test(
|
|
211
|
+
"organization.healthieIntegrationIds recomputed (emptied) after delete",
|
|
212
|
+
() => sdk.api.organizations.getOne(businessId),
|
|
213
|
+
{ onResult: o => !(o as any).healthieIntegrationIds?.includes(TAG) },
|
|
214
|
+
)
|
|
215
|
+
} finally {
|
|
216
|
+
for (const id of [primaryIntegrationId, taggedIntegrationId]) {
|
|
217
|
+
if (!id) continue
|
|
218
|
+
await sdk.api.integrations.deleteOne(id).catch(console.error)
|
|
219
|
+
}
|
|
220
|
+
if (enduserId) await sdk.api.endusers.deleteOne(enduserId).catch(console.error)
|
|
221
|
+
if (formsortEnduserId) await sdk.api.endusers.deleteOne(formsortEnduserId).catch(console.error)
|
|
222
|
+
if (formId) await sdk.api.forms.deleteOne(formId).catch(console.error)
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (require.main === module) {
|
|
227
|
+
console.log(`Using API URL: ${host}`)
|
|
228
|
+
const sdk = new Session({ host })
|
|
229
|
+
const sdkNonAdmin = new Session({ host })
|
|
230
|
+
|
|
231
|
+
const runTests = async () => {
|
|
232
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
233
|
+
await healthie_multi_integration_tests({ sdk, sdkNonAdmin })
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
runTests()
|
|
237
|
+
.then(() => {
|
|
238
|
+
console.log("✅ Multiple Healthie integrations test suite completed successfully")
|
|
239
|
+
process.exit(0)
|
|
240
|
+
})
|
|
241
|
+
.catch((error) => {
|
|
242
|
+
console.error("❌ Multiple Healthie integrations test suite failed:", error)
|
|
243
|
+
process.exit(1)
|
|
244
|
+
})
|
|
245
|
+
}
|
package/src/tests/tests.ts
CHANGED
|
@@ -39,6 +39,7 @@ import { enduser_observations_acknowledge_tests } from "./api_tests/enduser_obse
|
|
|
39
39
|
import { translations_tests } from "./api_tests/translations.test"
|
|
40
40
|
import { user_portal_settings_tests } from "./api_tests/user_portal_settings.test"
|
|
41
41
|
import { integrations_redacted_tests } from "./api_tests/integrations_redacted.test"
|
|
42
|
+
import { healthie_multi_integration_tests } from "./api_tests/healthie_multi_integration.test"
|
|
42
43
|
import { get_some_projection_tests } from "./api_tests/get_some_projection.test"
|
|
43
44
|
import { mdb_sort_tests } from "./api_tests/mdb_sort.test"
|
|
44
45
|
import { create_user_notifications_trigger_tests } from "./api_tests/create_user_notifications_trigger.test"
|
|
@@ -15073,6 +15074,7 @@ const ip_address_form_tests = async () => {
|
|
|
15073
15074
|
await openloop_webhooks_tests({ sdk, sdkNonAdmin })
|
|
15074
15075
|
await mdi_webhooks_tests({ sdk, sdkNonAdmin })
|
|
15075
15076
|
await integrations_redacted_tests({ sdk, sdkNonAdmin })
|
|
15077
|
+
await healthie_multi_integration_tests({ sdk, sdkNonAdmin })
|
|
15076
15078
|
await mdb_sort_tests({ sdk, sdkNonAdmin })
|
|
15077
15079
|
await search_tests()
|
|
15078
15080
|
await time_tracks_tests({ sdk, sdkNonAdmin })
|
package/test_generated.pdf
CHANGED
|
Binary file
|