@tellescope/sdk 1.242.9 → 1.243.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.
Files changed (64) hide show
  1. package/lib/cjs/enduser.d.ts +20 -0
  2. package/lib/cjs/enduser.d.ts.map +1 -1
  3. package/lib/cjs/sdk.d.ts +45 -0
  4. package/lib/cjs/sdk.d.ts.map +1 -1
  5. package/lib/cjs/sdk.js +2 -0
  6. package/lib/cjs/sdk.js.map +1 -1
  7. package/lib/cjs/tests/api_tests/concurrent_build_threads.test.d.ts +6 -0
  8. package/lib/cjs/tests/api_tests/concurrent_build_threads.test.d.ts.map +1 -0
  9. package/lib/cjs/tests/api_tests/concurrent_build_threads.test.js +169 -0
  10. package/lib/cjs/tests/api_tests/concurrent_build_threads.test.js.map +1 -0
  11. package/lib/cjs/tests/api_tests/custom_aggregation.test.d.ts.map +1 -1
  12. package/lib/cjs/tests/api_tests/custom_aggregation.test.js +109 -7
  13. package/lib/cjs/tests/api_tests/custom_aggregation.test.js.map +1 -1
  14. package/lib/cjs/tests/api_tests/custom_dashboards.test.d.ts +6 -0
  15. package/lib/cjs/tests/api_tests/custom_dashboards.test.d.ts.map +1 -0
  16. package/lib/cjs/tests/api_tests/custom_dashboards.test.js +304 -0
  17. package/lib/cjs/tests/api_tests/custom_dashboards.test.js.map +1 -0
  18. package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.d.ts.map +1 -1
  19. package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.js +655 -139
  20. package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.js.map +1 -1
  21. package/lib/cjs/tests/api_tests/no_access_permission_checks.test.d.ts +20 -0
  22. package/lib/cjs/tests/api_tests/no_access_permission_checks.test.d.ts.map +1 -0
  23. package/lib/cjs/tests/api_tests/no_access_permission_checks.test.js +481 -0
  24. package/lib/cjs/tests/api_tests/no_access_permission_checks.test.js.map +1 -0
  25. package/lib/cjs/tests/tests.d.ts.map +1 -1
  26. package/lib/cjs/tests/tests.js +125 -112
  27. package/lib/cjs/tests/tests.js.map +1 -1
  28. package/lib/esm/enduser.d.ts +20 -0
  29. package/lib/esm/enduser.d.ts.map +1 -1
  30. package/lib/esm/sdk.d.ts +45 -0
  31. package/lib/esm/sdk.d.ts.map +1 -1
  32. package/lib/esm/sdk.js +2 -0
  33. package/lib/esm/sdk.js.map +1 -1
  34. package/lib/esm/tests/api_tests/concurrent_build_threads.test.d.ts +6 -0
  35. package/lib/esm/tests/api_tests/concurrent_build_threads.test.d.ts.map +1 -0
  36. package/lib/esm/tests/api_tests/concurrent_build_threads.test.js +165 -0
  37. package/lib/esm/tests/api_tests/concurrent_build_threads.test.js.map +1 -0
  38. package/lib/esm/tests/api_tests/custom_aggregation.test.d.ts.map +1 -1
  39. package/lib/esm/tests/api_tests/custom_aggregation.test.js +110 -8
  40. package/lib/esm/tests/api_tests/custom_aggregation.test.js.map +1 -1
  41. package/lib/esm/tests/api_tests/custom_dashboards.test.d.ts +6 -0
  42. package/lib/esm/tests/api_tests/custom_dashboards.test.d.ts.map +1 -0
  43. package/lib/esm/tests/api_tests/custom_dashboards.test.js +300 -0
  44. package/lib/esm/tests/api_tests/custom_dashboards.test.js.map +1 -0
  45. package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.d.ts.map +1 -1
  46. package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.js +655 -139
  47. package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.js.map +1 -1
  48. package/lib/esm/tests/api_tests/no_access_permission_checks.test.d.ts +20 -0
  49. package/lib/esm/tests/api_tests/no_access_permission_checks.test.d.ts.map +1 -0
  50. package/lib/esm/tests/api_tests/no_access_permission_checks.test.js +477 -0
  51. package/lib/esm/tests/api_tests/no_access_permission_checks.test.js.map +1 -0
  52. package/lib/esm/tests/tests.d.ts.map +1 -1
  53. package/lib/esm/tests/tests.js +125 -112
  54. package/lib/esm/tests/tests.js.map +1 -1
  55. package/lib/tsconfig.tsbuildinfo +1 -1
  56. package/package.json +10 -10
  57. package/src/sdk.ts +9 -1
  58. package/src/tests/api_tests/concurrent_build_threads.test.ts +103 -0
  59. package/src/tests/api_tests/custom_aggregation.test.ts +74 -0
  60. package/src/tests/api_tests/custom_dashboards.test.ts +258 -0
  61. package/src/tests/api_tests/inbox_thread_assignment_updates.test.ts +431 -1
  62. package/src/tests/api_tests/no_access_permission_checks.test.ts +365 -0
  63. package/src/tests/tests.ts +8 -1
  64. package/test_generated.pdf +0 -0
