@webex/plugin-meetings 3.3.1 → 3.4.0-next.10
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/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +7 -2
- package/dist/breakouts/index.js.map +1 -1
- package/dist/constants.js +11 -4
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/selfUtils.js +0 -5
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +70 -15
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/index.js +18 -9
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/connectionStateHandler.js +67 -0
- package/dist/meeting/connectionStateHandler.js.map +1 -0
- package/dist/meeting/index.js +576 -374
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +7 -0
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +6 -1
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/index.js +4 -4
- package/dist/meeting-info/index.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +2 -2
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meeting-info/util.js +17 -17
- package/dist/meeting-info/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +16 -16
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/collection.js +1 -1
- package/dist/meetings/collection.js.map +1 -1
- package/dist/meetings/index.js +41 -35
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/meetings.types.js +8 -0
- package/dist/meetings/meetings.types.js.map +1 -1
- package/dist/meetings/util.js +3 -2
- package/dist/meetings/util.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/metrics/index.js +57 -0
- package/dist/metrics/index.js.map +1 -1
- package/dist/personal-meeting-room/index.js +1 -1
- package/dist/personal-meeting-room/index.js.map +1 -1
- package/dist/reachability/clusterReachability.js +108 -53
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +546 -115
- package/dist/reachability/index.js.map +1 -1
- package/dist/reconnection-manager/index.js +1 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/rtcMetrics/index.js +26 -6
- package/dist/rtcMetrics/index.js.map +1 -1
- package/dist/types/constants.d.ts +11 -3
- package/dist/types/media/MediaConnectionAwaiter.d.ts +24 -4
- package/dist/types/meeting/connectionStateHandler.d.ts +30 -0
- package/dist/types/meeting/index.d.ts +28 -8
- package/dist/types/meeting/locusMediaRequest.d.ts +2 -0
- package/dist/types/meeting-info/index.d.ts +3 -2
- package/dist/types/meeting-info/meeting-info-v2.d.ts +3 -2
- package/dist/types/meeting-info/util.d.ts +5 -4
- package/dist/types/meeting-info/utilv2.d.ts +3 -2
- package/dist/types/meetings/collection.d.ts +3 -2
- package/dist/types/meetings/index.d.ts +6 -4
- package/dist/types/meetings/meetings.types.d.ts +9 -0
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/metrics/index.d.ts +15 -0
- package/dist/types/reachability/clusterReachability.d.ts +31 -3
- package/dist/types/reachability/index.d.ts +107 -4
- package/dist/types/rtcMetrics/index.d.ts +11 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +23 -23
- package/src/breakouts/index.ts +7 -1
- package/src/constants.ts +13 -17
- package/src/locus-info/selfUtils.ts +0 -5
- package/src/media/MediaConnectionAwaiter.ts +89 -14
- package/src/media/index.ts +18 -9
- package/src/meeting/connectionStateHandler.ts +65 -0
- package/src/meeting/index.ts +541 -298
- package/src/meeting/locusMediaRequest.ts +5 -0
- package/src/meeting/muteState.ts +6 -1
- package/src/meeting/util.ts +1 -0
- package/src/meeting-info/index.ts +9 -6
- package/src/meeting-info/meeting-info-v2.ts +4 -4
- package/src/meeting-info/util.ts +23 -28
- package/src/meeting-info/utilv2.ts +18 -24
- package/src/meetings/collection.ts +3 -3
- package/src/meetings/index.ts +43 -43
- package/src/meetings/meetings.types.ts +11 -0
- package/src/meetings/util.ts +5 -4
- package/src/metrics/constants.ts +1 -0
- package/src/metrics/index.ts +44 -0
- package/src/personal-meeting-room/index.ts +2 -2
- package/src/reachability/clusterReachability.ts +86 -25
- package/src/reachability/index.ts +364 -30
- package/src/reconnection-manager/index.ts +1 -1
- package/src/rtcMetrics/index.ts +25 -5
- package/test/unit/spec/breakouts/index.ts +51 -32
- package/test/unit/spec/locus-info/selfUtils.js +25 -23
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +131 -32
- package/test/unit/spec/media/index.ts +75 -34
- package/test/unit/spec/meeting/connectionStateHandler.ts +102 -0
- package/test/unit/spec/meeting/index.js +807 -185
- package/test/unit/spec/meeting/locusMediaRequest.ts +7 -0
- package/test/unit/spec/meeting/muteState.js +24 -0
- package/test/unit/spec/meeting-info/index.js +4 -4
- package/test/unit/spec/meeting-info/meetinginfov2.js +24 -28
- package/test/unit/spec/meeting-info/request.js +2 -2
- package/test/unit/spec/meeting-info/utilv2.js +41 -49
- package/test/unit/spec/meetings/index.js +44 -3
- package/test/unit/spec/metrics/index.js +126 -0
- package/test/unit/spec/multistream/mediaRequestManager.ts +2 -2
- package/test/unit/spec/personal-meeting-room/personal-meeting-room.js +2 -2
- package/test/unit/spec/reachability/clusterReachability.ts +116 -22
- package/test/unit/spec/reachability/index.ts +1398 -131
- package/test/unit/spec/rtcMetrics/index.ts +32 -0
- package/dist/mediaQualityMetrics/config.js +0 -321
- package/dist/mediaQualityMetrics/config.js.map +0 -1
- package/dist/networkQualityMonitor/index.js +0 -227
- package/dist/networkQualityMonitor/index.js.map +0 -1
- package/dist/statsAnalyzer/global.js +0 -44
- package/dist/statsAnalyzer/global.js.map +0 -1
- package/dist/statsAnalyzer/index.js +0 -1072
- package/dist/statsAnalyzer/index.js.map +0 -1
- package/dist/statsAnalyzer/mqaUtil.js +0 -368
- package/dist/statsAnalyzer/mqaUtil.js.map +0 -1
- package/dist/types/mediaQualityMetrics/config.d.ts +0 -247
- package/dist/types/networkQualityMonitor/index.d.ts +0 -70
- package/dist/types/statsAnalyzer/global.d.ts +0 -36
- package/dist/types/statsAnalyzer/index.d.ts +0 -217
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +0 -48
- package/src/mediaQualityMetrics/config.ts +0 -255
- package/src/networkQualityMonitor/index.ts +0 -211
- package/src/statsAnalyzer/global.ts +0 -37
- package/src/statsAnalyzer/index.ts +0 -1318
- package/src/statsAnalyzer/mqaUtil.ts +0 -463
- package/test/unit/spec/networkQualityMonitor/index.js +0 -99
- package/test/unit/spec/stats-analyzer/index.js +0 -1819
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import {assert} from '@webex/test-helper-chai';
|
|
2
2
|
import MockWebex from '@webex/test-helper-mock-webex';
|
|
3
3
|
import sinon from 'sinon';
|
|
4
|
+
import EventEmitter from 'events';
|
|
5
|
+
import testUtils from '../../../utils/testUtils';
|
|
4
6
|
import Reachability, {
|
|
5
|
-
ReachabilityResults,
|
|
6
7
|
ReachabilityResultsForBackend,
|
|
7
8
|
} from '@webex/plugin-meetings/src/reachability/';
|
|
9
|
+
import {ClusterNode} from '../../../../src/reachability/request';
|
|
8
10
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
9
11
|
import * as ClusterReachabilityModule from '@webex/plugin-meetings/src/reachability/clusterReachability';
|
|
12
|
+
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
10
13
|
|
|
11
14
|
import {IP_VERSION} from '@webex/plugin-meetings/src/constants';
|
|
12
15
|
|
|
@@ -141,7 +144,6 @@ describe('isAnyPublicClusterReachable', () => {
|
|
|
141
144
|
});
|
|
142
145
|
});
|
|
143
146
|
|
|
144
|
-
|
|
145
147
|
describe('isWebexMediaBackendUnreachable', () => {
|
|
146
148
|
let webex;
|
|
147
149
|
|
|
@@ -381,156 +383,1066 @@ describe('isWebexMediaBackendUnreachable', () => {
|
|
|
381
383
|
tcp: {result: 'unreachable'},
|
|
382
384
|
xtls: {result: 'reachable'},
|
|
383
385
|
},
|
|
384
|
-
c: {
|
|
385
|
-
udp: {result: 'unreachable'},
|
|
386
|
+
c: {
|
|
387
|
+
udp: {result: 'unreachable'},
|
|
388
|
+
tcp: {result: 'unreachable'},
|
|
389
|
+
xtls: {result: 'unreachable'},
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
expectedResult: false,
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
title: 'multiple clusters with some missing results',
|
|
396
|
+
mockStorage: {
|
|
397
|
+
a: {udp: {result: 'unreachable'}},
|
|
398
|
+
b: {tcp: {result: 'unreachable'}},
|
|
399
|
+
c: {xtls: {result: 'unreachable'}},
|
|
400
|
+
d: {},
|
|
401
|
+
},
|
|
402
|
+
expectedResult: true,
|
|
403
|
+
},
|
|
404
|
+
].forEach(({mockStorage, expectedResult, title}) => {
|
|
405
|
+
it(`returns ${expectedResult} when ${title}`, async () => {
|
|
406
|
+
await runCheck(mockStorage, expectedResult);
|
|
407
|
+
});
|
|
408
|
+
});
|
|
409
|
+
});
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* helper class to mock ClusterReachability and allow to easily
|
|
413
|
+
* simulate 'resultReady' events from it
|
|
414
|
+
*/
|
|
415
|
+
class MockClusterReachability extends EventEmitter {
|
|
416
|
+
mockResult = {
|
|
417
|
+
udp: {
|
|
418
|
+
result: 'untested',
|
|
419
|
+
},
|
|
420
|
+
tcp: {
|
|
421
|
+
result: 'untested',
|
|
422
|
+
},
|
|
423
|
+
xtls: {
|
|
424
|
+
result: 'untested',
|
|
425
|
+
},
|
|
426
|
+
};
|
|
427
|
+
|
|
428
|
+
isVideoMesh: boolean;
|
|
429
|
+
name: string;
|
|
430
|
+
|
|
431
|
+
constructor(name: string, clusterInfo: ClusterNode) {
|
|
432
|
+
super();
|
|
433
|
+
this.name = name;
|
|
434
|
+
this.isVideoMesh = clusterInfo.isVideoMesh;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
abort = sinon.stub();
|
|
438
|
+
start = sinon.stub();
|
|
439
|
+
|
|
440
|
+
getResult() {
|
|
441
|
+
return this.mockResult;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
* Emits a fake 'resultReady' event and makes sure that the same result
|
|
446
|
+
* is returned when getResult() is called.
|
|
447
|
+
*
|
|
448
|
+
* @param protocol
|
|
449
|
+
* @param result
|
|
450
|
+
*/
|
|
451
|
+
public emitFakeResult(protocol, result) {
|
|
452
|
+
this.mockResult[protocol] = result;
|
|
453
|
+
this.emit(ClusterReachabilityModule.Events.resultReady, {protocol, ...result});
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
public emitFakeClientMediaIpUpdate(protocol, newIp) {
|
|
457
|
+
this.mockResult[protocol].clientMediaIPs.push(newIp);
|
|
458
|
+
this.emit(ClusterReachabilityModule.Events.clientMediaIpsUpdated, {
|
|
459
|
+
protocol,
|
|
460
|
+
clientMediaIPs: this.mockResult[protocol].clientMediaIPs,
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
describe('gatherReachability', () => {
|
|
466
|
+
let webex;
|
|
467
|
+
let clock;
|
|
468
|
+
let clusterReachabilityCtorStub;
|
|
469
|
+
let mockClusterReachabilityInstances: Record<string, MockClusterReachability>;
|
|
470
|
+
|
|
471
|
+
beforeEach(async () => {
|
|
472
|
+
webex = new MockWebex();
|
|
473
|
+
|
|
474
|
+
sinon.stub(Metrics, 'sendBehavioralMetric');
|
|
475
|
+
|
|
476
|
+
await webex.boundedStorage.put(
|
|
477
|
+
'Reachability',
|
|
478
|
+
'reachability.result',
|
|
479
|
+
JSON.stringify({old: 'results'})
|
|
480
|
+
);
|
|
481
|
+
await webex.boundedStorage.put(
|
|
482
|
+
'Reachability',
|
|
483
|
+
'reachability.joinCookie',
|
|
484
|
+
JSON.stringify({old: 'joinCookie'})
|
|
485
|
+
);
|
|
486
|
+
|
|
487
|
+
webex.internal.device.ipNetworkDetector = {
|
|
488
|
+
supportsIpV4: false,
|
|
489
|
+
supportsIpV6: false,
|
|
490
|
+
firstIpV4: -1,
|
|
491
|
+
firstIpV6: -1,
|
|
492
|
+
firstMdns: -1,
|
|
493
|
+
totalTime: -1,
|
|
494
|
+
detect: sinon.stub().resolves(),
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
clock = sinon.useFakeTimers();
|
|
498
|
+
|
|
499
|
+
mockClusterReachabilityInstances = {};
|
|
500
|
+
|
|
501
|
+
clusterReachabilityCtorStub = sinon
|
|
502
|
+
.stub(ClusterReachabilityModule, 'ClusterReachability')
|
|
503
|
+
.callsFake((id, cluster) => {
|
|
504
|
+
const mockInstance = new MockClusterReachability(id, cluster);
|
|
505
|
+
|
|
506
|
+
mockClusterReachabilityInstances[id] = mockInstance;
|
|
507
|
+
return mockInstance;
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
webex.config.meetings.experimental = {
|
|
511
|
+
enableTcpReachability: false,
|
|
512
|
+
enableTlsReachability: false,
|
|
513
|
+
};
|
|
514
|
+
});
|
|
515
|
+
|
|
516
|
+
afterEach(() => {
|
|
517
|
+
sinon.restore();
|
|
518
|
+
clock.restore();
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
// simulates time progression so that Reachability times out
|
|
522
|
+
const simulateTimeout = async () => {
|
|
523
|
+
await testUtils.flushPromises();
|
|
524
|
+
clock.tick(3000);
|
|
525
|
+
};
|
|
526
|
+
|
|
527
|
+
const checkResults = async (expectedResults, expectedJoinCookie) => {
|
|
528
|
+
const storedResultForReachabilityResult = await webex.boundedStorage.get(
|
|
529
|
+
'Reachability',
|
|
530
|
+
'reachability.result'
|
|
531
|
+
);
|
|
532
|
+
const storedResultForJoinCookie = await webex.boundedStorage.get(
|
|
533
|
+
'Reachability',
|
|
534
|
+
'reachability.joinCookie'
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
assert.equal(storedResultForReachabilityResult, JSON.stringify(expectedResults));
|
|
538
|
+
assert.equal(storedResultForJoinCookie, JSON.stringify(expectedJoinCookie));
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
[
|
|
542
|
+
// ========================================================================
|
|
543
|
+
{
|
|
544
|
+
title: '1 cluster with events triggered for each protocol',
|
|
545
|
+
waitShortTimeout: false,
|
|
546
|
+
waitLongTimeout: false,
|
|
547
|
+
mockClusters: {
|
|
548
|
+
cluster1: {
|
|
549
|
+
udp: ['udp-url1'],
|
|
550
|
+
tcp: ['tcp-url1'],
|
|
551
|
+
xtls: ['xtls-url1'],
|
|
552
|
+
isVideoMesh: false,
|
|
553
|
+
},
|
|
554
|
+
},
|
|
555
|
+
mockResultReadyEvents: [
|
|
556
|
+
{
|
|
557
|
+
clusterId: 'cluster1',
|
|
558
|
+
protocol: 'tcp',
|
|
559
|
+
result: {
|
|
560
|
+
result: 'reachable',
|
|
561
|
+
latencyInMilliseconds: 11,
|
|
562
|
+
},
|
|
563
|
+
},
|
|
564
|
+
{
|
|
565
|
+
clusterId: 'cluster1',
|
|
566
|
+
protocol: 'udp',
|
|
567
|
+
result: {
|
|
568
|
+
result: 'reachable',
|
|
569
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
570
|
+
latencyInMilliseconds: 22,
|
|
571
|
+
},
|
|
572
|
+
},
|
|
573
|
+
{
|
|
574
|
+
clusterId: 'cluster1',
|
|
575
|
+
protocol: 'xtls',
|
|
576
|
+
result: {
|
|
577
|
+
result: 'reachable',
|
|
578
|
+
latencyInMilliseconds: 33,
|
|
579
|
+
},
|
|
580
|
+
},
|
|
581
|
+
],
|
|
582
|
+
expectedResults: {
|
|
583
|
+
cluster1: {
|
|
584
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 22},
|
|
585
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 11},
|
|
586
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 33},
|
|
587
|
+
isVideoMesh: false,
|
|
588
|
+
},
|
|
589
|
+
},
|
|
590
|
+
expectedMetrics: {
|
|
591
|
+
vmn_udp_min: -1,
|
|
592
|
+
vmn_udp_max: -1,
|
|
593
|
+
vmn_udp_average: -1,
|
|
594
|
+
public_udp_min: 22,
|
|
595
|
+
public_udp_max: 22,
|
|
596
|
+
public_udp_average: 22,
|
|
597
|
+
public_tcp_min: 11,
|
|
598
|
+
public_tcp_max: 11,
|
|
599
|
+
public_tcp_average: 11,
|
|
600
|
+
public_xtls_min: 33,
|
|
601
|
+
public_xtls_max: 33,
|
|
602
|
+
public_xtls_average: 33,
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
// ========================================================================
|
|
606
|
+
{
|
|
607
|
+
title:
|
|
608
|
+
'3 clusters: one with an event for each protocol, one with no events, one with no urls for tcp and xtls',
|
|
609
|
+
waitShortTimeout: 'public',
|
|
610
|
+
waitLongTimeout: true,
|
|
611
|
+
mockClusters: {
|
|
612
|
+
cluster1: {
|
|
613
|
+
udp: ['udp-url1.1', 'udp-url1.2'],
|
|
614
|
+
tcp: ['tcp-url1.1', 'tcp-url1.2'],
|
|
615
|
+
xtls: ['xtls-url1.1', 'xtls-url1.2'],
|
|
616
|
+
isVideoMesh: false,
|
|
617
|
+
},
|
|
618
|
+
cluster2: {
|
|
619
|
+
udp: ['udp-url2.1'],
|
|
620
|
+
tcp: ['tcp-url2.1'],
|
|
621
|
+
xtls: ['xtls-url2.1'],
|
|
622
|
+
isVideoMesh: false,
|
|
623
|
+
},
|
|
624
|
+
cluster3: {
|
|
625
|
+
udp: ['udp-url1'],
|
|
626
|
+
tcp: [],
|
|
627
|
+
xtls: [],
|
|
628
|
+
isVideoMesh: true,
|
|
629
|
+
},
|
|
630
|
+
},
|
|
631
|
+
mockResultReadyEvents: [
|
|
632
|
+
{
|
|
633
|
+
clusterId: 'cluster1',
|
|
634
|
+
protocol: 'udp',
|
|
635
|
+
result: {
|
|
636
|
+
result: 'reachable',
|
|
637
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
638
|
+
latencyInMilliseconds: 13,
|
|
639
|
+
},
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
clusterId: 'cluster1',
|
|
643
|
+
protocol: 'tcp',
|
|
644
|
+
result: {
|
|
645
|
+
result: 'reachable',
|
|
646
|
+
latencyInMilliseconds: 53,
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
{
|
|
650
|
+
clusterId: 'cluster1',
|
|
651
|
+
protocol: 'xtls',
|
|
652
|
+
result: {
|
|
653
|
+
result: 'reachable',
|
|
654
|
+
latencyInMilliseconds: 113,
|
|
655
|
+
},
|
|
656
|
+
},
|
|
657
|
+
],
|
|
658
|
+
expectedResults: {
|
|
659
|
+
cluster1: {
|
|
660
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 13},
|
|
661
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 53},
|
|
662
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 113},
|
|
663
|
+
isVideoMesh: false,
|
|
664
|
+
},
|
|
665
|
+
cluster2: {
|
|
666
|
+
udp: {result: 'unreachable'},
|
|
667
|
+
tcp: {result: 'unreachable'},
|
|
668
|
+
xtls: {result: 'unreachable'},
|
|
669
|
+
isVideoMesh: false,
|
|
670
|
+
},
|
|
671
|
+
cluster3: {
|
|
672
|
+
udp: {result: 'unreachable'},
|
|
673
|
+
tcp: {result: 'untested'},
|
|
674
|
+
xtls: {result: 'untested'},
|
|
675
|
+
isVideoMesh: true,
|
|
676
|
+
},
|
|
677
|
+
},
|
|
678
|
+
expectedMetrics: {
|
|
679
|
+
vmn_udp_min: -1,
|
|
680
|
+
vmn_udp_max: -1,
|
|
681
|
+
vmn_udp_average: -1,
|
|
682
|
+
public_udp_min: 13,
|
|
683
|
+
public_udp_max: 13,
|
|
684
|
+
public_udp_average: 13,
|
|
685
|
+
public_tcp_min: 53,
|
|
686
|
+
public_tcp_max: 53,
|
|
687
|
+
public_tcp_average: 53,
|
|
688
|
+
public_xtls_min: 113,
|
|
689
|
+
public_xtls_max: 113,
|
|
690
|
+
public_xtls_average: 113,
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
// ========================================================================
|
|
694
|
+
{
|
|
695
|
+
title: '3 clusters: all with all results ready in time for all protocols',
|
|
696
|
+
waitShortTimeout: false,
|
|
697
|
+
waitLongTimeout: false,
|
|
698
|
+
mockClusters: {
|
|
699
|
+
cluster1: {
|
|
700
|
+
udp: ['udp-url1'],
|
|
701
|
+
tcp: ['tcp-url1'],
|
|
702
|
+
xtls: ['xtls-url1'],
|
|
703
|
+
isVideoMesh: false,
|
|
704
|
+
},
|
|
705
|
+
cluster2: {
|
|
706
|
+
udp: ['udp-url2'],
|
|
707
|
+
tcp: ['tcp-url2'],
|
|
708
|
+
xtls: ['xtls-url2'],
|
|
709
|
+
isVideoMesh: false,
|
|
710
|
+
},
|
|
711
|
+
cluster3: {
|
|
712
|
+
udp: ['udp-url3'],
|
|
713
|
+
tcp: ['tcp-url3'],
|
|
714
|
+
xtls: ['xtls-url3'],
|
|
715
|
+
isVideoMesh: false,
|
|
716
|
+
},
|
|
717
|
+
},
|
|
718
|
+
mockResultReadyEvents: [
|
|
719
|
+
{
|
|
720
|
+
clusterId: 'cluster1',
|
|
721
|
+
protocol: 'udp',
|
|
722
|
+
result: {
|
|
723
|
+
result: 'reachable',
|
|
724
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
725
|
+
latencyInMilliseconds: 10,
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
clusterId: 'cluster1',
|
|
730
|
+
protocol: 'tcp',
|
|
731
|
+
result: {
|
|
732
|
+
result: 'reachable',
|
|
733
|
+
latencyInMilliseconds: 100,
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
clusterId: 'cluster1',
|
|
738
|
+
protocol: 'xtls',
|
|
739
|
+
result: {
|
|
740
|
+
result: 'reachable',
|
|
741
|
+
latencyInMilliseconds: 200,
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
clusterId: 'cluster2',
|
|
746
|
+
protocol: 'udp',
|
|
747
|
+
result: {
|
|
748
|
+
result: 'reachable',
|
|
749
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
750
|
+
latencyInMilliseconds: 20,
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
clusterId: 'cluster2',
|
|
755
|
+
protocol: 'tcp',
|
|
756
|
+
result: {
|
|
757
|
+
result: 'reachable',
|
|
758
|
+
latencyInMilliseconds: 110,
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
clusterId: 'cluster2',
|
|
763
|
+
protocol: 'xtls',
|
|
764
|
+
result: {
|
|
765
|
+
result: 'reachable',
|
|
766
|
+
latencyInMilliseconds: 220,
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
{
|
|
770
|
+
clusterId: 'cluster3',
|
|
771
|
+
protocol: 'udp',
|
|
772
|
+
result: {
|
|
773
|
+
result: 'reachable',
|
|
774
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
775
|
+
latencyInMilliseconds: 30,
|
|
776
|
+
},
|
|
777
|
+
},
|
|
778
|
+
{
|
|
779
|
+
clusterId: 'cluster3',
|
|
780
|
+
protocol: 'tcp',
|
|
781
|
+
result: {
|
|
782
|
+
result: 'reachable',
|
|
783
|
+
latencyInMilliseconds: 120,
|
|
784
|
+
},
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
clusterId: 'cluster3',
|
|
788
|
+
protocol: 'xtls',
|
|
789
|
+
result: {
|
|
790
|
+
result: 'reachable',
|
|
791
|
+
latencyInMilliseconds: 240,
|
|
792
|
+
},
|
|
793
|
+
},
|
|
794
|
+
],
|
|
795
|
+
expectedResults: {
|
|
796
|
+
cluster1: {
|
|
797
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 10},
|
|
798
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 100},
|
|
799
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 200},
|
|
800
|
+
isVideoMesh: false,
|
|
801
|
+
},
|
|
802
|
+
cluster2: {
|
|
803
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 20},
|
|
804
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 110},
|
|
805
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 220},
|
|
806
|
+
isVideoMesh: false,
|
|
807
|
+
},
|
|
808
|
+
cluster3: {
|
|
809
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 30},
|
|
810
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 120},
|
|
811
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 240},
|
|
812
|
+
isVideoMesh: false,
|
|
813
|
+
},
|
|
814
|
+
},
|
|
815
|
+
expectedMetrics: {
|
|
816
|
+
vmn_udp_min: -1,
|
|
817
|
+
vmn_udp_max: -1,
|
|
818
|
+
vmn_udp_average: -1,
|
|
819
|
+
public_udp_min: 10,
|
|
820
|
+
public_udp_max: 30,
|
|
821
|
+
public_udp_average: 20,
|
|
822
|
+
public_tcp_min: 100,
|
|
823
|
+
public_tcp_max: 120,
|
|
824
|
+
public_tcp_average: 110,
|
|
825
|
+
public_xtls_min: 200,
|
|
826
|
+
public_xtls_max: 240,
|
|
827
|
+
public_xtls_average: 220,
|
|
828
|
+
},
|
|
829
|
+
},
|
|
830
|
+
// ========================================================================
|
|
831
|
+
{
|
|
832
|
+
title: '2 clusters: both with no results at all',
|
|
833
|
+
waitShortTimeout: 'public',
|
|
834
|
+
waitLongTimeout: true,
|
|
835
|
+
mockClusters: {
|
|
836
|
+
cluster1: {
|
|
837
|
+
udp: ['udp-url1'],
|
|
838
|
+
tcp: ['tcp-url1'],
|
|
839
|
+
xtls: ['xtls-url1'],
|
|
840
|
+
isVideoMesh: false,
|
|
841
|
+
},
|
|
842
|
+
cluster2: {
|
|
843
|
+
udp: ['udp-url2'],
|
|
844
|
+
tcp: ['tcp-url2'],
|
|
845
|
+
xtls: ['xtls-url2'],
|
|
846
|
+
isVideoMesh: false,
|
|
847
|
+
},
|
|
848
|
+
},
|
|
849
|
+
mockResultReadyEvents: [],
|
|
850
|
+
expectedResults: {
|
|
851
|
+
cluster1: {
|
|
852
|
+
udp: {result: 'unreachable'},
|
|
853
|
+
tcp: {result: 'unreachable'},
|
|
854
|
+
xtls: {result: 'unreachable'},
|
|
855
|
+
isVideoMesh: false,
|
|
856
|
+
},
|
|
857
|
+
cluster2: {
|
|
858
|
+
udp: {result: 'unreachable'},
|
|
859
|
+
tcp: {result: 'unreachable'},
|
|
860
|
+
xtls: {result: 'unreachable'},
|
|
861
|
+
isVideoMesh: false,
|
|
862
|
+
},
|
|
863
|
+
},
|
|
864
|
+
expectedMetrics: {
|
|
865
|
+
vmn_udp_min: -1,
|
|
866
|
+
vmn_udp_max: -1,
|
|
867
|
+
vmn_udp_average: -1,
|
|
868
|
+
public_udp_min: -1,
|
|
869
|
+
public_udp_max: -1,
|
|
870
|
+
public_udp_average: -1,
|
|
871
|
+
public_tcp_min: -1,
|
|
872
|
+
public_tcp_max: -1,
|
|
873
|
+
public_tcp_average: -1,
|
|
874
|
+
public_xtls_min: -1,
|
|
875
|
+
public_xtls_max: -1,
|
|
876
|
+
public_xtls_average: -1,
|
|
877
|
+
},
|
|
878
|
+
},
|
|
879
|
+
// ========================================================================
|
|
880
|
+
{
|
|
881
|
+
title:
|
|
882
|
+
'3 clusters: 2 VMN clusters missing results, but the public one has all results within 1s',
|
|
883
|
+
waitShortTimeout: 'vmn',
|
|
884
|
+
waitLongTimeout: true,
|
|
885
|
+
mockClusters: {
|
|
886
|
+
vmnCluster1: {
|
|
887
|
+
udp: ['udp-url1'],
|
|
888
|
+
tcp: ['tcp-url1'],
|
|
889
|
+
xtls: ['xtls-url1'],
|
|
890
|
+
isVideoMesh: true,
|
|
891
|
+
},
|
|
892
|
+
publicCluster: {
|
|
893
|
+
udp: ['udp-url2'],
|
|
894
|
+
tcp: ['tcp-url2'],
|
|
895
|
+
xtls: ['xtls-url2'],
|
|
896
|
+
isVideoMesh: false,
|
|
897
|
+
},
|
|
898
|
+
vmnCluster2: {
|
|
899
|
+
udp: ['udp-url3'],
|
|
900
|
+
tcp: ['tcp-url3'],
|
|
901
|
+
xtls: ['xtls-url3'],
|
|
902
|
+
isVideoMesh: true,
|
|
903
|
+
},
|
|
904
|
+
},
|
|
905
|
+
mockResultReadyEvents: [
|
|
906
|
+
{
|
|
907
|
+
clusterId: 'publicCluster',
|
|
908
|
+
protocol: 'udp',
|
|
909
|
+
result: {
|
|
910
|
+
result: 'reachable',
|
|
911
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
912
|
+
latencyInMilliseconds: 10,
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
{
|
|
916
|
+
clusterId: 'publicCluster',
|
|
917
|
+
protocol: 'tcp',
|
|
918
|
+
result: {
|
|
919
|
+
result: 'reachable',
|
|
920
|
+
latencyInMilliseconds: 100,
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
{
|
|
924
|
+
clusterId: 'publicCluster',
|
|
925
|
+
protocol: 'xtls',
|
|
926
|
+
result: {
|
|
927
|
+
result: 'reachable',
|
|
928
|
+
latencyInMilliseconds: 200,
|
|
929
|
+
},
|
|
930
|
+
},
|
|
931
|
+
],
|
|
932
|
+
expectedResults: {
|
|
933
|
+
vmnCluster1: {
|
|
934
|
+
udp: {result: 'unreachable'},
|
|
935
|
+
tcp: {result: 'untested'},
|
|
936
|
+
xtls: {result: 'untested'},
|
|
937
|
+
isVideoMesh: true,
|
|
938
|
+
},
|
|
939
|
+
publicCluster: {
|
|
940
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 10},
|
|
941
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 100},
|
|
942
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 200},
|
|
943
|
+
isVideoMesh: false,
|
|
944
|
+
},
|
|
945
|
+
vmnCluster2: {
|
|
946
|
+
udp: {result: 'unreachable'},
|
|
947
|
+
tcp: {result: 'untested'},
|
|
948
|
+
xtls: {result: 'untested'},
|
|
949
|
+
isVideoMesh: true,
|
|
950
|
+
},
|
|
951
|
+
},
|
|
952
|
+
expectedMetrics: {
|
|
953
|
+
vmn_udp_min: -1,
|
|
954
|
+
vmn_udp_max: -1,
|
|
955
|
+
vmn_udp_average: -1,
|
|
956
|
+
public_udp_min: 10,
|
|
957
|
+
public_udp_max: 10,
|
|
958
|
+
public_udp_average: 10,
|
|
959
|
+
public_tcp_min: 100,
|
|
960
|
+
public_tcp_max: 100,
|
|
961
|
+
public_tcp_average: 100,
|
|
962
|
+
public_xtls_min: 200,
|
|
963
|
+
public_xtls_max: 200,
|
|
964
|
+
public_xtls_average: 200,
|
|
965
|
+
},
|
|
966
|
+
},
|
|
967
|
+
// ========================================================================
|
|
968
|
+
{
|
|
969
|
+
title: '2 VMN clusters with all results',
|
|
970
|
+
waitShortTimeout: false,
|
|
971
|
+
waitLongTimeout: false,
|
|
972
|
+
mockClusters: {
|
|
973
|
+
vmnCluster1: {
|
|
974
|
+
udp: ['udp-url1'],
|
|
975
|
+
tcp: [],
|
|
976
|
+
xtls: [],
|
|
977
|
+
isVideoMesh: true,
|
|
978
|
+
},
|
|
979
|
+
vmnCluster2: {
|
|
980
|
+
udp: ['udp-url3'],
|
|
981
|
+
tcp: [],
|
|
982
|
+
xtls: [],
|
|
983
|
+
isVideoMesh: true,
|
|
984
|
+
},
|
|
985
|
+
},
|
|
986
|
+
mockResultReadyEvents: [
|
|
987
|
+
{
|
|
988
|
+
clusterId: 'vmnCluster1',
|
|
989
|
+
protocol: 'udp',
|
|
990
|
+
result: {
|
|
991
|
+
result: 'reachable',
|
|
992
|
+
clientMediaIPs: ['192.168.10.1'],
|
|
993
|
+
latencyInMilliseconds: 100,
|
|
994
|
+
},
|
|
995
|
+
},
|
|
996
|
+
{
|
|
997
|
+
clusterId: 'vmnCluster2',
|
|
998
|
+
protocol: 'udp',
|
|
999
|
+
result: {
|
|
1000
|
+
result: 'reachable',
|
|
1001
|
+
clientMediaIPs: ['192.168.0.1'],
|
|
1002
|
+
latencyInMilliseconds: 300,
|
|
1003
|
+
},
|
|
1004
|
+
},
|
|
1005
|
+
],
|
|
1006
|
+
expectedResults: {
|
|
1007
|
+
vmnCluster1: {
|
|
1008
|
+
udp: {result: 'reachable', clientMediaIPs: ['192.168.10.1'], latencyInMilliseconds: 100},
|
|
1009
|
+
tcp: {result: 'untested'},
|
|
1010
|
+
xtls: {result: 'untested'},
|
|
1011
|
+
isVideoMesh: true,
|
|
1012
|
+
},
|
|
1013
|
+
vmnCluster2: {
|
|
1014
|
+
udp: {result: 'reachable', clientMediaIPs: ['192.168.0.1'], latencyInMilliseconds: 300},
|
|
1015
|
+
tcp: {result: 'untested'},
|
|
1016
|
+
xtls: {result: 'untested'},
|
|
1017
|
+
isVideoMesh: true,
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
expectedMetrics: {
|
|
1021
|
+
vmn_udp_min: 100,
|
|
1022
|
+
vmn_udp_max: 300,
|
|
1023
|
+
vmn_udp_average: 200,
|
|
1024
|
+
public_udp_min: -1,
|
|
1025
|
+
public_udp_max: -1,
|
|
1026
|
+
public_udp_average: -1,
|
|
1027
|
+
public_tcp_min: -1,
|
|
1028
|
+
public_tcp_max: -1,
|
|
1029
|
+
public_tcp_average: -1,
|
|
1030
|
+
public_xtls_min: -1,
|
|
1031
|
+
public_xtls_max: -1,
|
|
1032
|
+
public_xtls_average: -1,
|
|
1033
|
+
},
|
|
1034
|
+
},
|
|
1035
|
+
].forEach(
|
|
1036
|
+
({
|
|
1037
|
+
title,
|
|
1038
|
+
waitShortTimeout,
|
|
1039
|
+
waitLongTimeout,
|
|
1040
|
+
mockClusters,
|
|
1041
|
+
mockResultReadyEvents,
|
|
1042
|
+
expectedResults,
|
|
1043
|
+
expectedMetrics,
|
|
1044
|
+
}) =>
|
|
1045
|
+
it(`works correctly for the case: ${title}`, async () => {
|
|
1046
|
+
webex.config.meetings.experimental = {
|
|
1047
|
+
enableTcpReachability: true,
|
|
1048
|
+
enableTlsReachability: true,
|
|
1049
|
+
};
|
|
1050
|
+
|
|
1051
|
+
// the metrics related to ipver and trigger are not tested in these tests and are all the same, so setting them up here
|
|
1052
|
+
const expectedMetricsFull = {
|
|
1053
|
+
...expectedMetrics,
|
|
1054
|
+
ipver_firstIpV4: -1,
|
|
1055
|
+
ipver_firstIpV6: -1,
|
|
1056
|
+
ipver_firstMdns: -1,
|
|
1057
|
+
ipver_totalTime: -1,
|
|
1058
|
+
trigger: 'test',
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
const receivedEvents = {
|
|
1062
|
+
done: 0,
|
|
1063
|
+
firstResultAvailable: {
|
|
1064
|
+
udp: 0,
|
|
1065
|
+
tcp: 0,
|
|
1066
|
+
xtls: 0,
|
|
1067
|
+
},
|
|
1068
|
+
};
|
|
1069
|
+
|
|
1070
|
+
const reachability = new Reachability(webex);
|
|
1071
|
+
|
|
1072
|
+
reachability.on('reachability:done', () => {
|
|
1073
|
+
receivedEvents.done += 1;
|
|
1074
|
+
});
|
|
1075
|
+
reachability.on('reachability:firstResultAvailable', ({protocol}) => {
|
|
1076
|
+
receivedEvents.firstResultAvailable[protocol] += 1;
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
const mockGetClustersResult = {
|
|
1080
|
+
clusters: {},
|
|
1081
|
+
joinCookie: {id: 'id'},
|
|
1082
|
+
};
|
|
1083
|
+
|
|
1084
|
+
Object.entries(mockClusters).forEach(([id, mockCluster]) => {
|
|
1085
|
+
mockGetClustersResult.clusters[id] = mockCluster;
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns(mockGetClustersResult);
|
|
1089
|
+
|
|
1090
|
+
const resultPromise = reachability.gatherReachability('test');
|
|
1091
|
+
|
|
1092
|
+
await testUtils.flushPromises();
|
|
1093
|
+
|
|
1094
|
+
// check that ClusterReachability instance was created for each cluster
|
|
1095
|
+
Object.entries(mockClusters).forEach(([id, mockCluster]) => {
|
|
1096
|
+
assert.calledWith(clusterReachabilityCtorStub, id, mockCluster);
|
|
1097
|
+
});
|
|
1098
|
+
|
|
1099
|
+
// trigger mock result events from ClusterReachability instances
|
|
1100
|
+
mockResultReadyEvents.forEach((mockEvent) => {
|
|
1101
|
+
mockClusterReachabilityInstances[mockEvent.clusterId].emitFakeResult(
|
|
1102
|
+
mockEvent.protocol,
|
|
1103
|
+
mockEvent.result
|
|
1104
|
+
);
|
|
1105
|
+
});
|
|
1106
|
+
|
|
1107
|
+
if (waitShortTimeout === 'public') {
|
|
1108
|
+
clock.tick(3000);
|
|
1109
|
+
}
|
|
1110
|
+
if (waitShortTimeout === 'vmn') {
|
|
1111
|
+
clock.tick(1000);
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
await resultPromise;
|
|
1115
|
+
|
|
1116
|
+
await checkResults(expectedResults, mockGetClustersResult.joinCookie);
|
|
1117
|
+
|
|
1118
|
+
if (waitLongTimeout) {
|
|
1119
|
+
// we need to wait either 14 or 12 seconds to get to the 15s timeout (depending on how much we waited earlier)
|
|
1120
|
+
clock.tick(waitShortTimeout === 'vmn' ? 14000 : 12000);
|
|
1121
|
+
|
|
1122
|
+
// we check the results again after the long timeout - they should be the same
|
|
1123
|
+
await checkResults(expectedResults, mockGetClustersResult.joinCookie);
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
// now check events emitted by Reachability class
|
|
1127
|
+
assert.equal(receivedEvents['done'], 1);
|
|
1128
|
+
|
|
1129
|
+
// if we've mocked at least one event for any protocol, check that we received
|
|
1130
|
+
// firstResultAvailable event for that protocol
|
|
1131
|
+
if (mockResultReadyEvents.filter((event) => event.protocol === 'udp').length > 0) {
|
|
1132
|
+
assert.equal(receivedEvents['firstResultAvailable']['udp'], 1);
|
|
1133
|
+
}
|
|
1134
|
+
if (mockResultReadyEvents.filter((event) => event.protocol === 'tcp').length > 0) {
|
|
1135
|
+
assert.equal(receivedEvents['firstResultAvailable']['tcp'], 1);
|
|
1136
|
+
}
|
|
1137
|
+
if (mockResultReadyEvents.filter((event) => event.protocol === 'xtls').length > 0) {
|
|
1138
|
+
assert.equal(receivedEvents['firstResultAvailable']['xtls'], 1);
|
|
1139
|
+
}
|
|
1140
|
+
|
|
1141
|
+
// finally, check the metrics
|
|
1142
|
+
assert.calledWith(
|
|
1143
|
+
Metrics.sendBehavioralMetric,
|
|
1144
|
+
'js_sdk_reachability_completed',
|
|
1145
|
+
expectedMetricsFull
|
|
1146
|
+
);
|
|
1147
|
+
})
|
|
1148
|
+
);
|
|
1149
|
+
|
|
1150
|
+
it('sends the trigger parameter in the metrics', async () => {
|
|
1151
|
+
const reachability = new TestReachability(webex);
|
|
1152
|
+
|
|
1153
|
+
const mockGetClustersResult = {
|
|
1154
|
+
clusters: {
|
|
1155
|
+
clusterA: {
|
|
1156
|
+
udp: ['udp-url'],
|
|
1157
|
+
tcp: [],
|
|
1158
|
+
xtls: [],
|
|
1159
|
+
isVideoMesh: false,
|
|
1160
|
+
},
|
|
1161
|
+
},
|
|
1162
|
+
joinCookie: {id: 'id'},
|
|
1163
|
+
};
|
|
1164
|
+
|
|
1165
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns(mockGetClustersResult);
|
|
1166
|
+
|
|
1167
|
+
const resultPromise = reachability.gatherReachability('some trigger');
|
|
1168
|
+
|
|
1169
|
+
// let it time out
|
|
1170
|
+
await testUtils.flushPromises();
|
|
1171
|
+
clock.tick(15000);
|
|
1172
|
+
await resultPromise;
|
|
1173
|
+
|
|
1174
|
+
// check the metric contains the right trigger value
|
|
1175
|
+
assert.calledWith(
|
|
1176
|
+
Metrics.sendBehavioralMetric,
|
|
1177
|
+
'js_sdk_reachability_completed',
|
|
1178
|
+
sinon.match({trigger: 'some trigger'})
|
|
1179
|
+
);
|
|
1180
|
+
});
|
|
1181
|
+
|
|
1182
|
+
it(`starts ip network version detection and includes the results in the metrics`, async () => {
|
|
1183
|
+
webex.config.meetings.experimental = {
|
|
1184
|
+
enableTcpReachability: true,
|
|
1185
|
+
enableTlsReachability: true,
|
|
1186
|
+
};
|
|
1187
|
+
webex.internal.device.ipNetworkDetector = {
|
|
1188
|
+
supportsIpV4: true,
|
|
1189
|
+
supportsIpV6: true,
|
|
1190
|
+
firstIpV4: 10,
|
|
1191
|
+
firstIpV6: 20,
|
|
1192
|
+
firstMdns: 30,
|
|
1193
|
+
totalTime: 40,
|
|
1194
|
+
detect: sinon.stub().resolves(),
|
|
1195
|
+
};
|
|
1196
|
+
|
|
1197
|
+
const receivedEvents = {
|
|
1198
|
+
done: 0,
|
|
1199
|
+
};
|
|
1200
|
+
|
|
1201
|
+
const reachability = new Reachability(webex);
|
|
1202
|
+
|
|
1203
|
+
reachability.on('reachability:done', () => {
|
|
1204
|
+
receivedEvents.done += 1;
|
|
1205
|
+
});
|
|
1206
|
+
|
|
1207
|
+
// simulate having just 1 cluster, we don't need more for this test
|
|
1208
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns({
|
|
1209
|
+
clusters: {
|
|
1210
|
+
publicCluster: {
|
|
1211
|
+
udp: ['udp-url'],
|
|
1212
|
+
tcp: [],
|
|
1213
|
+
xtls: [],
|
|
1214
|
+
isVideoMesh: false,
|
|
1215
|
+
},
|
|
1216
|
+
},
|
|
1217
|
+
joinCookie: {id: 'id'},
|
|
1218
|
+
});
|
|
1219
|
+
|
|
1220
|
+
const resultPromise = reachability.gatherReachability('test');
|
|
1221
|
+
|
|
1222
|
+
await testUtils.flushPromises();
|
|
1223
|
+
|
|
1224
|
+
// trigger mock result events from ClusterReachability instance
|
|
1225
|
+
mockClusterReachabilityInstances['publicCluster'].emitFakeResult('udp', {
|
|
1226
|
+
result: 'reachable',
|
|
1227
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
1228
|
+
latencyInMilliseconds: 100,
|
|
1229
|
+
});
|
|
1230
|
+
|
|
1231
|
+
await resultPromise;
|
|
1232
|
+
|
|
1233
|
+
// check events emitted by Reachability class
|
|
1234
|
+
assert.equal(receivedEvents['done'], 1);
|
|
1235
|
+
|
|
1236
|
+
// and that ip network detection was started
|
|
1237
|
+
assert.calledOnceWithExactly(webex.internal.device.ipNetworkDetector.detect);
|
|
1238
|
+
|
|
1239
|
+
// finally, check the metrics - they should contain values from ipNetworkDetector
|
|
1240
|
+
assert.calledWith(Metrics.sendBehavioralMetric, 'js_sdk_reachability_completed', {
|
|
1241
|
+
vmn_udp_min: -1,
|
|
1242
|
+
vmn_udp_max: -1,
|
|
1243
|
+
vmn_udp_average: -1,
|
|
1244
|
+
public_udp_min: 100,
|
|
1245
|
+
public_udp_max: 100,
|
|
1246
|
+
public_udp_average: 100,
|
|
1247
|
+
public_tcp_min: -1,
|
|
1248
|
+
public_tcp_max: -1,
|
|
1249
|
+
public_tcp_average: -1,
|
|
1250
|
+
public_xtls_min: -1,
|
|
1251
|
+
public_xtls_max: -1,
|
|
1252
|
+
public_xtls_average: -1,
|
|
1253
|
+
ipver_firstIpV4: webex.internal.device.ipNetworkDetector.firstIpV4,
|
|
1254
|
+
ipver_firstIpV6: webex.internal.device.ipNetworkDetector.firstIpV6,
|
|
1255
|
+
ipver_firstMdns: webex.internal.device.ipNetworkDetector.firstMdns,
|
|
1256
|
+
ipver_totalTime: webex.internal.device.ipNetworkDetector.totalTime,
|
|
1257
|
+
trigger: 'test',
|
|
1258
|
+
});
|
|
1259
|
+
});
|
|
1260
|
+
|
|
1261
|
+
it('keeps updating reachability results after the 3s public cloud timeout expires', async () => {
|
|
1262
|
+
webex.config.meetings.experimental = {
|
|
1263
|
+
enableTcpReachability: true,
|
|
1264
|
+
enableTlsReachability: true,
|
|
1265
|
+
};
|
|
1266
|
+
|
|
1267
|
+
const reachability = new Reachability(webex);
|
|
1268
|
+
|
|
1269
|
+
const mockGetClustersResult = {
|
|
1270
|
+
clusters: {
|
|
1271
|
+
clusterA: {
|
|
1272
|
+
udp: ['udp-urlA'],
|
|
1273
|
+
tcp: ['tcp-urlA'],
|
|
1274
|
+
xtls: ['xtls-urlA'],
|
|
1275
|
+
isVideoMesh: false,
|
|
1276
|
+
},
|
|
1277
|
+
clusterB: {
|
|
1278
|
+
udp: ['udp-urlB'],
|
|
1279
|
+
tcp: ['tcp-urlB'],
|
|
1280
|
+
xtls: ['xtls-urlB'],
|
|
1281
|
+
isVideoMesh: false,
|
|
1282
|
+
},
|
|
1283
|
+
},
|
|
1284
|
+
joinCookie: {id: 'id'},
|
|
1285
|
+
};
|
|
1286
|
+
|
|
1287
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns(mockGetClustersResult);
|
|
1288
|
+
|
|
1289
|
+
const resultPromise = reachability.gatherReachability('test');
|
|
1290
|
+
|
|
1291
|
+
await testUtils.flushPromises();
|
|
1292
|
+
|
|
1293
|
+
// trigger some mock result events from ClusterReachability instances
|
|
1294
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeResult('udp', {
|
|
1295
|
+
result: 'reachable',
|
|
1296
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
1297
|
+
latencyInMilliseconds: 11,
|
|
1298
|
+
});
|
|
1299
|
+
mockClusterReachabilityInstances['clusterB'].emitFakeResult('udp', {
|
|
1300
|
+
result: 'reachable',
|
|
1301
|
+
clientMediaIPs: ['10.20.30.40'],
|
|
1302
|
+
latencyInMilliseconds: 22,
|
|
1303
|
+
});
|
|
1304
|
+
|
|
1305
|
+
clock.tick(3000);
|
|
1306
|
+
await resultPromise;
|
|
1307
|
+
|
|
1308
|
+
// check that the reachability results contain the 2 results from above
|
|
1309
|
+
await checkResults(
|
|
1310
|
+
{
|
|
1311
|
+
clusterA: {
|
|
1312
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 11},
|
|
1313
|
+
tcp: {result: 'unreachable'},
|
|
1314
|
+
xtls: {result: 'unreachable'},
|
|
1315
|
+
isVideoMesh: false,
|
|
1316
|
+
},
|
|
1317
|
+
clusterB: {
|
|
1318
|
+
udp: {result: 'reachable', clientMediaIPs: ['10.20.30.40'], latencyInMilliseconds: 22},
|
|
386
1319
|
tcp: {result: 'unreachable'},
|
|
387
1320
|
xtls: {result: 'unreachable'},
|
|
1321
|
+
isVideoMesh: false,
|
|
388
1322
|
},
|
|
389
1323
|
},
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
{
|
|
393
|
-
title: 'multiple clusters with some missing results',
|
|
394
|
-
mockStorage: {
|
|
395
|
-
a: {udp: {result: 'unreachable'}},
|
|
396
|
-
b: {tcp: {result: 'unreachable'}},
|
|
397
|
-
c: {xtls: {result: 'unreachable'}},
|
|
398
|
-
d: {},
|
|
399
|
-
},
|
|
400
|
-
expectedResult: true,
|
|
401
|
-
},
|
|
402
|
-
].forEach(({mockStorage, expectedResult, title}) => {
|
|
403
|
-
it(`returns ${expectedResult} when ${title}`, async () => {
|
|
404
|
-
await runCheck(mockStorage, expectedResult);
|
|
405
|
-
});
|
|
406
|
-
});
|
|
407
|
-
});
|
|
408
|
-
|
|
1324
|
+
mockGetClustersResult.joinCookie
|
|
1325
|
+
);
|
|
409
1326
|
|
|
410
|
-
|
|
411
|
-
|
|
1327
|
+
// now simulate some more "late" results
|
|
1328
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeResult('tcp', {
|
|
1329
|
+
result: 'reachable',
|
|
1330
|
+
latencyInMilliseconds: 101,
|
|
1331
|
+
});
|
|
1332
|
+
mockClusterReachabilityInstances['clusterB'].emitFakeResult('xtls', {
|
|
1333
|
+
result: 'reachable',
|
|
1334
|
+
latencyInMilliseconds: 102,
|
|
1335
|
+
});
|
|
412
1336
|
|
|
413
|
-
|
|
414
|
-
|
|
1337
|
+
// and wait for the final overall timeout
|
|
1338
|
+
clock.tick(12000);
|
|
415
1339
|
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
1340
|
+
// the reachability results should include all results from above (including the late ones)
|
|
1341
|
+
await checkResults(
|
|
1342
|
+
{
|
|
1343
|
+
clusterA: {
|
|
1344
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 11},
|
|
1345
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 101},
|
|
1346
|
+
xtls: {result: 'unreachable'},
|
|
1347
|
+
isVideoMesh: false,
|
|
1348
|
+
},
|
|
1349
|
+
clusterB: {
|
|
1350
|
+
udp: {result: 'reachable', clientMediaIPs: ['10.20.30.40'], latencyInMilliseconds: 22},
|
|
1351
|
+
tcp: {result: 'unreachable'},
|
|
1352
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 102},
|
|
1353
|
+
isVideoMesh: false,
|
|
1354
|
+
},
|
|
1355
|
+
},
|
|
1356
|
+
mockGetClustersResult.joinCookie
|
|
425
1357
|
);
|
|
426
1358
|
});
|
|
427
1359
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
1360
|
+
it('handles clientMediaIpsUpdated event by updating clientMediaIps in results', async () => {
|
|
1361
|
+
webex.config.meetings.experimental = {
|
|
1362
|
+
enableTcpReachability: true,
|
|
1363
|
+
enableTlsReachability: true,
|
|
1364
|
+
};
|
|
431
1365
|
|
|
432
|
-
it('stores the reachability', async () => {
|
|
433
1366
|
const reachability = new Reachability(webex);
|
|
434
1367
|
|
|
435
|
-
const
|
|
1368
|
+
const mockGetClustersResult = {
|
|
436
1369
|
clusters: {
|
|
437
|
-
|
|
438
|
-
udp: '
|
|
1370
|
+
clusterA: {
|
|
1371
|
+
udp: ['udp-urlA'],
|
|
1372
|
+
tcp: ['tcp-urlA'],
|
|
1373
|
+
xtls: ['xtls-urlA'],
|
|
1374
|
+
isVideoMesh: false,
|
|
439
1375
|
},
|
|
440
1376
|
},
|
|
441
|
-
};
|
|
442
|
-
const getClustersResult = {
|
|
443
|
-
clusters: {clusterId: 'cluster'},
|
|
444
1377
|
joinCookie: {id: 'id'},
|
|
445
1378
|
};
|
|
446
1379
|
|
|
447
|
-
reachability.reachabilityRequest.getClusters = sinon.stub().returns(
|
|
448
|
-
(reachability as any).performReachabilityChecks = sinon.stub().returns(reachabilityResults);
|
|
1380
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns(mockGetClustersResult);
|
|
449
1381
|
|
|
450
|
-
const
|
|
1382
|
+
const resultPromise = reachability.gatherReachability('test');
|
|
451
1383
|
|
|
452
|
-
|
|
1384
|
+
await testUtils.flushPromises();
|
|
453
1385
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
);
|
|
1386
|
+
// trigger a mock result event
|
|
1387
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeResult('udp', {
|
|
1388
|
+
result: 'reachable',
|
|
1389
|
+
clientMediaIPs: ['64.103.40.20'],
|
|
1390
|
+
latencyInMilliseconds: 11,
|
|
1391
|
+
});
|
|
1392
|
+
// followed by some updates to client media IPs
|
|
1393
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeClientMediaIpUpdate('udp', '64.103.40.21');
|
|
1394
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeClientMediaIpUpdate('udp', '64.103.40.22');
|
|
1395
|
+
|
|
1396
|
+
// wait for the final overall timeout
|
|
1397
|
+
clock.tick(15000);
|
|
1398
|
+
await resultPromise;
|
|
462
1399
|
|
|
463
|
-
|
|
464
|
-
|
|
1400
|
+
// check that the reachability results contain all the client media ips
|
|
1401
|
+
await checkResults(
|
|
1402
|
+
{
|
|
1403
|
+
clusterA: {
|
|
1404
|
+
udp: {
|
|
1405
|
+
result: 'reachable',
|
|
1406
|
+
clientMediaIPs: ['64.103.40.20', '64.103.40.21', '64.103.40.22'],
|
|
1407
|
+
latencyInMilliseconds: 11,
|
|
1408
|
+
},
|
|
1409
|
+
tcp: {result: 'unreachable'},
|
|
1410
|
+
xtls: {result: 'unreachable'},
|
|
1411
|
+
isVideoMesh: false,
|
|
1412
|
+
},
|
|
1413
|
+
},
|
|
1414
|
+
mockGetClustersResult.joinCookie
|
|
1415
|
+
);
|
|
465
1416
|
});
|
|
466
1417
|
|
|
467
1418
|
it('keeps the stored reachability from previous call to gatherReachability if getClusters fails', async () => {
|
|
468
1419
|
const reachability = new Reachability(webex);
|
|
469
1420
|
|
|
470
|
-
const reachabilityResults = {
|
|
471
|
-
clusters: {
|
|
472
|
-
clusterId: {
|
|
473
|
-
udp: 'testUDP',
|
|
474
|
-
},
|
|
475
|
-
},
|
|
476
|
-
};
|
|
477
|
-
const getClustersResult = {
|
|
478
|
-
clusters: {clusterId: 'cluster'},
|
|
479
|
-
joinCookie: {id: 'id'},
|
|
480
|
-
};
|
|
481
|
-
|
|
482
1421
|
reachability.reachabilityRequest.getClusters = sinon.stub().throws();
|
|
483
1422
|
|
|
484
|
-
const result = await reachability.gatherReachability();
|
|
1423
|
+
const result = await reachability.gatherReachability('test');
|
|
485
1424
|
|
|
486
1425
|
assert.empty(result);
|
|
487
1426
|
|
|
488
|
-
|
|
489
|
-
'Reachability',
|
|
490
|
-
'reachability.result'
|
|
491
|
-
);
|
|
492
|
-
const storedResultForJoinCookie = await webex.boundedStorage.get(
|
|
493
|
-
'Reachability',
|
|
494
|
-
'reachability.joinCookie'
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
assert.equal(JSON.stringify({old: 'results'}), storedResultForReachabilityResult);
|
|
498
|
-
assert.equal(JSON.stringify({old: 'joinCookie'}), storedResultForJoinCookie);
|
|
1427
|
+
await checkResults({old: 'results'}, {old: 'joinCookie'});
|
|
499
1428
|
});
|
|
500
1429
|
|
|
501
1430
|
it('keeps the stored reachability from previous call to gatherReachability if performReachabilityChecks fails', async () => {
|
|
502
1431
|
const reachability = new Reachability(webex);
|
|
503
1432
|
|
|
504
|
-
const reachabilityResults = {
|
|
505
|
-
clusters: {
|
|
506
|
-
clusterId: {
|
|
507
|
-
udp: 'testUDP',
|
|
508
|
-
},
|
|
509
|
-
},
|
|
510
|
-
};
|
|
511
1433
|
const getClustersResult = {
|
|
512
1434
|
clusters: {clusterId: 'cluster'},
|
|
513
|
-
joinCookie: {id: 'id'},
|
|
1435
|
+
joinCookie: {id: 'cookie id'},
|
|
514
1436
|
};
|
|
515
1437
|
|
|
516
1438
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
517
1439
|
(reachability as any).performReachabilityChecks = sinon.stub().throws();
|
|
518
1440
|
|
|
519
|
-
const result = await reachability.gatherReachability();
|
|
1441
|
+
const result = await reachability.gatherReachability('test');
|
|
520
1442
|
|
|
521
1443
|
assert.empty(result);
|
|
522
1444
|
|
|
523
|
-
|
|
524
|
-
'Reachability',
|
|
525
|
-
'reachability.result'
|
|
526
|
-
);
|
|
527
|
-
const storedResultForJoinCookie = await webex.boundedStorage.get(
|
|
528
|
-
'Reachability',
|
|
529
|
-
'reachability.joinCookie'
|
|
530
|
-
);
|
|
531
|
-
|
|
532
|
-
assert.equal(JSON.stringify({old: 'results'}), storedResultForReachabilityResult);
|
|
533
|
-
assert.equal(JSON.stringify({old: 'joinCookie'}), storedResultForJoinCookie);
|
|
1445
|
+
await checkResults({old: 'results'}, {id: 'cookie id'});
|
|
534
1446
|
});
|
|
535
1447
|
|
|
536
1448
|
it('starts ClusterReachability on each media cluster', async () => {
|
|
@@ -561,14 +1473,10 @@ describe('gatherReachability', () => {
|
|
|
561
1473
|
|
|
562
1474
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
563
1475
|
|
|
564
|
-
const
|
|
565
|
-
const clusterReachabilityCtorStub = sinon
|
|
566
|
-
.stub(ClusterReachabilityModule, 'ClusterReachability')
|
|
567
|
-
.callsFake(() => ({
|
|
568
|
-
start: startStub,
|
|
569
|
-
}));
|
|
1476
|
+
const promise = reachability.gatherReachability('test');
|
|
570
1477
|
|
|
571
|
-
await
|
|
1478
|
+
await simulateTimeout();
|
|
1479
|
+
await promise;
|
|
572
1480
|
|
|
573
1481
|
assert.calledTwice(clusterReachabilityCtorStub);
|
|
574
1482
|
assert.calledWith(clusterReachabilityCtorStub, 'cluster 1', {
|
|
@@ -585,7 +1493,8 @@ describe('gatherReachability', () => {
|
|
|
585
1493
|
isVideoMesh: true,
|
|
586
1494
|
});
|
|
587
1495
|
|
|
588
|
-
assert.
|
|
1496
|
+
assert.calledOnce(mockClusterReachabilityInstances['cluster 1'].start);
|
|
1497
|
+
assert.calledOnce(mockClusterReachabilityInstances['cluster 2'].start);
|
|
589
1498
|
});
|
|
590
1499
|
|
|
591
1500
|
it('does not do TCP reachability if it is disabled in config', async () => {
|
|
@@ -610,13 +1519,9 @@ describe('gatherReachability', () => {
|
|
|
610
1519
|
|
|
611
1520
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
612
1521
|
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
start: sinon.stub().resolves({}),
|
|
617
|
-
}));
|
|
618
|
-
|
|
619
|
-
await reachability.gatherReachability();
|
|
1522
|
+
const promise = reachability.gatherReachability('test');
|
|
1523
|
+
await simulateTimeout();
|
|
1524
|
+
await promise;
|
|
620
1525
|
|
|
621
1526
|
assert.calledOnceWithExactly(clusterReachabilityCtorStub, 'cluster name', {
|
|
622
1527
|
isVideoMesh: false,
|
|
@@ -648,13 +1553,10 @@ describe('gatherReachability', () => {
|
|
|
648
1553
|
|
|
649
1554
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
650
1555
|
|
|
651
|
-
const
|
|
652
|
-
.stub(ClusterReachabilityModule, 'ClusterReachability')
|
|
653
|
-
.callsFake(() => ({
|
|
654
|
-
start: sinon.stub().resolves({}),
|
|
655
|
-
}));
|
|
1556
|
+
const promise = reachability.gatherReachability('test');
|
|
656
1557
|
|
|
657
|
-
await
|
|
1558
|
+
await simulateTimeout();
|
|
1559
|
+
await promise;
|
|
658
1560
|
|
|
659
1561
|
assert.calledOnceWithExactly(clusterReachabilityCtorStub, 'cluster name', {
|
|
660
1562
|
isVideoMesh: false,
|
|
@@ -686,13 +1588,10 @@ describe('gatherReachability', () => {
|
|
|
686
1588
|
|
|
687
1589
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
688
1590
|
|
|
689
|
-
const
|
|
690
|
-
.stub(ClusterReachabilityModule, 'ClusterReachability')
|
|
691
|
-
.callsFake(() => ({
|
|
692
|
-
start: sinon.stub().resolves({}),
|
|
693
|
-
}));
|
|
1591
|
+
const promise = reachability.gatherReachability('test');
|
|
694
1592
|
|
|
695
|
-
await
|
|
1593
|
+
await simulateTimeout();
|
|
1594
|
+
await promise;
|
|
696
1595
|
|
|
697
1596
|
assert.calledOnceWithExactly(clusterReachabilityCtorStub, 'cluster name', {
|
|
698
1597
|
isVideoMesh: false,
|
|
@@ -701,6 +1600,70 @@ describe('gatherReachability', () => {
|
|
|
701
1600
|
xtls: [], // empty list because TLS is disabled in config
|
|
702
1601
|
});
|
|
703
1602
|
});
|
|
1603
|
+
|
|
1604
|
+
it('retry of getClusters is succesfull', async () => {
|
|
1605
|
+
webex.config.meetings.experimental = {
|
|
1606
|
+
enableTcpReachability: true,
|
|
1607
|
+
enableTlsReachability: false,
|
|
1608
|
+
};
|
|
1609
|
+
|
|
1610
|
+
const getClustersResult = {
|
|
1611
|
+
clusters: {
|
|
1612
|
+
'cluster name': {
|
|
1613
|
+
udp: ['testUDP1', 'testUDP2'],
|
|
1614
|
+
tcp: ['testTCP1', 'testTCP2'],
|
|
1615
|
+
xtls: ['testXTLS1', 'testXTLS2'],
|
|
1616
|
+
isVideoMesh: false,
|
|
1617
|
+
},
|
|
1618
|
+
},
|
|
1619
|
+
joinCookie: {id: 'id'},
|
|
1620
|
+
};
|
|
1621
|
+
|
|
1622
|
+
const reachability = new Reachability(webex);
|
|
1623
|
+
|
|
1624
|
+
let getClustersCallCount = 0;
|
|
1625
|
+
|
|
1626
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().callsFake(() => {
|
|
1627
|
+
getClustersCallCount++;
|
|
1628
|
+
|
|
1629
|
+
if (getClustersCallCount == 1) {
|
|
1630
|
+
throw new Error('fake error');
|
|
1631
|
+
}
|
|
1632
|
+
|
|
1633
|
+
return getClustersResult;
|
|
1634
|
+
});
|
|
1635
|
+
|
|
1636
|
+
const promise = reachability.gatherReachability('test');
|
|
1637
|
+
|
|
1638
|
+
await simulateTimeout();
|
|
1639
|
+
await promise;
|
|
1640
|
+
|
|
1641
|
+
assert.equal(getClustersCallCount, 2);
|
|
1642
|
+
|
|
1643
|
+
assert.calledOnce(clusterReachabilityCtorStub);
|
|
1644
|
+
});
|
|
1645
|
+
|
|
1646
|
+
it('two failed calls to getClusters', async () => {
|
|
1647
|
+
const reachability = new Reachability(webex);
|
|
1648
|
+
|
|
1649
|
+
let getClustersCallCount = 0;
|
|
1650
|
+
|
|
1651
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().callsFake(() => {
|
|
1652
|
+
getClustersCallCount++;
|
|
1653
|
+
|
|
1654
|
+
throw new Error('fake error');
|
|
1655
|
+
});
|
|
1656
|
+
|
|
1657
|
+
const promise = reachability.gatherReachability('test');
|
|
1658
|
+
|
|
1659
|
+
await simulateTimeout();
|
|
1660
|
+
|
|
1661
|
+
await promise;
|
|
1662
|
+
|
|
1663
|
+
assert.equal(getClustersCallCount, 2);
|
|
1664
|
+
|
|
1665
|
+
assert.neverCalledWith(clusterReachabilityCtorStub);
|
|
1666
|
+
});
|
|
704
1667
|
});
|
|
705
1668
|
|
|
706
1669
|
describe('getReachabilityResults', () => {
|
|
@@ -1033,3 +1996,307 @@ describe('getReachabilityMetrics', () => {
|
|
|
1033
1996
|
);
|
|
1034
1997
|
});
|
|
1035
1998
|
});
|
|
1999
|
+
|
|
2000
|
+
class TestReachability extends Reachability {
|
|
2001
|
+
constructor(webex: object) {
|
|
2002
|
+
super(webex);
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
public testGetStatistics(
|
|
2006
|
+
results: Array<ClusterReachabilityModule.ClusterReachabilityResult & {isVideoMesh: boolean}>,
|
|
2007
|
+
protocol: 'udp' | 'tcp' | 'xtls',
|
|
2008
|
+
isVideoMesh: boolean
|
|
2009
|
+
) {
|
|
2010
|
+
return this.getStatistics(results, protocol, isVideoMesh);
|
|
2011
|
+
}
|
|
2012
|
+
|
|
2013
|
+
public testSendMetric() {
|
|
2014
|
+
return this.sendMetric();
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
public setFakeClusterReachability(fakeClusterReachability) {
|
|
2018
|
+
this.clusterReachability = fakeClusterReachability;
|
|
2019
|
+
}
|
|
2020
|
+
}
|
|
2021
|
+
|
|
2022
|
+
describe('getStatistics', () => {
|
|
2023
|
+
let webex;
|
|
2024
|
+
let reachability;
|
|
2025
|
+
|
|
2026
|
+
beforeEach(() => {
|
|
2027
|
+
webex = new MockWebex();
|
|
2028
|
+
reachability = new TestReachability(webex);
|
|
2029
|
+
});
|
|
2030
|
+
|
|
2031
|
+
it('takes values from the correct protocol', () => {
|
|
2032
|
+
const results = [
|
|
2033
|
+
{
|
|
2034
|
+
udp: {
|
|
2035
|
+
result: 'reachable',
|
|
2036
|
+
latencyInMilliseconds: 10,
|
|
2037
|
+
},
|
|
2038
|
+
tcp: {
|
|
2039
|
+
result: 'reachable',
|
|
2040
|
+
latencyInMilliseconds: 1010,
|
|
2041
|
+
},
|
|
2042
|
+
xtls: {
|
|
2043
|
+
result: 'reachable',
|
|
2044
|
+
latencyInMilliseconds: 2010,
|
|
2045
|
+
},
|
|
2046
|
+
isVideoMesh: false,
|
|
2047
|
+
},
|
|
2048
|
+
{
|
|
2049
|
+
udp: {
|
|
2050
|
+
result: 'reachable',
|
|
2051
|
+
latencyInMilliseconds: 20,
|
|
2052
|
+
},
|
|
2053
|
+
tcp: {
|
|
2054
|
+
result: 'reachable',
|
|
2055
|
+
latencyInMilliseconds: 1020,
|
|
2056
|
+
},
|
|
2057
|
+
xtls: {
|
|
2058
|
+
result: 'reachable',
|
|
2059
|
+
latencyInMilliseconds: 2020,
|
|
2060
|
+
},
|
|
2061
|
+
isVideoMesh: false,
|
|
2062
|
+
},
|
|
2063
|
+
{
|
|
2064
|
+
udp: {
|
|
2065
|
+
result: 'reachable',
|
|
2066
|
+
latencyInMilliseconds: 30,
|
|
2067
|
+
},
|
|
2068
|
+
tcp: {
|
|
2069
|
+
result: 'reachable',
|
|
2070
|
+
latencyInMilliseconds: 1030,
|
|
2071
|
+
},
|
|
2072
|
+
xtls: {
|
|
2073
|
+
result: 'reachable',
|
|
2074
|
+
latencyInMilliseconds: 2030,
|
|
2075
|
+
},
|
|
2076
|
+
isVideoMesh: false,
|
|
2077
|
+
},
|
|
2078
|
+
];
|
|
2079
|
+
|
|
2080
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'udp', false), {
|
|
2081
|
+
min: 10,
|
|
2082
|
+
max: 30,
|
|
2083
|
+
average: 20,
|
|
2084
|
+
});
|
|
2085
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'tcp', false), {
|
|
2086
|
+
min: 1010,
|
|
2087
|
+
max: 1030,
|
|
2088
|
+
average: 1020,
|
|
2089
|
+
});
|
|
2090
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'xtls', false), {
|
|
2091
|
+
min: 2010,
|
|
2092
|
+
max: 2030,
|
|
2093
|
+
average: 2020,
|
|
2094
|
+
});
|
|
2095
|
+
});
|
|
2096
|
+
|
|
2097
|
+
it('filters based on isVideoMesh value', () => {
|
|
2098
|
+
const results = [
|
|
2099
|
+
{
|
|
2100
|
+
udp: {
|
|
2101
|
+
result: 'reachable',
|
|
2102
|
+
latencyInMilliseconds: 10,
|
|
2103
|
+
},
|
|
2104
|
+
isVideoMesh: true,
|
|
2105
|
+
},
|
|
2106
|
+
{
|
|
2107
|
+
udp: {
|
|
2108
|
+
result: 'reachable',
|
|
2109
|
+
latencyInMilliseconds: 20,
|
|
2110
|
+
},
|
|
2111
|
+
isVideoMesh: true,
|
|
2112
|
+
},
|
|
2113
|
+
{
|
|
2114
|
+
udp: {
|
|
2115
|
+
result: 'reachable',
|
|
2116
|
+
latencyInMilliseconds: 30,
|
|
2117
|
+
},
|
|
2118
|
+
isVideoMesh: true,
|
|
2119
|
+
},
|
|
2120
|
+
{
|
|
2121
|
+
udp: {
|
|
2122
|
+
result: 'reachable',
|
|
2123
|
+
latencyInMilliseconds: 100,
|
|
2124
|
+
},
|
|
2125
|
+
isVideoMesh: false,
|
|
2126
|
+
},
|
|
2127
|
+
{
|
|
2128
|
+
udp: {
|
|
2129
|
+
result: 'reachable',
|
|
2130
|
+
latencyInMilliseconds: 200,
|
|
2131
|
+
},
|
|
2132
|
+
isVideoMesh: false,
|
|
2133
|
+
},
|
|
2134
|
+
];
|
|
2135
|
+
|
|
2136
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'udp', true), {
|
|
2137
|
+
min: 10,
|
|
2138
|
+
max: 30,
|
|
2139
|
+
average: 20,
|
|
2140
|
+
});
|
|
2141
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'udp', false), {
|
|
2142
|
+
min: 100,
|
|
2143
|
+
max: 200,
|
|
2144
|
+
average: 150,
|
|
2145
|
+
});
|
|
2146
|
+
});
|
|
2147
|
+
|
|
2148
|
+
it('only takes into account "reachable" results', () => {
|
|
2149
|
+
const results = [
|
|
2150
|
+
{
|
|
2151
|
+
udp: {
|
|
2152
|
+
result: 'reachable',
|
|
2153
|
+
latencyInMilliseconds: 10,
|
|
2154
|
+
},
|
|
2155
|
+
isVideoMesh: false,
|
|
2156
|
+
},
|
|
2157
|
+
{
|
|
2158
|
+
udp: {
|
|
2159
|
+
result: 'unreachable',
|
|
2160
|
+
latencyInMilliseconds: 100, // value put in here just for testing, in practice we wouldn't have any value here if it was unreachable
|
|
2161
|
+
},
|
|
2162
|
+
isVideoMesh: false,
|
|
2163
|
+
},
|
|
2164
|
+
{
|
|
2165
|
+
udp: {
|
|
2166
|
+
result: 'reachable',
|
|
2167
|
+
latencyInMilliseconds: 20,
|
|
2168
|
+
},
|
|
2169
|
+
isVideoMesh: false,
|
|
2170
|
+
},
|
|
2171
|
+
{
|
|
2172
|
+
udp: {
|
|
2173
|
+
result: 'untested',
|
|
2174
|
+
latencyInMilliseconds: 200, // value put in here just for testing, in practice we wouldn't have any value here if it was untested
|
|
2175
|
+
},
|
|
2176
|
+
isVideoMesh: false,
|
|
2177
|
+
},
|
|
2178
|
+
];
|
|
2179
|
+
|
|
2180
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'udp', false), {
|
|
2181
|
+
min: 10,
|
|
2182
|
+
max: 20,
|
|
2183
|
+
average: 15,
|
|
2184
|
+
});
|
|
2185
|
+
});
|
|
2186
|
+
|
|
2187
|
+
it('handles the case when results are empty', () => {
|
|
2188
|
+
assert.deepEqual(reachability.testGetStatistics([], 'udp', false), {
|
|
2189
|
+
min: -1,
|
|
2190
|
+
max: -1,
|
|
2191
|
+
average: -1,
|
|
2192
|
+
});
|
|
2193
|
+
});
|
|
2194
|
+
|
|
2195
|
+
it('handles the case when results are empty after filtering', () => {
|
|
2196
|
+
const fakeResults = [
|
|
2197
|
+
{
|
|
2198
|
+
udp: {
|
|
2199
|
+
result: 'untested', // it will get filtered out because of this value
|
|
2200
|
+
latencyInMilliseconds: 10,
|
|
2201
|
+
},
|
|
2202
|
+
tcp: {
|
|
2203
|
+
result: 'reachable',
|
|
2204
|
+
latencyInMilliseconds: 10, // it will get filtered out because of the tcp protocol
|
|
2205
|
+
},
|
|
2206
|
+
isVideoMesh: false,
|
|
2207
|
+
},
|
|
2208
|
+
{
|
|
2209
|
+
udp: {
|
|
2210
|
+
result: 'reachable',
|
|
2211
|
+
latencyInMilliseconds: 10,
|
|
2212
|
+
},
|
|
2213
|
+
isVideoMesh: true, // it will get filtered out because of this value
|
|
2214
|
+
},
|
|
2215
|
+
];
|
|
2216
|
+
|
|
2217
|
+
assert.deepEqual(reachability.testGetStatistics(fakeResults, 'udp', false), {
|
|
2218
|
+
min: -1,
|
|
2219
|
+
max: -1,
|
|
2220
|
+
average: -1,
|
|
2221
|
+
});
|
|
2222
|
+
});
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2225
|
+
describe('sendMetric', () => {
|
|
2226
|
+
let webex;
|
|
2227
|
+
let reachability;
|
|
2228
|
+
|
|
2229
|
+
beforeEach(() => {
|
|
2230
|
+
webex = new MockWebex();
|
|
2231
|
+
reachability = new TestReachability(webex);
|
|
2232
|
+
|
|
2233
|
+
sinon.stub(Metrics, 'sendBehavioralMetric');
|
|
2234
|
+
});
|
|
2235
|
+
|
|
2236
|
+
it('works as expected', async () => {
|
|
2237
|
+
// setup stub for getStatistics to return values that show what parameters it was called with,
|
|
2238
|
+
// this way we can verify that the correct results of calls to getStatistics are placed
|
|
2239
|
+
// in correct data fields when sendBehavioralMetric() is called
|
|
2240
|
+
const getStatisticsStub = sinon
|
|
2241
|
+
.stub(reachability, 'getStatistics')
|
|
2242
|
+
.callsFake((results, protocol, isVideoMesh) => {
|
|
2243
|
+
return {result: 'fake', protocol, isVideoMesh};
|
|
2244
|
+
});
|
|
2245
|
+
|
|
2246
|
+
// setup fake clusterReachability results
|
|
2247
|
+
reachability.setFakeClusterReachability({
|
|
2248
|
+
cluster1: {
|
|
2249
|
+
getResult: sinon.stub().returns({result: 'result 1'}),
|
|
2250
|
+
isVideoMesh: true,
|
|
2251
|
+
},
|
|
2252
|
+
cluster2: {
|
|
2253
|
+
getResult: sinon.stub().returns({result: 'result 2'}),
|
|
2254
|
+
isVideoMesh: false,
|
|
2255
|
+
},
|
|
2256
|
+
cluster3: {
|
|
2257
|
+
getResult: sinon.stub().returns({result: 'result 3'}),
|
|
2258
|
+
isVideoMesh: false,
|
|
2259
|
+
},
|
|
2260
|
+
});
|
|
2261
|
+
|
|
2262
|
+
await reachability.sendMetric();
|
|
2263
|
+
|
|
2264
|
+
// each call to getStatistics should be made with all the results from all fake clusterReachability:
|
|
2265
|
+
const expectedResults = [
|
|
2266
|
+
{
|
|
2267
|
+
result: 'result 1',
|
|
2268
|
+
isVideoMesh: true,
|
|
2269
|
+
},
|
|
2270
|
+
{
|
|
2271
|
+
result: 'result 2',
|
|
2272
|
+
isVideoMesh: false,
|
|
2273
|
+
},
|
|
2274
|
+
{
|
|
2275
|
+
result: 'result 3',
|
|
2276
|
+
isVideoMesh: false,
|
|
2277
|
+
},
|
|
2278
|
+
];
|
|
2279
|
+
|
|
2280
|
+
// check that getStatistics is called 4 times and each time with all the results
|
|
2281
|
+
assert.callCount(getStatisticsStub, 4);
|
|
2282
|
+
assert.alwaysCalledWith(getStatisticsStub, expectedResults, sinon.match.any, sinon.match.any);
|
|
2283
|
+
|
|
2284
|
+
assert.calledWith(Metrics.sendBehavioralMetric, 'js_sdk_reachability_completed', {
|
|
2285
|
+
vmn_udp_result: 'fake',
|
|
2286
|
+
vmn_udp_protocol: 'udp',
|
|
2287
|
+
vmn_udp_isVideoMesh: true,
|
|
2288
|
+
|
|
2289
|
+
public_udp_result: 'fake',
|
|
2290
|
+
public_udp_protocol: 'udp',
|
|
2291
|
+
public_udp_isVideoMesh: false,
|
|
2292
|
+
|
|
2293
|
+
public_tcp_result: 'fake',
|
|
2294
|
+
public_tcp_protocol: 'tcp',
|
|
2295
|
+
public_tcp_isVideoMesh: false,
|
|
2296
|
+
|
|
2297
|
+
public_xtls_result: 'fake',
|
|
2298
|
+
public_xtls_protocol: 'xtls',
|
|
2299
|
+
public_xtls_isVideoMesh: false,
|
|
2300
|
+
});
|
|
2301
|
+
});
|
|
2302
|
+
});
|