@webex/internal-plugin-metrics 3.8.1 → 3.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js +92 -14
  2. package/dist/call-diagnostic/call-diagnostic-metrics-latencies.js.map +1 -1
  3. package/dist/call-diagnostic/call-diagnostic-metrics.js +348 -48
  4. package/dist/call-diagnostic/call-diagnostic-metrics.js.map +1 -1
  5. package/dist/call-diagnostic/call-diagnostic-metrics.util.js +21 -0
  6. package/dist/call-diagnostic/call-diagnostic-metrics.util.js.map +1 -1
  7. package/dist/call-diagnostic/config.js +3 -1
  8. package/dist/call-diagnostic/config.js.map +1 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/metrics.js +1 -1
  11. package/dist/metrics.types.js.map +1 -1
  12. package/dist/new-metrics.js +43 -1
  13. package/dist/new-metrics.js.map +1 -1
  14. package/dist/types/call-diagnostic/call-diagnostic-metrics-latencies.d.ts +23 -1
  15. package/dist/types/call-diagnostic/call-diagnostic-metrics.d.ts +176 -10
  16. package/dist/types/call-diagnostic/config.d.ts +2 -0
  17. package/dist/types/index.d.ts +2 -2
  18. package/dist/types/metrics.types.d.ts +18 -7
  19. package/dist/types/new-metrics.d.ts +19 -2
  20. package/package.json +11 -12
  21. package/src/call-diagnostic/call-diagnostic-metrics-latencies.ts +104 -14
  22. package/src/call-diagnostic/call-diagnostic-metrics.ts +363 -25
  23. package/src/call-diagnostic/call-diagnostic-metrics.util.ts +20 -0
  24. package/src/call-diagnostic/config.ts +3 -0
  25. package/src/index.ts +2 -0
  26. package/src/metrics.types.ts +25 -6
  27. package/src/new-metrics.ts +52 -1
  28. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-batcher.ts +20 -1
  29. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics-latencies.ts +255 -0
  30. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.ts +829 -39
  31. package/test/unit/spec/call-diagnostic/call-diagnostic-metrics.util.ts +6 -0
  32. package/test/unit/spec/new-metrics.ts +67 -2
  33. package/test/unit/spec/prelogin-metrics-batcher.ts +72 -3
  34. package/dist/call-diagnostic-events-batcher.js +0 -60
  35. package/dist/call-diagnostic-events-batcher.js.map +0 -1
@@ -13,9 +13,12 @@ import {
13
13
  } from '@webex/internal-plugin-metrics';
14
14
  import uuid from 'uuid';
15
15
  import {omit} from 'lodash';
16
- import { glob } from 'glob';
17
- import { expect } from 'chai';
18
- import { ClientEmailInput, ClientUserNameInput } from '@webex/internal-plugin-metrics/src/metrics.types';
16
+ import {glob} from 'glob';
17
+ import {expect} from 'chai';
18
+ import {
19
+ ClientEmailInput,
20
+ ClientUserNameInput,
21
+ } from '@webex/internal-plugin-metrics/src/metrics.types';
19
22
 
20
23
  //@ts-ignore
21
24
  global.window = {location: {hostname: 'whatever'}};
@@ -65,14 +68,14 @@ describe('internal-plugin-metrics', () => {
65
68
  ...fakeMeeting,
66
69
  id: '4',
67
70
  isoLocalClientMeetingJoinTime: 'testTimeString',
68
- }
71
+ };
69
72
  const fakeMeeting5 = {
70
73
  ...fakeMeeting,
71
74
  id: '5',
72
75
  correlationId: 'correlationId5',
73
76
  sessionCorrelationId: 'sessionCorrelationId5',
74
77
  userNameInput: 'test',
75
- emailInput: 'test@test.com'
78
+ emailInput: 'test@test.com',
76
79
  };
77
80
 