@@ -0,0 +1,365 @@
1
+ require('source-map-support').install();
2
+
3
+ import { Session } from "../../sdk"
4
+ import {
5
+ async_test,
6
+ log_header,
7
+ wait,
8
+ } from "@tellescope/testing"
9
+ import { setup_tests } from "../setup"
10
+ import { PROVIDER_PERMISSIONS } from "@tellescope/constants"
11
+
12
+ const host = process.env.API_URL || 'http://localhost:8080' as const
13
+ const [nonAdminEmail, nonAdminPassword] = [process.env.NON_ADMIN_EMAIL, process.env.NON_ADMIN_PASSWORD]
14
+
15
+ /**
16
+ * Security tests for endpoints with noAccessPermissions: true
17
+ *
18
+ * These tests verify that endpoints which bypass the standard middleware access check
19
+ * still properly enforce NO_ACCESS restrictions in their handlers.
20
+ *
21
+ * Test approach:
22
+ * 1. Create a role with NO_ACCESS (null) for a specific model
23
+ * 2. Assign that role to a non-admin user
24
+ * 3. Attempt to call the endpoint
25
+ * 4. Verify whether access is properly denied
26
+ *
27
+ * If a test shows data is returned when it shouldn't be, that endpoint needs a fix.
28
+ */
29
+ export const no_access_permission_checks_tests = async ({ sdk, sdkNonAdmin } : { sdk: Session, sdkNonAdmin: Session }) => {
30
+ log_header("NO_ACCESS Permission Checks Tests")
31
+
32
+ // Create test data
33
+ const testEnduser = await sdk.api.endusers.createOne({
34
+ fname: 'NoAccessTest',
35
+ lname: 'User',
36
+ email: 'no-access-test@example.com',
37
+ })
38
+
39
+ // Create a role with NO_ACCESS to multiple models we want to test
40
+ const noAccessTestRole = 'no-access-test-role'
41
+ const rbap = await sdk.api.role_based_access_permissions.createOne({
42
+ role: noAccessTestRole,
43
+ permissions: {
44
+ ...PROVIDER_PERMISSIONS,
45
+ // Set NO_ACCESS for models we want to test
46
+ endusers: {
47
+ create: null,
48
+ read: null,
49
+ update: null,
50
+ delete: null,
51
+ },
52
+ inbox_threads: {
53
+ create: null,
54
+ read: null,
55
+ update: null,
56
+ delete: null,
57
+ },
58
+ templates: {
59
+ create: null,
60
+ read: null,
61
+ update: null,
62
+ delete: null,
63
+ },
64
+ waitlists: {
65
+ create: null,
66
+ read: null,
67
+ update: null,
68
+ delete: null,
69
+ },
70
+ background_errors: {
71
+ create: null,
72
+ read: null,
73
+ update: null,
74
+ delete: null,
75
+ },
76
+ },
77
+ })
78
+
79
+ // Save original role to restore later
80
+ const originalRoles = sdkNonAdmin.userInfo.roles
81
+
82
+ try {
83
+ // Assign the restricted role to non-admin user
84
+ await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { roles: [noAccessTestRole] }, { replaceObjectFields: true })
85
+ await wait(undefined, 1500) // wait for role change to propagate
86
+ await sdkNonAdmin.authenticate(nonAdminEmail!, nonAdminPassword!)
87
+
88
+ // ========================================
89
+ // Test 1: /bulk-actions/read (HIGH PRIORITY)
90
+ // ========================================
91
+ log_header("Test 1: /bulk-actions/read with NO_ACCESS to endusers")
92
+
93
+ await async_test(
94
+ "bulk_load - should block NO_ACCESS user from reading endusers",
95
+ () => sdkNonAdmin.bulk_load({
96
+ load: [{ model: 'endusers' as any, options: { limit: 10 } }]
97
+ }),
98
+ {
99
+ // If this returns records, it's a vulnerability
100
+ // If it returns empty records or errors, it's safe
101
+ onResult: r => {
102
+ const enduserResult = r.results[0]
103
+ if (enduserResult === null) {
104
+ console.log(" ✅ SAFE: bulk_load returned null for NO_ACCESS model")
105
+ return true
106
+ }
107
+ if (enduserResult.records.length === 0) {
108
+ console.log(" ✅ SAFE: bulk_load returned empty records for NO_ACCESS model")
109
+ return true
110
+ }
111
+ console.log(` ❌ VULNERABILITY: bulk_load returned ${enduserResult.records.length} records when user has NO_ACCESS!`)
112
+ return false
113
+ }
114
+ }
115
+ )
116
+
117
+ // ========================================
118
+ // Test 2: inbox_threads/build_threads
119
+ // ========================================
120
+ log_header("Test 2: inbox_threads/build_threads with NO_ACCESS")
121
+
122
+ await async_test(
123
+ "build_threads - should block NO_ACCESS user",
124
+ () => sdkNonAdmin.api.inbox_threads.build_threads({
125
+ from: new Date(Date.now() - 7 * 24 * 60 * 60 * 1000), // 1 week ago
126
+ to: new Date(),
127
+ }),
128
+ { shouldError: true, onError: (e: any) => e.message === "You do not have access to this resource" }
129
+ )
130
+
131
+ // ========================================
132
+ // Test 2b: inbox_threads/load_threads with NO_ACCESS
133
+ // ========================================
134
+ log_header("Test 2b: load_threads with NO_ACCESS")
135
+
136
+ await async_test(
137
+ "load_threads - should block NO_ACCESS user",
138
+ () => sdkNonAdmin.api.inbox_threads.load_threads({ limit: 10 }),
139
+ { shouldError: true, onError: (e: any) => e.message === "You do not have access to this resource" }
140
+ )
141
+
142
+ // ========================================
143
+ // Test 3a: get_templated_message - NO_ACCESS to templates
144
+ // ========================================
145
+ log_header("Test 3a: get_templated_message (templates NO_ACCESS)")
146
+
147
+ await async_test(
148
+ "get_templated_message - should block user with NO_ACCESS to templates",
149
+ () => sdkNonAdmin.api.templates.get_templated_message({
150
+ message: "Hello {{enduser.fname}}!",
151
+ userId: sdkNonAdmin.userInfo.id,
152
+ enduserId: testEnduser.id,
153
+ channel: 'Email',
154
+ }),
155
+ { shouldError: true, onError: (e: any) => e.message === "You do not have access to this resource" }
156
+ )
157
+
158
+ // ========================================
159
+ // Test 3b: get_templated_message - NO_ACCESS to endusers (PHI leak prevention)
160
+ // ========================================
161
+ log_header("Test 3b: get_templated_message (endusers NO_ACCESS)")
162
+
163
+ // Create a role with templates access but NO_ACCESS to endusers
164
+ const templatesOnlyRole = 'templates-only-test-role'
165
+ const rbapTemplatesOnly = await sdk.api.role_based_access_permissions.createOne({
166
+ role: templatesOnlyRole,
167
+ permissions: {
168
+ ...PROVIDER_PERMISSIONS,
169
+ // Allow templates access
170
+ templates: {
171
+ create: 'All',
172
+ read: 'All',
173
+ update: 'All',
174
+ delete: 'All',
175
+ },
176
+ // But NO_ACCESS to endusers
177
+ endusers: {
178
+ create: null,
179
+ read: null,
180
+ update: null,
181
+ delete: null,
182
+ },
183
+ },
184
+ })
185
+
186
+ try {
187
+ // Temporarily assign the templates-only role
188
+ await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { roles: [templatesOnlyRole] }, { replaceObjectFields: true })
189
+ await wait(undefined, 1500)
190
+ await sdkNonAdmin.authenticate(nonAdminEmail!, nonAdminPassword!)
191
+
192
+ await async_test(
193
+ "get_templated_message - should block user with NO_ACCESS to endusers (prevents PHI leak)",
194
+ () => sdkNonAdmin.api.templates.get_templated_message({
195
+ message: "Hello {{enduser.fname}} {{enduser.lname}} {{enduser.email}}!",
196
+ userId: sdkNonAdmin.userInfo.id,
197
+ enduserId: testEnduser.id,
198
+ channel: 'Email',
199
+ }),
200
+ { shouldError: true, onError: (e: any) => e.message === "You do not have access to this resource" }
201
+ )
202
+ } finally {
203
+ // Restore the original test role
204
+ await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { roles: [noAccessTestRole] }, { replaceObjectFields: true })
205
+ await wait(undefined, 1000)
206
+ await sdkNonAdmin.authenticate(nonAdminEmail!, nonAdminPassword!)
207
+
208
+ // Cleanup the templates-only role
209
+ await sdk.api.role_based_access_permissions.deleteOne(rbapTemplatesOnly.id)
210
+ }
211
+
212
+ // ========================================
213
+ // Test 4: waitlists/grant_access_from_waitlist
214
+ // ========================================
215
+ log_header("Test 4: waitlists/grant_access_from_waitlist with NO_ACCESS")
216
+
217
+ // First create a journey and waitlist entry to test with (as admin)
218
+ const testJourney = await sdk.api.journeys.createOne({
219
+ title: 'Waitlist Test Journey',
220
+ })
221
+
222
+ const waitlistEntry = await sdk.api.waitlists.createOne({
223
+ title: 'Test Waitlist',
224
+ journeyId: testJourney.id,
225
+ enduserIds: [],
226
+ })
227
+
228
+ try {
229
+ await async_test(
230
+ "grant_access_from_waitlist - should block NO_ACCESS user",
231
+ () => sdkNonAdmin.api.waitlists.grant_access_from_waitlist({
232
+ id: waitlistEntry.id,
233
+ count: 1,
234
+ }),
235
+ { shouldError: true, onError: (e: any) => e.message === "You do not have access to this resource" }
236
+ )
237
+ } finally {
238
+ // Cleanup
239
+ await sdk.api.waitlists.deleteOne(waitlistEntry.id)
240
+ await sdk.api.journeys.deleteOne(testJourney.id)
241
+ }
242
+
243
+ // ========================================
244
+ // Test 5: background_errors/mark_read
245
+ // ========================================
246
+ log_header("Test 5: background_errors/mark_read with NO_ACCESS")
247
+
248
+ await async_test(
249
+ "mark_read (background_errors) - should block NO_ACCESS user",
250
+ () => sdkNonAdmin.api.background_errors.mark_read({}),
251
+ { shouldError: true, onError: (e: any) => e.message === "You do not have access to this resource" }
252
+ )
253
+
254
+ // ========================================
255
+ // Test 6: load_threads searchKeywords redaction
256
+ // ========================================
257
+ log_header("Test 6: load_threads searchKeywords redaction")
258
+
259
+ // Create role with Assigned access to endusers (not 'All') but full message access
260
+ const assignedEnduserRole = 'assigned-enduser-test-role'
261
+ const rbapAssigned = await sdk.api.role_based_access_permissions.createOne({
262
+ role: assignedEnduserRole,
263
+ permissions: {
264
+ ...PROVIDER_PERMISSIONS,
265
+ endusers: { create: 'All', read: 'Assigned', update: 'Assigned', delete: 'Assigned' },
266
+ emails: { create: 'All', read: 'All', update: 'All', delete: 'All' },
267
+ sms_messages: { create: 'All', read: 'All', update: 'All', delete: 'All' },
268
+ inbox_threads: { create: 'All', read: 'All', update: 'All', delete: 'All' },
269
+ },
270
+ })
271
+
272
+ try {
273
+ await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { roles: [assignedEnduserRole] }, { replaceObjectFields: true })
274
+ await wait(undefined, 1500)
275
+ await sdkNonAdmin.authenticate(nonAdminEmail!, nonAdminPassword!)
276
+
277
+ // Load threads and verify searchKeywords is redacted for Assigned enduser access
278
+ const resultAssigned = await sdkNonAdmin.api.inbox_threads.load_threads({ limit: 10 })
279
+
280
+ await async_test(
281
+ "load_threads - searchKeywords redacted for Assigned enduser access",
282
+ async () => resultAssigned,
283
+ {
284
+ onResult: r => {
285
+ // All threads should have searchKeywords undefined/missing
286
+ const allRedacted = r.threads.every((t: any) => t.searchKeywords === undefined)
287
+ if (!allRedacted) {
288
+ console.log(" ❌ VULNERABILITY: searchKeywords visible to user with Assigned enduser access!")
289
+ } else {
290
+ console.log(" ✅ SAFE: searchKeywords properly redacted")
291
+ }
292
+ return allRedacted
293
+ }
294
+ }
295
+ )
296
+ } finally {
297
+ // Restore the original test role
298
+ await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { roles: [noAccessTestRole] }, { replaceObjectFields: true })
299
+ await wait(undefined, 1000)
300
+ await sdkNonAdmin.authenticate(nonAdminEmail!, nonAdminPassword!)
301
+
302
+ // Cleanup the assigned role
303
+ await sdk.api.role_based_access_permissions.deleteOne(rbapAssigned.id)
304
+ }
305
+
306
+ // Test that admin/full access users CAN see searchKeywords
307
+ await async_test(
308
+ "load_threads - searchKeywords visible for admin/full enduser access",
309
+ () => sdk.api.inbox_threads.load_threads({ limit: 10 }),
310
+ {
311
+ onResult: r => {
312
+ // Skip check if no threads exist (can't verify without data)
313
+ if (r.threads.length === 0) {
314
+ console.log(" ⏭️ SKIPPED: No threads exist to verify searchKeywords visibility")
315
+ return true
316
+ }
317
+ // At least some threads should have searchKeywords
318
+ const hasSearchKeywords = r.threads.some((t: any) => t.searchKeywords !== undefined)
319
+ if (hasSearchKeywords) {
320
+ console.log(" ✅ Admin can see searchKeywords")
321
+ } else {
322
+ console.log(" ⚠️ No searchKeywords found on threads (may not have been built yet)")
323
+ }
324
+ return true // Don't fail if keywords don't exist yet
325
+ }
326
+ }
327
+ )
328
+
329
+ console.log("\n" + "=".repeat(60))
330
+ console.log("NO_ACCESS Permission Checks Tests Complete")
331
+ console.log("=".repeat(60))
332
+
333
+ } finally {
334
+ // Restore original role
335
+ await sdk.api.users.updateOne(sdkNonAdmin.userInfo.id, { roles: originalRoles }, { replaceObjectFields: true })
336
+ await wait(undefined, 1000)
337
+ await sdkNonAdmin.authenticate(nonAdminEmail!, nonAdminPassword!)
338
+
339
+ // Cleanup
340
+ await sdk.api.role_based_access_permissions.deleteOne(rbap.id)
341
+ await sdk.api.endusers.deleteOne(testEnduser.id)
342
+ }
343
+ }
344
+
345
+ // Allow running this test file independently
346
+ if (require.main === module) {
347
+ console.log(`🌐 Using API URL: ${host}`)
348
+ const sdk = new Session({ host })
349
+ const sdkNonAdmin = new Session({ host })
350
+
351
+ const runTests = async () => {
352
+ await setup_tests(sdk, sdkNonAdmin)
353
+ await no_access_permission_checks_tests({ sdk, sdkNonAdmin })
354
+ }
355
+
356
+ runTests()
357
+ .then(() => {
358
+ console.log("✅ NO_ACCESS permission checks test suite completed successfully")
359
+ process.exit(0)
360
+ })
361
+ .catch((error) => {
362
+ console.error("❌ NO_ACCESS permission checks test suite failed:", error)
363
+ process.exit(1)
364
+ })
365
+ }
@@ -40,6 +40,7 @@ import { create_user_notifications_trigger_tests } from "./api_tests/create_user
40
40
  import { inbox_thread_assignment_updates_tests } from "./api_tests/inbox_thread_assignment_updates.test"
