@app-connect/core 1.7.10 → 1.7.12

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 (59) hide show
  1. package/connector/developerPortal.js +43 -0
  2. package/connector/proxy/index.js +10 -3
  3. package/connector/registry.js +8 -6
  4. package/handlers/admin.js +135 -22
  5. package/handlers/auth.js +89 -67
  6. package/handlers/calldown.js +10 -4
  7. package/handlers/contact.js +4 -104
  8. package/handlers/disposition.js +7 -145
  9. package/handlers/log.js +174 -258
  10. package/handlers/user.js +19 -6
  11. package/index.js +280 -47
  12. package/lib/analytics.js +3 -1
  13. package/lib/authSession.js +68 -0
  14. package/lib/callLogComposer.js +498 -420
  15. package/lib/errorHandler.js +206 -0
  16. package/lib/jwt.js +2 -0
  17. package/lib/logger.js +190 -0
  18. package/lib/oauth.js +21 -10
  19. package/lib/ringcentral.js +2 -10
  20. package/lib/sharedSMSComposer.js +471 -0
  21. package/mcp/SupportedPlatforms.md +12 -0
  22. package/mcp/lib/validator.js +91 -0
  23. package/mcp/mcpHandler.js +166 -0
  24. package/mcp/tools/checkAuthStatus.js +110 -0
  25. package/mcp/tools/collectAuthInfo.js +91 -0
  26. package/mcp/tools/createCallLog.js +308 -0
  27. package/mcp/tools/createContact.js +117 -0
  28. package/mcp/tools/createMessageLog.js +283 -0
  29. package/mcp/tools/doAuth.js +190 -0
  30. package/mcp/tools/findContactByName.js +92 -0
  31. package/mcp/tools/findContactByPhone.js +101 -0
  32. package/mcp/tools/getCallLog.js +98 -0
  33. package/mcp/tools/getGoogleFilePicker.js +103 -0
  34. package/mcp/tools/getHelp.js +44 -0
  35. package/mcp/tools/getPublicConnectors.js +53 -0
  36. package/mcp/tools/index.js +64 -0
  37. package/mcp/tools/logout.js +68 -0
  38. package/mcp/tools/rcGetCallLogs.js +78 -0
  39. package/mcp/tools/setConnector.js +69 -0
  40. package/mcp/tools/updateCallLog.js +122 -0
  41. package/models/cacheModel.js +3 -0
  42. package/package.json +71 -70
  43. package/releaseNotes.json +24 -0
  44. package/test/handlers/log.test.js +11 -4
  45. package/test/lib/logger.test.js +206 -0
  46. package/test/lib/ringcentral.test.js +0 -6
  47. package/test/lib/sharedSMSComposer.test.js +1084 -0
  48. package/test/mcp/tools/collectAuthInfo.test.js +234 -0
  49. package/test/mcp/tools/createCallLog.test.js +425 -0
  50. package/test/mcp/tools/createMessageLog.test.js +580 -0
  51. package/test/mcp/tools/doAuth.test.js +376 -0
  52. package/test/mcp/tools/findContactByName.test.js +263 -0
  53. package/test/mcp/tools/findContactByPhone.test.js +284 -0
  54. package/test/mcp/tools/getCallLog.test.js +286 -0
  55. package/test/mcp/tools/getGoogleFilePicker.test.js +281 -0
  56. package/test/mcp/tools/getPublicConnectors.test.js +128 -0
  57. package/test/mcp/tools/logout.test.js +169 -0
  58. package/test/mcp/tools/setConnector.test.js +177 -0
  59. package/test/mcp/tools/updateCallLog.test.js +346 -0
