@tellescope/sdk 0.0.94 → 0.0.97

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 (43) hide show
  1. package/lib/cjs/enduser.d.ts +24 -0
  2. package/lib/cjs/enduser.d.ts.map +1 -1
  3. package/lib/cjs/sdk.d.ts +24 -0
  4. package/lib/cjs/sdk.d.ts.map +1 -1
  5. package/lib/cjs/sdk.js +1 -0
  6. package/lib/cjs/sdk.js.map +1 -1
  7. package/lib/cjs/session.d.ts +3 -0
  8. package/lib/cjs/session.d.ts.map +1 -1
  9. package/lib/cjs/session.js +11 -7
  10. package/lib/cjs/session.js.map +1 -1
  11. package/lib/cjs/tests/socket_tests.d.ts.map +1 -1
  12. package/lib/cjs/tests/socket_tests.js +244 -128
  13. package/lib/cjs/tests/socket_tests.js.map +1 -1
  14. package/lib/cjs/tests/tests.d.ts.map +1 -1
  15. package/lib/cjs/tests/tests.js +89 -215
  16. package/lib/cjs/tests/tests.js.map +1 -1
  17. package/lib/cjs/tests/webhooks_tests.js +4 -4
  18. package/lib/cjs/tests/webhooks_tests.js.map +1 -1
  19. package/lib/esm/enduser.d.ts +24 -1
  20. package/lib/esm/enduser.d.ts.map +1 -1
  21. package/lib/esm/sdk.d.ts +24 -0
  22. package/lib/esm/sdk.d.ts.map +1 -1
  23. package/lib/esm/sdk.js +1 -0
  24. package/lib/esm/sdk.js.map +1 -1
  25. package/lib/esm/session.d.ts +3 -1
  26. package/lib/esm/session.d.ts.map +1 -1
  27. package/lib/esm/session.js +11 -7
  28. package/lib/esm/session.js.map +1 -1
  29. package/lib/esm/tests/socket_tests.d.ts.map +1 -1
  30. package/lib/esm/tests/socket_tests.js +244 -128
  31. package/lib/esm/tests/socket_tests.js.map +1 -1
  32. package/lib/esm/tests/tests.d.ts.map +1 -1
  33. package/lib/esm/tests/tests.js +89 -215
  34. package/lib/esm/tests/tests.js.map +1 -1
  35. package/lib/esm/tests/webhooks_tests.js +4 -4
  36. package/lib/esm/tests/webhooks_tests.js.map +1 -1
  37. package/lib/tsconfig.tsbuildinfo +1 -1
  38. package/package.json +10 -10
  39. package/src/sdk.ts +1 -0
  40. package/src/session.ts +11 -7
  41. package/src/tests/socket_tests.ts +184 -63
  42. package/src/tests/tests.ts +35 -149
  43. package/src/tests/webhooks_tests.ts +4 -5
@@ -23,17 +23,20 @@ export const get_sha256 = (s='') => createHash('sha256').update(s).digest('hex')
23
23
 
24
24
  const VERBOSE = true
25
25
 
26
- const AWAIT_SOCKET_DURATION = 150 // 25ms was generally passing for Redis, 1000ms should be upper limit of performance
26
+ const AWAIT_SOCKET_DURATION = 250 // 25ms was generally passing for Redis, 1000ms should be upper limit of performance
27
27
 
28
28
  const host = process.env.TEST_URL || 'http://localhost:8080'
29
29
  const [email, password] = [process.env.TEST_EMAIL, process.env.TEST_PASSWORD]
30
30
  const [email2, password2] = [process.env.TEST_EMAIL_2, process.env.TEST_PASSWORD_2]
31
+ const [nonAdminEmail, nonAdminPassword] = [process.env.NON_ADMIN_EMAIL, process.env.NON_ADMIN_PASSWORD]
31
32
  const businessId = '60398b1131a295e64f084ff6'
32
33
 
33
34
  const user1 = new Session({ host, enableSocketLogging: VERBOSE })
34
35
  const user2 = new Session({ host, enableSocketLogging: VERBOSE })
36
+ const sdkNonAdmin = new Session({ host })
37
+
35
38
  const enduserSDK = new EnduserSession({ host, businessId, enableSocketLogging: VERBOSE })
