@tellescope/sdk 1.256.4 → 1.256.6

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tellescope/sdk",
3
- "version": "1.256.4",
3
+ "version": "1.256.6",
4
4
  "description": "Code for interacting with the Tellescope API",
5
5
  "main": "./lib/cjs/sdk.js",
6
6
  "module": "./lib/esm/sdk.js",
@@ -32,14 +32,14 @@
32
32
  },
33
33
  "homepage": "https://github.com/tellescope-os/tellescope#readme",
34
34
  "dependencies": {
35
- "@tellescope/constants": "1.256.4",
36
- "@tellescope/schema": "1.256.4",
37
- "@tellescope/testing": "1.256.4",
38
- "@tellescope/types-client": "1.256.4",
39
- "@tellescope/types-models": "1.256.4",
40
- "@tellescope/types-utilities": "1.256.4",
41
- "@tellescope/utilities": "1.256.4",
42
- "@tellescope/validation": "1.256.4",
35
+ "@tellescope/constants": "1.256.6",
36
+ "@tellescope/schema": "1.256.6",
37
+ "@tellescope/testing": "1.256.6",
38
+ "@tellescope/types-client": "1.256.6",
39
+ "@tellescope/types-models": "1.256.6",
40
+ "@tellescope/types-utilities": "1.256.6",
41
+ "@tellescope/utilities": "1.256.6",
42
+ "@tellescope/validation": "1.256.6",
43
43
  "axios": "0.30.3",
44
44
  "dotenv": "14.3.2",
45
45
  "form-data": "4.0.4",
@@ -55,5 +55,5 @@
55
55
  "publishConfig": {
56
56
  "access": "public"
57
57
  },
58
- "gitHead": "f8b3327e5602d5dae690109eb9271aa1c2628c37"
58
+ "gitHead": "b5672761d4ea6995488faaeb9a0ccc12436f7b30"
59
59
  }
@@ -6,6 +6,7 @@ import {
6
6
  log_header,
7
7
  passOnAnyResult,
8
8
  } from "@tellescope/testing"
9
+ import { BELUGA_TITLE } from "@tellescope/constants"
9
10
  import { setup_tests } from "../setup"
10
11
 
11
12
  const host = process.env.API_URL || 'http://localhost:8080' as const
@@ -24,6 +25,13 @@ export const beluga_trigger_refill_tests = async ({ sdk, sdkNonAdmin }: { sdk: S
24
25
  let formId: string | undefined
25
26
  const stepIds: string[] = []
26
27
 
28
+ // cross-patient guard setup (created only if we reach that section)
29
+ let enduserAId: string | undefined
30
+ let enduserBId: string | undefined
31
+ let otherPatientFormResponseId: string | undefined
32
+ let automatedActionId: string | undefined
33
+ let createdBelugaIntegrationId: string | undefined
34
+
27
35
  try {
28
36
  const journey = await sdk.api.journeys.createOne({ title: 'Beluga Trigger Refill Test Journey' })
29
37
  journeyId = journey.id
@@ -121,7 +129,80 @@ export const beluga_trigger_refill_tests = async ({ sdk, sdkNonAdmin }: { sdk: S
121
129
  },
122
130
  },
123
131
  )
132
+
133
+ // ── Cross-patient guard (pre-deploy review H3) ──────────────────────────────
134
+ // The process endpoint reads journeyContext.formResponseId; without an ownership check a
135
+ // switchToRelatedContact step could point it at a DIFFERENT patient's form response and issue a
136
+ // refill against their visit. This exercises the guard end-to-end: it returns BEFORE any live
137
+ // Beluga call (no sandbox creds needed), but integration resolution precedes it in the handler,
138
+ // so a Beluga integration must exist — the test self-skips when one can't be created.
139
+ const enduserA = await sdk.api.endusers.createOne({ fname: 'Beluga', lname: 'PatientA' })
140
+ enduserAId = enduserA.id
141
+ const enduserB = await sdk.api.endusers.createOne({ fname: 'Beluga', lname: 'PatientB' })
142
+ enduserBId = enduserB.id
143
+
144
+ // a form response owned by patient A
145
+ const otherResponse = await sdk.api.form_responses.createOne({
146
+ formId: form.id,
147
+ formTitle: form.title,
148
+ enduserId: enduserA.id,
149
+ })
150
+ otherPatientFormResponseId = otherResponse.id
151
+
152
+ let belugaIntegrationAvailable = false
153
+ try {
154
+ const existing = await sdk.api.integrations.load_redacted({})
155
+ belugaIntegrationAvailable = !!existing.integrations.find((i: any) => i.title === BELUGA_TITLE)
156
+ } catch { /* load not permitted — fall through to create attempt */ }
157
+
158
+ if (!belugaIntegrationAvailable) {
159
+ try {
160
+ const created = await sdk.api.integrations.createOne({
161
+ title: BELUGA_TITLE,
162
+ authentication: {
163
+ type: 'oauth2',
164
+ info: { access_token: 'test-access-token', refresh_token: 'test-refresh-token', scope: '', token_type: 'Bearer', expiry_date: new Date().getTime() },
165
+ },
166
+ } as any)
167
+ createdBelugaIntegrationId = created.id
168
+ belugaIntegrationAvailable = true
169
+ } catch (e) {
170
+ console.log("Could not create a Beluga integration; skipping cross-patient guard test:", e)
171
+ }
172
+ }
173
+
174
+ if (belugaIntegrationAvailable) {
175
+ // an automated action for patient B whose journeyContext points at patient A's response.
176
+ // processAfter is set in the future so no background worker picks it up before the explicit call.
177
+ const automatedAction = await sdk.api.automated_actions.createOne({
178
+ journeyId: journey.id,
179
+ automationStepId: stepIds[0],
180
+ enduserId: enduserB.id,
181
+ event: { type: 'onJourneyStart', info: {} },
182
+ action: {
183
+ type: 'belugaTriggerRefill',
184
+ info: { formId: form.id, patientPreferences: [{ medId: 'med-123' }] },
185
+ },
186
+ status: 'active',
187
+ processAfter: new Date().getTime() + 60 * 60 * 1000,
188
+ journeyContext: { formResponseId: otherResponse.id },
189
+ } as any)
190
+ automatedActionId = automatedAction.id
191
+
192
+ await async_test(
193
+ "belugaTriggerRefill rejects a form response owned by a different patient",
194
+ () => sdk.api.automated_actions.process({ automatedActionId: automatedAction.id }),
195
+ { onResult: (r: any) => r?.success === false && /does not belong to this patient/i.test(r?.error || '') },
196
+ )
197
+ } else {
198
+ console.log("⏭️ Skipping belugaTriggerRefill cross-patient guard test (no Beluga integration available)")
199
+ }
124
200
  } finally {
201
+ if (automatedActionId) await sdk.api.automated_actions.deleteOne(automatedActionId).catch(console.error)
202
+ if (otherPatientFormResponseId) await sdk.api.form_responses.deleteOne(otherPatientFormResponseId).catch(console.error)
203
+ if (enduserAId) await sdk.api.endusers.deleteOne(enduserAId).catch(console.error)
204
+ if (enduserBId) await sdk.api.endusers.deleteOne(enduserBId).catch(console.error)
205
+ if (createdBelugaIntegrationId) await sdk.api.integrations.deleteOne(createdBelugaIntegrationId).catch(console.error)
125
206
  for (const id of stepIds) {
126
207
  await sdk.api.automation_steps.deleteOne(id).catch(console.error)
127
208
  }
Binary file