@tellescope/sdk 1.75.0 → 1.77.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.
@@ -140,6 +140,44 @@ const setup_tests = async () => {
140
140
  { expectedResult: 'Authenticated!' }
141
141
  )
142
142
 
143
+ // login rate limit tests
144
+ const badSDK = new Session({ host });
145
+ await badSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
146
+ await badSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
147
+ await badSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
148
+ await badSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
149
+ await badSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
150
+ await async_test(
151
+ 'login rate limited',
152
+ () => badSDK.authenticate('bademail@tellescope.com', 'badpassword@tellescope.com'),
153
+ { shouldError: true, onError: e => e.message === 'Too many login attempts' }
154
+ )
155
+ await async_test(
156
+ 'login not rate limited for other user',
157
+ () => sdk.authenticate(email, password),
158
+ passOnAnyResult
159
+ )
160
+
161
+ const badEnduserSDK = new EnduserSession({ host, businessId });
162
+ await badEnduserSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
163
+ await badEnduserSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
164
+ await badEnduserSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
165
+ await badEnduserSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
166
+ await badEnduserSDK.authenticate('bademail@tellescope.com', 'badpassword').catch(console.error)
167
+ await async_test(
168
+ 'login rate limited',
169
+ () => badEnduserSDK.authenticate('bademail@tellescope.com', 'badpassword@tellescope.com'),
170
+ { shouldError: true, onError: e => e.message === 'Too many login attempts' }
171
+ )
172
+ await async_test(
173
+ 'login not rate limited for other enduser',
174
+ () => badEnduserSDK.authenticate('otherbademail@tellescope.com', 'badpassword@tellescope.com'),
175
+ { shouldError: true, onError: e => e.message !== 'Too many login attempts' }
176
+ )
177
+
178
+ // prevent additional login throttling
179
+ await async_test('reset_db', () => sdk.reset_db(), passOnVoid)
180
+
143
181
  await sdk.logout()
144
182
  await async_test<string, string>('test_authenticated - (logout invalidates jwt)', sdk.test_authenticated, { shouldError: true, onError: e => e === 'Unauthenticated' })
145
183
  await sdk.authenticate(email, password)