@@ -0,0 +1,234 @@
1
+ const collectAuthInfo = require('../../../mcp/tools/collectAuthInfo');
2
+
3
+ describe('MCP Tool: collectAuthInfo', () => {
4
+ describe('tool definition', () => {
5
+ test('should have correct tool definition', () => {
6
+ expect(collectAuthInfo.definition).toBeDefined();
7
+ expect(collectAuthInfo.definition.name).toBe('collectAuthInfo');
8
+ expect(collectAuthInfo.definition.description).toContain('Auth flow step.3');
9
+ expect(collectAuthInfo.definition.inputSchema).toBeDefined();
10
+ });
11
+
12
+ test('should require connectorManifest and connectorName parameters', () => {
13
+ expect(collectAuthInfo.definition.inputSchema.required).toContain('connectorManifest');
14
+ expect(collectAuthInfo.definition.inputSchema.required).toContain('connectorName');
15
+ });
16
+ });
17
+
18
+ describe('execute', () => {
19
+ test('should handle selectable environment type', async () => {
20
+ // Arrange
21
+ const mockManifest = {
22
+ platforms: {
23
+ salesforce: {
24
+ name: 'salesforce',
25
+ auth: {
26
+ type: 'oauth',
27
+ oauth: {
28
+ authUrl: 'https://login.salesforce.com/services/oauth2/authorize',
29
+ clientId: 'test-client-id'
30
+ }
31
+ },
32
+ environment: {
33
+ type: 'selectable',
34
+ selections: [
35
+ { name: 'Production', const: 'https://login.salesforce.com' },
36
+ { name: 'Sandbox', const: 'https://test.salesforce.com' }
37
+ ]
38
+ }
39
+ }
40
+ }
41
+ };
42
+
43
+ // Act
44
+ const result = await collectAuthInfo.execute({
45
+ connectorManifest: mockManifest,
46
+ connectorName: 'salesforce',
47
+ selection: 'Production'
48
+ });
49
+
50
+ // Assert
51
+ expect(result).toEqual({
52
+ success: true,
53
+ data: {
54
+ hostname: 'login.salesforce.com',
55
+ message: expect.stringContaining('IMPORTANT')
56
+ }
57
+ });
58
+ });
59
+
60
+ test('should handle dynamic environment type', async () => {
61
+ // Arrange
62
+ const mockManifest = {
63
+ platforms: {
64
+ netsuite: {
65
+ name: 'netsuite',
66
+ auth: {
67
+ type: 'oauth',
68
+ oauth: {
69
+ authUrl: 'https://system.netsuite.com/app/login/oauth2/authorize.nl',
70
+ clientId: 'test-client-id'
71
+ }
72
+ },
73
+ environment: {
74
+ type: 'dynamic'
75
+ }
76
+ }
77
+ }
78
+ };
79
+
80
+ // Act
81
+ const result = await collectAuthInfo.execute({
82
+ connectorManifest: mockManifest,
83
+ connectorName: 'netsuite',
84
+ hostname: 'https://1234567.app.netsuite.com'
85
+ });
86
+
87
+ // Assert
88
+ expect(result).toEqual({
89
+ success: true,
90
+ data: {
91
+ hostname: '1234567.app.netsuite.com',
92
+ message: expect.stringContaining('IMPORTANT')
93
+ }
94
+ });
95
+ });
96
+
97
+ test('should handle sandbox selection', async () => {
98
+ // Arrange
99
+ const mockManifest = {
100
+ platforms: {
101
+ salesforce: {
102
+ name: 'salesforce',
103
+ auth: {
104
+ type: 'oauth',
105
+ oauth: {
106
+ authUrl: 'https://login.salesforce.com/services/oauth2/authorize',
107
+ clientId: 'test-client-id'
108
+ }
109
+ },
110
+ environment: {
111
+ type: 'selectable',
112
+ selections: [
113
+ { name: 'Production', const: 'https://login.salesforce.com' },
114
+ { name: 'Sandbox', const: 'https://test.salesforce.com' }
115
+ ]
116
+ }
117
+ }
118
+ }
119
+ };
120
+
121
+ // Act
122
+ const result = await collectAuthInfo.execute({
123
+ connectorManifest: mockManifest,
124
+ connectorName: 'salesforce',
125
+ selection: 'Sandbox'
126
+ });
127
+
128
+ // Assert
129
+ expect(result.success).toBe(true);
130
+ expect(result.data.hostname).toBe('test.salesforce.com');
131
+ });
132
+
133
+ test('should handle invalid hostname URL', async () => {
134
+ // Arrange
135
+ const mockManifest = {
136
+ platforms: {
137
+ netsuite: {
138
+ name: 'netsuite',
139
+ auth: {
140
+ type: 'oauth',
141
+ oauth: {
142
+ authUrl: 'https://system.netsuite.com/app/login/oauth2/authorize.nl',
143
+ clientId: 'test-client-id'
144
+ }
145
+ },
146
+ environment: {
147
+ type: 'dynamic'
148
+ }
149
+ }
150
+ }
151
+ };
152
+
153
+ // Act
154
+ const result = await collectAuthInfo.execute({
155
+ connectorManifest: mockManifest,
156
+ connectorName: 'netsuite',
157
+ hostname: 'invalid-url'
158
+ });
159
+
160
+ // Assert
161
+ expect(result.success).toBe(false);
162
+ expect(result.error).toBeDefined();
163
+ expect(result.errorDetails).toBeDefined();
164
+ });
165
+
166
+ test('should handle missing selection for selectable type', async () => {
167
+ // Arrange
168
+ const mockManifest = {
169
+ platforms: {
170
+ salesforce: {
171
+ name: 'salesforce',
172
+ auth: {
173
+ type: 'oauth',
174
+ oauth: {
175
+ authUrl: 'https://login.salesforce.com/services/oauth2/authorize',
176
+ clientId: 'test-client-id'
177
+ }
178
+ },
179
+ environment: {
180
+ type: 'selectable',
181
+ selections: [
182
+ { name: 'Production', const: 'https://login.salesforce.com' }
183
+ ]
184
+ }
185
+ }
186
+ }
187
+ };
188
+
189
+ // Act
190
+ const result = await collectAuthInfo.execute({
191
+ connectorManifest: mockManifest,
192
+ connectorName: 'salesforce',
193
+ selection: 'NonExistent'
194
+ });
195
+
196
+ // Assert
197
+ expect(result.success).toBe(false);
198
+ expect(result.error).toBeDefined();
199
+ });
200
+
201
+ test('should handle missing hostname for dynamic type', async () => {
202
+ // Arrange
203
+ const mockManifest = {
204
+ platforms: {
205
+ netsuite: {
206
+ name: 'netsuite',
207
+ auth: {
208
+ type: 'oauth',
209
+ oauth: {
210
+ authUrl: 'https://system.netsuite.com/app/login/oauth2/authorize.nl',
211
+ clientId: 'test-client-id'
212
+ }
213
+ },
214
+ environment: {
215
+ type: 'dynamic'
216
+ }
217
+ }
218
+ }
219
+ };
220
+
221
+ // Act
222
+ const result = await collectAuthInfo.execute({
223
+ connectorManifest: mockManifest,
224
+ connectorName: 'netsuite'
225
+ // hostname is missing
226
+ });
227
+
228
+ // Assert
229
+ expect(result.success).toBe(false);
230
+ expect(result.error).toBeDefined();
231
+ });
232
+ });
233
+ });
234
+
@@ -0,0 +1,425 @@
1
+ const createCallLog = require('../../../mcp/tools/createCallLog');
2
+ const jwt = require('../../../lib/jwt');
3
+ const connectorRegistry = require('../../../connector/registry');
4
+ const logCore = require('../../../handlers/log');
5
+ const contactCore = require('../../../handlers/contact');
6
+ const util = require('../../../lib/util');
7
+ const { CallLogModel } = require('../../../models/callLogModel');
8
+
9
+ // Mock dependencies
10
+ jest.mock('../../../lib/jwt');
11
+ jest.mock('../../../connector/registry');
12
+ jest.mock('../../../handlers/log');
13
+ jest.mock('../../../handlers/contact');
14
+ jest.mock('../../../lib/util');
15
+ jest.mock('../../../models/callLogModel');
16
+
17
+ describe('MCP Tool: createCallLog', () => {
18
+ beforeEach(() => {
19
+ jest.clearAllMocks();
20
+ process.env.HASH_KEY = 'test-hash-key';
21
+ });
22
+
23
+ describe('tool definition', () => {
24
+ test('should have correct tool definition', () => {
25
+ expect(createCallLog.definition).toBeDefined();
26
+ expect(createCallLog.definition.name).toBe('createCallLog');
27
+ expect(createCallLog.definition.description).toContain('REQUIRES AUTHENTICATION');
28
+ expect(createCallLog.definition.inputSchema).toBeDefined();
29
+ });
30
+
31
+ test('should require jwtToken and incomingData parameters', () => {
32
+ expect(createCallLog.definition.inputSchema.required).toContain('jwtToken');
33
+ expect(createCallLog.definition.inputSchema.required).toContain('incomingData');
34
+ });
35
+
36
+ test('should have detailed inputSchema for incomingData', () => {
37
+ const incomingDataSchema = createCallLog.definition.inputSchema.properties.incomingData;
38
+ expect(incomingDataSchema.properties).toHaveProperty('logInfo');
39
+ expect(incomingDataSchema.properties).toHaveProperty('note');
40
+ expect(incomingDataSchema.required).toContain('logInfo');
41
+ });
42
+ });
43
+
44
+ describe('execute', () => {
45
+ test('should create call log successfully', async () => {
46
+ // Arrange
47
+ const mockIncomingData = {
48
+ logInfo: {
49
+ id: 'rc-call-123',
50
+ sessionId: 'session-123',
51
+ direction: 'Inbound',
52
+ startTime: '2024-01-01T10:00:00Z',
53
+ duration: 120,
54
+ from: { phoneNumber: '+1234567890', name: 'John Doe' },
55
+ to: { phoneNumber: '+0987654321', name: 'Company' },
56
+ accountId: 'rc-account-123'
57
+ },
58
+ contactName: 'John Doe',
59
+ contactType: 'Contact',
60
+ note: 'Test call note'
61
+ };
62
+
63
+ jwt.decodeJwt.mockReturnValue({
64
+ id: 'user-123',
65
+ platform: 'testCRM'
66
+ });
67
+
68
+ const mockConnector = {
69
+ createCallLog: jest.fn()
70
+ };
71
+ connectorRegistry.getConnector.mockReturnValue(mockConnector);
72
+
73
+ CallLogModel.findOne.mockResolvedValue(null); // No existing log
74
+
75
+ util.getHashValue.mockReturnValue('hashed-account-id');
76
+
77
+ // Mock contactCore.findContact to return a contact
78
+ contactCore.findContact.mockResolvedValue({
79
+ successful: true,
80
+ contact: [{ id: 'contact-123', isNewContact: false }]
81
+ });
82
+
83
+ logCore.createCallLog.mockResolvedValue({
84
+ successful: true,
85
+ logId: 'crm-log-123',
86
+ returnMessage: { message: 'Call logged successfully' }
87
+ });
88
+
89
+ // Act
90
+ const result = await createCallLog.execute({
91
+ jwtToken: 'mock-jwt-token',
92
+ incomingData: mockIncomingData
93
+ });
94
+
95
+ // Assert
96
+ expect(result).toEqual({
97
+ success: true,
98
+ data: {
99
+ logId: 'crm-log-123',
100
+ message: 'Call logged successfully'
101
+ }
102
+ });
103
+ expect(jwt.decodeJwt).toHaveBeenCalledWith('mock-jwt-token');
104
+ expect(CallLogModel.findOne).toHaveBeenCalledWith({
105
+ where: { sessionId: 'session-123' }
106
+ });
107
+ });
108
+
109
+ test('should create call log with AI note and transcript', async () => {
110
+ // Arrange
111
+ const mockIncomingData = {
112
+ logInfo: {
113
+ id: 'rc-call-456',
114
+ sessionId: 'session-456',
115
+ direction: 'Outbound',
116
+ startTime: '2024-01-01T11:00:00Z',
117
+ duration: 300,
118
+ from: { phoneNumber: '+0987654321' },
119
+ to: { phoneNumber: '+1234567890' }
120
+ },
121
+ aiNote: 'AI generated summary of the call',
122
+ transcript: 'Full call transcript text'
123
+ };
124
+
125
+ jwt.decodeJwt.mockReturnValue({
126
+ id: 'user-123',
127
+ platform: 'testCRM'
128
+ });
129
+
130
+ const mockConnector = {
131
+ createCallLog: jest.fn()
132
+ };
133
+ connectorRegistry.getConnector.mockReturnValue(mockConnector);
134
+
135
+ CallLogModel.findOne.mockResolvedValue(null);
136
+
137
+ // Mock contactCore.findContact to return a contact
138
+ contactCore.findContact.mockResolvedValue({
139
+ successful: true,
140
+ contact: [{ id: 'contact-456', isNewContact: false }]
141
+ });
142
+
143
+ logCore.createCallLog.mockResolvedValue({
144
+ successful: true,
145
+ logId: 'crm-log-456'
146
+ });
147
+
148
+ // Act
149
+ const result = await createCallLog.execute({
150
+ jwtToken: 'mock-jwt-token',
151
+ incomingData: mockIncomingData
152
+ });
153
+
154
+ // Assert
155
+ expect(result.success).toBe(true);
156
+ expect(result.data.logId).toBe('crm-log-456');
157
+ });
158
+
159
+ test('should create call log with additional submission', async () => {
160
+ // Arrange
161
+ const mockIncomingData = {
162
+ logInfo: {
163
+ id: 'rc-call-789',
164
+ sessionId: 'session-789',
165
+ direction: 'Inbound',
166
+ startTime: '2024-01-01T12:00:00Z',
167
+ duration: 60,
168
+ from: { phoneNumber: '+1234567890' },
169
+ to: { phoneNumber: '+0987654321' }
170
+ },
171
+ additionalSubmission: {
172
+ isAssignedToUser: true,
173
+ adminAssignedUserToken: 'admin-jwt-token',
174
+ adminAssignedUserRcId: 'rc-ext-101'
175
+ }
176
+ };
177
+
178
+ jwt.decodeJwt.mockReturnValue({
179
+ id: 'user-123',
180
+ platform: 'testCRM'
181
+ });
182
+
183
+ const mockConnector = {
184
+ createCallLog: jest.fn()
185
+ };
186
+ connectorRegistry.getConnector.mockReturnValue(mockConnector);
187
+
188
+ CallLogModel.findOne.mockResolvedValue(null);
189
+
190
+ // Mock contactCore.findContact to return a contact
191
+ contactCore.findContact.mockResolvedValue({
192
+ successful: true,
193
+ contact: [{ id: 'contact-789', isNewContact: false }]
194
+ });
195
+
196
+ logCore.createCallLog.mockResolvedValue({
197
+ successful: true,
198
+ logId: 'crm-log-789'
199
+ });
200
+
201
+ // Act
202
+ const result = await createCallLog.execute({
203
+ jwtToken: 'mock-jwt-token',
204
+ incomingData: mockIncomingData
205
+ });
206
+
207
+ // Assert
208
+ expect(result.success).toBe(true);
209
+ });
210
+
211
+ test('should return error when call log already exists', async () => {
212
+ // Arrange
213
+ const mockIncomingData = {
214
+ logInfo: {
215
+ sessionId: 'existing-session'
216
+ }
217
+ };
218
+
219
+ CallLogModel.findOne.mockResolvedValue({
220
+ id: 'existing-log',
221
+ sessionId: 'existing-session'
222
+ });
223
+
224
+ // Act
225
+ const result = await createCallLog.execute({
226
+ jwtToken: 'mock-jwt-token',
227
+ incomingData: mockIncomingData
228
+ });
229
+
230
+ // Assert
231
+ expect(result.success).toBe(false);
232
+ expect(result.error).toContain('already exists');
233
+ });
234
+
235
+ test('should return error when jwtToken is missing', async () => {
236
+ // Act
237
+ const result = await createCallLog.execute({
238
+ incomingData: { logInfo: {} }
239
+ });
240
+
241
+ // Assert
242
+ expect(result.success).toBe(false);
243
+ expect(result.error).toContain('authorize CRM platform');
244
+ });
245
+
246
+ test('should return error when incomingData is missing', async () => {
247
+ // Act
248
+ const result = await createCallLog.execute({
249
+ jwtToken: 'mock-jwt-token'
250
+ });
251
+
252
+ // Assert
253
+ expect(result.success).toBe(false);
254
+ expect(result.error).toContain('Incoming data must be provided');
255
+ });
256
+
257
+ test('should return error when logInfo is missing', async () => {
258
+ // Act
259
+ const result = await createCallLog.execute({
260
+ jwtToken: 'mock-jwt-token',
261
+ incomingData: { contactId: 'contact-123' }
262
+ });
263
+
264
+ // Assert
265
+ expect(result.success).toBe(false);
266
+ expect(result.error).toContain('logInfo is required');
267
+ });
268
+
269
+ test('should return error when JWT is invalid', async () => {
270
+ // Arrange
271
+ const mockIncomingData = {
272
+ logInfo: {
273
+ sessionId: 'session-123'
274
+ }
275
+ };
276
+
277
+ CallLogModel.findOne.mockResolvedValue(null);
278
+
279
+ jwt.decodeJwt.mockReturnValue({
280
+ platform: 'testCRM'
281
+ // id is missing
282
+ });
283
+
284
+ // Act
285
+ const result = await createCallLog.execute({
286
+ jwtToken: 'invalid-token',
287
+ incomingData: mockIncomingData
288
+ });
289
+
290
+ // Assert
291
+ expect(result.success).toBe(false);
292
+ expect(result.error).toContain('Invalid JWT token');
293
+ });
294
+
295
+ test('should return error when platform connector not found', async () => {
296
+ // Arrange
297
+ const mockIncomingData = {
298
+ logInfo: {
299
+ sessionId: 'session-123'
300
+ }
301
+ };
302
+
303
+ CallLogModel.findOne.mockResolvedValue(null);
304
+
305
+ jwt.decodeJwt.mockReturnValue({
306
+ id: 'user-123',
307
+ platform: 'unknownCRM'
308
+ });
309
+
310
+ connectorRegistry.getConnector.mockReturnValue(null);
311
+
312
+ // Act
313
+ const result = await createCallLog.execute({
314
+ jwtToken: 'mock-jwt-token',
315
+ incomingData: mockIncomingData
316
+ });
317
+
318
+ // Assert
319
+ expect(result.success).toBe(false);
320
+ expect(result.error).toContain('Platform connector not found');
321
+ });
322
+
323
+ test('should return error when createCallLog is not implemented', async () => {
324
+ // Arrange
325
+ const mockIncomingData = {
326
+ logInfo: {
327
+ sessionId: 'session-123'
328
+ }
329
+ };
330
+
331
+ CallLogModel.findOne.mockResolvedValue(null);
332
+
333
+ jwt.decodeJwt.mockReturnValue({
334
+ id: 'user-123',
335
+ platform: 'testCRM'
336
+ });
337
+
338
+ const mockConnector = {}; // No createCallLog method
339
+ connectorRegistry.getConnector.mockReturnValue(mockConnector);
340
+
341
+ // Act
342
+ const result = await createCallLog.execute({
343
+ jwtToken: 'mock-jwt-token',
344
+ incomingData: mockIncomingData
345
+ });
346
+
347
+ // Assert
348
+ expect(result.success).toBe(false);
349
+ expect(result.error).toContain('not implemented');
350
+ });
351
+
352
+ test('should return error when creation fails', async () => {
353
+ // Arrange
354
+ const mockIncomingData = {
355
+ logInfo: {
356
+ id: 'rc-call-999',
357
+ sessionId: 'session-999',
358
+ direction: 'Inbound',
359
+ startTime: '2024-01-01T13:00:00Z',
360
+ duration: 45,
361
+ from: { phoneNumber: '+1234567890' },
362
+ to: { phoneNumber: '+0987654321' }
363
+ }
364
+ };
365
+
366
+ jwt.decodeJwt.mockReturnValue({
367
+ id: 'user-123',
368
+ platform: 'testCRM'
369
+ });
370
+
371
+ const mockConnector = {
372
+ createCallLog: jest.fn()
373
+ };
374
+ connectorRegistry.getConnector.mockReturnValue(mockConnector);
375
+
376
+ CallLogModel.findOne.mockResolvedValue(null);
377
+
378
+ // Mock contactCore.findContact to return a contact
379
+ contactCore.findContact.mockResolvedValue({
380
+ successful: true,
381
+ contact: [{ id: 'contact-999', isNewContact: false }]
382
+ });
383
+
384
+ logCore.createCallLog.mockResolvedValue({
385
+ successful: false,
386
+ returnMessage: { message: 'Failed to create log in CRM' }
387
+ });
388
+
389
+ // Act
390
+ const result = await createCallLog.execute({
391
+ jwtToken: 'mock-jwt-token',
392
+ incomingData: mockIncomingData
393
+ });
394
+
395
+ // Assert
396
+ expect(result.success).toBe(false);
397
+ expect(result.error).toBe('Failed to create log in CRM');
398
+ });
399
+
400
+ test('should handle unexpected errors gracefully', async () => {
401
+ // Arrange
402
+ const mockIncomingData = {
403
+ logInfo: {
404
+ sessionId: 'session-error'
405
+ }
406
+ };
407
+
408
+ CallLogModel.findOne.mockRejectedValue(
409
+ new Error('Database connection failed')
410
+ );
411
+
412
+ // Act
413
+ const result = await createCallLog.execute({
414
+ jwtToken: 'mock-jwt-token',
415
+ incomingData: mockIncomingData
416
+ });
417
+
418
+ // Assert
419
+ expect(result.success).toBe(false);
420
+ expect(result.error).toBe('Database connection failed');
421
+ expect(result.errorDetails).toBeDefined();
422
+ });
423
+ });
424
+ });
425
+