36
- if (!(email && password && email2 && password2)) {
39
+ if (!(email && password && email2 && password2 && nonAdminEmail && nonAdminPassword)) {
37
40
  console.error("Set TEST_EMAIL and TEST_PASSWORD")
38
41
  process.exit(1)
39
42
  }
@@ -42,7 +45,7 @@ if (!(email && password && email2 && password2)) {
42
45
  const basic_tests = async () => {
43
46
  const socket_events: Indexable[] = []
44
47
 
45
- user2.subscribe({ 'endusers': 'endusers' }, {
48
+ user2.handle_events({
46
49
  'created-endusers': es => socket_events.push(es),
47
50
  'updated-endusers': es => socket_events.push(es),
48
51
  'deleted-endusers': es => socket_events.push(es),
@@ -68,52 +71,37 @@ const basic_tests = async () => {
68
71
 
69
72
  const access_tests = async () => {
70
73
  const user1Events: Indexable[] = []
71
- const user2Events: Indexable[] = []
72
- // const user1Deletions: string[] = []
73
- // const user2Deletions: string[] = []
74
+ const nonAdminEvents: Indexable[] = []
75
+ const enduserEvents: Indexable[] = []
74
76
 
75
- user1.handle_events({
76
- 'created-chat_rooms': rs => user1Events.push(...rs),
77
- // 'updated-chat_rooms': rs => user1Events.push(...rs), sent message will create update event as cached messagepreview/sender updated
78
- // 'deleted-chat_rooms': rs => user1Events.push(...rs),
79
- })
80
- user2.handle_events({
81
- 'created-chat_rooms': rs => user2Events.push(...rs),
82
- // 'updated-chat_rooms': rs => user2Events.push(...rs),
83
- // 'deleted-chat_rooms': rs => user2Events.push(...rs),
84
- })
77
+ user1.handle_events({ 'created-chat_rooms': rs => user1Events.push(...rs) })
78
+ sdkNonAdmin.handle_events({ 'created-chat_rooms': rs => nonAdminEvents.push(...rs) })
85
79
 
86
80
  const user1Id = user1.userInfo.id
87
- const user2Id = user2.userInfo.id
81
+ const user2Id = sdkNonAdmin.userInfo.id
88
82
 
89
83
  const room = await user1.api.chat_rooms.createOne({ type: 'internal', userIds: [user1Id] })
90
84
  const sharedRoom = await user1.api.chat_rooms.createOne({ type: 'internal', userIds: [user1Id, user2Id] })
91
85
  await wait(undefined, AWAIT_SOCKET_DURATION)
92
86
 
93
- assert(user1Events.length === 0, 'bad event distribution for filter', 'verify filter socket no self')
94
- assert(user2Events.length === 1 && sharedRoom.id === user2Events[0].id, 'bad event distribution for filter', 'verify filter socket push')
95
-
96
- user1.removeAllSocketListeners('created-chat_rooms')
97
- user1.removeAllSocketListeners('update-chat_rooms')
98
- user1.removeAllSocketListeners('deleted-chat_rooms')
99
- user2.removeAllSocketListeners('created-chat_rooms')
100
- user2.removeAllSocketListeners('update-chat_rooms')
101
- user2.removeAllSocketListeners('deleted-chat_rooms')
102
-
103
- user1.subscribe({ [room.id]: 'chats' })
104
- user2.subscribe({ [room.id]: 'chats' }) // connection should be rejected
105
- user1.subscribe({ [sharedRoom.id]: 'chats' })
106
- user2.subscribe({ [sharedRoom.id]: 'chats' })
87
+ assert(user1Events.length === 2, 'bad event distribution for filter', 'creator gets socket notifications')
88
+ assert(nonAdminEvents.length === 1 && sharedRoom.id === nonAdminEvents[0].id, 'bad event distribution for filter', 'verify filter socket push')
107
89
 
108
90
  user1.handle_events({
91
+ 'created-chat_rooms': rs => user1Events.push(...rs),
109
92
  'created-chats': rs => user1Events.push(...rs),
93
+ 'created-tickets': rs => user1Events.push(...rs),
94
+ 'created-endusers': rs => user1Events.push(...rs),
110
95
  'updated-chats': rs => user1Events.push(...rs),
111
96
  'deleted-chats': rs => user1Events.push(...rs),
112
97
  })
113
- user2.handle_events({
114
- 'created-chats': rs => user2Events.push(...rs),
115
- 'updated-chats': rs => user2Events.push(...rs),
116
- 'deleted-chats': rs => user2Events.push(...rs),
98
+ sdkNonAdmin.handle_events({
99
+ 'created-chat_rooms': rs => nonAdminEvents.push(...rs),
100
+ 'created-chats': rs => nonAdminEvents.push(...rs),
101
+ 'created-tickets': rs => nonAdminEvents.push(...rs),
102
+ 'created-endusers': rs => nonAdminEvents.push(...rs),
103
+ 'updated-chats': rs => nonAdminEvents.push(...rs),
104
+ 'deleted-chats': rs => nonAdminEvents.push(...rs),
117
105
  })
118
106
  await wait(undefined, AWAIT_SOCKET_DURATION)
119
107
 
@@ -121,18 +109,160 @@ const access_tests = async () => {
121
109
  await user1.api.chats.createOne({ roomId: room.id, message: "Hello...", })
122
110
  await wait(undefined, AWAIT_SOCKET_DURATION)
123
111
 
124
- assert(user1Events.length === 0, 'bad chats subscription', 'verify chats no self')
125
- assert(user2Events.length === 1, 'bad chats subscription', 'push only for valid subscribers')
112
+ assert(user1Events.length === 4, 'bad chats self', 'chats to self')
113
+ assert(nonAdminEvents.length === 1, 'non-admin got chats', 'non admin doesnt get chats for unassigned room')
126
114
 
127
- const sharedChat = await user1.api.chats.createOne({ roomId: sharedRoom.id, message: "Hello!", })
128
- const sharedChat2 = await user2.api.chats.createOne({ roomId: sharedRoom.id, message: "Hello there!", })
115
+ const sharedChat = await user1.api.chats.createOne({ roomId: sharedRoom.id, message: "Hello from admin on shared", })
116
+ const sharedChat2 = await sdkNonAdmin.api.chats.createOne({ roomId: sharedRoom.id, message: "Hello from nonadmin on shared", })
129
117
  await wait(undefined, AWAIT_SOCKET_DURATION)
130
118
 
131
- assert(user1Events.length === 1 && user1Events[0].id === sharedChat2.id, 'bad chats subscription', 'verify chat received')
119
+ assert(user1Events.length === 6 && !!user1Events.find((r: any) => r?.id === sharedChat2.id), 'bad chats other', 'verify chat received')
120
+ assert(
121
+ nonAdminEvents.length === 3 && !!nonAdminEvents.find((e: any) => e.id === sharedChat.id),
122
+ 'bad chats self, non-admin', 'push valid for non-admin default access'
123
+ )
124
+
125
+ const unassignedEnduser = await user1.api.endusers.createOne({ email: 'unassigned@tellescope.com' })
126
+ const assignedEnduser = await user1.api.endusers.createOne({ email: 'assigned@tellescope.com', assignedTo: [user2Id] })
127
+ await wait (undefined, AWAIT_SOCKET_DURATION)
128
+
129
+ assert(
130
+ !!user1Events.find(e => e.id === unassignedEnduser.id) && !!user1Events.find(e => e.id === assignedEnduser.id),
131
+ 'admin did not get endusers',
132
+ 'admin got assigned and unassigned endusers',
133
+ )
134
+
135
+ assert(
136
+ !nonAdminEvents.find(e => e.id === unassignedEnduser.id) && !!nonAdminEvents.find(e => e.id === assignedEnduser.id),
137
+ 'non-admin got incorrect endusers',
138
+ 'non-admin got assigned enduser',
139
+ )
140
+
141
+ await user1.api.endusers.set_password({ id: unassignedEnduser.id, password: 'enduserPassword!' })
142
+ await enduserSDK.authenticate(unassignedEnduser.email as string, 'enduserPassword!')
143
+ await enduserSDK.authenticate_socket()
144
+
145
+ enduserSDK.handle_events({
146
+ 'created-chat_rooms': rs => enduserEvents.push(...rs),
147
+ 'created-chats': rs => enduserEvents.push(...rs),
148
+ 'created-tickets': rs => enduserEvents.push(...rs),
149
+ })
150
+
151
+ // test admin and enduser get messages
152
+ const roomUnassigned = await user1.api.chat_rooms.createOne({ enduserIds: [unassignedEnduser.id] })
153
+ // test non-admin user in userIds gets message
154
+ const roomUnassignedWithUser = await user1.api.chat_rooms.createOne({ enduserIds: [unassignedEnduser.id], userIds: [user2Id] })
155
+ // test non-admin user who is assigned to enduser gets messages
156
+ const roomAssigned = await user1.api.chat_rooms.createOne({ enduserIds: [assignedEnduser.id] })
157
+ await wait (undefined, AWAIT_SOCKET_DURATION)
158
+
159
+ assert((
160
+ !!user1Events.find(e => e.id === roomUnassigned.id)
161
+ && !!user1Events.find(e => e.id === roomUnassignedWithUser.id)
162
+ && !!user1Events.find(e => e.id === roomAssigned.id)
163
+ ),
164
+ 'enduser did not get chat rooms',
165
+ 'enduser got chat rooms',
166
+ )
167
+ assert((
168
+ !nonAdminEvents.find(e => e.id === roomUnassigned.id) // shouldn't get as non-admin
169
+ && !!nonAdminEvents.find(e => e.id === roomUnassignedWithUser.id) // gets for in room
170
+ && !!nonAdminEvents.find(e => e.id === roomAssigned.id) // gets for enduser assignment
171
+ ),
172
+ 'non-admin did not get chat rooms',
173
+ 'non-admin got chat rooms',
174
+ )
175
+ assert(
176
+ (
177
+ !!enduserEvents.find(e => e.id === roomUnassigned.id)
178
+ && !!enduserEvents.find(e => e.id === roomUnassignedWithUser.id)
179
+ && !enduserEvents.find(e => e.id === roomAssigned.id)
180
+ ),
181
+ 'enduser did not get chat rooms',
182
+ 'enduser got chat rooms',
183
+ )
184
+
185
+ let userMessage = await user1.api.chats.createOne({ message: 'user unassigned', roomId: roomUnassigned.id })
186
+ let enduserMessage = await enduserSDK.api.chats.createOne({ message: 'enduseruser unassigned', roomId: roomUnassigned.id })
187
+ await wait (undefined, AWAIT_SOCKET_DURATION)
188
+
189
+ assert(
190
+ !!enduserEvents.find(e => e.id === userMessage.id),
191
+ 'enduser did not get message',
192
+ 'enduser got message from user',
193
+ )
132
194
  assert(
133
- user2Events.length === 2 && user2Events[1].id === sharedChat.id,
134
- 'bad chats subscription', 'push only for valid subscribers shared'
195
+ !!user1Events.find(e => e.id === enduserMessage.id),
196
+ 'user did not get message',
197
+ 'user got message from enduser',
135
198
  )
199
+ assert(
200
+ !nonAdminEvents.find(e => e.id === enduserMessage.id) && !nonAdminEvents.find(e => e.id === userMessage.id),
201
+ 'non-admin got unexpected message',
202
+ 'non-admin correctly did not get message from enduser or user',
203
+ )
204
+
205
+
206
+ userMessage = await user1.api.chats.createOne({ message: 'user unassigned with user', roomId: roomUnassignedWithUser.id })
207
+ enduserMessage = await enduserSDK.api.chats.createOne({ message: 'enduser unassigned with user', roomId: roomUnassignedWithUser.id })
208
+ let nonAdminMessage = await sdkNonAdmin.api.chats.createOne({ message: 'non-admin unassigned with non', roomId: roomUnassignedWithUser.id })
209
+ await wait (undefined, AWAIT_SOCKET_DURATION)
210
+
211
+ assert(
212
+ !!enduserEvents.find(e => e.id === userMessage.id) && !!enduserEvents.find(e => e.id === nonAdminMessage.id),
213
+ 'enduser did not get message',
214
+ 'enduser got message from user',
215
+ )
216
+ assert(
217
+ !!user1Events.find(e => e.id === enduserMessage.id) && !!user1Events.find(e => e.id === nonAdminMessage.id),
218
+ 'user did not get messages',
219
+ 'user got messages',
220
+ )
221
+ assert(
222
+ !!nonAdminEvents.find(e => e.id === enduserMessage.id) && !!nonAdminEvents.find(e => e.id === userMessage.id),
223
+ 'non-admin didnt get messages',
224
+ 'non-admin got message from user and enduser',
225
+ )
226
+
227
+
228
+ await user1.api.endusers.set_password({ id: assignedEnduser.id, password: 'enduserPassword!' })
229
+ await enduserSDK.authenticate(assignedEnduser.email as string, 'enduserPassword!')
230
+ await enduserSDK.authenticate_socket()
231
+
232
+ enduserSDK.handle_events({
233
+ 'created-chats': rs => enduserEvents.push(...rs),
234
+ })
235
+
236
+
237
+ userMessage = await user1.api.chats.createOne({ message: 'user assigned', roomId: roomAssigned.id })
238
+ enduserMessage = await enduserSDK.api.chats.createOne({ message: 'enduser assigned', roomId: roomAssigned.id })
239
+ nonAdminMessage = await sdkNonAdmin.api.chats.createOne({ message: 'non-admin assigned', roomId: roomAssigned.id })
240
+ await wait (undefined, AWAIT_SOCKET_DURATION)
241
+
242
+ assert(
243
+ !!enduserEvents.find(e => e.id === userMessage.id) && !!enduserEvents.find(e => e.id === nonAdminMessage.id),
244
+ 'enduser did not get messages',
245
+ 'enduser got messages from users',
246
+ )
247
+ assert(
248
+ !!user1Events.find(e => e.id === enduserMessage.id) && !!user1Events.find(e => e.id === nonAdminMessage.id),
249
+ 'user did not get messages',
250
+ 'user got messages',
251
+ )
252
+ assert(
253
+ !!nonAdminEvents.find(e => e.id === enduserMessage.id) && !!nonAdminEvents.find(e => e.id === userMessage.id),
254
+ 'non-admin didnt get messages',
255
+ 'non-admin got message from user and enduser',
256
+ )
257
+
258
+
259
+ await Promise.all([
260
+ await user1.api.endusers.deleteOne(unassignedEnduser.id),
261
+ await user1.api.endusers.deleteOne(assignedEnduser.id),
262
+ await user1.api.chat_rooms.deleteOne(roomAssigned.id),
263
+ await user1.api.chat_rooms.deleteOne(roomUnassigned.id),
264
+ await user1.api.chat_rooms.deleteOne(roomUnassignedWithUser.id),
265
+ ])
136
266
  }
137
267
 
138
268
  const enduser_tests = async () => {
@@ -160,30 +290,23 @@ const enduser_tests = async () => {
160
290
  enduserIds: [enduser.id],
161
291
  })
162
292
 
163
- user1.subscribe({ [room.id]: 'chats' })
164
- user1.subscribe({ tickets: 'tickets' })
165
- enduserSDK.subscribe({
166
- [room.id]: 'chats',
167
- dontCache: 'chats', // prevents cacheing of otherwise identical-looking subscriptions for user1 and enduserSDK for chats
168
- })
169
-
170
293
  await wait(undefined, AWAIT_SOCKET_DURATION)
171
294
 
172
295
  const messageToEnduser = await user1.api.chats.createOne({ roomId: room.id, message: "Hello!" })
173
296
  const messageToUser = await enduserSDK.api.chats.createOne({ roomId: room.id, message: "Hello right back!" })
174
297
  await wait(undefined, AWAIT_SOCKET_DURATION)
175
298
 
176
- assert(objects_equivalent(userEvents[0], messageToUser), 'no message on socket', 'push message to user')
177
- assert(objects_equivalent(enduserEvents[0], messageToEnduser), 'no message on socket', 'push message to enduser')
299
+ assert(!!userEvents.find(e => e.id === messageToUser.id), 'no message on socket for user', 'push message to user')
300
+ assert(!!enduserEvents.find(e => e.id === messageToEnduser.id), 'no message on socket for enduser', 'push message to enduser')
178
301
 
179
302
  const unusedTicket = await user1.api.tickets.createOne({ enduserId: PLACEHOLDER_ID, title: "For Noone" }) // should not get pushed to enduser
180
303
  const ticketForEnduser = await user1.api.tickets.createOne({ enduserId: enduser.id, title: "For enduser" })
181
304
  const ticketFromEnduser = await enduserSDK.api.tickets.createOne({ enduserId: enduser.id, title: "By enduser" })
182
305
  await wait(undefined, AWAIT_SOCKET_DURATION)
183
306
 
184
- assert(objects_equivalent(userEvents[1], ticketFromEnduser), 'no ticket on socket for user', 'push ticket to user')
185
- assert(objects_equivalent(enduserEvents[1], ticketForEnduser), 'no ticket on socket for enduser', 'push ticket to enduser')
186
- assert(enduserEvents[2] === undefined, 'enduser got an orgwide ticket', 'enduser does not receive org-wide ticket')
307
+ assert(!!userEvents.find(t => t.id === ticketFromEnduser.id), 'no ticket on socket for user', 'push ticket to user')
308
+ assert(!!enduserEvents.find(t => t.id === ticketForEnduser.id), 'no ticket on socket for enduser', 'push ticket to enduser')
309
+ assert(!enduserEvents.find(t => t.id === unusedTicket.id), 'enduser got an orgwide ticket', 'enduser does not receive org-wide ticket')
187
310
 
188
311
  await user1.api.tickets.deleteOne(unusedTicket.id)
189
312
  await user1.api.tickets.deleteOne(ticketForEnduser.id)
@@ -222,8 +345,6 @@ const deauthentication_tests = async (byTimeout=false) => {
222
345
 
223
346
  const userEvents = [] as ChatMessage[]
224
347
  const enduserEvents = [] as ChatMessage[]
225
- user1.subscribe({ [room.id]: 'chats' })
226
- enduserSDK.subscribe({ [room.id]: 'chats' })
227
348
  user1.handle_events({ 'created-chats': rs => userEvents.push(...rs) })
228
349
  enduserSDK.handle_events({ 'created-chats': rs => enduserEvents.push(...rs) })
229
350
  await wait(undefined, AWAIT_SOCKET_DURATION)
@@ -233,9 +354,9 @@ const deauthentication_tests = async (byTimeout=false) => {
233
354
  } else {
234
355
  await wait(undefined, TEST_SESSION_DURATION * 1000 + SESSION_TIMEOUT_DELAY)
235
356
  }
236
- await user1.api.chats.createOne({ roomId: room.id, message: "Hello!" })
357
+ const badChatEnduser = await user1.api.chats.createOne({ roomId: room.id, message: "Hello!" })
237
358
  await wait(undefined, AWAIT_SOCKET_DURATION)
238
- assert(objects_equivalent(enduserEvents[0], undefined), 'enduser got message after logout on socket', 'enduser logged out')
359
+ assert(enduserEvents.find(c => c.id === badChatEnduser.id) === undefined, 'enduser got message after logout on socket', 'enduser logged out')
239
360
 
240
361
  // re-authenticate enduser to send message to user
241
362
  await enduserSDK.authenticate(enduser.email as string, 'enduserPassword!')
@@ -248,9 +369,9 @@ const deauthentication_tests = async (byTimeout=false) => {
248
369
  } else {
249
370
  await wait(undefined, AWAIT_SOCKET_DURATION)
250
371
  }
251
- await enduserSDK.api.chats.createOne({ roomId: room.id, message: "Hello right back!" })
372
+ const badChat = await enduserSDK.api.chats.createOne({ roomId: room.id, message: "Hello right back!" })
252
373
  await wait(undefined, AWAIT_SOCKET_DURATION)
253
- assert(objects_equivalent(userEvents[0], undefined), 'user got message after logout', 'user logged out')
374
+ assert(userEvents.find(e => e.id === badChat.id) === undefined, 'user got message after logout', 'user logged out')
254
375
 
255
376
 
256
377
  // must come before cleanup, so cleanup works
@@ -303,7 +424,7 @@ const calendar_events = async () => {
303
424
  })
304
425
  await wait(undefined, AWAIT_SOCKET_DURATION)
305
426
 
306
- assert(userEvents.length === 0, 'creator got calendar event', 'calendar event not gone to creator')
427
+ assert(userEvents.length === 1, 'creator push bad', 'calendar event gone to creator')
307
428
  assert(enduserEvents.length === 1 && enduserEvents[0].id === event.id, 'enduser did not get calendar event', 'calendar event on create for attending enduser')
308
429
 
309
430
  // cleanup
@@ -317,10 +438,11 @@ const calendar_events = async () => {
317
438
  await user1.authenticate(email, password)
318
439
  await user1.reset_db()
319
440
  await user2.authenticate(email2, password2) // generate authToken + socket connection for API keyj
441
+ await sdkNonAdmin.authenticate(nonAdminEmail, nonAdminPassword)
320
442
 
321
443
  await user1.connectSocket()
322
444
  await user2.connectSocket()
323
-
445
+ await sdkNonAdmin.connectSocket()
324
446
 
325
447
  await basic_tests()
326
448
  await access_tests()
@@ -330,7 +452,6 @@ const calendar_events = async () => {
330
452
 
331
453
  await deauthentication_tests() // should come last!
332
454
  await deauthentication_tests(true) // should come last!
333
-
334
455
  } catch(err) {
335
456
  console.error(err)
336
457
  }
@@ -923,7 +923,7 @@ const chat_room_tests = async () => {
923
923
  let roomWithMessage = await sdk.api.chat_rooms.getOne(room.id)
924
924
  assert(roomWithMessage.numMessages === 1, 'num mesages no update', 'num messages on send message')
925
925
  assert((roomWithMessage?.recentMessageSentAt ?? 0) > Date.now() - 1000, 'recent message timestamp bad', 'recent message timestamp')
926
- assert(roomWithMessage?.infoForUser?.[userId]?.unreadCount === 1, 'bad unread count for user', 'unread count for user')
926
+ assert(roomWithMessage?.infoForUser?.[userId]?.unreadCount === undefined, 'bad unread count for user', 'unread count for user')
927
927
  assert(roomWithMessage?.infoForUser?.[enduserSDK.userInfo.id]?.unreadCount === 1, 'bad unread count for enduser', 'unread count for enduser')
928
928
 
929
929
  roomWithMessage = await sdk.api.chat_rooms.updateOne(roomWithMessage.id, { infoForUser: { [userId]: { unreadCount: 0 }}})
@@ -1389,141 +1389,7 @@ const calendar_events_tests = async () => {
1389
1389
 
1390
1390
  const automation_events_tests = async () => {
1391
1391
  log_header("Automation Events")
1392
- const form = await sdk.api.forms.createOne({
1393
- title: 'Form', fields: [{ title: 'Question 1', type: 'string' }]
1394
- })
1395
-
1396
- const state1 = "State 1", state2 = "State 2";
1397
- const journey = await sdk.api.journeys.createOne({
1398
- title: "Automations Test",
1399
- defaultState: state1,
1400
- states: [
1401
- { name: state1, priority: 'N/A' },
1402
- { name: state2, priority: 'N/A' },
1403
- ]
1404
- })
1405
-
1406
- await async_test(
1407
- `enterState cannot match updateStateForJourney`,
1408
- () => sdk.api.automation_steps.createOne({
1409
- journeyId: journey.id,
1410
- event: {
1411
- type: "enterState",
1412
- info: { state: state1, journeyId: journey.id }
1413
- },
1414
- action: {
1415
- type: 'updateStateForJourney',
1416
- info: { state: state1, journeyId: journey.id },
1417
- },
1418
- }),
1419
- { shouldError: true, onError: e => e.message === 'updateStateForJourney cannot have the same journey and state as the enterState event' }
1420
- )
1421
- await async_test(
1422
- `leaveState cannot match updateStateForJourney`,
1423
- () => sdk.api.automation_steps.createOne({
1424
- journeyId: journey.id,
1425
- event: {
1426
- type: "leaveState",
1427
- info: { state: state1, journeyId: journey.id }
1428
- },
1429
- action: {
1430
- type: 'updateStateForJourney',
1431
- info: { state: state1, journeyId: journey.id },
1432
- },
1433
- }),
1434
- { shouldError: true, onError: e => e.message === 'updateStateForJourney cannot have the same journey and state as the leaveState event' }
1435
- )
1436
-
1437
- const testAction: AutomationAction = {
1438
- type: 'sendWebhook',
1439
- info: { message: 'test' }
1440
- }
1441
- await sdk.api.automation_steps.createOne({
1442
- journeyId: journey.id,
1443
- event: {
1444
- type: "enterState",
1445
- info: { state: state1, journeyId: journey.id }
1446
- },
1447
- action: testAction,
1448
- })
1449
- await sdk.api.automation_steps.createOne({
1450
- journeyId: journey.id,
1451
- event: {
1452
- type: "leaveState",
1453
- info: { state: state1, journeyId: journey.id }
1454
- },
1455
- action: testAction,
1456
- })
1457
- await sdk.api.automation_steps.createOne({
1458
- journeyId: journey.id,
1459
- event: {
1460
- type: "enterState",
1461
- info: { state: state2, journeyId: journey.id }
1462
- },
1463
- action: testAction,
1464
- })
1465
-
1466
- await sdk.api.automation_steps.createOne({
1467
- journeyId: journey.id,
1468
- event: {
1469
- type: "formResponse",
1470
- info: { formId: form.id },
1471
- },
1472
- conditions: [
1473
- {
1474
- type: 'atJourneyState',
1475
- info: { state: state2, journeyId: journey.id }
1476
- }
1477
- ],
1478
- action: testAction,
1479
- })
1480
-
1481
- await async_test(
1482
- `Cannot insert duplicate event/action pair`,
1483
- () => sdk.api.automation_steps.createOne({
1484
- journeyId: journey.id,
1485
- event: {
1486
- type: "enterState",
1487
- info: { state: state2, journeyId: journey.id }
1488
- },
1489
- action: testAction,
1490
- }),
1491
- { shouldError: true, onError: e => e.message === "You cannot create two identical event automations" }
1492
- )
1493
-
1494
- // trigger a1 on create
1495
- const enduser = await sdk.api.endusers.createOne({
1496
- email: "automations@tellescope.com",
1497
- journeys: { [journey.id]: journey.defaultState }
1498
- })
1499
-
1500
- // should NOT trigger while user not in state 2
1501
- await sdk.api.form_responses.submit_form_response({
1502
- accessCode: (await sdk.api.form_responses.prepare_form_response({ formId: form.id, enduserId: enduser.id })).accessCode,
1503
- responses: ['Answer']
1504
- })
1505
-
1506
- // trigger a2 and a3 by leaving state 1 an going to state 2
1507
- await sdk.api.endusers.updateOne(enduser.id, { journeys: { [journey.id]: state2 } })
1508
-
1509
- // SHOULD trigger now that user is in state 2
1510
- await sdk.api.form_responses.submit_form_response({
1511
- accessCode: (await sdk.api.form_responses.prepare_form_response({ formId: form.id, enduserId: enduser.id })).accessCode,
1512
- responses: ['Answer 2']
1513
- })
1514
-
1515
- await async_test(
1516
- `Automation events triggered correctly`,
1517
- () => sdk.api.automated_actions.getSome({ filter: { enduserId: enduser.id }}),
1518
- { onResult: es => es && es.length === 4 && es.filter(a => a.automationStepId === "ONE_TIME").length === 4 }
1519
- )
1520
-
1521
- // cleanup
1522
- await Promise.all([
1523
- sdk.api.journeys.deleteOne(journey.id), // automation events deleted as side effect
1524
- sdk.api.endusers.deleteOne(enduser.id),
1525
- sdk.api.forms.deleteOne(form.id),
1526
- ])
1392
+ console.warn("Need new test coverage for automation stemps")
1527
1393
  }
1528
1394
 
1529
1395
  const form_response_tests = async () => {
@@ -1543,19 +1409,19 @@ const form_response_tests = async () => {
1543
1409
  intakeField: stringIntakeField
1544
1410
  }]
1545
1411
  })
1546
- await sdk.api.automation_steps.createOne({
1547
- event: { type: "formResponse", info: { formId: form.id } },
1548
- action: { type: 'sendWebhook', info: { message: 'test' } },
1549
- })
1412
+ // await sdk.api.automation_steps.createOne({
1413
+ // event: { type: "formResponse", info: { formId: form.id } },
1414
+ // action: { type: 'sendWebhook', info: { message: 'test' } },
1415
+ // })
1550
1416
 
1551
1417
  const { accessCode } = await sdk.api.form_responses.prepare_form_response({ formId: form.id, enduserId: enduser.id })
1552
1418
  await sdk.api.form_responses.submit_form_response({ accessCode, responses: [stringResponse] })
1553
1419
 
1554
- const [triggeredAutomation] = await sdk.api.automated_actions.getSome()
1420
+ // const [triggeredAutomation] = await sdk.api.automated_actions.getSome()
1555
1421
  const enduserWithUpdate = await sdk.api.endusers.getOne(enduser.id)
1556
1422
  const recordedResponse = await sdk.api.form_responses.getOne({ accessCode })
1557
1423
 
1558
- assert(triggeredAutomation?.event?.type === 'formResponse', 'no form response event', 'form response event triggered')
1424
+ // assert(triggeredAutomation?.event?.type === 'formResponse', 'no form response event', 'form response event triggered')
1559
1425
  assert(enduserWithUpdate?.fields?.[stringIntakeField] === stringResponse, 'no enduser update', 'enduser updated')
1560
1426
  assert(
1561
1427
  recordedResponse?.responses?.length === 1 && recordedResponse.responses[0]?.[stringTitle] === stringResponse,
@@ -1622,19 +1488,19 @@ const notifications_tests = async () => {
1622
1488
  const chat = await sdk.api.chats.createOne({ message: 'test', roomId: room.id, })
1623
1489
  const ticket = await sdk.api.tickets.createOne({ title: 'Ticket for notification', owner: sdkNonAdmin.userInfo.id })
1624
1490
 
1625
- await wait(undefined, 50) // notifications may be created in background
1491
+ await wait(undefined, 250) // notifications may be created in background
1626
1492
 
1627
1493
  // neither should throw error
1628
- const ticketNotification = await sdk.api.user_notifications.getOne({ type: 'newTicket' })
1629
- const chatNotifications = await sdk.api.user_notifications.getSome({ filter: { type: 'newChatMessage' } })
1494
+ const ticketNotifications = await sdk.api.user_notifications.getSome({ filter: { type: 'newTicket' } })
1495
+ const chatNotifications = await sdk.api.user_notifications.getSome({ filter: { type: 'newTeamChatMessage' } })
1630
1496
 
1631
- assert(!!ticketNotification.relatedRecords?.find(r => r.id === ticket.id), 'No ticket notification', 'Got notification for new new ticket')
1497
+ assert(!!ticketNotifications.find(n => n.relatedRecords?.find(r => r.id === ticket.id)), 'No ticket notification', 'Got notification for new new ticket')
1632
1498
  assert(!!chatNotifications.find(notification => notification.relatedRecords?.find(r => r.id === chat.id)), 'No chat notification', 'Got notification for new chat')
1633
1499
 
1634
1500
  await Promise.all([
1635
1501
  sdk.api.chat_rooms.deleteOne(room.id),
1636
1502
  sdk.api.tickets.deleteOne(ticket.id),
1637
- sdk.api.user_notifications.deleteOne(ticketNotification.id),
1503
+ sdk.api.user_notifications.deleteOne(ticketNotifications.find(n => n.relatedRecords?.find(r => r.id === ticket.id))!.id),
1638
1504
  ...chatNotifications.map(n =>
1639
1505
  sdk.api.user_notifications.deleteOne(n.id),
1640
1506
  ),
@@ -1793,8 +1659,27 @@ const role_based_access_tests = async () => {
1793
1659
  sdk.api.sms_messages.deleteOne(sms.id),
1794
1660
  sdk.api.calendar_events.deleteOne(calendarEvent.id),
1795
1661
  sdk.api.chat_rooms.deleteOne(chatRoom.id),
1796
- sdk.api.chats.deleteOne(chatMessage.id),
1797
- sdk.api.chats.deleteOne(chatMessage2.id),
1662
+ ])
1663
+ }
1664
+
1665
+ const status_update_tests = async () => {
1666
+ log_header("Enduser Status Updates")
1667
+
1668
+ const journey = await sdk.api.journeys.createOne({ title: 'test' })
1669
+ const enduser = await sdk.api.endusers.createOne({ email: 'delete@tellescope.com' })
1670
+ const status = await sdk.api.enduser_status_updates.createOne({ enduserId: enduser.id, journeyId: journey.id, status: "Working"})
1671
+
1672
+ // status update on enduser is a side effect
1673
+ await wait(undefined, 100)
1674
+ await async_test(
1675
+ `status update`, () => sdk.api.endusers.getOne(enduser.id), {
1676
+ onResult: e => e.journeys?.[journey.id] === status.status
1677
+ },
1678
+ )
1679
+
1680
+ await Promise.all([
1681
+ sdk.api.journeys.deleteOne(journey.id), // status deleted as side effect
1682
+ sdk.api.endusers.deleteOne(enduser.id), // status deleted as side effect
1798
1683
  ])
1799
1684
  }
1800
1685
 
@@ -1822,6 +1707,7 @@ const tests: { [K in keyof ClientModelForName]: () => void } = {
1822
1707
  automation_steps: automation_events_tests,
1823
1708
  sequence_automations: NO_TEST,
1824
1709
  automated_actions: NO_TEST,
1710
+ enduser_status_updates: status_update_tests,
1825
1711
  user_logs: NO_TEST,
1826
1712
  user_notifications: notifications_tests,
1827
1713
  };
@@ -42,7 +42,7 @@ const app = express()
42
42
  app.use(bodyParser.urlencoded({ extended: true, limit: '25mb' }))
43
43
  app.use(bodyParser.json({ limit: "25mb" }))
44
44
 
45
- const PORT = 4000
45
+ const PORT = 3999
46
46
  const TEST_SECRET = "this is a test secret for verifying integrity of web hooks"
47
47
  const webhookEndpoint = '/handle-webhook'
48
48
  const webhookURL = `http://127.0.0.1:${PORT}${webhookEndpoint}`
@@ -98,7 +98,7 @@ const check_next_webhook = async (evaluate: (hook: WebhookCall) => boolean, name
98
98
  if (noHookExpected) {
99
99
  assert(!event, error, name)
100
100
  } else {
101
- assert(!!event, 'did not get hook', 'got hook')
101
+ assert(!!event, error || 'did not get hook', name || 'got hook')
102
102
  }
103
103
  if (!event) return // ensure webhookIndex not incremented
104
104
 
@@ -190,7 +190,7 @@ const meetings_tests = async (isSubscribed: boolean) => {
190
190
  await sdk.api.endusers.deleteOne(enduser.id)
191
191
  }
192
192
 
193
- const AUTOMATION_POLLING_DELAY_MS = 2000 - CHECK_WEBHOOK_DELAY_MS
193
+ const AUTOMATION_POLLING_DELAY_MS = 3000 - CHECK_WEBHOOK_DELAY_MS
194
194
  const test_automation_webhooks = async () => {
195
195
  log_header("Automation Events")
196
196
  const state1 = "State 1", state2 = "State 2";
@@ -221,12 +221,11 @@ const test_automation_webhooks = async () => {
221
221
 
222
222
  await check_next_webhook(
223
223
  ({ message }) => message === testMessage,
224
- 'Automation webhook error',
225
224
  'Automation webhook received',
225
+ 'Automation webhook error',
226
226
  true
227
227
  )
228
228
 
229
-
230
229
  // cleanup
231
230
  await sdk.api.journeys.deleteOne(journey.id) // automation events deleted as side effect
232
231
  await sdk.api.endusers.deleteOne(enduser.id)