@@ -319,8 +357,11 @@ const sub_organization_enduser_tests = async() => {
319
357
  log_header("Sub Organizations (Enduser-Facing Tests)")
320
358
 
321
359
  await enduserSDK.register({ email: 'root@tellescope.com', password })
360
+ await wait(undefined, 1000) // avoid rate limiting error
322
361
  await subEnduserSDK.register({ email: 'sub@tellescope.com', password })
362
+ await wait(undefined, 1000) // avoid rate limiting error
323
363
  await enduserSDK.authenticate('root@tellescope.com', password)
364
+ await wait(undefined, 1000) // avoid rate limiting error
324
365
  await subEnduserSDK.authenticate('sub@tellescope.com', password)
325
366
 
326
367
  assert(!enduserSDK.userInfo.organizationIds?.length, 'bad root organizationIds', 'root auth org ids')
@@ -4085,8 +4126,10 @@ export const self_serve_appointment_booking_tests = async () => {
4085
4126
 
4086
4127
  const e1 = await sdk.api.endusers.createOne({ email: 'sebass+ny@tellescope.com', state: 'NY' })
4087
4128
  const e2 = await sdk.api.endusers.createOne({ email: 'sebass+ca@tellescope.com', state: 'CA' })
4129
+ const e3 = await sdk.api.endusers.createOne({ email: 'sebass+3@tellescope.com' })
4088
4130
  await sdk.api.endusers.set_password({ id: e1.id, password })
4089
4131
  await sdk.api.endusers.set_password({ id: e2.id, password })
4132
+ await sdk.api.endusers.set_password({ id: e3.id, password })
4090
4133
 
4091
4134
  const event15min = await sdk.api.calendar_event_templates.createOne({
4092
4135
  title: 'test 2', durationInMinutes: 15,
@@ -4098,6 +4141,12 @@ export const self_serve_appointment_booking_tests = async () => {
4098
4141
  confirmationEmailDisabled: true,
4099
4142
  confirmationSMSDisabled: true,
4100
4143
  })
4144
+ const event30minGroup = await sdk.api.calendar_event_templates.createOne({
4145
+ title: 'test group', durationInMinutes: 30,
4146
+ confirmationEmailDisabled: true,
4147
+ confirmationSMSDisabled: true,
4148
+ enduserAttendeeLimit: 2,
4149
+ })
4101
4150
 
4102
4151
  // ensure it doesn't match current day, to avoid errors on testing
4103
4152
  const dayOfWeekStartingSundayIndexedByZero = (new Date().getDay() + 1) % 7
@@ -4135,6 +4184,12 @@ export const self_serve_appointment_booking_tests = async () => {
4135
4184
  replaceObjectFields: true,
4136
4185
  })
4137
4186
 
4187
+ const enduserSDK2 = new EnduserSession({ host, businessId })
4188
+ await enduserSDK2.authenticate('sebass+ca@tellescope.com', password).catch(console.error)
4189
+
4190
+ const enduserSDK3 = new EnduserSession({ host, businessId })
4191
+ await enduserSDK3.authenticate('sebass+3@tellescope.com', password).catch(console.error)
4192
+
4138
4193
  // NY Enduser Tests
4139
4194
  await enduserSDK.authenticate('sebass+ny@tellescope.com', password).catch(console.error)
4140
4195
  await async_test(
@@ -4258,6 +4313,78 @@ export const self_serve_appointment_booking_tests = async () => {
4258
4313
  handleAnyError
4259
4314
  )
4260
4315
 
4316
+ // test group bookings
4317
+ await sdk.api.calendar_events.updateOne(conflict.id, { enduserAttendeeLimit: 2 })
4318
+ await async_test(
4319
+ '[group booking] different event type conflict as group still blocks availability',
4320
+ () => enduserSDK.api.calendar_events.get_appointment_availability({
4321
+ calendarEventTemplateId: event30minGroup.id,
4322
+ from: new Date(Date.now() - 10000),
4323
+ to: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
4324
+ }),
4325
+ { onResult: r => r.availabilityBlocks.length === 2 },
4326
+ )
4327
+ await sdk.api.calendar_events.deleteOne(conflict.id)
4328
+ await async_test(
4329
+ '[group booking] availability',
4330
+ () => enduserSDK.api.calendar_events.get_appointment_availability({
4331
+ calendarEventTemplateId: event30minGroup.id,
4332
+ from: new Date(Date.now() - 10000),
4333
+ to: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
4334
+ }),
4335
+ { onResult: r => r.availabilityBlocks.length === 3 },
4336
+ )
4337
+ const groupEvent = (await enduserSDK.api.calendar_events.book_appointment({
4338
+ calendarEventTemplateId: event30minGroup.id,
4339
+ startTime: new Date(nySlots.availabilityBlocks[1].startTimeInMS),
4340
+ userId: nySlots.availabilityBlocks[1].userId,
4341
+ })).createdEvent
4342
+ await async_test(
4343
+ '[group booking] more booking allowed',
4344
+ () => enduserSDK.api.calendar_events.get_appointment_availability({
4345
+ calendarEventTemplateId: event30minGroup.id,
4346
+ from: new Date(Date.now() - 10000),
4347
+ to: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
4348
+ }),
4349
+ { onResult: r => r.availabilityBlocks.length === 3 },
4350
+ )
4351
+ await async_test(
4352
+ '[group booking] prevent double-book same-enduser',
4353
+ () => enduserSDK.api.calendar_events.book_appointment({
4354
+ calendarEventTemplateId: event30minGroup.id,
4355
+ startTime: new Date(nySlots.availabilityBlocks[1].startTimeInMS),
4356
+ userId: nySlots.availabilityBlocks[1].userId,
4357
+ }),
4358
+ handleAnyError
4359
+ )
4360
+ await async_test(
4361
+ '[group booking] allow other enduser to book',
4362
+ () => enduserSDK2.api.calendar_events.book_appointment({
4363
+ calendarEventTemplateId: event30minGroup.id,
4364
+ startTime: new Date(nySlots.availabilityBlocks[1].startTimeInMS),
4365
+ userId: nySlots.availabilityBlocks[1].userId,
4366
+ }),
4367
+ passOnAnyResult
4368
+ )
4369
+ await async_test(
4370
+ '[group booking] no more booking allowed',
4371
+ () => enduserSDK.api.calendar_events.get_appointment_availability({
4372
+ calendarEventTemplateId: event30minGroup.id,
4373
+ from: new Date(Date.now() - 10000),
4374
+ to: new Date(Date.now() + 3 * 24 * 60 * 60 * 1000),
4375
+ }),
4376
+ { onResult: r => r.availabilityBlocks.length === 2 },
4377
+ )
4378
+ await async_test(
4379
+ '[group booking] other enduser cant book over capacity',
4380
+ () => enduserSDK3.api.calendar_events.book_appointment({
4381
+ calendarEventTemplateId: event30minGroup.id,
4382
+ startTime: new Date(nySlots.availabilityBlocks[1].startTimeInMS),
4383
+ userId: nySlots.availabilityBlocks[1].userId,
4384
+ }),
4385
+ handleAnyError
4386
+ )
4387
+
4261
4388
  // test 'multi' flag for booking multiple providers for a given patient
4262
4389
  await sdk.api.users.updateOne(sdk.userInfo.id, {
4263
4390
  weeklyAvailabilities: [
@@ -4341,11 +4468,13 @@ export const self_serve_appointment_booking_tests = async () => {
4341
4468
  await Promise.all([
4342
4469
  sdk.api.endusers.deleteOne(e1.id),
4343
4470
  sdk.api.endusers.deleteOne(e2.id),
4471
+ sdk.api.endusers.deleteOne(e3.id),
4344
4472
  sdk.api.calendar_event_templates.deleteOne(event30min.id),
4473
+ sdk.api.calendar_event_templates.deleteOne(event30minGroup.id),
4345
4474
  sdk.api.calendar_event_templates.deleteOne(event15min.id),
4346
4475
  sdk.api.calendar_events.deleteOne(bookedAppointment.id),
4347
- sdk.api.calendar_events.deleteOne(conflict.id),
4348
4476
  sdk.api.calendar_events.deleteOne(bookedMultiAppointment.id),
4477
+ sdk.api.calendar_events.deleteOne(groupEvent.id),
4349
4478
  ])
4350
4479
  }
4351
4480
 
@@ -5954,6 +6083,8 @@ const validate_schema = () => {
5954
6083
  endpoints.add(path)
5955
6084
  }
5956
6085
  }
6086
+
6087
+ console.log("Schema validated")
5957
6088
  }
5958
6089
 
5959
6090
  const test_weighted_round_robin = async () => {
@@ -6519,8 +6650,8 @@ export const ticket_reminder_tests = async () => {
6519
6650
  && t.nextReminderInMS !== -1
6520
6651
  && t.reminders?.filter(r => r.didRemind)?.length === 1
6521
6652
  ),
6522
- 25,
6523
- 200,
6653
+ 10,
6654
+ 500,
6524
6655
  ),
6525
6656
  passOnAnyResult
6526
6657
  )
@@ -6620,6 +6751,7 @@ const test_send_with_template = async () => {
6620
6751
  }
6621
6752
 
6622
6753
  const delete_user_tests = async () => {
6754
+ log_header("Delete user tests")
6623
6755
  // delete if previous test failed and user still exists
6624
6756
  const existing = await sdk.api.users.getSome({ filter: { email: 'deleteme@tellescope.com'}})
6625
6757
  if (existing[0]?.email === 'deleteme@tellescope.com') {
@@ -6644,6 +6776,369 @@ const delete_user_tests = async () => {
6644
6776
  createdUserSDK.test_authenticated,
6645
6777
  handleAnyError
6646
6778
  )
6779
+
6780
+ const enduser = await sdk.api.endusers.createOne({ })
6781
+ const { authToken: enduserAuthToken } = await sdk.api.endusers.generate_auth_token({ id: enduser.id })
6782
+ const enduserSDK = new EnduserSession({ host, businessId, authToken: enduserAuthToken })
6783
+
6784
+ await async_test(
6785
+ "Enduser Authenticated",
6786
+ () => enduserSDK.api.endusers.getSome(),
6787
+ passOnAnyResult
6788
+ )
6789
+
6790
+ await sdk.api.endusers.deleteOne(enduser.id)
6791
+ await wait(undefined, 250)
6792
+ await async_test(
6793
+ "Enduser De-authenticated after deletion",
6794
+ () => enduserSDK.api.endusers.getSome(),
6795
+ handleAnyError
6796
+ )
6797
+ }
6798
+
6799
+ const sdkMfaApiKeyUserId = '6525a43e1e75f0350d62afc4'
6800
+ const lockout_tests = async () => {
6801
+ log_header("Lockout tests")
6802
+
6803
+ await async_test(
6804
+ "API Key is authenticated",
6805
+ sdkMfaApiKey.test_authenticated,
6806
+ passOnAnyResult,
6807
+ )
6808
+ await async_test(
6809
+ "API Key lock to future date",
6810
+ () => sdk.api.users.updateOne(sdkMfaApiKeyUserId, { lockedOutUntil: 0 }),
6811
+ passOnAnyResult
6812
+ )
6813
+ await wait(undefined, 250)
6814
+ await async_test(
6815
+ "API Key is de-authenticated when locked",
6816
+ sdkMfaApiKey.test_authenticated,
6817
+ handleAnyError,
6818
+ )
6819
+ await async_test(
6820
+ "API Key unlock to -1",
6821
+ () => sdk.api.users.updateOne(sdkMfaApiKeyUserId, { lockedOutUntil: -1 }),
6822
+ passOnAnyResult
6823
+ )
6824
+ await async_test(
6825
+ "API Key is authenticated",
6826
+ sdkMfaApiKey.test_authenticated,
6827
+ passOnAnyResult,
6828
+ )
6829
+
6830
+ const nonAdminId = sdkNonAdmin.userInfo.id
6831
+ await async_test(
6832
+ "users cannot update own lock status",
6833
+ () => sdk.api.users.updateOne(sdk.userInfo.id, { lockedOutUntil: -1 }),
6834
+ handleAnyError,
6835
+ )
6836
+ await async_test(
6837
+ "non-admin can't lock out others",
6838
+ () => sdkNonAdmin.api.users.updateOne(sdk.userInfo.id, { lockedOutUntil: Date.now() }),
6839
+ handleAnyError
6840
+ )
6841
+ await async_test(
6842
+ "non-admin is authenticated",
6843
+ sdkNonAdmin.test_authenticated,
6844
+ passOnAnyResult,
6845
+ )
6846
+ await async_test(
6847
+ "admin unlock to -1",
6848
+ () => sdk.api.users.updateOne(nonAdminId, { lockedOutUntil: -1 }),
6849
+ passOnAnyResult,
6850
+ )
6851
+ await async_test(
6852
+ "non-admin is authenticated (-1)",
6853
+ sdkNonAdmin.test_authenticated,
6854
+ passOnAnyResult,
6855
+ )
6856
+ await async_test(
6857
+ "admin lock to past date",
6858
+ () => sdk.api.users.updateOne(nonAdminId, { lockedOutUntil: Date.now() - 1000 }),
6859
+ passOnAnyResult,
6860
+ )
6861
+ await async_test(
6862
+ "non-admin is authenticated (past date)",
6863
+ sdkNonAdmin.test_authenticated,
6864
+ passOnAnyResult,
6865
+ )
6866
+
6867
+ await async_test(
6868
+ "admin lock to 0 (indefinite)",
6869
+ () => sdk.api.users.updateOne(nonAdminId, { lockedOutUntil: 0 }),
6870
+ passOnAnyResult
6871
+ )
6872
+ await wait(undefined, 250)
6873
+ await async_test(
6874
+ "non-admin is de-authenticated when locked to 0",
6875
+ sdkNonAdmin.test_authenticated,
6876
+ handleAnyError,
6877
+ )
6878
+ await async_test(
6879
+ "non-admin can't authenciate when locked to 0",
6880
+ () => sdkNonAdmin.authenticate(nonAdminEmail, nonAdminPassword),
6881
+ handleAnyError,
6882
+ )
6883
+ await async_test(
6884
+ "admin unlock to -1",
6885
+ () => sdk.api.users.updateOne(nonAdminId, { lockedOutUntil: -1 }),
6886
+ passOnAnyResult
6887
+ )
6888
+ await async_test(
6889
+ "non-admin can re authenciate when locked to 0",
6890
+ () => sdkNonAdmin.authenticate(nonAdminEmail, nonAdminPassword),
6891
+ passOnAnyResult,
6892
+ )
6893
+ await async_test(
6894
+ "non-admin is authenticated",
6895
+ sdkNonAdmin.test_authenticated,
6896
+ passOnAnyResult,
6897
+ )
6898
+
6899
+ await async_test(
6900
+ "admin lock to future date",
6901
+ () => sdk.api.users.updateOne(nonAdminId, { lockedOutUntil: Date.now() + 10000 }),
6902
+ passOnAnyResult
6903
+ )
6904
+ await wait(undefined, 250)
6905
+ await async_test(
6906
+ "non-admin is de-authenticated when locked to future date",
6907
+ sdkNonAdmin.test_authenticated,
6908
+ handleAnyError,
6909
+ )
6910
+ await async_test(
6911
+ "non-admin can't authenciate when locked to future date",
6912
+ () => sdkNonAdmin.authenticate(nonAdminEmail, nonAdminPassword),
6913
+ handleAnyError,
6914
+ )
6915
+ await async_test(
6916
+ "admin unlock to -1",
6917
+ () => sdk.api.users.updateOne(nonAdminId, { lockedOutUntil: -1 }),
6918
+ passOnAnyResult
6919
+ )
6920
+ await async_test(
6921
+ "non-admin can re authenciate when locked to future date",
6922
+ () => sdkNonAdmin.authenticate(nonAdminEmail, nonAdminPassword),
6923
+ passOnAnyResult,
6924
+ )
6925
+ await async_test(
6926
+ "non-admin is authenticated",
6927
+ sdkNonAdmin.test_authenticated,
6928
+ passOnAnyResult,
6929
+ )
6930
+ }
6931
+
6932
+ const sync_tests = async () => {
6933
+ log_header("Data Sync")
6934
+
6935
+ const from = new Date()
6936
+
6937
+ await async_test(
6938
+ "No new records, admin",
6939
+ () => sdk.sync({ from }),
6940
+ { onResult: ({ results }) => results.length === 0 },
6941
+ )
6942
+ await async_test(
6943
+ "No new records, non-admin",
6944
+ () => sdkNonAdmin.sync({ from }),
6945
+ { onResult: ({ results }) => results.length === 0 },
6946
+ )
6947
+
6948
+ const e = await sdk.api.endusers.createOne({ })
6949
+ await wait(undefined, 100)
6950
+ await async_test(
6951
+ "Enduser create, admin",
6952
+ () => sdk.sync({ from }),
6953
+ { onResult: ({ results }) => (
6954
+ results.length === 1
6955
+ && results[0].modelName === 'endusers'
6956
+ && results[0].recordId === e.id
6957
+ && results[0].data.includes(e.id)
6958
+ && JSON.parse(results[0].data) // tests no error throwing
6959
+ )},
6960
+ )
6961
+ await async_test(
6962
+ "Enduser create, non-admin",
6963
+ () => sdkNonAdmin.sync({ from }),
6964
+ { onResult: ({ results }) => results.length === 0 },
6965
+ )
6966
+ await async_test(
6967
+ "Enduser create, sub organization",
6968
+ () => sdkSub.sync({ from }),
6969
+ { onResult: ({ results }) => results.length === 0 },
6970
+ )
6971
+
6972
+ await sdk.api.endusers.updateOne(e.id, { fname: "UPDATE_TEST"})
6973
+ await wait(undefined, 100)
6974
+ await async_test(
6975
+ "Enduser update, admin",
6976
+ () => sdk.sync({ from }),
6977
+ { onResult: ({ results }) => (
6978
+ results.length === 1
6979
+ && results[0].modelName === 'endusers'
6980
+ && results[0].recordId === e.id
6981
+ && results[0].data.includes("UPDATE_TEST")
6982
+ )},
6983
+ )
6984
+ await async_test(
6985
+ "Enduser update, non-admin",
6986
+ () => sdkNonAdmin.sync({ from }),
6987
+ { onResult: ({ results }) => results.length === 0 },
6988
+ )
6989
+ await async_test(
6990
+ "Enduser update, sub organization",
6991
+ () => sdkSub.sync({ from }),
6992
+ { onResult: ({ results }) => results.length === 0 },
6993
+ )
6994
+
6995
+ const t = await sdk.api.tickets.createOne({ title: 'access test' })
6996
+ await wait(undefined, 100)
6997
+ await async_test(
6998
+ "Non-admin can't access ticket",
6999
+ () => sdkNonAdmin.sync({ from }),
7000
+ { onResult: ({ results }) => results.length === 0 },
7001
+ )
7002
+
7003
+ // creates a user notification which increments count/index
7004
+ sdk.api.tickets.updateOne(t.id, { owner: sdkNonAdmin.userInfo.id })
7005
+ await wait(undefined, 100)
7006
+
7007
+ await async_test(
7008
+ "Non-admin can access tickets on assignment",
7009
+ () => sdkNonAdmin.sync({ from }),
7010
+ { onResult: ({ results }) => results.length === 2 },
7011
+ )
7012
+ sdk.api.tickets.updateOne(t.id, { owner: '' })
7013
+ await wait(undefined, 100)
7014
+ await async_test(
7015
+ "Non-admin can't access tickets on unassignment",
7016
+ () => sdkNonAdmin.sync({ from }),
7017
+ { onResult: ({ results }) => results.length === 1 }, // still includes user notification
7018
+ )
7019
+
7020
+ await sdk.api.endusers.updateOne(e.id, { assignedTo: [sdkNonAdmin.userInfo.id] }, { replaceObjectFields: true })
7021
+ await wait(undefined, 100)
7022
+ await async_test(
7023
+ "Enduser update non-admin assignment, can access",
7024
+ () => sdkNonAdmin.sync({ from }),
7025
+ { onResult: ({ results }) => results.length === 2 }, // enduser and ticket user notification
7026
+ )
7027
+
7028
+ sdk.api.tickets.updateOne(t.id, { owner: '', enduserId: e.id })
7029
+ await wait(undefined, 100)
7030
+ await async_test(
7031
+ "Non-admin can access ticket (and enduser) after enduser assignment",
7032
+ () => sdkNonAdmin.sync({ from }),
7033
+ { onResult: ({ results }) => results.length === 3 },
7034
+ )
7035
+
7036
+ await sdk.api.endusers.updateOne(e.id, { assignedTo: [] }, { replaceObjectFields: true })
7037
+ await wait(undefined, 100)
7038
+ await async_test(
7039
+ "Enduser update non-admin assignment, revoked access to enduser and ticket",
7040
+ () => sdkNonAdmin.sync({ from }),
7041
+ { onResult: ({ results }) => results.length === 1 }, // still has user notification
7042
+ )
7043
+
7044
+ // enduser, ticket, and ticket assignment user_notification created
7045
+ await sdk.api.endusers.deleteOne(e.id)
7046
+ await wait(undefined, 100)
7047
+ await async_test(
7048
+ "Enduser delete, admin",
7049
+ () => sdk.sync({ from }),
7050
+ { onResult: ({ results }) => (
7051
+ results.length === 3
7052
+ && results[0].modelName === 'endusers'
7053
+ && results[0].recordId === e.id
7054
+ && results[0].data === 'deleted'
7055
+ )},
7056
+ )
7057
+ await async_test(
7058
+ "Enduser delete, non-admin",
7059
+ () => sdkNonAdmin.sync({ from }),
7060
+ { onResult: ({ results }) => results.length === 1 }, // still includes user notification
7061
+ )
7062
+ await async_test(
7063
+ "Enduser delete, sub organization",
7064
+ () => sdkSub.sync({ from }),
7065
+ { onResult: ({ results }) => results.length === 0 },
7066
+ )
7067
+
7068
+ // bulk create test coverage
7069
+ const [e2] = (await sdk.api.endusers.createSome([{ }])).created
7070
+ await wait(undefined, 100)
7071
+ await async_test(
7072
+ "Bulk Enduser create, admin",
7073
+ () => sdk.sync({ from }),
7074
+ { onResult: ({ results }) => (
7075
+ results.length === 4
7076
+ && results[0].modelName === 'endusers'
7077
+ && results[0].recordId === e2.id
7078
+ && results[0].data.includes(e2.id)
7079
+ && JSON.parse(results[0].data) // tests no error throwing
7080
+ )},
7081
+ )
7082
+ await async_test(
7083
+ "Bulk Enduser create, non-admin",
7084
+ () => sdkNonAdmin.sync({ from }),
7085
+ { onResult: ({ results }) => results.length === 1 }, // still includes user notification
7086
+ )
7087
+ await async_test(
7088
+ "Bulk Enduser create, sub organization",
7089
+ () => sdkSub.sync({ from }),
7090
+ { onResult: ({ results }) => results.length === 0 },
7091
+ )
7092
+
7093
+ await sdk.api.endusers.deleteOne(e2.id)
7094
+ await wait(undefined, 100)
7095
+ await async_test(
7096
+ "Bulk Enduser delete, admin",
7097
+ () => sdk.sync({ from }),
7098
+ { onResult: ({ results }) => (
7099
+ results.length === 4
7100
+ && results[0].modelName === 'endusers'
7101
+ && results[0].recordId === e2.id
7102
+ && results[0].data === 'deleted'
7103
+ )},
7104
+ )
7105
+ await async_test(
7106
+ "Bulk Enduser delete, non-admin",
7107
+ () => sdkNonAdmin.sync({ from }),
7108
+ { onResult: ({ results }) => results.length === 1 }, // still includes user notification
7109
+ )
7110
+ await async_test(
7111
+ "Bulk Enduser delete, sub organization",
7112
+ () => sdkSub.sync({ from }),
7113
+ { onResult: ({ results }) => results.length === 0 },
7114
+ )
7115
+ }
7116
+
7117
+ // to cover potential vulernabilities with enduser public register endpoint
7118
+ const register_as_enduser_tests = async () => {
7119
+ log_header("Register as Enduser")
7120
+
7121
+ await async_test(
7122
+ "Enduser register",
7123
+ () => enduserSDK.register({ email: 'test@tellescope.com', password: 'testpassWord12345!' }),
7124
+ passOnAnyResult
7125
+ )
7126
+ await async_test(
7127
+ "Enduser register (rate limited)",
7128
+ () => enduserSDK.register({ email: 'test@tellescope.com', password: 'testpassWord12345!' }),
7129
+ { shouldError: true, onError: e => e.message === "Too many requests" }
7130
+ )
7131
+ await wait(undefined, 1000)
7132
+ await async_test(
7133
+ "Enduser duplicate register (same response, no ability to enumerate contacts)",
7134
+ () => enduserSDK.register({ email: 'test@tellescope.com', password: 'testpassWord12345!' }),
7135
+ passOnAnyResult
7136
+ )
7137
+
7138
+ const enduser = await sdk.api.endusers.getOne({ email: 'test@tellescope.com'})
7139
+ if (enduser) {
7140
+ await sdk.api.endusers.deleteOne(enduser.id)
7141
+ }
6647
7142
  }
6648
7143
 
6649
7144
  (async () => {
@@ -6669,6 +7164,7 @@ const delete_user_tests = async () => {
6669
7164
  sdkSubSub.authenticate(subSubUserEmail, password),
6670
7165
  sdkNonAdmin.authenticate(nonAdminEmail, nonAdminPassword),
6671
7166
  ])
7167
+ console.log("Authentication done")
6672
7168
 
6673
7169
  // console.log(JSON.stringify(await sdk.bulk_load({ load: [{ model: 'users' }]}), null, 2))
6674
7170
 
@@ -6688,9 +7184,12 @@ const delete_user_tests = async () => {
6688
7184
  await mfa_tests()
6689
7185
  await setup_tests()
6690
7186
  await multi_tenant_tests() // should come right after setup tests
7187
+ await register_as_enduser_tests()
7188
+ await sync_tests()
7189
+ await lockout_tests()
7190
+ await self_serve_appointment_booking_tests()
6691
7191
  await delete_user_tests()
6692
7192
  // await test_send_with_template()
6693
- await self_serve_appointment_booking_tests()
6694
7193
  await bulk_read_tests()
6695
7194
  await ticket_reminder_tests()
6696
7195
  await enduser_access_tags_tests()
Binary file