78
81
  const fakeMeetings = {
@@ -182,7 +185,7 @@ describe('internal-plugin-metrics', () => {
182
185
  publicNetworkPrefix: '1.1.1.1',
183
186
  localNetworkPrefix: '1.1.1.1',
184
187
  os: getOSNameInternal(),
185
- osVersion: getOSVersion(),
188
+ osVersion: getOSVersion() || 'unknown',
186
189
  subClientType: 'WEB_APP',
187
190
  },
188
191
  environment: 'meeting_evn',
@@ -215,7 +218,7 @@ describe('internal-plugin-metrics', () => {
215
218
  publicNetworkPrefix: '1.1.1.1',
216
219
  localNetworkPrefix: '1.1.1.1',
217
220
  os: getOSNameInternal(),
218
- osVersion: getOSVersion(),
221
+ osVersion: getOSVersion() || 'unknown',
219
222
  subClientType: 'WEB_APP',
220
223
  clientLaunchMethod: 'url-handler',
221
224
  },
@@ -250,7 +253,7 @@ describe('internal-plugin-metrics', () => {
250
253
  publicNetworkPrefix: '1.1.1.1',
251
254
  localNetworkPrefix: '1.1.1.1',
252
255
  os: getOSNameInternal(),
253
- osVersion: getOSVersion(),
256
+ osVersion: getOSVersion() || 'unknown',
254
257
  subClientType: 'WEB_APP',
255
258
  clientLaunchMethod: 'url-handler',
256
259
  },
@@ -285,7 +288,7 @@ describe('internal-plugin-metrics', () => {
285
288
  publicNetworkPrefix: '1.1.1.1',
286
289
  localNetworkPrefix: '1.1.1.1',
287
290
  os: getOSNameInternal(),
288
- osVersion: getOSVersion(),
291
+ osVersion: getOSVersion() || 'unknown',
289
292
  subClientType: 'WEB_APP',
290
293
  clientLaunchMethod: 'url-handler',
291
294
  browserLaunchMethod: 'thinclient',
@@ -313,7 +316,7 @@ describe('internal-plugin-metrics', () => {
313
316
  publicNetworkPrefix: '1.1.1.1',
314
317
  localNetworkPrefix: '1.1.1.1',
315
318
  os: getOSNameInternal(),
316
- osVersion: getOSVersion(),
319
+ osVersion: getOSVersion() || 'unknown',
317
320
  subClientType: 'WEB_APP',
318
321
  },
319
322
  name: 'endpoint',
@@ -342,7 +345,7 @@ describe('internal-plugin-metrics', () => {
342
345
  majorVersion: 43,
343
346
  minorVersion: 9,
344
347
  os: getOSNameInternal(),
345
- osVersion: getOSVersion(),
348
+ osVersion: getOSVersion() || 'unknown',
346
349
  subClientType: 'WEB_APP',
347
350
  },
348
351
  environment: 'meeting_evn',
@@ -365,7 +368,7 @@ describe('internal-plugin-metrics', () => {
365
368
  publicNetworkPrefix: '1.3.4.0',
366
369
  localNetworkPrefix: undefined,
367
370
  os: getOSNameInternal(),
368
- osVersion: getOSVersion(),
371
+ osVersion: getOSVersion() || 'unknown',
369
372
  subClientType: 'WEB_APP',
370
373
  },
371
374
  name: 'endpoint',
@@ -823,6 +826,7 @@ describe('internal-plugin-metrics', () => {
823
826
  meetingId: fakeMeeting.id,
824
827
  mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
825
828
  };
829
+ cd.setMercuryConnectedStatus(true);
826
830
 
827
831
  cd.submitClientEvent({
828
832
  name: 'client.alert.displayed',
@@ -844,6 +848,7 @@ describe('internal-plugin-metrics', () => {
844
848
  canProceed: true,
845
849
  eventData: {
846
850
  webClientDomain: 'whatever',
851
+ isMercuryConnected: true,
847
852
  },
848
853
  identifiers: {
849
854
  correlationId: 'correlationId',
@@ -870,6 +875,7 @@ describe('internal-plugin-metrics', () => {
870
875
  canProceed: true,
871
876
  eventData: {
872
877
  webClientDomain: 'whatever',
878
+ isMercuryConnected: true,
873
879
  },
874
880
  identifiers: {
875
881
  correlationId: 'correlationId',
@@ -907,6 +913,146 @@ describe('internal-plugin-metrics', () => {
907
913
  canProceed: true,
908
914
  eventData: {
909
915
  webClientDomain: 'whatever',
916
+ isMercuryConnected: true,
917
+ },
918
+ identifiers: {
919
+ correlationId: 'correlationId',
920
+ deviceId: 'deviceUrl',
921
+ locusId: 'url',
922
+ locusStartTime: 'lastActive',
923
+ locusUrl: 'locus/url',
924
+ mediaAgentAlias: 'alias',
925
+ mediaAgentGroupId: '1',
926
+ orgId: 'orgId',
927
+ userId: 'userId',
928
+ },
929
+ loginType: 'login-ci',
930
+ name: 'client.alert.displayed',
931
+ userType: 'host',
932
+ isConvergedArchitectureEnabled: undefined,
933
+ webexSubServiceType: undefined,
934
+ webClientPreload: undefined,
935
+ },
936
+ eventId: 'my-fake-id',
937
+ origin: {
938
+ origin: 'fake-origin',
939
+ },
940
+ originTime: {
941
+ sent: 'not_defined_yet',
942
+ triggered: now.toISOString(),
943
+ },
944
+ senderCountryCode: 'UK',
945
+ version: 1,
946
+ },
947
+ });
948
+
949
+ const webexLoggerLogCalls = webex.logger.log.getCalls();
950
+ assert.deepEqual(webexLoggerLogCalls[1].args, [
951
+ 'call-diagnostic-events -> ',
952
+ 'CallDiagnosticMetrics: @submitClientEvent. Submit Client Event CA event.',
953
+ `name: client.alert.displayed`,
954
+ ]);
955
+ });
956
+
957
+ it('should submit client event correctly when mercury is not connected', () => {
958
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
959
+ const submitToCallDiagnosticsSpy = sinon.spy(cd, 'submitToCallDiagnostics');
960
+ const generateClientEventErrorPayloadSpy = sinon.spy(cd, 'generateClientEventErrorPayload');
961
+ const getIdentifiersSpy = sinon.spy(cd, 'getIdentifiers');
962
+ const getSubServiceTypeSpy = sinon.spy(cd, 'getSubServiceType');
963
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
964
+ const validatorSpy = sinon.spy(cd, 'validator');
965
+ const options = {
966
+ meetingId: fakeMeeting.id,
967
+ mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
968
+ };
969
+ cd.setMercuryConnectedStatus(false);
970
+ cd.submitClientEvent({
971
+ name: 'client.alert.displayed',
972
+ options,
973
+ });
974
+
975
+ assert.called(getIdentifiersSpy);
976
+ assert.calledWith(getIdentifiersSpy, {
977
+ meeting: fakeMeeting,
978
+ mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
979
+ webexConferenceIdStr: undefined,
980
+ sessionCorrelationId: undefined,
981
+ globalMeetingId: undefined,
982
+ });
983
+ assert.notCalled(generateClientEventErrorPayloadSpy);
984
+ assert.calledWith(
985
+ prepareDiagnosticEventSpy,
986
+ {
987
+ canProceed: true,
988
+ eventData: {
989
+ webClientDomain: 'whatever',
990
+ isMercuryConnected: false,
991
+ },
992
+ identifiers: {
993
+ correlationId: 'correlationId',
994
+ deviceId: 'deviceUrl',
995
+ locusId: 'url',
996
+ locusStartTime: 'lastActive',
997
+ locusUrl: 'locus/url',
998
+ mediaAgentAlias: 'alias',
999
+ mediaAgentGroupId: '1',
1000
+ orgId: 'orgId',
1001
+ userId: 'userId',
1002
+ },
1003
+ loginType: 'login-ci',
1004
+ name: 'client.alert.displayed',
1005
+ userType: 'host',
1006
+ isConvergedArchitectureEnabled: undefined,
1007
+ webexSubServiceType: undefined,
1008
+ webClientPreload: undefined,
1009
+ },
1010
+ options
1011
+ );
1012
+ assert.calledWith(submitToCallDiagnosticsSpy, {
1013
+ event: {
1014
+ canProceed: true,
1015
+ eventData: {
1016
+ webClientDomain: 'whatever',
1017
+ isMercuryConnected: false,
1018
+ },
1019
+ identifiers: {
1020
+ correlationId: 'correlationId',
1021
+ deviceId: 'deviceUrl',
1022
+ locusId: 'url',
1023
+ locusStartTime: 'lastActive',
1024
+ locusUrl: 'locus/url',
1025
+ mediaAgentAlias: 'alias',
1026
+ mediaAgentGroupId: '1',
1027
+ orgId: 'orgId',
1028
+ userId: 'userId',
1029
+ },
1030
+ loginType: 'login-ci',
1031
+ name: 'client.alert.displayed',
1032
+ userType: 'host',
1033
+ isConvergedArchitectureEnabled: undefined,
1034
+ webexSubServiceType: undefined,
1035
+ webClientPreload: undefined,
1036
+ },
1037
+ eventId: 'my-fake-id',
1038
+ origin: {
1039
+ origin: 'fake-origin',
1040
+ },
1041
+ originTime: {
1042
+ sent: 'not_defined_yet',
1043
+ triggered: now.toISOString(),
1044
+ },
1045
+ senderCountryCode: 'UK',
1046
+ version: 1,
1047
+ });
1048
+ assert.calledWith(validatorSpy, {
1049
+ type: 'ce',
1050
+ event: {
1051
+ event: {
1052
+ canProceed: true,
1053
+ eventData: {
1054
+ webClientDomain: 'whatever',
1055
+ isMercuryConnected: false,
910
1056
  },
911
1057
  identifiers: {
912
1058
  correlationId: 'correlationId',
@@ -959,7 +1105,7 @@ describe('internal-plugin-metrics', () => {
959
1105
  meetingId: fakeMeeting3.id,
960
1106
  mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
961
1107
  };
962
-
1108
+ cd.setMercuryConnectedStatus(true);
963
1109
  cd.submitClientEvent({
964
1110
  name: 'client.alert.displayed',
965
1111
  options,
@@ -980,6 +1126,7 @@ describe('internal-plugin-metrics', () => {
980
1126
  canProceed: true,
981
1127
  eventData: {
982
1128
  webClientDomain: 'whatever',
1129
+ isMercuryConnected: true,
983
1130
  },
984
1131
  identifiers: {
985
1132
  correlationId: 'correlationId3',
@@ -1007,6 +1154,7 @@ describe('internal-plugin-metrics', () => {
1007
1154
  canProceed: true,
1008
1155
  eventData: {
1009
1156
  webClientDomain: 'whatever',
1157
+ isMercuryConnected: true,
1010
1158
  },
1011
1159
  identifiers: {
1012
1160
  correlationId: 'correlationId3',
@@ -1045,6 +1193,7 @@ describe('internal-plugin-metrics', () => {
1045
1193
  canProceed: true,
1046
1194
  eventData: {
1047
1195
  webClientDomain: 'whatever',
1196
+ isMercuryConnected: true,
1048
1197
  },
1049
1198
  identifiers: {
1050
1199
  correlationId: 'correlationId3',
@@ -1097,7 +1246,7 @@ describe('internal-plugin-metrics', () => {
1097
1246
  meetingId: fakeMeeting4.id,
1098
1247
  mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
1099
1248
  };
1100
-
1249
+ cd.setMercuryConnectedStatus(true);
1101
1250
  cd.submitClientEvent({
1102
1251
  name: 'client.alert.displayed',
1103
1252
  options,
@@ -1119,6 +1268,7 @@ describe('internal-plugin-metrics', () => {
1119
1268
  canProceed: true,
1120
1269
  eventData: {
1121
1270
  webClientDomain: 'whatever',
1271
+ isMercuryConnected: true,
1122
1272
  },
1123
1273
  identifiers: {
1124
1274
  correlationId: 'correlationId',
@@ -1146,6 +1296,7 @@ describe('internal-plugin-metrics', () => {
1146
1296
  canProceed: true,
1147
1297
  eventData: {
1148
1298
  webClientDomain: 'whatever',
1299
+ isMercuryConnected: true,
1149
1300
  },
1150
1301
  identifiers: {
1151
1302
  correlationId: 'correlationId',
@@ -1184,6 +1335,7 @@ describe('internal-plugin-metrics', () => {
1184
1335
  canProceed: true,
1185
1336
  eventData: {
1186
1337
  webClientDomain: 'whatever',
1338
+ isMercuryConnected: true,
1187
1339
  },
1188
1340
  identifiers: {
1189
1341
  correlationId: 'correlationId',
@@ -1235,7 +1387,7 @@ describe('internal-plugin-metrics', () => {
1235
1387
  meetingId: fakeMeeting5.id,
1236
1388
  mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
1237
1389
  };
1238
-
1390
+ cd.setMercuryConnectedStatus(true);
1239
1391
  cd.submitClientEvent({
1240
1392
  name: 'client.alert.displayed',
1241
1393
  options,
@@ -1256,6 +1408,7 @@ describe('internal-plugin-metrics', () => {
1256
1408
  canProceed: true,
1257
1409
  eventData: {
1258
1410
  webClientDomain: 'whatever',
1411
+ isMercuryConnected: true,
1259
1412
  },
1260
1413
  identifiers: {
1261
1414
  correlationId: 'correlationId5',
@@ -1285,6 +1438,7 @@ describe('internal-plugin-metrics', () => {
1285
1438
  canProceed: true,
1286
1439
  eventData: {
1287
1440
  webClientDomain: 'whatever',
1441
+ isMercuryConnected: true,
1288
1442
  },
1289
1443
  identifiers: {
1290
1444
  correlationId: 'correlationId5',
@@ -1325,6 +1479,7 @@ describe('internal-plugin-metrics', () => {
1325
1479
  canProceed: true,
1326
1480
  eventData: {
1327
1481
  webClientDomain: 'whatever',
1482
+ isMercuryConnected: true,
1328
1483
  },
1329
1484
  identifiers: {
1330
1485
  correlationId: 'correlationId5',
@@ -1436,7 +1591,7 @@ describe('internal-plugin-metrics', () => {
1436
1591
  globalMeetingId: 'globalMeetingId1',
1437
1592
  sessionCorrelationId: 'sessionCorrelationId1',
1438
1593
  };
1439
-
1594
+ cd.setMercuryConnectedStatus(true);
1440
1595
  cd.submitClientEvent({
1441
1596
  name: 'client.alert.displayed',
1442
1597
  options,
@@ -1457,6 +1612,7 @@ describe('internal-plugin-metrics', () => {
1457
1612
  canProceed: true,
1458
1613
  eventData: {
1459
1614
  webClientDomain: 'whatever',
1615
+ isMercuryConnected: true,
1460
1616
  },
1461
1617
  identifiers: {
1462
1618
  correlationId: 'correlationId',
@@ -1470,7 +1626,7 @@ describe('internal-plugin-metrics', () => {
1470
1626
  },
1471
1627
  loginType: 'login-ci',
1472
1628
  name: 'client.alert.displayed',
1473
- webClientPreload: undefined
1629
+ webClientPreload: undefined,
1474
1630
  },
1475
1631
  options
1476
1632
  );
@@ -1479,6 +1635,7 @@ describe('internal-plugin-metrics', () => {
1479
1635
  canProceed: true,
1480
1636
  eventData: {
1481
1637
  webClientDomain: 'whatever',
1638
+ isMercuryConnected: true,
1482
1639
  },
1483
1640
  identifiers: {
1484
1641
  correlationId: 'correlationId',
@@ -1492,7 +1649,7 @@ describe('internal-plugin-metrics', () => {
1492
1649
  },
1493
1650
  loginType: 'login-ci',
1494
1651
  name: 'client.alert.displayed',
1495
- webClientPreload: undefined
1652
+ webClientPreload: undefined,
1496
1653
  },
1497
1654
  eventId: 'my-fake-id',
1498
1655
  origin: {
@@ -1524,7 +1681,7 @@ describe('internal-plugin-metrics', () => {
1524
1681
  const generateClientEventErrorPayloadSpy = sinon.spy(cd, 'generateClientEventErrorPayload');
1525
1682
  const getIdentifiersSpy = sinon.spy(cd, 'getIdentifiers');
1526
1683
  sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
1527
-
1684
+ cd.setMercuryConnectedStatus(true);
1528
1685
  const options = {
1529
1686
  correlationId: 'correlationId',
1530
1687
  webexConferenceIdStr: 'webexConferenceIdStr1',
@@ -1553,6 +1710,7 @@ describe('internal-plugin-metrics', () => {
1553
1710
  canProceed: true,
1554
1711
  eventData: {
1555
1712
  webClientDomain: 'whatever',
1713
+ isMercuryConnected: true,
1556
1714
  },
1557
1715
  identifiers: {
1558
1716
  correlationId: 'correlationId',
@@ -1592,7 +1750,7 @@ describe('internal-plugin-metrics', () => {
1592
1750
  webexConferenceIdStr: 'webexConferenceIdStr1',
1593
1751
  globalMeetingId: 'globalMeetingId1',
1594
1752
  },
1595
- eventData: {webClientDomain: 'whatever'},
1753
+ eventData: {webClientDomain: 'whatever', isMercuryConnected: true},
1596
1754
  loginType: 'login-ci',
1597
1755
  webClientPreload: undefined,
1598
1756
  },
@@ -1610,7 +1768,7 @@ describe('internal-plugin-metrics', () => {
1610
1768
  const generateClientEventErrorPayloadSpy = sinon.spy(cd, 'generateClientEventErrorPayload');
1611
1769
  const getIdentifiersSpy = sinon.spy(cd, 'getIdentifiers');
1612
1770
  sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
1613
-
1771
+ cd.setMercuryConnectedStatus(true);
1614
1772
  const options = {
1615
1773
  correlationId: 'correlationId',
1616
1774
  webexConferenceIdStr: 'webexConferenceIdStr1',
@@ -1641,6 +1799,7 @@ describe('internal-plugin-metrics', () => {
1641
1799
  canProceed: true,
1642
1800
  eventData: {
1643
1801
  webClientDomain: 'whatever',
1802
+ isMercuryConnected: true,
1644
1803
  },
1645
1804
  identifiers: {
1646
1805
  correlationId: 'correlationId',
@@ -1682,7 +1841,7 @@ describe('internal-plugin-metrics', () => {
1682
1841
  webexConferenceIdStr: 'webexConferenceIdStr1',
1683
1842
  globalMeetingId: 'globalMeetingId1',
1684
1843
  },
1685
- eventData: {webClientDomain: 'whatever'},
1844
+ eventData: {webClientDomain: 'whatever', isMercuryConnected: true},
1686
1845
  loginType: 'login-ci',
1687
1846
  userNameInput: 'current',
1688
1847
  emailInput: 'current',
@@ -1700,7 +1859,7 @@ describe('internal-plugin-metrics', () => {
1700
1859
  meetingId: fakeMeeting2.id,
1701
1860
  mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
1702
1861
  };
1703
-
1862
+ cd.setMercuryConnectedStatus(true);
1704
1863
  cd.submitClientEvent({
1705
1864
  name: 'client.alert.displayed',
1706
1865
  options,
@@ -1711,6 +1870,7 @@ describe('internal-plugin-metrics', () => {
1711
1870
  canProceed: true,
1712
1871
  eventData: {
1713
1872
  webClientDomain: 'whatever',
1873
+ isMercuryConnected: true,
1714
1874
  },
1715
1875
  identifiers: {
1716
1876
  correlationId: 'correlationId2',
@@ -1752,7 +1912,7 @@ describe('internal-plugin-metrics', () => {
1752
1912
  mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
1753
1913
  sessionCorrelationId: 'sessionCorrelationId1',
1754
1914
  };
1755
-
1915
+ cd.setMercuryConnectedStatus(true);
1756
1916
  cd.submitClientEvent({
1757
1917
  name: 'client.alert.displayed',
1758
1918
  options,
@@ -1763,6 +1923,7 @@ describe('internal-plugin-metrics', () => {
1763
1923
  canProceed: true,
1764
1924
  eventData: {
1765
1925
  webClientDomain: 'whatever',
1926
+ isMercuryConnected: true,
1766
1927
  },
1767
1928
  identifiers: {
1768
1929
  correlationId: 'correlationId2',
@@ -1811,7 +1972,7 @@ describe('internal-plugin-metrics', () => {
1811
1972
  globalMeetingId: 'globalMeetingId1',
1812
1973
  sessionCorrelationId: 'sessionCorrelationId1',
1813
1974
  };
1814
-
1975
+ cd.setMercuryConnectedStatus(true);
1815
1976
  cd.submitClientEvent({
1816
1977
  name: 'client.alert.displayed',
1817
1978
  options,
@@ -1824,6 +1985,7 @@ describe('internal-plugin-metrics', () => {
1824
1985
  canProceed: true,
1825
1986
  eventData: {
1826
1987
  webClientDomain: 'whatever',
1988
+ isMercuryConnected: true,
1827
1989
  },
1828
1990
  identifiers: {
1829
1991
  correlationId: 'correlationId',
@@ -1846,6 +2008,7 @@ describe('internal-plugin-metrics', () => {
1846
2008
  canProceed: true,
1847
2009
  eventData: {
1848
2010
  webClientDomain: 'whatever',
2011
+ isMercuryConnected: true,
1849
2012
  },
1850
2013
  identifiers: {
1851
2014
  correlationId: 'correlationId',
@@ -1889,7 +2052,7 @@ describe('internal-plugin-metrics', () => {
1889
2052
  },
1890
2053
  },
1891
2054
  };
1892
-
2055
+ cd.setMercuryConnectedStatus(true);
1893
2056
  cd.submitClientEvent({
1894
2057
  name: 'client.alert.displayed',
1895
2058
  options,
@@ -1900,6 +2063,7 @@ describe('internal-plugin-metrics', () => {
1900
2063
  canProceed: true,
1901
2064
  eventData: {
1902
2065
  webClientDomain: 'whatever',
2066
+ isMercuryConnected: true,
1903
2067
  },
1904
2068
  identifiers: {
1905
2069
  correlationId: 'correlationId',
@@ -1968,7 +2132,7 @@ describe('internal-plugin-metrics', () => {
1968
2132
  mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
1969
2133
  rawError: new Error('bad times'),
1970
2134
  };
1971
-
2135
+ cd.setMercuryConnectedStatus(true);
1972
2136
  cd.submitClientEvent({
1973
2137
  name: 'client.alert.displayed',
1974
2138
  options,
@@ -1979,6 +2143,7 @@ describe('internal-plugin-metrics', () => {
1979
2143
  canProceed: true,
1980
2144
  eventData: {
1981
2145
  webClientDomain: 'whatever',
2146
+ isMercuryConnected: true,
1982
2147
  },
1983
2148
  identifiers: {
1984
2149
  correlationId: 'correlationId',
@@ -2047,7 +2212,7 @@ describe('internal-plugin-metrics', () => {
2047
2212
  correlationId: 'correlationId',
2048
2213
  rawError: new Error('bad times'),
2049
2214
  };
2050
-
2215
+ cd.setMercuryConnectedStatus(true);
2051
2216
  cd.submitClientEvent({
2052
2217
  name: 'client.alert.displayed',
2053
2218
  options,
@@ -2058,6 +2223,7 @@ describe('internal-plugin-metrics', () => {
2058
2223
  canProceed: true,
2059
2224
  eventData: {
2060
2225
  webClientDomain: 'whatever',
2226
+ isMercuryConnected: true,
2061
2227
  },
2062
2228
  identifiers: {
2063
2229
  correlationId: 'correlationId',
@@ -2124,7 +2290,7 @@ describe('internal-plugin-metrics', () => {
2124
2290
  },
2125
2291
  },
2126
2292
  };
2127
-
2293
+ cd.setMercuryConnectedStatus(true);
2128
2294
  cd.submitClientEvent({
2129
2295
  name: 'client.alert.displayed',
2130
2296
  options,
@@ -2135,6 +2301,7 @@ describe('internal-plugin-metrics', () => {
2135
2301
  canProceed: true,
2136
2302
  eventData: {
2137
2303
  webClientDomain: 'whatever',
2304
+ isMercuryConnected: true,
2138
2305
  },
2139
2306
  identifiers: {
2140
2307
  correlationId: 'correlationId',
@@ -2194,7 +2361,7 @@ describe('internal-plugin-metrics', () => {
2194
2361
  meetingId: fakeMeeting.id,
2195
2362
  mediaConnections: [{mediaAgentAlias: 'alias', mediaAgentGroupId: '1'}],
2196
2363
  };
2197
-
2364
+ cd.setMercuryConnectedStatus(true);
2198
2365
  cd.submitClientEvent({
2199
2366
  name: 'client.alert.displayed',
2200
2367
  payload: {
@@ -2215,6 +2382,7 @@ describe('internal-plugin-metrics', () => {
2215
2382
  canProceed: true,
2216
2383
  eventData: {
2217
2384
  webClientDomain: 'whatever',
2385
+ isMercuryConnected: true,
2218
2386
  },
2219
2387
  identifiers: {
2220
2388
  correlationId: 'correlationId',
@@ -2263,7 +2431,7 @@ describe('internal-plugin-metrics', () => {
2263
2431
  );
2264
2432
  });
2265
2433
 
2266
- it('should send behavioral event if meetingId provided but meeting is undefined', () => {
2434
+ it('should record failure metric when meetingId is provided but meeting is undefined', () => {
2267
2435
  webex.meetings.getBasicMeetingInformation = sinon.stub().returns(undefined);
2268
2436
 
2269
2437
  cd.submitClientEvent({name: 'client.alert.displayed', options: {meetingId: 'meetingId'}});
@@ -2297,6 +2465,228 @@ describe('internal-plugin-metrics', () => {
2297
2465
  assert.calledWith(cd.submitToCallDiagnosticsPreLogin, testEvent);
2298
2466
  assert.notCalled(cd.submitToCallDiagnostics);
2299
2467
  });
2468
+
2469
+ describe('Limiting repeated events', () => {
2470
+ beforeEach(() => {
2471
+ cd.clearEventLimits();
2472
+ });
2473
+
2474
+ const createEventLimitRegex = (eventName: string, eventType: string) => {
2475
+ const escapedEventName = eventName.replace(/\./g, '\\.');
2476
+ return new RegExp(`Event limit reached for ${escapedEventName} for ${eventType}`);
2477
+ };
2478
+
2479
+ it('should always send events that are not in the limiting switch cases', () => {
2480
+ const options = {
2481
+ meetingId: fakeMeeting.id,
2482
+ };
2483
+ const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
2484
+
2485
+ const baselineCallCount = webex.logger.log.callCount;
2486
+ cd.submitClientEvent({
2487
+ name: 'client.alert.displayed',
2488
+ options,
2489
+ });
2490
+
2491
+ cd.submitClientEvent({
2492
+ name: 'client.alert.displayed',
2493
+ options,
2494
+ });
2495
+
2496
+ cd.submitClientEvent({
2497
+ name: 'client.alert.displayed',
2498
+ options,
2499
+ });
2500
+
2501
+ assert.calledThrice(submitToCallDiagnosticsStub);
2502
+ });
2503
+
2504
+ ([
2505
+ ['client.media.render.start'],
2506
+ ['client.media.render.stop'],
2507
+ ['client.media.rx.start'],
2508
+ ['client.media.rx.stop'],
2509
+ ['client.media.tx.start'],
2510
+ ['client.media.tx.stop']
2511
+ ] as const).forEach(([name]) => {
2512
+ it(`should only send ${name} once per mediaType`, () => {
2513
+ const options = {
2514
+ meetingId: fakeMeeting.id,
2515
+ };
2516
+ const payload = {
2517
+ mediaType: 'video' as const,
2518
+ };
2519
+ const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
2520
+
2521
+ const baselineCallCount = webex.logger.log.callCount;
2522
+ // Send first event
2523
+ cd.submitClientEvent({
2524
+ name,
2525
+ payload,
2526
+ options,
2527
+ });
2528
+
2529
+ assert.calledOnce(submitToCallDiagnosticsStub);
2530
+ submitToCallDiagnosticsStub.resetHistory();
2531
+
2532
+ // Send second event of same type
2533
+ cd.submitClientEvent({
2534
+ name,
2535
+ payload,
2536
+ options,
2537
+ });
2538
+
2539
+ assert.notCalled(submitToCallDiagnosticsStub);
2540
+ assert.calledWith(
2541
+ webex.logger.log,
2542
+ 'call-diagnostic-events -> ',
2543
+ sinon.match(createEventLimitRegex(name, 'mediaType video'))
2544
+ );
2545
+ webex.logger.log.resetHistory();
2546
+
2547
+ // Send third event of same type
2548
+ cd.submitClientEvent({
2549
+ name,
2550
+ payload,
2551
+ options,
2552
+ });
2553
+
2554
+ assert.notCalled(submitToCallDiagnosticsStub);
2555
+ assert.neverCalledWithMatch(webex.logger.log,
2556
+ 'call-diagnostic-events -> ',
2557
+ sinon.match(createEventLimitRegex(name, 'mediaType video'))
2558
+ );
2559
+
2560
+ // Send fourth event with a different mediaType
2561
+ cd.submitClientEvent({
2562
+ name,
2563
+ payload: {mediaType: 'audio'},
2564
+ options,
2565
+ });
2566
+
2567
+ assert.calledOnce(submitToCallDiagnosticsStub);
2568
+ });
2569
+
2570
+ it(`should handle share media type with shareInstanceId correctly for ${name}`, () => {
2571
+ const options = {
2572
+ meetingId: fakeMeeting.id,
2573
+ };
2574
+ const payload = {
2575
+ mediaType: 'share' as const,
2576
+ shareInstanceId: 'instance-1',
2577
+ };
2578
+ const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
2579
+
2580
+ const baselineCallCount = webex.logger.log.callCount;
2581
+ // Send first event
2582
+ cd.submitClientEvent({
2583
+ name,
2584
+ payload,
2585
+ options,
2586
+ });
2587
+
2588
+ // Send second event with same shareInstanceId
2589
+ cd.submitClientEvent({
2590
+ name,
2591
+ payload,
2592
+ options,
2593
+ });
2594
+
2595
+ // Send event with different shareInstanceId
2596
+ cd.submitClientEvent({
2597
+ name,
2598
+ payload: { ...payload, shareInstanceId: 'instance-2' },
2599
+ options,
2600
+ });
2601
+
2602
+ assert.calledTwice(submitToCallDiagnosticsStub);
2603
+ });
2604
+ });
2605
+
2606
+ ([
2607
+ ['client.roap-message.received'],
2608
+ ['client.roap-message.sent']
2609
+ ] as const).forEach(([name]) => {
2610
+ it(`should not send third event of same type and not log warning again for ${name}`, () => {
2611
+ const options = {
2612
+ meetingId: fakeMeeting.id,
2613
+ };
2614
+ const payload = {
2615
+ roap: {
2616
+ messageType: 'OFFER' as const,
2617
+ },
2618
+ };
2619
+ const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
2620
+
2621
+ // Clear any existing call history to get accurate counts
2622
+ webex.logger.log.resetHistory();
2623
+
2624
+ // Send first event
2625
+ cd.submitClientEvent({
2626
+ name,
2627
+ payload,
2628
+ options,
2629
+ });
2630
+
2631
+ assert.calledOnce(submitToCallDiagnosticsStub);
2632
+ submitToCallDiagnosticsStub.resetHistory();
2633
+
2634
+ // Send second event (should trigger warning)
2635
+ cd.submitClientEvent({
2636
+ name,
2637
+ payload,
2638
+ options,
2639
+ });
2640
+
2641
+ assert.notCalled(submitToCallDiagnosticsStub);
2642
+ assert.calledWith(
2643
+ webex.logger.log,
2644
+ 'call-diagnostic-events -> ',
2645
+ sinon.match(createEventLimitRegex(name, 'ROAP type OFFER'))
2646
+ );
2647
+ webex.logger.log.resetHistory();
2648
+
2649
+ cd.submitClientEvent({
2650
+ name,
2651
+ payload,
2652
+ options,
2653
+ });
2654
+
2655
+ assert.notCalled(submitToCallDiagnosticsStub);
2656
+ assert.neverCalledWithMatch(
2657
+ webex.logger.log,
2658
+ 'call-diagnostic-events -> ',
2659
+ sinon.match(createEventLimitRegex(name, 'ROAP type OFFER'))
2660
+ );
2661
+ });
2662
+
2663
+ it(`should handle roap.type instead of roap.messageType for ${name}`, () => {
2664
+ const options = {
2665
+ meetingId: fakeMeeting.id,
2666
+ };
2667
+ const payload = {
2668
+ roap: {
2669
+ type: 'ANSWER' as const,
2670
+ },
2671
+ };
2672
+ const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
2673
+
2674
+ cd.submitClientEvent({
2675
+ name,
2676
+ payload,
2677
+ options,
2678
+ });
2679
+
2680
+ cd.submitClientEvent({
2681
+ name,
2682
+ payload,
2683
+ options,
2684
+ });
2685
+
2686
+ assert.calledOnce(submitToCallDiagnosticsStub);
2687
+ });
2688
+ });
2689
+ });
2300
2690
  });
2301
2691
 
2302
2692
  describe('#submitToCallDiagnostics', () => {
@@ -2371,7 +2761,7 @@ describe('internal-plugin-metrics', () => {
2371
2761
  applicationSoftwareType: 'webex-js-sdk',
2372
2762
  applicationSoftwareVersion: 'webex-version',
2373
2763
  mediaEngineSoftwareType: 'browser',
2374
- mediaEngineSoftwareVersion: getOSVersion(),
2764
+ mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2375
2765
  startTime: now.toISOString(),
2376
2766
  },
2377
2767
  },
@@ -2410,7 +2800,7 @@ describe('internal-plugin-metrics', () => {
2410
2800
  applicationSoftwareType: 'webex-js-sdk',
2411
2801
  applicationSoftwareVersion: 'webex-version',
2412
2802
  mediaEngineSoftwareType: 'browser',
2413
- mediaEngineSoftwareVersion: getOSVersion(),
2803
+ mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2414
2804
  startTime: now.toISOString(),
2415
2805
  },
2416
2806
  },
@@ -2447,7 +2837,7 @@ describe('internal-plugin-metrics', () => {
2447
2837
  applicationSoftwareType: 'webex-js-sdk',
2448
2838
  applicationSoftwareVersion: 'webex-version',
2449
2839
  mediaEngineSoftwareType: 'browser',
2450
- mediaEngineSoftwareVersion: getOSVersion(),
2840
+ mediaEngineSoftwareVersion: getOSVersion() || 'unknown',
2451
2841
  startTime: now.toISOString(),
2452
2842
  },
2453
2843
  },
@@ -2574,7 +2964,7 @@ describe('internal-plugin-metrics', () => {
2574
2964
  name: 'other',
2575
2965
  rawErrorMessage: 'bad times',
2576
2966
  errorDescription: 'UnknownError',
2577
- }
2967
+ };
2578
2968
 
2579
2969
  const [res, cached] = cd.generateClientEventErrorPayload(error);
2580
2970
  assert.isFalse(cached);
@@ -2903,7 +3293,10 @@ describe('internal-plugin-metrics', () => {
2903
3293
  });
2904
3294
 
2905
3295
  it('should return unknown error otherwise', () => {
2906
- const [res, cached] = cd.generateClientEventErrorPayload({something: 'new', message: 'bad times'});
3296
+ const [res, cached] = cd.generateClientEventErrorPayload({
3297
+ something: 'new',
3298
+ message: 'bad times',
3299
+ });
2907
3300
  assert.deepEqual(res, {
2908
3301
  category: 'other',
2909
3302
  errorDescription: 'UnknownError',
@@ -3251,7 +3644,7 @@ describe('internal-plugin-metrics', () => {
3251
3644
  meetingId: fakeMeeting.id,
3252
3645
  preLoginId,
3253
3646
  };
3254
-
3647
+ cd.setMercuryConnectedStatus(true);
3255
3648
  const triggered = new Date();
3256
3649
  const fetchOptions = await cd.buildClientEventFetchRequestOptions({
3257
3650
  name: 'client.exit.app',
@@ -3267,6 +3660,7 @@ describe('internal-plugin-metrics', () => {
3267
3660
  canProceed: false,
3268
3661
  eventData: {
3269
3662
  webClientDomain: 'whatever',
3663
+ isMercuryConnected: true,
3270
3664
  },
3271
3665
  identifiers: {
3272
3666
  correlationId: 'correlationId',
@@ -3294,7 +3688,7 @@ describe('internal-plugin-metrics', () => {
3294
3688
  localNetworkPrefix: '192.168.1.80',
3295
3689
  publicNetworkPrefix: '1.3.4.0',
3296
3690
  os: getOSNameInternal() || 'unknown',
3297
- osVersion: getOSVersion(),
3691
+ osVersion: getOSVersion() || 'unknown',
3298
3692
  subClientType: 'WEB_APP',
3299
3693
  },
3300
3694
  environment: 'meeting_evn',
@@ -3573,7 +3967,7 @@ describe('internal-plugin-metrics', () => {
3573
3967
 
3574
3968
  const overrides = {
3575
3969
  correlationId: 'newCorrelationId',
3576
- }
3970
+ };
3577
3971
 
3578
3972
  cd.submitClientEvent({
3579
3973
  name: 'client.alert.displayed',
@@ -3635,5 +4029,401 @@ describe('internal-plugin-metrics', () => {
3635
4029
  assert.notCalled(submitClientEventSpy);
3636
4030
  });
3637
4031
  });
4032
+
4033
+ describe('#submitFeatureEvent', () => {
4034
+ it('should submit feature event successfully with meetingId', () => {
4035
+ const prepareDiagnosticEventSpy = sinon.spy(cd, 'prepareDiagnosticEvent');
4036
+ const submitToCallFeaturesSpy = sinon.spy(cd, 'submitToCallFeatures');
4037
+ sinon.stub(cd, 'getOrigin').returns({origin: 'fake-origin'});
4038
+
4039
+ const options = {
4040
+ meetingId: fakeMeeting.id,
4041
+ };
4042
+ cd.setMercuryConnectedStatus(true);
4043
+
4044
+ cd.submitFeatureEvent({
4045
+ name: 'client.feature.meeting.summary',
4046
+ payload: {
4047
+ meetingSummaryInfo: {
4048
+ featureName: 'syncSystemMuteStatus',
4049
+ featureActions: [{
4050
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4051
+ actionId: '14200',
4052
+ isInitialValue: false,
4053
+ clickCount: '1'
4054
+ }]
4055
+ },
4056
+ },
4057
+ options,
4058
+ });
4059
+
4060
+ assert.calledWith(
4061
+ prepareDiagnosticEventSpy,
4062
+ {
4063
+ name: 'client.feature.meeting.summary',
4064
+ canProceed: true,
4065
+ identifiers: {
4066
+ correlationId: 'correlationId',
4067
+ userId: 'userId',
4068
+ deviceId: 'deviceUrl',
4069
+ orgId: 'orgId',
4070
+ locusUrl: 'locus/url',
4071
+ locusId: 'url',
4072
+ locusStartTime: 'lastActive',
4073
+ },
4074
+ eventData: { webClientDomain: 'whatever'},
4075
+ userType: 'host',
4076
+ loginType: 'login-ci',
4077
+ isConvergedArchitectureEnabled: undefined,
4078
+ webexSubServiceType: undefined,
4079
+ webClientPreload: undefined,
4080
+ meetingSummaryInfo: {
4081
+ featureName: 'syncSystemMuteStatus',
4082
+ featureActions: [{
4083
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4084
+ actionId: '14200',
4085
+ isInitialValue: false,
4086
+ clickCount: '1'
4087
+ }]
4088
+ },
4089
+ key: "UcfFeatureUsage",
4090
+ },
4091
+ options
4092
+ );
4093
+
4094
+ assert.calledWith(submitToCallFeaturesSpy, {
4095
+ eventId: 'my-fake-id',
4096
+ version: 1,
4097
+ origin: {
4098
+ origin: 'fake-origin',
4099
+ },
4100
+ event: {
4101
+ canProceed: true,
4102
+ eventData: { webClientDomain: 'whatever'},
4103
+ identifiers: {
4104
+ correlationId: 'correlationId',
4105
+ deviceId: 'deviceUrl',
4106
+ locusId: 'url',
4107
+ locusStartTime: 'lastActive',
4108
+ locusUrl: 'locus/url',
4109
+ orgId: 'orgId',
4110
+ userId: 'userId',
4111
+ },
4112
+ loginType: 'login-ci',
4113
+ name: 'client.feature.meeting.summary',
4114
+ userType: 'host',
4115
+ isConvergedArchitectureEnabled: undefined,
4116
+ webexSubServiceType: undefined,
4117
+ webClientPreload: undefined,
4118
+ meetingSummaryInfo: {
4119
+ featureName: 'syncSystemMuteStatus',
4120
+ featureActions: [{
4121
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4122
+ actionId: '14200',
4123
+ isInitialValue: false,
4124
+ clickCount: '1'
4125
+ }]
4126
+ },
4127
+ key: "UcfFeatureUsage",
4128
+ },
4129
+ originTime: {
4130
+ sent: 'not_defined_yet',
4131
+ triggered: now.toISOString(),
4132
+ },
4133
+ senderCountryCode: 'UK',
4134
+ });
4135
+
4136
+ const webexLoggerLogCalls = webex.logger.log.getCalls();
4137
+ assert.deepEqual(webexLoggerLogCalls[1].args, [
4138
+ 'call-diagnostic-events-feature -> ',
4139
+ 'CallFeatureMetrics: @submitFeatureEvent. Submit Client Feature Event CA event.',
4140
+ `name: client.feature.meeting.summary`,
4141
+ ]);
4142
+ });
4143
+ });
4144
+
4145
+ describe('#submitDelayedClientFeatureEvents', () => {
4146
+ it('does not call submitFeatureEvent if there were no delayed events', () => {
4147
+ const submitFeatureEventSpy = sinon.spy(cd, 'submitFeatureEvent');
4148
+
4149
+ cd.submitDelayedClientFeatureEvents();
4150
+
4151
+ assert.notCalled(submitFeatureEventSpy);
4152
+ });
4153
+
4154
+ it('calls submitFeatureEvent for every delayed event and clears delayedClientFeatureEvents array', () => {
4155
+ const submitFeatureEventSpy = sinon.spy(cd, 'submitFeatureEvent');
4156
+ const submitToCallFeaturesSpy = sinon.spy(cd, 'submitToCallFeatures');
4157
+
4158
+ const options = {
4159
+ meetingId: 'meetingId',
4160
+ };
4161
+
4162
+ cd.submitFeatureEvent({
4163
+ name: 'client.feature.meeting.summary',
4164
+ options,
4165
+ payload: {
4166
+ meetingSummaryInfo: {
4167
+ featureName: 'syncSystemMuteStatus',
4168
+ featureActions: [{
4169
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4170
+ actionId: '14200',
4171
+ isInitialValue: false,
4172
+ clickCount: '1'
4173
+ }]
4174
+ },
4175
+ },
4176
+ delaySubmitEvent: true,
4177
+ });
4178
+
4179
+ cd.submitFeatureEvent({
4180
+ name: 'client.feature.meeting.summary',
4181
+ options,
4182
+ payload: {
4183
+ meetingSummaryInfo: {
4184
+ featureName: 'syncSystemVideoStatus',
4185
+ featureActions: [{
4186
+ actionName: 'syncMeetingVideoUnmuteStatusToSystem',
4187
+ actionId: '13400',
4188
+ isInitialValue: false,
4189
+ clickCount: '1'
4190
+ }]
4191
+ },
4192
+ },
4193
+ delaySubmitEvent: true,
4194
+ });
4195
+
4196
+ assert.notCalled(submitToCallFeaturesSpy);
4197
+ assert.calledTwice(submitFeatureEventSpy);
4198
+ submitFeatureEventSpy.resetHistory();
4199
+
4200
+ cd.submitDelayedClientFeatureEvents();
4201
+
4202
+ assert.calledTwice(submitFeatureEventSpy);
4203
+ assert.calledWith(submitFeatureEventSpy.firstCall, {
4204
+ name: 'client.feature.meeting.summary',
4205
+ payload: {
4206
+ meetingSummaryInfo: {
4207
+ featureName: 'syncSystemMuteStatus',
4208
+ featureActions: [{
4209
+ actionName: 'syncMeetingMicUnmuteStatusToSystem',
4210
+ actionId: '14200',
4211
+ isInitialValue: false,
4212
+ clickCount: '1'
4213
+ }]
4214
+ },
4215
+ },
4216
+ options: {
4217
+ meetingId: 'meetingId',
4218
+ triggeredTime: now.toISOString(),
4219
+ },
4220
+ });
4221
+ assert.calledWith(submitFeatureEventSpy.secondCall, {
4222
+ name: 'client.feature.meeting.summary',
4223
+ payload: {
4224
+ meetingSummaryInfo: {
4225
+ featureName: 'syncSystemVideoStatus',
4226
+ featureActions: [{
4227
+ actionName: 'syncMeetingVideoUnmuteStatusToSystem',
4228
+ actionId: '13400',
4229
+ isInitialValue: false,
4230
+ clickCount: '1'
4231
+ }]
4232
+ },
4233
+ },
4234
+ options: {
4235
+ meetingId: 'meetingId',
4236
+ triggeredTime: now.toISOString(),
4237
+ },
4238
+ });
4239
+ submitFeatureEventSpy.resetHistory();
4240
+
4241
+ cd.submitDelayedClientFeatureEvents();
4242
+
4243
+ // should not call submitFeatureEventSpy again if delayedClientFeatureEvents was cleared
4244
+ assert.notCalled(submitFeatureEventSpy);
4245
+ });
4246
+ });
4247
+
4248
+ describe('#clearEventLimitsForCorrelationId', () => {
4249
+ beforeEach(() => {
4250
+ cd.clearEventLimits();
4251
+ });
4252
+
4253
+ it('should clear event limits for specific correlationId only', () => {
4254
+ // Use the actual correlationIds from our fakeMeeting fixtures
4255
+ const correlationId1 = fakeMeeting.correlationId; // e.g. 'correlationId1'
4256
+ const correlationId2 = fakeMeeting2.correlationId; // e.g. 'correlationId2'
4257
+ const options1 = { meetingId: fakeMeeting.id };
4258
+ const options2 = { meetingId: fakeMeeting2.id };
4259
+ const payload = { mediaType: 'video' as const };
4260
+
4261
+ // Set up events for both correlations to trigger limits
4262
+ cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options1 });
4263
+ cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options2 });
4264
+ cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options1 });
4265
+ cd.submitClientEvent({ name: 'client.media.render.start', payload, options: options2 });
4266
+ assert.isTrue(cd.eventLimitTracker.size > 0);
4267
+ assert.isTrue(cd.eventLimitWarningsLogged.size > 0);
4268
+
4269
+ // Clear limits for only correlationId1 (present)
4270
+ cd.clearEventLimitsForCorrelationId(correlationId1);
4271
+
4272
+ const remainingTrackerKeys = Array.from(cd.eventLimitTracker.keys());
4273
+ const remainingWarningKeys = Array.from(cd.eventLimitWarningsLogged.keys());
4274
+
4275
+ // Should have no keys with correlationId1
4276
+ assert.isFalse(remainingTrackerKeys.some(key => key.split(':')[1] === correlationId1));
4277
+ assert.isFalse(remainingWarningKeys.some(key => key.split(':')[1] === correlationId1));
4278
+
4279
+ // Should still have keys with correlationId2
4280
+ assert.isTrue(remainingTrackerKeys.some(key => key.split(':')[1] === correlationId2));
4281
+ assert.isTrue(remainingWarningKeys.some(key => key.split(':')[1] === correlationId2));
4282
+ });
4283
+
4284
+ it('should handle empty correlationId gracefully', () => {
4285
+ const options = { meetingId: fakeMeeting.id };
4286
+ const payload = { mediaType: 'video' as const };
4287
+
4288
+ // Set up some tracking data
4289
+ cd.submitClientEvent({
4290
+ name: 'client.media.render.start',
4291
+ payload,
4292
+ options,
4293
+ });
4294
+
4295
+ cd.submitClientEvent({
4296
+ name: 'client.media.render.start',
4297
+ payload,
4298
+ options,
4299
+ });
4300
+
4301
+ const initialTrackerSize = cd.eventLimitTracker.size;
4302
+ const initialWarningsSize = cd.eventLimitWarningsLogged.size;
4303
+
4304
+ // Should not clear anything for empty correlationId
4305
+ cd.clearEventLimitsForCorrelationId('');
4306
+ cd.clearEventLimitsForCorrelationId(null as any);
4307
+ cd.clearEventLimitsForCorrelationId(undefined as any);
4308
+
4309
+ assert.equal(cd.eventLimitTracker.size, initialTrackerSize);
4310
+ assert.equal(cd.eventLimitWarningsLogged.size, initialWarningsSize);
4311
+ });
4312
+
4313
+ it('should handle non-existent correlationId gracefully', () => {
4314
+ const options = { meetingId: fakeMeeting.id };
4315
+ const payload = { mediaType: 'video' as const };
4316
+
4317
+ // Set up some tracking data
4318
+ cd.submitClientEvent({
4319
+ name: 'client.media.render.start',
4320
+ payload,
4321
+ options,
4322
+ });
4323
+
4324
+ const initialTrackerSize = cd.eventLimitTracker.size;
4325
+ const initialWarningsSize = cd.eventLimitWarningsLogged.size;
4326
+
4327
+ // Should not clear anything for non-existent correlationId
4328
+ cd.clearEventLimitsForCorrelationId('nonExistentCorrelationId');
4329
+
4330
+ assert.equal(cd.eventLimitTracker.size, initialTrackerSize);
4331
+ assert.equal(cd.eventLimitWarningsLogged.size, initialWarningsSize);
4332
+ });
4333
+
4334
+ it('should clear multiple event types for the same correlationId', () => {
4335
+ const correlationId = fakeMeeting.correlationId;
4336
+ const options = { meetingId: fakeMeeting.id };
4337
+ const videoPayload = { mediaType: 'video' as const };
4338
+ const audioPayload = { mediaType: 'audio' as const };
4339
+ const roapPayload = { roap: { messageType: 'OFFER' as const } };
4340
+
4341
+ // Set up multiple event types for the same correlation
4342
+ cd.submitClientEvent({
4343
+ name: 'client.media.render.start',
4344
+ payload: videoPayload,
4345
+ options,
4346
+ });
4347
+
4348
+ cd.submitClientEvent({
4349
+ name: 'client.media.render.start',
4350
+ payload: audioPayload,
4351
+ options,
4352
+ });
4353
+
4354
+ cd.submitClientEvent({
4355
+ name: 'client.roap-message.sent',
4356
+ payload: roapPayload,
4357
+ options,
4358
+ });
4359
+
4360
+ // Trigger limits
4361
+ cd.submitClientEvent({
4362
+ name: 'client.media.render.start',
4363
+ payload: videoPayload,
4364
+ options,
4365
+ });
4366
+
4367
+ cd.submitClientEvent({
4368
+ name: 'client.media.render.start',
4369
+ payload: audioPayload,
4370
+ options,
4371
+ });
4372
+
4373
+ cd.submitClientEvent({
4374
+ name: 'client.roap-message.sent',
4375
+ payload: roapPayload,
4376
+ options,
4377
+ });
4378
+
4379
+ assert.isTrue(cd.eventLimitTracker.size > 0);
4380
+ assert.isTrue(cd.eventLimitWarningsLogged.size > 0);
4381
+
4382
+ // Clear all limits for this correlationId
4383
+ cd.clearEventLimitsForCorrelationId(correlationId);
4384
+
4385
+ // Should clear all tracking data for this correlationId
4386
+ assert.equal(cd.eventLimitTracker.size, 0);
4387
+ assert.equal(cd.eventLimitWarningsLogged.size, 0);
4388
+ });
4389
+
4390
+ it('should allow events to be sent again after clearing limits for correlationId', () => {
4391
+ const correlationId = fakeMeeting.correlationId;
4392
+ const options = { meetingId: fakeMeeting.id };
4393
+ const payload = { mediaType: 'video' as const };
4394
+ const submitToCallDiagnosticsStub = sinon.stub(cd, 'submitToCallDiagnostics');
4395
+
4396
+ // Send first event (should succeed)
4397
+ cd.submitClientEvent({
4398
+ name: 'client.media.render.start',
4399
+ payload,
4400
+ options,
4401
+ });
4402
+
4403
+ assert.calledOnce(submitToCallDiagnosticsStub);
4404
+ submitToCallDiagnosticsStub.resetHistory();
4405
+
4406
+ // Send second event (should be blocked)
4407
+ cd.submitClientEvent({
4408
+ name: 'client.media.render.start',
4409
+ payload,
4410
+ options,
4411
+ });
4412
+
4413
+ assert.notCalled(submitToCallDiagnosticsStub);
4414
+
4415
+ // Clear limits for this correlationId
4416
+ cd.clearEventLimitsForCorrelationId(correlationId);
4417
+
4418
+ // Send event again (should succeed after clearing)
4419
+ cd.submitClientEvent({
4420
+ name: 'client.media.render.start',
4421
+ payload,
4422
+ options,
4423
+ });
4424
+
4425
+ assert.calledOnce(submitToCallDiagnosticsStub);
4426
+ });
4427
+ });
3638
4428
  });
3639
4429
  });