@tellescope/sdk 1.237.0 → 1.237.2
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/sdk.d.ts +1 -0
- package/lib/cjs/sdk.d.ts.map +1 -1
- package/lib/cjs/sdk.js +1 -0
- package/lib/cjs/sdk.js.map +1 -1
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.d.ts.map +1 -1
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.js +227 -64
- package/lib/cjs/tests/api_tests/inbox_thread_assignment_updates.test.js.map +1 -1
- package/lib/cjs/tests/api_tests/inbox_thread_draft_scheduled.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/inbox_thread_draft_scheduled.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/inbox_thread_draft_scheduled.test.js +717 -0
- package/lib/cjs/tests/api_tests/inbox_thread_draft_scheduled.test.js.map +1 -0
- package/lib/cjs/tests/api_tests/inbox_thread_mdb_filter.test.d.ts +6 -0
- package/lib/cjs/tests/api_tests/inbox_thread_mdb_filter.test.d.ts.map +1 -0
- package/lib/cjs/tests/api_tests/inbox_thread_mdb_filter.test.js +372 -0
- package/lib/cjs/tests/api_tests/inbox_thread_mdb_filter.test.js.map +1 -0
- package/lib/cjs/tests/tests.d.ts.map +1 -1
- package/lib/cjs/tests/tests.js +575 -105
- package/lib/cjs/tests/tests.js.map +1 -1
- package/lib/esm/sdk.d.ts +1 -0
- package/lib/esm/sdk.d.ts.map +1 -1
- package/lib/esm/sdk.js +1 -0
- package/lib/esm/sdk.js.map +1 -1
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.d.ts.map +1 -1
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.js +227 -64
- package/lib/esm/tests/api_tests/inbox_thread_assignment_updates.test.js.map +1 -1
- package/lib/esm/tests/api_tests/inbox_thread_draft_scheduled.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/inbox_thread_draft_scheduled.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/inbox_thread_draft_scheduled.test.js +713 -0
- package/lib/esm/tests/api_tests/inbox_thread_draft_scheduled.test.js.map +1 -0
- package/lib/esm/tests/api_tests/inbox_thread_mdb_filter.test.d.ts +6 -0
- package/lib/esm/tests/api_tests/inbox_thread_mdb_filter.test.d.ts.map +1 -0
- package/lib/esm/tests/api_tests/inbox_thread_mdb_filter.test.js +368 -0
- package/lib/esm/tests/api_tests/inbox_thread_mdb_filter.test.js.map +1 -0
- package/lib/esm/tests/tests.d.ts.map +1 -1
- package/lib/esm/tests/tests.js +575 -105
- package/lib/esm/tests/tests.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/sdk.ts +4 -0
- package/src/tests/api_tests/inbox_thread_assignment_updates.test.ts +149 -0
- package/src/tests/api_tests/inbox_thread_draft_scheduled.test.ts +625 -0
- package/src/tests/tests.ts +401 -1
- package/test_generated.pdf +0 -0
|
@@ -0,0 +1,625 @@
|
|
|
1
|
+
require('source-map-support').install();
|
|
2
|
+
|
|
3
|
+
import { Session } from "../../sdk"
|
|
4
|
+
import {
|
|
5
|
+
assert,
|
|
6
|
+
log_header,
|
|
7
|
+
wait,
|
|
8
|
+
} from "@tellescope/testing"
|
|
9
|
+
import { setup_tests } from "../setup"
|
|
10
|
+
|
|
11
|
+
const host = process.env.API_URL || 'http://localhost:8080' as const
|
|
12
|
+
|
|
13
|
+
export const inbox_thread_draft_scheduled_tests = async ({ sdk, sdkNonAdmin }: { sdk: Session, sdkNonAdmin: Session }) => {
|
|
14
|
+
log_header("InboxThread Draft and Scheduled Message Tests")
|
|
15
|
+
|
|
16
|
+
const timestamp = Date.now()
|
|
17
|
+
const from = new Date(Date.now() - 1000 * 60 * 60) // 1 hour ago
|
|
18
|
+
|
|
19
|
+
// Test data setup
|
|
20
|
+
const testUser = await sdk.api.users.createOne({
|
|
21
|
+
fname: "Test",
|
|
22
|
+
lname: "User",
|
|
23
|
+
email: `test-draft-scheduled-${timestamp}@test.com`,
|
|
24
|
+
notificationEmailsDisabled: true
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
const testEnduser = await sdk.api.endusers.createOne({
|
|
28
|
+
fname: "Test",
|
|
29
|
+
lname: "Patient",
|
|
30
|
+
email: `test-patient-draft-${timestamp}@test.com`,
|
|
31
|
+
phone: "+15555555555", // Required for SMS tests
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
// Helper to reset and rebuild threads
|
|
35
|
+
const resetAndBuildThreads = async () => {
|
|
36
|
+
await sdk.api.inbox_threads.reset_threads()
|
|
37
|
+
await sdk.api.inbox_threads.build_threads({ from, to: new Date() })
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
// ===== SMS Draft/Scheduled Tests =====
|
|
42
|
+
log_header("SMS Draft and Scheduled Tests")
|
|
43
|
+
|
|
44
|
+
// Test 1: Draft SMS should be tracked in draftMessageIds but excluded from preview
|
|
45
|
+
console.log("Testing draft SMS tracking...")
|
|
46
|
+
|
|
47
|
+
const phoneNumber = "+15555555550"
|
|
48
|
+
const enduserPhoneNumber = "+15555555551"
|
|
49
|
+
|
|
50
|
+
// Create an inbound SMS to establish the thread with proper phone numbers
|
|
51
|
+
await sdk.api.sms_messages.createOne({
|
|
52
|
+
message: "Inbound message to establish thread",
|
|
53
|
+
enduserId: testEnduser.id,
|
|
54
|
+
userId: testUser.id,
|
|
55
|
+
inbound: true,
|
|
56
|
+
phoneNumber,
|
|
57
|
+
enduserPhoneNumber,
|
|
58
|
+
logOnly: true,
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
// Create a regular outbound SMS
|
|
62
|
+
const sentSMS = await sdk.api.sms_messages.createOne({
|
|
63
|
+
message: "This is a sent message",
|
|
64
|
+
enduserId: testEnduser.id,
|
|
65
|
+
userId: testUser.id,
|
|
66
|
+
inbound: false,
|
|
67
|
+
phoneNumber,
|
|
68
|
+
enduserPhoneNumber,
|
|
69
|
+
logOnly: true,
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Create a draft SMS
|
|
73
|
+
const draftSMS = await sdk.api.sms_messages.createOne({
|
|
74
|
+
message: "This is a draft message",
|
|
75
|
+
enduserId: testEnduser.id,
|
|
76
|
+
userId: testUser.id,
|
|
77
|
+
inbound: false,
|
|
78
|
+
phoneNumber,
|
|
79
|
+
enduserPhoneNumber,
|
|
80
|
+
isDraft: true,
|
|
81
|
+
logOnly: true,
|
|
82
|
+
})
|
|
83
|
+
|
|
84
|
+
// Build threads from messages
|
|
85
|
+
await resetAndBuildThreads()
|
|
86
|
+
|
|
87
|
+
// Load the thread and verify
|
|
88
|
+
const loadedThreads = await sdk.api.inbox_threads.load_threads({})
|
|
89
|
+
const smsThread = loadedThreads.threads.find(t =>
|
|
90
|
+
t.type === 'SMS' && t.phoneNumber === phoneNumber && t.enduserPhoneNumber === enduserPhoneNumber
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
assert(!!smsThread, "SMS thread should be found")
|
|
94
|
+
assert(
|
|
95
|
+
smsThread!.draftMessageIds?.includes(draftSMS.id) === true,
|
|
96
|
+
`Draft SMS ID should be in draftMessageIds. Got: ${JSON.stringify(smsThread!.draftMessageIds)}`
|
|
97
|
+
)
|
|
98
|
+
assert(
|
|
99
|
+
smsThread!.preview?.includes("This is a sent message") === true,
|
|
100
|
+
`Preview should be from sent message, not draft. Got: ${smsThread!.preview}`
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
console.log("✅ Draft SMS tracking test passed")
|
|
104
|
+
|
|
105
|
+
// Test 2: Scheduled SMS (future sendAt) should be tracked in scheduledMessageIds
|
|
106
|
+
console.log("Testing scheduled SMS tracking...")
|
|
107
|
+
|
|
108
|
+
const futureDate = new Date(Date.now() + 24 * 60 * 60 * 1000) // 24 hours from now
|
|
109
|
+
|
|
110
|
+
const scheduledSMS = await sdk.api.sms_messages.createOne({
|
|
111
|
+
message: "This is a scheduled message",
|
|
112
|
+
enduserId: testEnduser.id,
|
|
113
|
+
userId: testUser.id,
|
|
114
|
+
inbound: false,
|
|
115
|
+
phoneNumber,
|
|
116
|
+
enduserPhoneNumber,
|
|
117
|
+
sendAt: futureDate,
|
|
118
|
+
logOnly: true,
|
|
119
|
+
})
|
|
120
|
+
|
|
121
|
+
// Rebuild threads
|
|
122
|
+
await resetAndBuildThreads()
|
|
123
|
+
|
|
124
|
+
const loadedThreads2 = await sdk.api.inbox_threads.load_threads({})
|
|
125
|
+
const threadWithScheduled = loadedThreads2.threads.find(t =>
|
|
126
|
+
t.type === 'SMS' && t.phoneNumber === phoneNumber && t.enduserPhoneNumber === enduserPhoneNumber
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
assert(!!threadWithScheduled, "SMS thread should be found")
|
|
130
|
+
assert(
|
|
131
|
+
threadWithScheduled!.scheduledMessageIds?.includes(scheduledSMS.id) === true,
|
|
132
|
+
`Scheduled SMS ID should be in scheduledMessageIds. Got: ${JSON.stringify(threadWithScheduled!.scheduledMessageIds)}`
|
|
133
|
+
)
|
|
134
|
+
assert(
|
|
135
|
+
threadWithScheduled!.preview?.includes("This is a scheduled message") !== true,
|
|
136
|
+
`Preview should NOT be from scheduled message. Got: ${threadWithScheduled!.preview}`
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
console.log("✅ Scheduled SMS tracking test passed")
|
|
140
|
+
|
|
141
|
+
// ===== Email Draft/Scheduled Tests =====
|
|
142
|
+
log_header("Email Draft and Scheduled Tests")
|
|
143
|
+
|
|
144
|
+
// Test 3: Draft email should be tracked in draftMessageIds
|
|
145
|
+
console.log("Testing draft email tracking...")
|
|
146
|
+
|
|
147
|
+
const emailSubject = `Draft Test Email ${timestamp}`
|
|
148
|
+
|
|
149
|
+
// Create a regular email first
|
|
150
|
+
const sentEmail = await sdk.api.emails.createOne({
|
|
151
|
+
subject: emailSubject,
|
|
152
|
+
textContent: "This is a sent email",
|
|
153
|
+
enduserId: testEnduser.id,
|
|
154
|
+
userId: testUser.id,
|
|
155
|
+
messageId: `sent-email-${timestamp}`,
|
|
156
|
+
logOnly: true,
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
// Create a draft email
|
|
160
|
+
const draftEmail = await sdk.api.emails.createOne({
|
|
161
|
+
subject: emailSubject,
|
|
162
|
+
textContent: "This is a draft email",
|
|
163
|
+
enduserId: testEnduser.id,
|
|
164
|
+
userId: testUser.id,
|
|
165
|
+
messageId: `draft-email-${timestamp}`,
|
|
166
|
+
isDraft: true,
|
|
167
|
+
logOnly: true,
|
|
168
|
+
})
|
|
169
|
+
|
|
170
|
+
// Rebuild threads
|
|
171
|
+
await resetAndBuildThreads()
|
|
172
|
+
|
|
173
|
+
const loadedThreads3 = await sdk.api.inbox_threads.load_threads({})
|
|
174
|
+
const emailThread = loadedThreads3.threads.find(t => t.type === 'Email' && t.title === emailSubject)
|
|
175
|
+
|
|
176
|
+
assert(!!emailThread, "Email thread should be found")
|
|
177
|
+
assert(
|
|
178
|
+
emailThread!.draftMessageIds?.includes(draftEmail.id) === true,
|
|
179
|
+
`Draft Email ID should be in draftMessageIds. Got: ${JSON.stringify(emailThread!.draftMessageIds)}`
|
|
180
|
+
)
|
|
181
|
+
assert(
|
|
182
|
+
emailThread!.preview?.includes("This is a sent email") === true,
|
|
183
|
+
`Preview should be from sent email, not draft. Got: ${emailThread!.preview}`
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
console.log("✅ Draft email tracking test passed")
|
|
187
|
+
|
|
188
|
+
// Test 4: Scheduled email (future sendAt) should be tracked in scheduledMessageIds
|
|
189
|
+
console.log("Testing scheduled email tracking...")
|
|
190
|
+
|
|
191
|
+
const scheduledEmail = await sdk.api.emails.createOne({
|
|
192
|
+
subject: emailSubject,
|
|
193
|
+
textContent: "This is a scheduled email",
|
|
194
|
+
enduserId: testEnduser.id,
|
|
195
|
+
userId: testUser.id,
|
|
196
|
+
messageId: `scheduled-email-${timestamp}`,
|
|
197
|
+
sendAt: futureDate,
|
|
198
|
+
logOnly: true,
|
|
199
|
+
})
|
|
200
|
+
|
|
201
|
+
// Rebuild threads
|
|
202
|
+
await resetAndBuildThreads()
|
|
203
|
+
|
|
204
|
+
const loadedThreads4 = await sdk.api.inbox_threads.load_threads({})
|
|
205
|
+
const emailThreadWithScheduled = loadedThreads4.threads.find(t => t.type === 'Email' && t.title === emailSubject)
|
|
206
|
+
|
|
207
|
+
assert(!!emailThreadWithScheduled, "Email thread should be found")
|
|
208
|
+
assert(
|
|
209
|
+
emailThreadWithScheduled!.scheduledMessageIds?.includes(scheduledEmail.id) === true,
|
|
210
|
+
`Scheduled Email ID should be in scheduledMessageIds. Got: ${JSON.stringify(emailThreadWithScheduled!.scheduledMessageIds)}`
|
|
211
|
+
)
|
|
212
|
+
assert(
|
|
213
|
+
emailThreadWithScheduled!.preview?.includes("This is a scheduled email") !== true,
|
|
214
|
+
`Preview should NOT be from scheduled email. Got: ${emailThreadWithScheduled!.preview}`
|
|
215
|
+
)
|
|
216
|
+
|
|
217
|
+
console.log("✅ Scheduled email tracking test passed")
|
|
218
|
+
|
|
219
|
+
// Test 5: Thread with ONLY drafts should still have a preview from the drafts
|
|
220
|
+
console.log("Testing thread with only draft messages...")
|
|
221
|
+
|
|
222
|
+
const draftOnlyEnduserPhoneNumber = "+15555555561"
|
|
223
|
+
|
|
224
|
+
// Only create a draft - no inbound message, so phoneNumber will be placeholder
|
|
225
|
+
const draftOnlySMS = await sdk.api.sms_messages.createOne({
|
|
226
|
+
message: "This is the only message and it is a draft",
|
|
227
|
+
enduserId: testEnduser.id,
|
|
228
|
+
userId: testUser.id,
|
|
229
|
+
inbound: false,
|
|
230
|
+
phoneNumber: "+15555555560",
|
|
231
|
+
enduserPhoneNumber: draftOnlyEnduserPhoneNumber,
|
|
232
|
+
isDraft: true,
|
|
233
|
+
logOnly: true,
|
|
234
|
+
})
|
|
235
|
+
|
|
236
|
+
// Rebuild threads
|
|
237
|
+
await resetAndBuildThreads()
|
|
238
|
+
|
|
239
|
+
const loadedThreads5 = await sdk.api.inbox_threads.load_threads({})
|
|
240
|
+
// Match only on enduserPhoneNumber since there's no inbound to set phoneNumber
|
|
241
|
+
const draftOnlyThread = loadedThreads5.threads.find(t =>
|
|
242
|
+
t.type === 'SMS' && t.enduserPhoneNumber === draftOnlyEnduserPhoneNumber
|
|
243
|
+
)
|
|
244
|
+
|
|
245
|
+
assert(!!draftOnlyThread, "Draft-only thread should be found")
|
|
246
|
+
assert(
|
|
247
|
+
draftOnlyThread!.draftMessageIds?.includes(draftOnlySMS.id) === true,
|
|
248
|
+
`Draft SMS ID should be in draftMessageIds`
|
|
249
|
+
)
|
|
250
|
+
// When only drafts exist, the draft is used as fallback for preview
|
|
251
|
+
assert(
|
|
252
|
+
draftOnlyThread!.preview?.includes("only message") === true,
|
|
253
|
+
`Preview should fall back to draft when no sent messages exist. Got: ${draftOnlyThread!.preview}`
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
console.log("✅ Thread with only draft messages test passed")
|
|
257
|
+
|
|
258
|
+
// Test 6: Verify fields are correctly typed as arrays
|
|
259
|
+
console.log("Testing field types...")
|
|
260
|
+
|
|
261
|
+
assert(
|
|
262
|
+
Array.isArray(smsThread!.draftMessageIds),
|
|
263
|
+
"draftMessageIds should be an array"
|
|
264
|
+
)
|
|
265
|
+
assert(
|
|
266
|
+
Array.isArray(threadWithScheduled!.scheduledMessageIds),
|
|
267
|
+
"scheduledMessageIds should be an array"
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
console.log("✅ Field types test passed")
|
|
271
|
+
|
|
272
|
+
// Test 7: Threads with NO drafts/scheduled should have empty arrays (not undefined)
|
|
273
|
+
console.log("Testing empty array defaults for threads with only sent messages...")
|
|
274
|
+
|
|
275
|
+
const sentOnlyPhoneNumber = "+15555555570"
|
|
276
|
+
const sentOnlyEnduserPhoneNumber = "+15555555571"
|
|
277
|
+
|
|
278
|
+
// Create an inbound message to establish the thread with proper phone numbers
|
|
279
|
+
await sdk.api.sms_messages.createOne({
|
|
280
|
+
message: "Inbound message",
|
|
281
|
+
enduserId: testEnduser.id,
|
|
282
|
+
userId: testUser.id,
|
|
283
|
+
inbound: true,
|
|
284
|
+
phoneNumber: sentOnlyPhoneNumber,
|
|
285
|
+
enduserPhoneNumber: sentOnlyEnduserPhoneNumber,
|
|
286
|
+
logOnly: true,
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
// Create an outbound sent message (no draft, no scheduled)
|
|
290
|
+
await sdk.api.sms_messages.createOne({
|
|
291
|
+
message: "This is a sent message only",
|
|
292
|
+
enduserId: testEnduser.id,
|
|
293
|
+
userId: testUser.id,
|
|
294
|
+
inbound: false,
|
|
295
|
+
phoneNumber: sentOnlyPhoneNumber,
|
|
296
|
+
enduserPhoneNumber: sentOnlyEnduserPhoneNumber,
|
|
297
|
+
logOnly: true,
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
// Rebuild threads
|
|
301
|
+
await resetAndBuildThreads()
|
|
302
|
+
|
|
303
|
+
const loadedThreads6 = await sdk.api.inbox_threads.load_threads({})
|
|
304
|
+
const sentOnlyThread = loadedThreads6.threads.find(t =>
|
|
305
|
+
t.type === 'SMS' && t.phoneNumber === sentOnlyPhoneNumber && t.enduserPhoneNumber === sentOnlyEnduserPhoneNumber
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
assert(!!sentOnlyThread, "Sent-only thread should be found")
|
|
309
|
+
assert(
|
|
310
|
+
Array.isArray(sentOnlyThread!.draftMessageIds) && sentOnlyThread!.draftMessageIds.length === 0,
|
|
311
|
+
`draftMessageIds should be an empty array when no drafts exist. Got: ${JSON.stringify(sentOnlyThread!.draftMessageIds)}`
|
|
312
|
+
)
|
|
313
|
+
assert(
|
|
314
|
+
Array.isArray(sentOnlyThread!.scheduledMessageIds) && sentOnlyThread!.scheduledMessageIds.length === 0,
|
|
315
|
+
`scheduledMessageIds should be an empty array when no scheduled messages exist. Got: ${JSON.stringify(sentOnlyThread!.scheduledMessageIds)}`
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
console.log("✅ Empty array defaults test passed")
|
|
319
|
+
|
|
320
|
+
// ===== Chat Draft Cleanup on Send Tests =====
|
|
321
|
+
log_header("Chat Draft Cleanup on Send Tests")
|
|
322
|
+
|
|
323
|
+
// Test 8: Verify draftMessageIds is cleaned up when a draft chat is sent
|
|
324
|
+
// Note: Chat threads don't auto-populate draftMessageIds during build_threads,
|
|
325
|
+
// so we manually set it up to test the cleanup logic in the event handler
|
|
326
|
+
console.log("Testing draft chat cleanup on send...")
|
|
327
|
+
|
|
328
|
+
// Create a chat room for testing
|
|
329
|
+
const chatRoom = await sdk.api.chat_rooms.createOne({
|
|
330
|
+
userIds: [testUser.id],
|
|
331
|
+
enduserIds: [testEnduser.id],
|
|
332
|
+
})
|
|
333
|
+
|
|
334
|
+
// Create a non-draft message first to establish the room (sets recentMessageSentAt)
|
|
335
|
+
// This is required for the room to be included in build_threads
|
|
336
|
+
await sdk.api.chats.createOne({
|
|
337
|
+
roomId: chatRoom.id,
|
|
338
|
+
message: "Initial message to establish room",
|
|
339
|
+
senderId: testEnduser.id,
|
|
340
|
+
})
|
|
341
|
+
|
|
342
|
+
// Create a draft chat message (isDraft: true, sendAt far in future like webapp does)
|
|
343
|
+
const farFuture = new Date(Date.now() + 100 * 365 * 24 * 60 * 60 * 1000) // 100 years
|
|
344
|
+
const draftChat = await sdk.api.chats.createOne({
|
|
345
|
+
roomId: chatRoom.id,
|
|
346
|
+
message: "This is a draft chat message",
|
|
347
|
+
senderId: testUser.id,
|
|
348
|
+
isDraft: true,
|
|
349
|
+
sendAt: farFuture,
|
|
350
|
+
})
|
|
351
|
+
|
|
352
|
+
// Build threads to create the Chat thread
|
|
353
|
+
await resetAndBuildThreads()
|
|
354
|
+
|
|
355
|
+
const loadedThreads7 = await sdk.api.inbox_threads.load_threads({})
|
|
356
|
+
const chatThreadBefore = loadedThreads7.threads.find(t =>
|
|
357
|
+
t.type === 'Chat' && t.enduserIds?.includes(testEnduser.id)
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
assert(!!chatThreadBefore, "Chat thread should be found")
|
|
361
|
+
|
|
362
|
+
// Manually add the draft ID to draftMessageIds to simulate the setup
|
|
363
|
+
// (Chat thread building doesn't auto-populate this, but the cleanup should still work)
|
|
364
|
+
await sdk.api.inbox_threads.updateOne(chatThreadBefore!.id, {
|
|
365
|
+
draftMessageIds: [draftChat.id],
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
// Verify manual setup worked
|
|
369
|
+
const loadedThreads7b = await sdk.api.inbox_threads.load_threads({})
|
|
370
|
+
const chatThreadWithDraft = loadedThreads7b.threads.find(t => t.id === chatThreadBefore!.id)
|
|
371
|
+
assert(
|
|
372
|
+
chatThreadWithDraft!.draftMessageIds?.includes(draftChat.id) === true,
|
|
373
|
+
`Draft chat ID should be in draftMessageIds after manual setup. Got: ${JSON.stringify(chatThreadWithDraft!.draftMessageIds)}`
|
|
374
|
+
)
|
|
375
|
+
|
|
376
|
+
console.log("✅ Draft chat manually added to draftMessageIds")
|
|
377
|
+
|
|
378
|
+
// "Send" the draft by updating isDraft to false and sendAt to now
|
|
379
|
+
// This mimics what the webapp does when user clicks send
|
|
380
|
+
await sdk.api.chats.updateOne(draftChat.id, {
|
|
381
|
+
isDraft: false,
|
|
382
|
+
sendAt: new Date(), // Now - triggers the scheduled chat handler
|
|
383
|
+
})
|
|
384
|
+
|
|
385
|
+
// Wait for the side effect to process
|
|
386
|
+
await wait(undefined, 500)
|
|
387
|
+
|
|
388
|
+
// Reload the thread and verify cleanup happened
|
|
389
|
+
const loadedThreads8 = await sdk.api.inbox_threads.load_threads({})
|
|
390
|
+
const chatThreadAfter = loadedThreads8.threads.find(t => t.id === chatThreadBefore!.id)
|
|
391
|
+
|
|
392
|
+
assert(!!chatThreadAfter, "Chat thread should be found after send")
|
|
393
|
+
assert(
|
|
394
|
+
chatThreadAfter!.draftMessageIds?.includes(draftChat.id) === false,
|
|
395
|
+
`Draft chat ID should be REMOVED from draftMessageIds after send. Got: ${JSON.stringify(chatThreadAfter!.draftMessageIds)}`
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
console.log("✅ Draft chat cleanup on send test passed")
|
|
399
|
+
|
|
400
|
+
// Cleanup chat room
|
|
401
|
+
await sdk.api.chat_rooms.deleteOne(chatRoom.id).catch(console.error)
|
|
402
|
+
|
|
403
|
+
// ===== Message Deletion Cleanup Tests =====
|
|
404
|
+
log_header("Message Deletion Cleanup Tests")
|
|
405
|
+
|
|
406
|
+
// Test 9: SMS draft deletion should remove ID from draftMessageIds
|
|
407
|
+
console.log("Testing SMS draft deletion cleanup...")
|
|
408
|
+
|
|
409
|
+
const deleteTestPhoneNumber = "+15555555580"
|
|
410
|
+
const deleteTestEnduserPhoneNumber = "+15555555581"
|
|
411
|
+
|
|
412
|
+
// Create an inbound SMS to establish the thread
|
|
413
|
+
await sdk.api.sms_messages.createOne({
|
|
414
|
+
message: "Inbound message to establish thread for deletion test",
|
|
415
|
+
enduserId: testEnduser.id,
|
|
416
|
+
userId: testUser.id,
|
|
417
|
+
inbound: true,
|
|
418
|
+
phoneNumber: deleteTestPhoneNumber,
|
|
419
|
+
enduserPhoneNumber: deleteTestEnduserPhoneNumber,
|
|
420
|
+
logOnly: true,
|
|
421
|
+
})
|
|
422
|
+
|
|
423
|
+
// Create a draft SMS
|
|
424
|
+
const draftSmsToDelete = await sdk.api.sms_messages.createOne({
|
|
425
|
+
message: "This draft will be deleted",
|
|
426
|
+
enduserId: testEnduser.id,
|
|
427
|
+
userId: testUser.id,
|
|
428
|
+
inbound: false,
|
|
429
|
+
phoneNumber: deleteTestPhoneNumber,
|
|
430
|
+
enduserPhoneNumber: deleteTestEnduserPhoneNumber,
|
|
431
|
+
isDraft: true,
|
|
432
|
+
logOnly: true,
|
|
433
|
+
})
|
|
434
|
+
|
|
435
|
+
// Build threads to populate draftMessageIds
|
|
436
|
+
await resetAndBuildThreads()
|
|
437
|
+
|
|
438
|
+
// Verify the draft ID is in the thread
|
|
439
|
+
const loadedThreads9a = await sdk.api.inbox_threads.load_threads({})
|
|
440
|
+
const smsDeleteThread = loadedThreads9a.threads.find(t =>
|
|
441
|
+
t.type === 'SMS' && t.phoneNumber === deleteTestPhoneNumber && t.enduserPhoneNumber === deleteTestEnduserPhoneNumber
|
|
442
|
+
)
|
|
443
|
+
|
|
444
|
+
assert(!!smsDeleteThread, "SMS thread for deletion test should be found")
|
|
445
|
+
assert(
|
|
446
|
+
smsDeleteThread!.draftMessageIds?.includes(draftSmsToDelete.id) === true,
|
|
447
|
+
`Draft SMS ID should be in draftMessageIds before deletion. Got: ${JSON.stringify(smsDeleteThread!.draftMessageIds)}`
|
|
448
|
+
)
|
|
449
|
+
|
|
450
|
+
// Delete the draft SMS
|
|
451
|
+
await sdk.api.sms_messages.deleteOne(draftSmsToDelete.id)
|
|
452
|
+
|
|
453
|
+
// Wait for the delete side effect to process
|
|
454
|
+
await wait(undefined, 500)
|
|
455
|
+
|
|
456
|
+
// Verify the draft ID has been removed
|
|
457
|
+
const loadedThreads9b = await sdk.api.inbox_threads.load_threads({})
|
|
458
|
+
const smsDeleteThreadAfter = loadedThreads9b.threads.find(t => t.id === smsDeleteThread!.id)
|
|
459
|
+
|
|
460
|
+
assert(!!smsDeleteThreadAfter, "SMS thread should still exist after draft deletion")
|
|
461
|
+
assert(
|
|
462
|
+
smsDeleteThreadAfter!.draftMessageIds?.includes(draftSmsToDelete.id) === false,
|
|
463
|
+
`Draft SMS ID should be REMOVED from draftMessageIds after deletion. Got: ${JSON.stringify(smsDeleteThreadAfter!.draftMessageIds)}`
|
|
464
|
+
)
|
|
465
|
+
|
|
466
|
+
console.log("✅ SMS draft deletion cleanup test passed")
|
|
467
|
+
|
|
468
|
+
// Test 10: Email draft deletion should remove ID from draftMessageIds
|
|
469
|
+
console.log("Testing email draft deletion cleanup...")
|
|
470
|
+
|
|
471
|
+
const deleteEmailSubject = `Delete Test Email ${timestamp}`
|
|
472
|
+
|
|
473
|
+
// Create a sent email first to establish the thread
|
|
474
|
+
await sdk.api.emails.createOne({
|
|
475
|
+
subject: deleteEmailSubject,
|
|
476
|
+
textContent: "Sent email to establish thread for deletion test",
|
|
477
|
+
enduserId: testEnduser.id,
|
|
478
|
+
userId: testUser.id,
|
|
479
|
+
messageId: `sent-email-delete-test-${timestamp}`,
|
|
480
|
+
logOnly: true,
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
// Create a draft email
|
|
484
|
+
const draftEmailToDelete = await sdk.api.emails.createOne({
|
|
485
|
+
subject: deleteEmailSubject,
|
|
486
|
+
textContent: "This draft email will be deleted",
|
|
487
|
+
enduserId: testEnduser.id,
|
|
488
|
+
userId: testUser.id,
|
|
489
|
+
messageId: `draft-email-delete-test-${timestamp}`,
|
|
490
|
+
isDraft: true,
|
|
491
|
+
logOnly: true,
|
|
492
|
+
})
|
|
493
|
+
|
|
494
|
+
// Build threads to populate draftMessageIds
|
|
495
|
+
await resetAndBuildThreads()
|
|
496
|
+
|
|
497
|
+
// Verify the draft ID is in the thread
|
|
498
|
+
const loadedThreads10a = await sdk.api.inbox_threads.load_threads({})
|
|
499
|
+
const emailDeleteThread = loadedThreads10a.threads.find(t => t.type === 'Email' && t.title === deleteEmailSubject)
|
|
500
|
+
|
|
501
|
+
assert(!!emailDeleteThread, "Email thread for deletion test should be found")
|
|
502
|
+
assert(
|
|
503
|
+
emailDeleteThread!.draftMessageIds?.includes(draftEmailToDelete.id) === true,
|
|
504
|
+
`Draft Email ID should be in draftMessageIds before deletion. Got: ${JSON.stringify(emailDeleteThread!.draftMessageIds)}`
|
|
505
|
+
)
|
|
506
|
+
|
|
507
|
+
// Delete the draft email
|
|
508
|
+
await sdk.api.emails.deleteOne(draftEmailToDelete.id)
|
|
509
|
+
|
|
510
|
+
// Wait for the delete side effect to process
|
|
511
|
+
await wait(undefined, 500)
|
|
512
|
+
|
|
513
|
+
// Verify the draft ID has been removed
|
|
514
|
+
const loadedThreads10b = await sdk.api.inbox_threads.load_threads({})
|
|
515
|
+
const emailDeleteThreadAfter = loadedThreads10b.threads.find(t => t.id === emailDeleteThread!.id)
|
|
516
|
+
|
|
517
|
+
assert(!!emailDeleteThreadAfter, "Email thread should still exist after draft deletion")
|
|
518
|
+
assert(
|
|
519
|
+
emailDeleteThreadAfter!.draftMessageIds?.includes(draftEmailToDelete.id) === false,
|
|
520
|
+
`Draft Email ID should be REMOVED from draftMessageIds after deletion. Got: ${JSON.stringify(emailDeleteThreadAfter!.draftMessageIds)}`
|
|
521
|
+
)
|
|
522
|
+
|
|
523
|
+
console.log("✅ Email draft deletion cleanup test passed")
|
|
524
|
+
|
|
525
|
+
// Test 11: Chat draft deletion should remove ID from draftMessageIds
|
|
526
|
+
console.log("Testing chat draft deletion cleanup...")
|
|
527
|
+
|
|
528
|
+
// Create a chat room for testing
|
|
529
|
+
const chatRoomForDeletion = await sdk.api.chat_rooms.createOne({
|
|
530
|
+
userIds: [testUser.id],
|
|
531
|
+
enduserIds: [testEnduser.id],
|
|
532
|
+
})
|
|
533
|
+
|
|
534
|
+
// Create a non-draft message first to establish the room
|
|
535
|
+
await sdk.api.chats.createOne({
|
|
536
|
+
roomId: chatRoomForDeletion.id,
|
|
537
|
+
message: "Initial message to establish room for deletion test",
|
|
538
|
+
senderId: testEnduser.id,
|
|
539
|
+
})
|
|
540
|
+
|
|
541
|
+
// Create a draft chat message
|
|
542
|
+
const farFuture2 = new Date(Date.now() + 100 * 365 * 24 * 60 * 60 * 1000)
|
|
543
|
+
const draftChatToDelete = await sdk.api.chats.createOne({
|
|
544
|
+
roomId: chatRoomForDeletion.id,
|
|
545
|
+
message: "This draft chat will be deleted",
|
|
546
|
+
senderId: testUser.id,
|
|
547
|
+
isDraft: true,
|
|
548
|
+
sendAt: farFuture2,
|
|
549
|
+
})
|
|
550
|
+
|
|
551
|
+
// Build threads to create the Chat thread
|
|
552
|
+
await resetAndBuildThreads()
|
|
553
|
+
|
|
554
|
+
const loadedThreads11a = await sdk.api.inbox_threads.load_threads({})
|
|
555
|
+
const chatDeleteThread = loadedThreads11a.threads.find(t =>
|
|
556
|
+
t.type === 'Chat' && t.enduserIds?.includes(testEnduser.id)
|
|
557
|
+
)
|
|
558
|
+
|
|
559
|
+
assert(!!chatDeleteThread, "Chat thread for deletion test should be found")
|
|
560
|
+
|
|
561
|
+
// Manually add the draft ID to draftMessageIds (since chat threads don't auto-populate this)
|
|
562
|
+
await sdk.api.inbox_threads.updateOne(chatDeleteThread!.id, {
|
|
563
|
+
draftMessageIds: [draftChatToDelete.id],
|
|
564
|
+
})
|
|
565
|
+
|
|
566
|
+
// Verify manual setup worked
|
|
567
|
+
const loadedThreads11b = await sdk.api.inbox_threads.load_threads({})
|
|
568
|
+
const chatDeleteThreadWithDraft = loadedThreads11b.threads.find(t => t.id === chatDeleteThread!.id)
|
|
569
|
+
assert(
|
|
570
|
+
chatDeleteThreadWithDraft!.draftMessageIds?.includes(draftChatToDelete.id) === true,
|
|
571
|
+
`Draft chat ID should be in draftMessageIds after manual setup. Got: ${JSON.stringify(chatDeleteThreadWithDraft!.draftMessageIds)}`
|
|
572
|
+
)
|
|
573
|
+
|
|
574
|
+
// Delete the draft chat
|
|
575
|
+
await sdk.api.chats.deleteOne(draftChatToDelete.id)
|
|
576
|
+
|
|
577
|
+
// Wait for the delete side effect to process
|
|
578
|
+
await wait(undefined, 500)
|
|
579
|
+
|
|
580
|
+
// Verify the draft ID has been removed
|
|
581
|
+
const loadedThreads11c = await sdk.api.inbox_threads.load_threads({})
|
|
582
|
+
const chatDeleteThreadAfter = loadedThreads11c.threads.find(t => t.id === chatDeleteThread!.id)
|
|
583
|
+
|
|
584
|
+
assert(!!chatDeleteThreadAfter, "Chat thread should still exist after draft deletion")
|
|
585
|
+
assert(
|
|
586
|
+
chatDeleteThreadAfter!.draftMessageIds?.includes(draftChatToDelete.id) === false,
|
|
587
|
+
`Draft Chat ID should be REMOVED from draftMessageIds after deletion. Got: ${JSON.stringify(chatDeleteThreadAfter!.draftMessageIds)}`
|
|
588
|
+
)
|
|
589
|
+
|
|
590
|
+
console.log("✅ Chat draft deletion cleanup test passed")
|
|
591
|
+
|
|
592
|
+
// Cleanup deletion test chat room
|
|
593
|
+
await sdk.api.chat_rooms.deleteOne(chatRoomForDeletion.id).catch(console.error)
|
|
594
|
+
|
|
595
|
+
console.log("🎉 All InboxThread draft/scheduled tests passed!")
|
|
596
|
+
|
|
597
|
+
} finally {
|
|
598
|
+
// Cleanup
|
|
599
|
+
await sdk.api.inbox_threads.reset_threads().catch(console.error)
|
|
600
|
+
await sdk.api.endusers.deleteOne(testEnduser.id).catch(console.error)
|
|
601
|
+
await sdk.api.users.deleteOne(testUser.id).catch(console.error)
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// Allow running this test file independently
|
|
606
|
+
if (require.main === module) {
|
|
607
|
+
console.log(`🌐 Using API URL: ${host}`)
|
|
608
|
+
const sdk = new Session({ host })
|
|
609
|
+
const sdkNonAdmin = new Session({ host })
|
|
610
|
+
|
|
611
|
+
const runTests = async () => {
|
|
612
|
+
await setup_tests(sdk, sdkNonAdmin)
|
|
613
|
+
await inbox_thread_draft_scheduled_tests({ sdk, sdkNonAdmin })
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
runTests()
|
|
617
|
+
.then(() => {
|
|
618
|
+
console.log("✅ InboxThread draft/scheduled test suite completed successfully")
|
|
619
|
+
process.exit(0)
|
|
620
|
+
})
|
|
621
|
+
.catch((error) => {
|
|
622
|
+
console.error("❌ InboxThread draft/scheduled test suite failed:", error)
|
|
623
|
+
process.exit(1)
|
|
624
|
+
})
|
|
625
|
+
}
|