41
41
  import { inbox_thread_draft_scheduled_tests } from "./api_tests/inbox_thread_draft_scheduled.test"
42
42
  import { load_threads_autobuild_tests } from "./api_tests/load_threads_autobuild.test"
43
+ import { concurrent_build_threads_tests } from "./api_tests/concurrent_build_threads.test"
43
44
  import { appointment_completed_trigger_tests } from "./api_tests/appointment_completed_trigger.test"
44
45
  import { purchase_made_trigger_tests } from "./api_tests/purchase_made_trigger.test"
45
46
  import { appointment_rescheduled_trigger_tests } from "./api_tests/appointment_rescheduled_trigger.test"
@@ -70,11 +71,13 @@ import {
70
71
 
71
72
  import fs from "fs"
72
73
  import { load_inbox_data_tests } from "./api_tests/load_inbox_data.test";
74
+ import { custom_dashboards_tests } from "./api_tests/custom_dashboards.test";
73
75
  import { message_assignment_trigger_tests } from "./api_tests/message_assignment_trigger.test";
74
76
  import { time_tracks_tests } from "./api_tests/time_tracks.test";
75
77
  import { monthly_availability_restrictions_tests } from "./api_tests/monthly_availability_restrictions.test";
76
78
  import { calendar_event_limits_tests } from "./api_tests/calendar_event_limits.test";
77
79
  import { custom_aggregation_tests } from "./api_tests/custom_aggregation.test";
80
+ import { no_access_permission_checks_tests } from "./api_tests/no_access_permission_checks.test";
78
81
  import { bulk_assignment_tests } from "./api_tests/bulk_assignment.test";
79
82
  import { managed_content_enduser_access_tests } from "./api_tests/managed_content_enduser_access.test";
80
83
  import { auto_merge_form_submission_tests } from "./api_tests/auto_merge_form_submission.test";
@@ -9183,6 +9186,7 @@ const tests: { [K in keyof ClientModelForName]: () => void } = {
9183
9186
  superbill_providers: NO_TEST,
9184
9187
  superbills: NO_TEST,
9185
9188
  enduser_profile_views: NO_TEST,
9189
+ custom_dashboards: NO_TEST, // Called in main runner with SDK dependency
9186
9190
  enduser_custom_types: NO_TEST,
9187
9191
  table_views: NO_TEST,
9188
9192
  email_sync_denials: NO_TEST,
@@ -13924,6 +13928,10 @@ const ip_address_form_tests = async () => {
13924
13928
  await replace_enduser_template_values_tests()
13925
13929
  await mfa_tests()
13926
13930
  await setup_tests(sdk, sdkNonAdmin)
13931
+ await custom_dashboards_tests({ sdk, sdkNonAdmin })
13932
+ await concurrent_build_threads_tests({ sdk, sdkNonAdmin })
13933
+ await custom_aggregation_tests({ sdk, sdkNonAdmin })
13934
+ await no_access_permission_checks_tests({ sdk, sdkNonAdmin })
13927
13935
  await enduser_tests()
13928
13936
  await form_started_trigger_tests({ sdk, sdkNonAdmin })
13929
13937
  await load_team_chat_tests({ sdk, sdkNonAdmin })
@@ -13938,7 +13946,6 @@ const ip_address_form_tests = async () => {
13938
13946
  await managed_content_enduser_access_tests({ sdk, sdkNonAdmin })
13939
13947
  await afteraction_day_of_month_delay_tests({ sdk, sdkNonAdmin })
13940
13948
  await bulk_assignment_tests({ sdk, sdkNonAdmin })
13941
- await custom_aggregation_tests({ sdk, sdkNonAdmin })
13942
13949
  await formsort_tests()
13943
13950
  await self_serve_appointment_booking_tests()
13944
13951
  await time_tracks_tests({ sdk, sdkNonAdmin })
Binary file