@app-connect/core 1.7.21 → 1.7.23
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/README.md +8 -1
- package/connector/developerPortal.js +4 -4
- package/docs/README.md +50 -0
- package/docs/architecture.md +93 -0
- package/docs/connectors.md +117 -0
- package/docs/handlers.md +125 -0
- package/docs/libraries.md +101 -0
- package/docs/models.md +144 -0
- package/docs/routes.md +115 -0
- package/docs/tests.md +73 -0
- package/handlers/admin.js +22 -2
- package/handlers/auth.js +57 -10
- package/handlers/log.js +217 -109
- package/handlers/managedAuth.js +446 -0
- package/handlers/plugin.js +183 -1
- package/handlers/user.js +1 -1
- package/index.js +410 -35
- package/lib/callLogComposer.js +36 -36
- package/lib/jwt.js +1 -1
- package/lib/util.js +0 -18
- package/mcp/tools/createCallLog.js +5 -1
- package/mcp/tools/createContact.js +5 -1
- package/mcp/tools/createMessageLog.js +5 -1
- package/mcp/tools/findContactByName.js +5 -1
- package/mcp/tools/findContactByPhone.js +6 -2
- package/mcp/tools/getCallLog.js +5 -1
- package/mcp/tools/rcGetCallLogs.js +6 -2
- package/mcp/tools/updateCallLog.js +5 -1
- package/mcp/ui/App/lib/developerPortal.ts +1 -1
- package/package.json +72 -72
- package/releaseNotes.json +16 -0
- package/test/handlers/admin.test.js +33 -0
- package/test/handlers/auth.test.js +402 -6
- package/test/handlers/log.test.js +60 -0
- package/test/handlers/managedAuth.test.js +458 -0
- package/test/handlers/plugin.test.js +93 -0
- package/test/index.test.js +105 -0
- package/test/lib/callLogComposer.test.js +21 -21
- package/test/lib/jwt.test.js +15 -0
- package/test/lib/util.test.js +1 -332
- package/test/mcp/tools/createCallLog.test.js +11 -0
- package/test/mcp/tools/createContact.test.js +58 -0
- package/test/mcp/tools/createMessageLog.test.js +15 -0
- package/test/mcp/tools/findContactByName.test.js +12 -0
- package/test/mcp/tools/findContactByPhone.test.js +12 -0
- package/test/mcp/tools/getCallLog.test.js +12 -0
- package/test/mcp/tools/rcGetCallLogs.test.js +56 -0
- package/test/mcp/tools/updateCallLog.test.js +14 -0
- package/test/routes/managedAuthRoutes.test.js +129 -0
- package/test/setup.js +2 -0
|
@@ -136,7 +136,7 @@ describe('callLogComposer', () => {
|
|
|
136
136
|
expect(result).toContain('Hello, this is a test transcript.');
|
|
137
137
|
});
|
|
138
138
|
|
|
139
|
-
test('should add
|
|
139
|
+
test('should add ACE data when provided', async () => {
|
|
140
140
|
const result = await composeCallLog({
|
|
141
141
|
...baseParams,
|
|
142
142
|
ringSenseTranscript: 'RS Transcript',
|
|
@@ -146,10 +146,10 @@ describe('callLogComposer', () => {
|
|
|
146
146
|
ringSenseLink: 'https://ringsense.example.com/123'
|
|
147
147
|
});
|
|
148
148
|
|
|
149
|
-
expect(result).toContain('
|
|
150
|
-
expect(result).toContain('
|
|
149
|
+
expect(result).toContain('ACE transcript');
|
|
150
|
+
expect(result).toContain('ACE summary');
|
|
151
151
|
expect(result).toContain('Call score: 85');
|
|
152
|
-
expect(result).toContain('
|
|
152
|
+
expect(result).toContain('ACE recording link');
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
test('should add call legs when provided', async () => {
|
|
@@ -1025,16 +1025,16 @@ describe('callLogComposer', () => {
|
|
|
1025
1025
|
});
|
|
1026
1026
|
});
|
|
1027
1027
|
|
|
1028
|
-
describe('
|
|
1028
|
+
describe('ACE Functions', () => {
|
|
1029
1029
|
describe('upsertRingSenseTranscript', () => {
|
|
1030
|
-
test('should add
|
|
1030
|
+
test('should add ACE transcript', () => {
|
|
1031
1031
|
const result = upsertRingSenseTranscript({
|
|
1032
1032
|
body: '',
|
|
1033
1033
|
transcript: 'RS transcript content',
|
|
1034
1034
|
logFormat: LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT
|
|
1035
1035
|
});
|
|
1036
1036
|
|
|
1037
|
-
expect(result).toContain('
|
|
1037
|
+
expect(result).toContain('ACE transcript');
|
|
1038
1038
|
expect(result).toContain('RS transcript content');
|
|
1039
1039
|
});
|
|
1040
1040
|
|
|
@@ -1045,7 +1045,7 @@ describe('callLogComposer', () => {
|
|
|
1045
1045
|
logFormat: LOG_DETAILS_FORMAT_TYPE.HTML
|
|
1046
1046
|
});
|
|
1047
1047
|
|
|
1048
|
-
expect(result).toContain('<b>
|
|
1048
|
+
expect(result).toContain('<b>ACE transcript</b>');
|
|
1049
1049
|
});
|
|
1050
1050
|
|
|
1051
1051
|
test('should add in Markdown format', () => {
|
|
@@ -1055,20 +1055,20 @@ describe('callLogComposer', () => {
|
|
|
1055
1055
|
logFormat: LOG_DETAILS_FORMAT_TYPE.MARKDOWN
|
|
1056
1056
|
});
|
|
1057
1057
|
|
|
1058
|
-
expect(result).toContain('###
|
|
1058
|
+
expect(result).toContain('### ACE transcript');
|
|
1059
1059
|
expect(result).toContain('RS transcript content');
|
|
1060
1060
|
});
|
|
1061
1061
|
});
|
|
1062
1062
|
|
|
1063
1063
|
describe('upsertRingSenseSummary', () => {
|
|
1064
|
-
test('should add
|
|
1064
|
+
test('should add ACE summary', () => {
|
|
1065
1065
|
const result = upsertRingSenseSummary({
|
|
1066
1066
|
body: '',
|
|
1067
1067
|
summary: 'RS summary content',
|
|
1068
1068
|
logFormat: LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT
|
|
1069
1069
|
});
|
|
1070
1070
|
|
|
1071
|
-
expect(result).toContain('
|
|
1071
|
+
expect(result).toContain('ACE summary');
|
|
1072
1072
|
expect(result).toContain('RS summary content');
|
|
1073
1073
|
});
|
|
1074
1074
|
|
|
@@ -1079,7 +1079,7 @@ describe('callLogComposer', () => {
|
|
|
1079
1079
|
logFormat: LOG_DETAILS_FORMAT_TYPE.HTML
|
|
1080
1080
|
});
|
|
1081
1081
|
|
|
1082
|
-
expect(result).toContain('<b>
|
|
1082
|
+
expect(result).toContain('<b>ACE summary</b>');
|
|
1083
1083
|
});
|
|
1084
1084
|
|
|
1085
1085
|
test('should add in Markdown format', () => {
|
|
@@ -1089,13 +1089,13 @@ describe('callLogComposer', () => {
|
|
|
1089
1089
|
logFormat: LOG_DETAILS_FORMAT_TYPE.MARKDOWN
|
|
1090
1090
|
});
|
|
1091
1091
|
|
|
1092
|
-
expect(result).toContain('###
|
|
1092
|
+
expect(result).toContain('### ACE summary');
|
|
1093
1093
|
expect(result).toContain('RS summary content');
|
|
1094
1094
|
});
|
|
1095
1095
|
});
|
|
1096
1096
|
|
|
1097
1097
|
describe('upsertRingSenseAIScore', () => {
|
|
1098
|
-
test('should add
|
|
1098
|
+
test('should add ACE AI score', () => {
|
|
1099
1099
|
const result = upsertRingSenseAIScore({
|
|
1100
1100
|
body: '',
|
|
1101
1101
|
score: '85',
|
|
@@ -1127,14 +1127,14 @@ describe('callLogComposer', () => {
|
|
|
1127
1127
|
});
|
|
1128
1128
|
|
|
1129
1129
|
describe('upsertRingSenseBulletedSummary', () => {
|
|
1130
|
-
test('should add
|
|
1130
|
+
test('should add ACE bulleted summary', () => {
|
|
1131
1131
|
const result = upsertRingSenseBulletedSummary({
|
|
1132
1132
|
body: '',
|
|
1133
1133
|
summary: '- Point 1\n- Point 2',
|
|
1134
1134
|
logFormat: LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT
|
|
1135
1135
|
});
|
|
1136
1136
|
|
|
1137
|
-
expect(result).toContain('
|
|
1137
|
+
expect(result).toContain('ACE bulleted summary');
|
|
1138
1138
|
expect(result).toContain('- Point 1');
|
|
1139
1139
|
});
|
|
1140
1140
|
|
|
@@ -1145,7 +1145,7 @@ describe('callLogComposer', () => {
|
|
|
1145
1145
|
logFormat: LOG_DETAILS_FORMAT_TYPE.HTML
|
|
1146
1146
|
});
|
|
1147
1147
|
|
|
1148
|
-
expect(result).toContain('<b>
|
|
1148
|
+
expect(result).toContain('<b>ACE bulleted summary</b>');
|
|
1149
1149
|
});
|
|
1150
1150
|
|
|
1151
1151
|
test('should add in Markdown format', () => {
|
|
@@ -1155,20 +1155,20 @@ describe('callLogComposer', () => {
|
|
|
1155
1155
|
logFormat: LOG_DETAILS_FORMAT_TYPE.MARKDOWN
|
|
1156
1156
|
});
|
|
1157
1157
|
|
|
1158
|
-
expect(result).toContain('###
|
|
1158
|
+
expect(result).toContain('### ACE bulleted summary');
|
|
1159
1159
|
expect(result).toContain('- Point 1');
|
|
1160
1160
|
});
|
|
1161
1161
|
});
|
|
1162
1162
|
|
|
1163
1163
|
describe('upsertRingSenseLink', () => {
|
|
1164
|
-
test('should add
|
|
1164
|
+
test('should add ACE recording link', () => {
|
|
1165
1165
|
const result = upsertRingSenseLink({
|
|
1166
1166
|
body: '',
|
|
1167
1167
|
link: 'https://ringsense.example.com/123',
|
|
1168
1168
|
logFormat: LOG_DETAILS_FORMAT_TYPE.PLAIN_TEXT
|
|
1169
1169
|
});
|
|
1170
1170
|
|
|
1171
|
-
expect(result).toContain('
|
|
1171
|
+
expect(result).toContain('ACE recording link: https://ringsense.example.com/123');
|
|
1172
1172
|
});
|
|
1173
1173
|
|
|
1174
1174
|
test('should add in HTML format with anchor', () => {
|
|
@@ -1188,7 +1188,7 @@ describe('callLogComposer', () => {
|
|
|
1188
1188
|
logFormat: LOG_DETAILS_FORMAT_TYPE.MARKDOWN
|
|
1189
1189
|
});
|
|
1190
1190
|
|
|
1191
|
-
expect(result).toContain('**
|
|
1191
|
+
expect(result).toContain('**ACE recording link**: https://ringsense.example.com/123');
|
|
1192
1192
|
});
|
|
1193
1193
|
});
|
|
1194
1194
|
});
|
package/test/lib/jwt.test.js
CHANGED
|
@@ -35,6 +35,21 @@ describe('JWT Utility', () => {
|
|
|
35
35
|
// Assert
|
|
36
36
|
expect(token1).not.toBe(token2);
|
|
37
37
|
});
|
|
38
|
+
|
|
39
|
+
test('should generate token with about 2 weeks lifetime', () => {
|
|
40
|
+
// Arrange
|
|
41
|
+
const payload = { id: 'user-ttl', platform: 'testCRM' };
|
|
42
|
+
|
|
43
|
+
// Act
|
|
44
|
+
const token = jwt.generateJwt(payload);
|
|
45
|
+
const decoded = jwt.decodeJwt(token);
|
|
46
|
+
const lifetimeSeconds = decoded.exp - decoded.iat;
|
|
47
|
+
|
|
48
|
+
// Assert
|
|
49
|
+
// Keep a tiny tolerance to avoid timing flakiness.
|
|
50
|
+
expect(lifetimeSeconds).toBeGreaterThanOrEqual((14 * 24 * 60 * 60) - 2);
|
|
51
|
+
expect(lifetimeSeconds).toBeLessThanOrEqual((14 * 24 * 60 * 60) + 2);
|
|
52
|
+
});
|
|
38
53
|
});
|
|
39
54
|
|
|
40
55
|
describe('decodeJwt', () => {
|
package/test/lib/util.test.js
CHANGED
|
@@ -8,8 +8,7 @@ const {
|
|
|
8
8
|
getHashValue,
|
|
9
9
|
secondsToHoursMinutesSeconds,
|
|
10
10
|
getMostRecentDate,
|
|
11
|
-
getMediaReaderLinkByPlatformMediaLink
|
|
12
|
-
getPluginsFromUserSettings
|
|
11
|
+
getMediaReaderLinkByPlatformMediaLink
|
|
13
12
|
} = require('../../lib/util');
|
|
14
13
|
|
|
15
14
|
describe('Utility Functions', () => {
|
|
@@ -326,335 +325,5 @@ describe('Utility Functions', () => {
|
|
|
326
325
|
});
|
|
327
326
|
});
|
|
328
327
|
|
|
329
|
-
describe('getPluginsFromUserSettings', () => {
|
|
330
|
-
test('should return empty array when userSettings is null', () => {
|
|
331
|
-
const result = getPluginsFromUserSettings({
|
|
332
|
-
userSettings: null,
|
|
333
|
-
logType: 'call'
|
|
334
|
-
});
|
|
335
|
-
|
|
336
|
-
expect(result).toEqual([]);
|
|
337
|
-
});
|
|
338
|
-
|
|
339
|
-
test('should return empty array when userSettings is undefined', () => {
|
|
340
|
-
const result = getPluginsFromUserSettings({
|
|
341
|
-
userSettings: undefined,
|
|
342
|
-
logType: 'call'
|
|
343
|
-
});
|
|
344
|
-
|
|
345
|
-
expect(result).toEqual([]);
|
|
346
|
-
});
|
|
347
|
-
|
|
348
|
-
test('should return empty array when userSettings is empty object', () => {
|
|
349
|
-
const result = getPluginsFromUserSettings({
|
|
350
|
-
userSettings: {},
|
|
351
|
-
logType: 'call'
|
|
352
|
-
});
|
|
353
|
-
|
|
354
|
-
expect(result).toEqual([]);
|
|
355
|
-
});
|
|
356
|
-
|
|
357
|
-
test('should return plugins matching logType', () => {
|
|
358
|
-
const userSettings = {
|
|
359
|
-
plugin_googleDrive: {
|
|
360
|
-
value: {
|
|
361
|
-
activated: true,
|
|
362
|
-
logTypes: ['call'],
|
|
363
|
-
name: 'Google Drive Upload',
|
|
364
|
-
isAsync: true
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
};
|
|
368
|
-
|
|
369
|
-
const result = getPluginsFromUserSettings({
|
|
370
|
-
userSettings,
|
|
371
|
-
logType: 'call'
|
|
372
|
-
});
|
|
373
|
-
|
|
374
|
-
expect(result).toHaveLength(1);
|
|
375
|
-
expect(result[0].id).toBe('googleDrive');
|
|
376
|
-
expect(result[0].value.name).toBe('Google Drive Upload');
|
|
377
|
-
});
|
|
378
|
-
|
|
379
|
-
test('should filter out non-plugin settings', () => {
|
|
380
|
-
const userSettings = {
|
|
381
|
-
plugin_piiRedaction: {
|
|
382
|
-
value: {
|
|
383
|
-
activated: true,
|
|
384
|
-
logTypes: ['call'],
|
|
385
|
-
name: 'PII Redaction'
|
|
386
|
-
}
|
|
387
|
-
},
|
|
388
|
-
theme: { value: 'dark' },
|
|
389
|
-
autoLog: { value: true },
|
|
390
|
-
notificationSound: { value: 'default' }
|
|
391
|
-
};
|
|
392
|
-
|
|
393
|
-
const result = getPluginsFromUserSettings({
|
|
394
|
-
userSettings,
|
|
395
|
-
logType: 'call'
|
|
396
|
-
});
|
|
397
|
-
|
|
398
|
-
expect(result).toHaveLength(1);
|
|
399
|
-
expect(result[0].id).toBe('piiRedaction');
|
|
400
|
-
});
|
|
401
|
-
|
|
402
|
-
test('should return all matching plugins regardless of logType mismatch', () => {
|
|
403
|
-
const userSettings = {
|
|
404
|
-
plugin_googleDrive: {
|
|
405
|
-
value: {
|
|
406
|
-
activated: false,
|
|
407
|
-
logTypes: ['message'],
|
|
408
|
-
name: 'Google Drive Upload'
|
|
409
|
-
}
|
|
410
|
-
},
|
|
411
|
-
plugin_piiRedaction: {
|
|
412
|
-
value: {
|
|
413
|
-
activated: true,
|
|
414
|
-
logTypes: ['call'],
|
|
415
|
-
name: 'PII Redaction'
|
|
416
|
-
}
|
|
417
|
-
}
|
|
418
|
-
};
|
|
419
|
-
|
|
420
|
-
const result = getPluginsFromUserSettings({
|
|
421
|
-
userSettings,
|
|
422
|
-
logType: 'call'
|
|
423
|
-
});
|
|
424
|
-
|
|
425
|
-
expect(result).toHaveLength(1);
|
|
426
|
-
expect(result[0].id).toBe('piiRedaction');
|
|
427
|
-
});
|
|
428
|
-
|
|
429
|
-
test('should correctly parse plugin ID from setting key', () => {
|
|
430
|
-
const userSettings = {
|
|
431
|
-
plugin_myCustomPlugin: {
|
|
432
|
-
value: {
|
|
433
|
-
activated: true,
|
|
434
|
-
logTypes: ['call'],
|
|
435
|
-
name: 'My Custom Plugin'
|
|
436
|
-
}
|
|
437
|
-
},
|
|
438
|
-
plugin_anotherOne: {
|
|
439
|
-
value: {
|
|
440
|
-
activated: true,
|
|
441
|
-
logTypes: ['call'],
|
|
442
|
-
name: 'Another One'
|
|
443
|
-
}
|
|
444
|
-
}
|
|
445
|
-
};
|
|
446
|
-
|
|
447
|
-
const result = getPluginsFromUserSettings({
|
|
448
|
-
userSettings,
|
|
449
|
-
logType: 'call'
|
|
450
|
-
});
|
|
451
|
-
|
|
452
|
-
expect(result).toHaveLength(2);
|
|
453
|
-
const ids = result.map(r => r.id);
|
|
454
|
-
expect(ids).toContain('myCustomPlugin');
|
|
455
|
-
expect(ids).toContain('anotherOne');
|
|
456
|
-
});
|
|
457
|
-
|
|
458
|
-
test('should return all plugins matching logType', () => {
|
|
459
|
-
const userSettings = {
|
|
460
|
-
plugin_piiRedaction: {
|
|
461
|
-
value: {
|
|
462
|
-
activated: true,
|
|
463
|
-
logTypes: ['call'],
|
|
464
|
-
name: 'PII Redaction'
|
|
465
|
-
}
|
|
466
|
-
},
|
|
467
|
-
plugin_googleDrive: {
|
|
468
|
-
value: {
|
|
469
|
-
activated: true,
|
|
470
|
-
logTypes: ['call'],
|
|
471
|
-
name: 'Google Drive Upload'
|
|
472
|
-
}
|
|
473
|
-
},
|
|
474
|
-
plugin_analytics: {
|
|
475
|
-
value: {
|
|
476
|
-
activated: true,
|
|
477
|
-
logTypes: ['call'],
|
|
478
|
-
name: 'Analytics'
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
|
-
};
|
|
482
|
-
|
|
483
|
-
const result = getPluginsFromUserSettings({
|
|
484
|
-
userSettings,
|
|
485
|
-
logType: 'call'
|
|
486
|
-
});
|
|
487
|
-
|
|
488
|
-
expect(result).toHaveLength(3);
|
|
489
|
-
const ids = result.map(r => r.id);
|
|
490
|
-
expect(ids).toContain('piiRedaction');
|
|
491
|
-
expect(ids).toContain('googleDrive');
|
|
492
|
-
expect(ids).toContain('analytics');
|
|
493
|
-
});
|
|
494
|
-
|
|
495
|
-
test('should filter by logType - call only', () => {
|
|
496
|
-
const userSettings = {
|
|
497
|
-
plugin_callOnly: {
|
|
498
|
-
value: {
|
|
499
|
-
activated: true,
|
|
500
|
-
logTypes: ['call'],
|
|
501
|
-
name: 'Call Only Plugin'
|
|
502
|
-
}
|
|
503
|
-
},
|
|
504
|
-
plugin_messageOnly: {
|
|
505
|
-
value: {
|
|
506
|
-
activated: true,
|
|
507
|
-
logTypes: ['message'],
|
|
508
|
-
name: 'Message Only Plugin'
|
|
509
|
-
}
|
|
510
|
-
}
|
|
511
|
-
};
|
|
512
|
-
|
|
513
|
-
const result = getPluginsFromUserSettings({
|
|
514
|
-
userSettings,
|
|
515
|
-
logType: 'call'
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
expect(result).toHaveLength(1);
|
|
519
|
-
expect(result[0].id).toBe('callOnly');
|
|
520
|
-
});
|
|
521
|
-
|
|
522
|
-
test('should filter by logType - message only', () => {
|
|
523
|
-
const userSettings = {
|
|
524
|
-
plugin_callOnly: {
|
|
525
|
-
value: {
|
|
526
|
-
activated: true,
|
|
527
|
-
logTypes: ['call'],
|
|
528
|
-
name: 'Call Only Plugin'
|
|
529
|
-
}
|
|
530
|
-
},
|
|
531
|
-
plugin_messageOnly: {
|
|
532
|
-
value: {
|
|
533
|
-
activated: true,
|
|
534
|
-
logTypes: ['message'],
|
|
535
|
-
name: 'Message Only Plugin'
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
};
|
|
539
|
-
|
|
540
|
-
const result = getPluginsFromUserSettings({
|
|
541
|
-
userSettings,
|
|
542
|
-
logType: 'message'
|
|
543
|
-
});
|
|
544
|
-
|
|
545
|
-
expect(result).toHaveLength(1);
|
|
546
|
-
expect(result[0].id).toBe('messageOnly');
|
|
547
|
-
});
|
|
548
|
-
|
|
549
|
-
test('should only return plugin when logType matches supportedLogType', () => {
|
|
550
|
-
const userSettings = {
|
|
551
|
-
plugin_callPlugin: {
|
|
552
|
-
value: {
|
|
553
|
-
activated: true,
|
|
554
|
-
logTypes: ['call'],
|
|
555
|
-
name: 'Call Plugin'
|
|
556
|
-
}
|
|
557
|
-
}
|
|
558
|
-
};
|
|
559
|
-
|
|
560
|
-
const callResult = getPluginsFromUserSettings({
|
|
561
|
-
userSettings,
|
|
562
|
-
logType: 'call'
|
|
563
|
-
});
|
|
564
|
-
expect(callResult).toHaveLength(1);
|
|
565
|
-
expect(callResult[0].id).toBe('callPlugin');
|
|
566
|
-
|
|
567
|
-
const messageResult = getPluginsFromUserSettings({
|
|
568
|
-
userSettings,
|
|
569
|
-
logType: 'message'
|
|
570
|
-
});
|
|
571
|
-
expect(messageResult).toHaveLength(0);
|
|
572
|
-
});
|
|
573
|
-
|
|
574
|
-
test('should return empty array when no plugins match logType', () => {
|
|
575
|
-
const userSettings = {
|
|
576
|
-
plugin_googleDrive: {
|
|
577
|
-
value: {
|
|
578
|
-
activated: true,
|
|
579
|
-
logTypes: ['call'],
|
|
580
|
-
name: 'Google Drive Upload'
|
|
581
|
-
}
|
|
582
|
-
}
|
|
583
|
-
};
|
|
584
|
-
|
|
585
|
-
const result = getPluginsFromUserSettings({
|
|
586
|
-
userSettings,
|
|
587
|
-
logType: 'message'
|
|
588
|
-
});
|
|
589
|
-
expect(result).toEqual([]);
|
|
590
|
-
});
|
|
591
|
-
|
|
592
|
-
test('should preserve full plugin value in result', () => {
|
|
593
|
-
const pluginValue = {
|
|
594
|
-
activated: true,
|
|
595
|
-
logTypes: ['call'],
|
|
596
|
-
name: 'Google Drive Upload',
|
|
597
|
-
isAsync: true,
|
|
598
|
-
customOption: 'someValue'
|
|
599
|
-
};
|
|
600
|
-
|
|
601
|
-
const userSettings = {
|
|
602
|
-
plugin_googleDrive: {
|
|
603
|
-
value: pluginValue
|
|
604
|
-
}
|
|
605
|
-
};
|
|
606
|
-
|
|
607
|
-
const result = getPluginsFromUserSettings({
|
|
608
|
-
userSettings,
|
|
609
|
-
logType: 'call'
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
expect(result).toHaveLength(1);
|
|
613
|
-
expect(result[0].value).toEqual(pluginValue);
|
|
614
|
-
});
|
|
615
|
-
|
|
616
|
-
test('should correctly parse plugin ID containing underscores', () => {
|
|
617
|
-
// Bug test: plugin IDs with underscores should be fully preserved
|
|
618
|
-
const userSettings = {
|
|
619
|
-
plugin_my_custom_plugin: {
|
|
620
|
-
value: {
|
|
621
|
-
activated: true,
|
|
622
|
-
logTypes: ['call'],
|
|
623
|
-
name: 'My Custom Plugin'
|
|
624
|
-
}
|
|
625
|
-
}
|
|
626
|
-
};
|
|
627
|
-
|
|
628
|
-
const result = getPluginsFromUserSettings({
|
|
629
|
-
userSettings,
|
|
630
|
-
logType: 'call'
|
|
631
|
-
});
|
|
632
|
-
|
|
633
|
-
expect(result).toHaveLength(1);
|
|
634
|
-
// The full ID should be 'my_custom_plugin', not just 'my'
|
|
635
|
-
expect(result[0].id).toBe('my_custom_plugin');
|
|
636
|
-
});
|
|
637
|
-
|
|
638
|
-
test('should handle plugin settings with missing value property gracefully', () => {
|
|
639
|
-
const userSettings = {
|
|
640
|
-
plugin_broken: null,
|
|
641
|
-
plugin_working: {
|
|
642
|
-
value: {
|
|
643
|
-
activated: true,
|
|
644
|
-
logTypes: ['call'],
|
|
645
|
-
name: 'Working Plugin'
|
|
646
|
-
}
|
|
647
|
-
}
|
|
648
|
-
};
|
|
649
|
-
|
|
650
|
-
// This should not throw, even with malformed settings
|
|
651
|
-
expect(() => {
|
|
652
|
-
getPluginsFromUserSettings({
|
|
653
|
-
userSettings,
|
|
654
|
-
logType: 'call'
|
|
655
|
-
});
|
|
656
|
-
}).toThrow(); // Currently throws - documenting existing behavior
|
|
657
|
-
});
|
|
658
|
-
});
|
|
659
328
|
});
|
|
660
329
|
|
|
@@ -292,6 +292,17 @@ describe('MCP Tool: createCallLog', () => {
|
|
|
292
292
|
expect(result.error).toContain('Invalid JWT token');
|
|
293
293
|
});
|
|
294
294
|
|
|
295
|
+
test('should return error when decodeJwt returns null', async () => {
|
|
296
|
+
jwt.decodeJwt.mockReturnValue(null);
|
|
297
|
+
const result = await createCallLog.execute({
|
|
298
|
+
jwtToken: 'invalid-token',
|
|
299
|
+
incomingData: { logInfo: { sessionId: 'session-123' } }
|
|
300
|
+
});
|
|
301
|
+
|
|
302
|
+
expect(result.success).toBe(false);
|
|
303
|
+
expect(result.error).toContain('Invalid JWT token');
|
|
304
|
+
});
|
|
305
|
+
|
|
295
306
|
test('should return error when platform connector not found', async () => {
|
|
296
307
|
// Arrange
|
|
297
308
|
const mockIncomingData = {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
const createContact = require('../../../mcp/tools/createContact');
|
|
2
|
+
const jwt = require('../../../lib/jwt');
|
|
3
|
+
const connectorRegistry = require('../../../connector/registry');
|
|
4
|
+
const contactCore = require('../../../handlers/contact');
|
|
5
|
+
|
|
6
|
+
jest.mock('../../../lib/jwt');
|
|
7
|
+
jest.mock('../../../connector/registry');
|
|
8
|
+
jest.mock('../../../handlers/contact');
|
|
9
|
+
|
|
10
|
+
describe('MCP Tool: createContact', () => {
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
jest.clearAllMocks();
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test('should have correct tool definition', () => {
|
|
16
|
+
expect(createContact.definition).toBeDefined();
|
|
17
|
+
expect(createContact.definition.name).toBe('createContact');
|
|
18
|
+
expect(createContact.definition.inputSchema.required).toContain('phoneNumber');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('should create contact successfully', async () => {
|
|
22
|
+
jwt.decodeJwt.mockReturnValue({ id: 'user-123', platform: 'testCRM' });
|
|
23
|
+
connectorRegistry.getConnector.mockReturnValue({ createContact: jest.fn() });
|
|
24
|
+
contactCore.createContact.mockResolvedValue({
|
|
25
|
+
successful: true,
|
|
26
|
+
returnMessage: { message: 'Created' },
|
|
27
|
+
contact: { id: 'contact-1' }
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
const result = await createContact.execute({
|
|
31
|
+
jwtToken: 'mock-jwt-token',
|
|
32
|
+
phoneNumber: '+14155551234',
|
|
33
|
+
newContactName: 'John Doe'
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
expect(result).toEqual({
|
|
37
|
+
success: true,
|
|
38
|
+
data: {
|
|
39
|
+
contact: { id: 'contact-1' },
|
|
40
|
+
message: 'Created'
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('should return error when decodeJwt returns null', async () => {
|
|
46
|
+
jwt.decodeJwt.mockReturnValue(null);
|
|
47
|
+
|
|
48
|
+
const result = await createContact.execute({
|
|
49
|
+
jwtToken: 'invalid-jwt',
|
|
50
|
+
phoneNumber: '+14155551234',
|
|
51
|
+
newContactName: 'John Doe'
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
expect(result.success).toBe(false);
|
|
55
|
+
expect(result.error).toContain('Invalid JWT token');
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
|
|
@@ -427,6 +427,21 @@ describe('MCP Tool: createMessageLog', () => {
|
|
|
427
427
|
expect(result.error).toContain('Invalid JWT token');
|
|
428
428
|
});
|
|
429
429
|
|
|
430
|
+
test('should return error when decodeJwt returns null', async () => {
|
|
431
|
+
jwt.decodeJwt.mockReturnValue(null);
|
|
432
|
+
|
|
433
|
+
const result = await createMessageLog.execute({
|
|
434
|
+
jwtToken: 'invalid-token',
|
|
435
|
+
incomingData: {
|
|
436
|
+
sessionId: 'session-123',
|
|
437
|
+
messageInfo: { from: { phoneNumber: '+1234567890' }, to: [{ phoneNumber: '+1098765432' }] }
|
|
438
|
+
}
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
expect(result.success).toBe(false);
|
|
442
|
+
expect(result.error).toContain('Invalid JWT token');
|
|
443
|
+
});
|
|
444
|
+
|
|
430
445
|
test('should return error when platform connector not found', async () => {
|
|
431
446
|
// Arrange
|
|
432
447
|
const mockIncomingData = {
|
|
@@ -162,6 +162,18 @@ describe('MCP Tool: findContactByName', () => {
|
|
|
162
162
|
expect(result.error).toContain('Invalid JWT token');
|
|
163
163
|
});
|
|
164
164
|
|
|
165
|
+
test('should return error when decodeJwt returns null', async () => {
|
|
166
|
+
jwt.decodeJwt.mockReturnValue(null);
|
|
167
|
+
|
|
168
|
+
const result = await findContactByName.execute({
|
|
169
|
+
jwtToken: 'invalid-token',
|
|
170
|
+
name: 'John Doe'
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(result.success).toBe(false);
|
|
174
|
+
expect(result.error).toContain('Invalid JWT token');
|
|
175
|
+
});
|
|
176
|
+
|
|
165
177
|
test('should return error when platform connector not found', async () => {
|
|
166
178
|
// Arrange
|
|
167
179
|
jwt.decodeJwt.mockReturnValue({
|
|
@@ -211,6 +211,18 @@ describe('MCP Tool: findContactByPhone', () => {
|
|
|
211
211
|
expect(result.error).toContain('Invalid JWT token');
|
|
212
212
|
});
|
|
213
213
|
|
|
214
|
+
test('should return error when decodeJwt returns null', async () => {
|
|
215
|
+
jwt.decodeJwt.mockReturnValue(null);
|
|
216
|
+
|
|
217
|
+
const result = await findContactByPhone.execute({
|
|
218
|
+
jwtToken: 'invalid-token',
|
|
219
|
+
phoneNumber: '+1234567890'
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
expect(result.success).toBe(false);
|
|
223
|
+
expect(result.error).toContain('Invalid JWT token');
|
|
224
|
+
});
|
|
225
|
+
|
|
214
226
|
test('should return error when platform connector not found', async () => {
|
|
215
227
|
// Arrange
|
|
216
228
|
jwt.decodeJwt.mockReturnValue({
|
|
@@ -213,6 +213,18 @@ describe('MCP Tool: getCallLog', () => {
|
|
|
213
213
|
expect(result.error).toContain('Invalid JWT token');
|
|
214
214
|
});
|
|
215
215
|
|
|
216
|
+
test('should return error when decodeJwt returns null', async () => {
|
|
217
|
+
jwt.decodeJwt.mockReturnValue(null);
|
|
218
|
+
|
|
219
|
+
const result = await getCallLog.execute({
|
|
220
|
+
jwtToken: 'invalid-token',
|
|
221
|
+
sessionIds: ['session-123']
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
expect(result.success).toBe(false);
|
|
225
|
+
expect(result.error).toContain('Invalid JWT token');
|
|
226
|
+
});
|
|
227
|
+
|
|
216
228
|
test('should return error when platform connector not found', async () => {
|
|
217
229
|
// Arrange
|
|
218
230
|
jwt.decodeJwt.mockReturnValue({
|