@webex/plugin-meetings 3.3.1-next.13 → 3.3.1-next.15
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/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/index.js +5 -1
- package/dist/meeting/index.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +108 -53
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +415 -56
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/reachability/clusterReachability.d.ts +31 -3
- package/dist/types/reachability/index.d.ts +93 -2
- package/dist/webinar/index.js +1 -1
- package/package.json +4 -4
- package/src/breakouts/index.ts +7 -1
- package/src/meeting/index.ts +7 -2
- package/src/metrics/constants.ts +1 -0
- package/src/reachability/clusterReachability.ts +86 -25
- package/src/reachability/index.ts +313 -27
- package/test/unit/spec/breakouts/index.ts +51 -32
- package/test/unit/spec/meeting/index.js +34 -1
- package/test/unit/spec/reachability/clusterReachability.ts +116 -22
- package/test/unit/spec/reachability/index.ts +1120 -84
|
@@ -1,12 +1,16 @@
|
|
|
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
7
|
ReachabilityResults,
|
|
6
8
|
ReachabilityResultsForBackend,
|
|
7
9
|
} from '@webex/plugin-meetings/src/reachability/';
|
|
10
|
+
import { ClusterNode } from '../../../../src/reachability/request';
|
|
8
11
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
9
12
|
import * as ClusterReachabilityModule from '@webex/plugin-meetings/src/reachability/clusterReachability';
|
|
13
|
+
import Metrics from '@webex/plugin-meetings/src/metrics';
|
|
10
14
|
|
|
11
15
|
import {IP_VERSION} from '@webex/plugin-meetings/src/constants';
|
|
12
16
|
|
|
@@ -406,13 +410,71 @@ describe('isWebexMediaBackendUnreachable', () => {
|
|
|
406
410
|
});
|
|
407
411
|
});
|
|
408
412
|
|
|
413
|
+
/**
|
|
414
|
+
* helper class to mock ClusterReachability and allow to easily
|
|
415
|
+
* simulate 'resultReady' events from it
|
|
416
|
+
*/
|
|
417
|
+
class MockClusterReachability extends EventEmitter {
|
|
418
|
+
mockResult = {
|
|
419
|
+
udp: {
|
|
420
|
+
result: 'untested',
|
|
421
|
+
},
|
|
422
|
+
tcp: {
|
|
423
|
+
result: 'untested',
|
|
424
|
+
},
|
|
425
|
+
xtls: {
|
|
426
|
+
result: 'untested',
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
isVideoMesh: boolean;
|
|
431
|
+
name: string;
|
|
432
|
+
|
|
433
|
+
constructor(name: string, clusterInfo: ClusterNode) {
|
|
434
|
+
super();
|
|
435
|
+
this.name = name;
|
|
436
|
+
this.isVideoMesh = clusterInfo.isVideoMesh;
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
abort = sinon.stub();
|
|
440
|
+
start = sinon.stub();
|
|
441
|
+
|
|
442
|
+
getResult() {
|
|
443
|
+
return this.mockResult;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/**
|
|
447
|
+
* Emits a fake 'resultReady' event and makes sure that the same result
|
|
448
|
+
* is returned when getResult() is called.
|
|
449
|
+
*
|
|
450
|
+
* @param protocol
|
|
451
|
+
* @param result
|
|
452
|
+
*/
|
|
453
|
+
public emitFakeResult(protocol, result) {
|
|
454
|
+
this.mockResult[protocol] = result;
|
|
455
|
+
this.emit(ClusterReachabilityModule.Events.resultReady, {protocol, ...result});
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
public emitFakeClientMediaIpUpdate(protocol, newIp) {
|
|
459
|
+
this.mockResult[protocol].clientMediaIPs.push(newIp);
|
|
460
|
+
this.emit(ClusterReachabilityModule.Events.clientMediaIpsUpdated, {
|
|
461
|
+
protocol,
|
|
462
|
+
clientMediaIPs: this.mockResult[protocol].clientMediaIPs,
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
}
|
|
409
466
|
|
|
410
467
|
describe('gatherReachability', () => {
|
|
411
468
|
let webex;
|
|
469
|
+
let clock;
|
|
470
|
+
let clusterReachabilityCtorStub;
|
|
471
|
+
let mockClusterReachabilityInstances: Record<string, MockClusterReachability>;
|
|
412
472
|
|
|
413
473
|
beforeEach(async () => {
|
|
414
474
|
webex = new MockWebex();
|
|
415
475
|
|
|
476
|
+
sinon.stub(Metrics, 'sendBehavioralMetric');
|
|
477
|
+
|
|
416
478
|
await webex.boundedStorage.put(
|
|
417
479
|
'Reachability',
|
|
418
480
|
'reachability.result',
|
|
@@ -423,94 +485,784 @@ describe('gatherReachability', () => {
|
|
|
423
485
|
'reachability.joinCookie',
|
|
424
486
|
JSON.stringify({old: 'joinCookie'})
|
|
425
487
|
);
|
|
488
|
+
|
|
489
|
+
clock = sinon.useFakeTimers();
|
|
490
|
+
|
|
491
|
+
mockClusterReachabilityInstances = {};
|
|
492
|
+
|
|
493
|
+
clusterReachabilityCtorStub = sinon
|
|
494
|
+
.stub(ClusterReachabilityModule, 'ClusterReachability')
|
|
495
|
+
.callsFake((id, cluster) => {
|
|
496
|
+
const mockInstance = new MockClusterReachability(id, cluster);
|
|
497
|
+
|
|
498
|
+
mockClusterReachabilityInstances[id] = mockInstance;
|
|
499
|
+
return mockInstance;
|
|
500
|
+
});
|
|
426
501
|
});
|
|
427
502
|
|
|
428
503
|
afterEach(() => {
|
|
429
504
|
sinon.restore();
|
|
505
|
+
clock.restore();
|
|
430
506
|
});
|
|
431
507
|
|
|
432
|
-
|
|
508
|
+
// simulates time progression so that Reachability times out
|
|
509
|
+
const simulateTimeout = async () => {
|
|
510
|
+
await testUtils.flushPromises();
|
|
511
|
+
clock.tick(3000);
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
const checkResults = async (expectedResults, expectedJoinCookie) => {
|
|
515
|
+
const storedResultForReachabilityResult = await webex.boundedStorage.get(
|
|
516
|
+
'Reachability',
|
|
517
|
+
'reachability.result'
|
|
518
|
+
);
|
|
519
|
+
const storedResultForJoinCookie = await webex.boundedStorage.get(
|
|
520
|
+
'Reachability',
|
|
521
|
+
'reachability.joinCookie'
|
|
522
|
+
);
|
|
523
|
+
|
|
524
|
+
assert.equal(storedResultForReachabilityResult, JSON.stringify(expectedResults));
|
|
525
|
+
assert.equal(storedResultForJoinCookie, JSON.stringify(expectedJoinCookie));
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
[
|
|
529
|
+
// ========================================================================
|
|
530
|
+
{
|
|
531
|
+
title: '1 cluster with events triggered for each protocol',
|
|
532
|
+
waitShortTimeout: false,
|
|
533
|
+
waitLongTimeout: false,
|
|
534
|
+
mockClusters: {
|
|
535
|
+
cluster1: {
|
|
536
|
+
udp: ['udp-url1'],
|
|
537
|
+
tcp: ['tcp-url1'],
|
|
538
|
+
xtls: ['xtls-url1'],
|
|
539
|
+
isVideoMesh: false,
|
|
540
|
+
},
|
|
541
|
+
},
|
|
542
|
+
mockResultReadyEvents: [
|
|
543
|
+
{
|
|
544
|
+
clusterId: 'cluster1',
|
|
545
|
+
protocol: 'tcp',
|
|
546
|
+
result: {
|
|
547
|
+
result: 'reachable',
|
|
548
|
+
latencyInMilliseconds: 11,
|
|
549
|
+
},
|
|
550
|
+
},
|
|
551
|
+
{
|
|
552
|
+
clusterId: 'cluster1',
|
|
553
|
+
protocol: 'udp',
|
|
554
|
+
result: {
|
|
555
|
+
result: 'reachable',
|
|
556
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
557
|
+
latencyInMilliseconds: 22,
|
|
558
|
+
},
|
|
559
|
+
},
|
|
560
|
+
{
|
|
561
|
+
clusterId: 'cluster1',
|
|
562
|
+
protocol: 'xtls',
|
|
563
|
+
result: {
|
|
564
|
+
result: 'reachable',
|
|
565
|
+
latencyInMilliseconds: 33,
|
|
566
|
+
},
|
|
567
|
+
},
|
|
568
|
+
],
|
|
569
|
+
expectedResults: {
|
|
570
|
+
cluster1: {
|
|
571
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 22},
|
|
572
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 11},
|
|
573
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 33},
|
|
574
|
+
isVideoMesh: false,
|
|
575
|
+
},
|
|
576
|
+
},
|
|
577
|
+
expectedMetrics: {
|
|
578
|
+
vmn: {udp: {min: -1, max: -1, average: -1}},
|
|
579
|
+
public: {
|
|
580
|
+
udp: {min: 22, max: 22, average: 22},
|
|
581
|
+
tcp: {min: 11, max: 11, average: 11},
|
|
582
|
+
xtls: {min: 33, max: 33, average: 33},
|
|
583
|
+
},
|
|
584
|
+
},
|
|
585
|
+
},
|
|
586
|
+
// ========================================================================
|
|
587
|
+
{
|
|
588
|
+
title:
|
|
589
|
+
'3 clusters: one with an event for each protocol, one with no events, one with no urls for tcp and xtls',
|
|
590
|
+
waitShortTimeout: 'public',
|
|
591
|
+
waitLongTimeout: true,
|
|
592
|
+
mockClusters: {
|
|
593
|
+
cluster1: {
|
|
594
|
+
udp: ['udp-url1.1', 'udp-url1.2'],
|
|
595
|
+
tcp: ['tcp-url1.1', 'tcp-url1.2'],
|
|
596
|
+
xtls: ['xtls-url1.1', 'xtls-url1.2'],
|
|
597
|
+
isVideoMesh: false,
|
|
598
|
+
},
|
|
599
|
+
cluster2: {
|
|
600
|
+
udp: ['udp-url2.1'],
|
|
601
|
+
tcp: ['tcp-url2.1'],
|
|
602
|
+
xtls: ['xtls-url2.1'],
|
|
603
|
+
isVideoMesh: false,
|
|
604
|
+
},
|
|
605
|
+
cluster3: {
|
|
606
|
+
udp: ['udp-url1'],
|
|
607
|
+
tcp: [],
|
|
608
|
+
xtls: [],
|
|
609
|
+
isVideoMesh: true,
|
|
610
|
+
},
|
|
611
|
+
},
|
|
612
|
+
mockResultReadyEvents: [
|
|
613
|
+
{
|
|
614
|
+
clusterId: 'cluster1',
|
|
615
|
+
protocol: 'udp',
|
|
616
|
+
result: {
|
|
617
|
+
result: 'reachable',
|
|
618
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
619
|
+
latencyInMilliseconds: 13,
|
|
620
|
+
},
|
|
621
|
+
},
|
|
622
|
+
{
|
|
623
|
+
clusterId: 'cluster1',
|
|
624
|
+
protocol: 'tcp',
|
|
625
|
+
result: {
|
|
626
|
+
result: 'reachable',
|
|
627
|
+
latencyInMilliseconds: 53,
|
|
628
|
+
},
|
|
629
|
+
},
|
|
630
|
+
{
|
|
631
|
+
clusterId: 'cluster1',
|
|
632
|
+
protocol: 'xtls',
|
|
633
|
+
result: {
|
|
634
|
+
result: 'reachable',
|
|
635
|
+
latencyInMilliseconds: 113,
|
|
636
|
+
},
|
|
637
|
+
},
|
|
638
|
+
],
|
|
639
|
+
expectedResults: {
|
|
640
|
+
cluster1: {
|
|
641
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 13},
|
|
642
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 53},
|
|
643
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 113},
|
|
644
|
+
isVideoMesh: false,
|
|
645
|
+
},
|
|
646
|
+
cluster2: {
|
|
647
|
+
udp: {result: 'unreachable'},
|
|
648
|
+
tcp: {result: 'unreachable'},
|
|
649
|
+
xtls: {result: 'unreachable'},
|
|
650
|
+
isVideoMesh: false,
|
|
651
|
+
},
|
|
652
|
+
cluster3: {
|
|
653
|
+
udp: {result: 'unreachable'},
|
|
654
|
+
tcp: {result: 'untested'},
|
|
655
|
+
xtls: {result: 'untested'},
|
|
656
|
+
isVideoMesh: true,
|
|
657
|
+
},
|
|
658
|
+
},
|
|
659
|
+
expectedMetrics: {
|
|
660
|
+
vmn: {udp: {min: -1, max: -1, average: -1}},
|
|
661
|
+
public: {
|
|
662
|
+
udp: {min: 13, max: 13, average: 13},
|
|
663
|
+
tcp: {min: 53, max: 53, average: 53},
|
|
664
|
+
xtls: {min: 113, max: 113, average: 113},
|
|
665
|
+
},
|
|
666
|
+
},
|
|
667
|
+
},
|
|
668
|
+
// ========================================================================
|
|
669
|
+
{
|
|
670
|
+
title: '3 clusters: all with all results ready in time for all protocols',
|
|
671
|
+
waitShortTimeout: false,
|
|
672
|
+
waitLongTimeout: false,
|
|
673
|
+
mockClusters: {
|
|
674
|
+
cluster1: {
|
|
675
|
+
udp: ['udp-url1'],
|
|
676
|
+
tcp: ['tcp-url1'],
|
|
677
|
+
xtls: ['xtls-url1'],
|
|
678
|
+
isVideoMesh: false,
|
|
679
|
+
},
|
|
680
|
+
cluster2: {
|
|
681
|
+
udp: ['udp-url2'],
|
|
682
|
+
tcp: ['tcp-url2'],
|
|
683
|
+
xtls: ['xtls-url2'],
|
|
684
|
+
isVideoMesh: false,
|
|
685
|
+
},
|
|
686
|
+
cluster3: {
|
|
687
|
+
udp: ['udp-url3'],
|
|
688
|
+
tcp: ['tcp-url3'],
|
|
689
|
+
xtls: ['xtls-url3'],
|
|
690
|
+
isVideoMesh: false,
|
|
691
|
+
},
|
|
692
|
+
},
|
|
693
|
+
mockResultReadyEvents: [
|
|
694
|
+
{
|
|
695
|
+
clusterId: 'cluster1',
|
|
696
|
+
protocol: 'udp',
|
|
697
|
+
result: {
|
|
698
|
+
result: 'reachable',
|
|
699
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
700
|
+
latencyInMilliseconds: 10,
|
|
701
|
+
},
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
clusterId: 'cluster1',
|
|
705
|
+
protocol: 'tcp',
|
|
706
|
+
result: {
|
|
707
|
+
result: 'reachable',
|
|
708
|
+
latencyInMilliseconds: 100,
|
|
709
|
+
},
|
|
710
|
+
},
|
|
711
|
+
{
|
|
712
|
+
clusterId: 'cluster1',
|
|
713
|
+
protocol: 'xtls',
|
|
714
|
+
result: {
|
|
715
|
+
result: 'reachable',
|
|
716
|
+
latencyInMilliseconds: 200,
|
|
717
|
+
},
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
clusterId: 'cluster2',
|
|
721
|
+
protocol: 'udp',
|
|
722
|
+
result: {
|
|
723
|
+
result: 'reachable',
|
|
724
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
725
|
+
latencyInMilliseconds: 20,
|
|
726
|
+
},
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
clusterId: 'cluster2',
|
|
730
|
+
protocol: 'tcp',
|
|
731
|
+
result: {
|
|
732
|
+
result: 'reachable',
|
|
733
|
+
latencyInMilliseconds: 110,
|
|
734
|
+
},
|
|
735
|
+
},
|
|
736
|
+
{
|
|
737
|
+
clusterId: 'cluster2',
|
|
738
|
+
protocol: 'xtls',
|
|
739
|
+
result: {
|
|
740
|
+
result: 'reachable',
|
|
741
|
+
latencyInMilliseconds: 220,
|
|
742
|
+
},
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
clusterId: 'cluster3',
|
|
746
|
+
protocol: 'udp',
|
|
747
|
+
result: {
|
|
748
|
+
result: 'reachable',
|
|
749
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
750
|
+
latencyInMilliseconds: 30,
|
|
751
|
+
},
|
|
752
|
+
},
|
|
753
|
+
{
|
|
754
|
+
clusterId: 'cluster3',
|
|
755
|
+
protocol: 'tcp',
|
|
756
|
+
result: {
|
|
757
|
+
result: 'reachable',
|
|
758
|
+
latencyInMilliseconds: 120,
|
|
759
|
+
},
|
|
760
|
+
},
|
|
761
|
+
{
|
|
762
|
+
clusterId: 'cluster3',
|
|
763
|
+
protocol: 'xtls',
|
|
764
|
+
result: {
|
|
765
|
+
result: 'reachable',
|
|
766
|
+
latencyInMilliseconds: 240,
|
|
767
|
+
},
|
|
768
|
+
},
|
|
769
|
+
],
|
|
770
|
+
expectedResults: {
|
|
771
|
+
cluster1: {
|
|
772
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 10},
|
|
773
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 100},
|
|
774
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 200},
|
|
775
|
+
isVideoMesh: false,
|
|
776
|
+
},
|
|
777
|
+
cluster2: {
|
|
778
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 20},
|
|
779
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 110},
|
|
780
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 220},
|
|
781
|
+
isVideoMesh: false,
|
|
782
|
+
},
|
|
783
|
+
cluster3: {
|
|
784
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 30},
|
|
785
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 120},
|
|
786
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 240},
|
|
787
|
+
isVideoMesh: false,
|
|
788
|
+
},
|
|
789
|
+
},
|
|
790
|
+
expectedMetrics: {
|
|
791
|
+
vmn: {udp: {min: -1, max: -1, average: -1}},
|
|
792
|
+
public: {
|
|
793
|
+
udp: {min: 10, max: 30, average: 20},
|
|
794
|
+
tcp: {min: 100, max: 120, average: 110},
|
|
795
|
+
xtls: {min: 200, max: 240, average: 220},
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
},
|
|
799
|
+
// ========================================================================
|
|
800
|
+
{
|
|
801
|
+
title: '2 clusters: both with no results at all',
|
|
802
|
+
waitShortTimeout: 'public',
|
|
803
|
+
waitLongTimeout: true,
|
|
804
|
+
mockClusters: {
|
|
805
|
+
cluster1: {
|
|
806
|
+
udp: ['udp-url1'],
|
|
807
|
+
tcp: ['tcp-url1'],
|
|
808
|
+
xtls: ['xtls-url1'],
|
|
809
|
+
isVideoMesh: false,
|
|
810
|
+
},
|
|
811
|
+
cluster2: {
|
|
812
|
+
udp: ['udp-url2'],
|
|
813
|
+
tcp: ['tcp-url2'],
|
|
814
|
+
xtls: ['xtls-url2'],
|
|
815
|
+
isVideoMesh: false,
|
|
816
|
+
},
|
|
817
|
+
},
|
|
818
|
+
mockResultReadyEvents: [],
|
|
819
|
+
expectedResults: {
|
|
820
|
+
cluster1: {
|
|
821
|
+
udp: {result: 'unreachable'},
|
|
822
|
+
tcp: {result: 'unreachable'},
|
|
823
|
+
xtls: {result: 'unreachable'},
|
|
824
|
+
isVideoMesh: false,
|
|
825
|
+
},
|
|
826
|
+
cluster2: {
|
|
827
|
+
udp: {result: 'unreachable'},
|
|
828
|
+
tcp: {result: 'unreachable'},
|
|
829
|
+
xtls: {result: 'unreachable'},
|
|
830
|
+
isVideoMesh: false,
|
|
831
|
+
},
|
|
832
|
+
},
|
|
833
|
+
expectedMetrics: {
|
|
834
|
+
vmn: {udp: {min: -1, max: -1, average: -1}},
|
|
835
|
+
public: {
|
|
836
|
+
udp: {min: -1, max: -1, average: -1},
|
|
837
|
+
tcp: {min: -1, max: -1, average: -1},
|
|
838
|
+
xtls: {min: -1, max: -1, average: -1},
|
|
839
|
+
},
|
|
840
|
+
},
|
|
841
|
+
},
|
|
842
|
+
// ========================================================================
|
|
843
|
+
{
|
|
844
|
+
title:
|
|
845
|
+
'3 clusters: 2 VMN clusters missing results, but the public one has all results within 1s',
|
|
846
|
+
waitShortTimeout: 'vmn',
|
|
847
|
+
waitLongTimeout: true,
|
|
848
|
+
mockClusters: {
|
|
849
|
+
vmnCluster1: {
|
|
850
|
+
udp: ['udp-url1'],
|
|
851
|
+
tcp: ['tcp-url1'],
|
|
852
|
+
xtls: ['xtls-url1'],
|
|
853
|
+
isVideoMesh: true,
|
|
854
|
+
},
|
|
855
|
+
publicCluster: {
|
|
856
|
+
udp: ['udp-url2'],
|
|
857
|
+
tcp: ['tcp-url2'],
|
|
858
|
+
xtls: ['xtls-url2'],
|
|
859
|
+
isVideoMesh: false,
|
|
860
|
+
},
|
|
861
|
+
vmnCluster2: {
|
|
862
|
+
udp: ['udp-url3'],
|
|
863
|
+
tcp: ['tcp-url3'],
|
|
864
|
+
xtls: ['xtls-url3'],
|
|
865
|
+
isVideoMesh: true,
|
|
866
|
+
},
|
|
867
|
+
},
|
|
868
|
+
mockResultReadyEvents: [
|
|
869
|
+
{
|
|
870
|
+
clusterId: 'publicCluster',
|
|
871
|
+
protocol: 'udp',
|
|
872
|
+
result: {
|
|
873
|
+
result: 'reachable',
|
|
874
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
875
|
+
latencyInMilliseconds: 10,
|
|
876
|
+
},
|
|
877
|
+
},
|
|
878
|
+
{
|
|
879
|
+
clusterId: 'publicCluster',
|
|
880
|
+
protocol: 'tcp',
|
|
881
|
+
result: {
|
|
882
|
+
result: 'reachable',
|
|
883
|
+
latencyInMilliseconds: 100,
|
|
884
|
+
},
|
|
885
|
+
},
|
|
886
|
+
{
|
|
887
|
+
clusterId: 'publicCluster',
|
|
888
|
+
protocol: 'xtls',
|
|
889
|
+
result: {
|
|
890
|
+
result: 'reachable',
|
|
891
|
+
latencyInMilliseconds: 200,
|
|
892
|
+
},
|
|
893
|
+
},
|
|
894
|
+
],
|
|
895
|
+
expectedResults: {
|
|
896
|
+
vmnCluster1: {
|
|
897
|
+
udp: {result: 'unreachable'},
|
|
898
|
+
tcp: {result: 'untested'},
|
|
899
|
+
xtls: {result: 'untested'},
|
|
900
|
+
isVideoMesh: true,
|
|
901
|
+
},
|
|
902
|
+
publicCluster: {
|
|
903
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 10},
|
|
904
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 100},
|
|
905
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 200},
|
|
906
|
+
isVideoMesh: false,
|
|
907
|
+
},
|
|
908
|
+
vmnCluster2: {
|
|
909
|
+
udp: {result: 'unreachable'},
|
|
910
|
+
tcp: {result: 'untested'},
|
|
911
|
+
xtls: {result: 'untested'},
|
|
912
|
+
isVideoMesh: true,
|
|
913
|
+
},
|
|
914
|
+
},
|
|
915
|
+
expectedMetrics: {
|
|
916
|
+
vmn: {udp: {min: -1, max: -1, average: -1}},
|
|
917
|
+
public: {
|
|
918
|
+
udp: {min: 10, max: 10, average: 10},
|
|
919
|
+
tcp: {min: 100, max: 100, average: 100},
|
|
920
|
+
xtls: {min: 200, max: 200, average: 200},
|
|
921
|
+
},
|
|
922
|
+
},
|
|
923
|
+
},
|
|
924
|
+
// ========================================================================
|
|
925
|
+
{
|
|
926
|
+
title: '2 VMN clusters with all results',
|
|
927
|
+
waitShortTimeout: false,
|
|
928
|
+
waitLongTimeout: false,
|
|
929
|
+
mockClusters: {
|
|
930
|
+
vmnCluster1: {
|
|
931
|
+
udp: ['udp-url1'],
|
|
932
|
+
tcp: [],
|
|
933
|
+
xtls: [],
|
|
934
|
+
isVideoMesh: true,
|
|
935
|
+
},
|
|
936
|
+
vmnCluster2: {
|
|
937
|
+
udp: ['udp-url3'],
|
|
938
|
+
tcp: [],
|
|
939
|
+
xtls: [],
|
|
940
|
+
isVideoMesh: true,
|
|
941
|
+
},
|
|
942
|
+
},
|
|
943
|
+
mockResultReadyEvents: [
|
|
944
|
+
{
|
|
945
|
+
clusterId: 'vmnCluster1',
|
|
946
|
+
protocol: 'udp',
|
|
947
|
+
result: {
|
|
948
|
+
result: 'reachable',
|
|
949
|
+
clientMediaIPs: ['192.168.10.1'],
|
|
950
|
+
latencyInMilliseconds: 100,
|
|
951
|
+
},
|
|
952
|
+
},
|
|
953
|
+
{
|
|
954
|
+
clusterId: 'vmnCluster2',
|
|
955
|
+
protocol: 'udp',
|
|
956
|
+
result: {
|
|
957
|
+
result: 'reachable',
|
|
958
|
+
clientMediaIPs: ['192.168.0.1'],
|
|
959
|
+
latencyInMilliseconds: 300,
|
|
960
|
+
},
|
|
961
|
+
},
|
|
962
|
+
],
|
|
963
|
+
expectedResults: {
|
|
964
|
+
vmnCluster1: {
|
|
965
|
+
udp: {result: 'reachable', clientMediaIPs: ['192.168.10.1'], latencyInMilliseconds: 100},
|
|
966
|
+
tcp: {result: 'untested'},
|
|
967
|
+
xtls: {result: 'untested'},
|
|
968
|
+
isVideoMesh: true,
|
|
969
|
+
},
|
|
970
|
+
vmnCluster2: {
|
|
971
|
+
udp: {result: 'reachable', clientMediaIPs: ['192.168.0.1'], latencyInMilliseconds: 300},
|
|
972
|
+
tcp: {result: 'untested'},
|
|
973
|
+
xtls: {result: 'untested'},
|
|
974
|
+
isVideoMesh: true,
|
|
975
|
+
},
|
|
976
|
+
},
|
|
977
|
+
expectedMetrics: {
|
|
978
|
+
vmn: {udp: {min: 100, max: 300, average: 200}},
|
|
979
|
+
public: {
|
|
980
|
+
udp: {min: -1, max: -1, average: -1},
|
|
981
|
+
tcp: {min: -1, max: -1, average: -1},
|
|
982
|
+
xtls: {min: -1, max: -1, average: -1},
|
|
983
|
+
},
|
|
984
|
+
},
|
|
985
|
+
},
|
|
986
|
+
].forEach(
|
|
987
|
+
({
|
|
988
|
+
title,
|
|
989
|
+
waitShortTimeout,
|
|
990
|
+
waitLongTimeout,
|
|
991
|
+
mockClusters,
|
|
992
|
+
mockResultReadyEvents,
|
|
993
|
+
expectedResults,
|
|
994
|
+
expectedMetrics,
|
|
995
|
+
}) =>
|
|
996
|
+
it(`works correctly for the case: ${title}`, async () => {
|
|
997
|
+
webex.config.meetings.experimental = {
|
|
998
|
+
enableTcpReachability: true,
|
|
999
|
+
enableTlsReachability: true,
|
|
1000
|
+
};
|
|
1001
|
+
|
|
1002
|
+
const receivedEvents = {
|
|
1003
|
+
done: 0,
|
|
1004
|
+
firstResultAvailable: {
|
|
1005
|
+
udp: 0,
|
|
1006
|
+
tcp: 0,
|
|
1007
|
+
xtls: 0,
|
|
1008
|
+
},
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
const reachability = new Reachability(webex);
|
|
1012
|
+
|
|
1013
|
+
reachability.on('reachability:done', () => {
|
|
1014
|
+
receivedEvents.done += 1;
|
|
1015
|
+
});
|
|
1016
|
+
reachability.on('reachability:firstResultAvailable', ({protocol}) => {
|
|
1017
|
+
receivedEvents.firstResultAvailable[protocol] += 1;
|
|
1018
|
+
});
|
|
1019
|
+
|
|
1020
|
+
const mockGetClustersResult = {
|
|
1021
|
+
clusters: {},
|
|
1022
|
+
joinCookie: {id: 'id'},
|
|
1023
|
+
};
|
|
1024
|
+
|
|
1025
|
+
Object.entries(mockClusters).forEach(([id, mockCluster]) => {
|
|
1026
|
+
mockGetClustersResult.clusters[id] = mockCluster;
|
|
1027
|
+
});
|
|
1028
|
+
|
|
1029
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns(mockGetClustersResult);
|
|
1030
|
+
|
|
1031
|
+
const resultPromise = reachability.gatherReachability();
|
|
1032
|
+
|
|
1033
|
+
await testUtils.flushPromises();
|
|
1034
|
+
|
|
1035
|
+
// check that ClusterReachability instance was created for each cluster
|
|
1036
|
+
Object.entries(mockClusters).forEach(([id, mockCluster]) => {
|
|
1037
|
+
assert.calledWith(clusterReachabilityCtorStub, id, mockCluster);
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
// trigger mock result events from ClusterReachability instances
|
|
1041
|
+
mockResultReadyEvents.forEach((mockEvent) => {
|
|
1042
|
+
mockClusterReachabilityInstances[mockEvent.clusterId].emitFakeResult(
|
|
1043
|
+
mockEvent.protocol,
|
|
1044
|
+
mockEvent.result
|
|
1045
|
+
);
|
|
1046
|
+
});
|
|
1047
|
+
|
|
1048
|
+
if (waitShortTimeout === 'public') {
|
|
1049
|
+
clock.tick(3000);
|
|
1050
|
+
}
|
|
1051
|
+
if (waitShortTimeout === 'vmn') {
|
|
1052
|
+
clock.tick(1000);
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
await resultPromise;
|
|
1056
|
+
|
|
1057
|
+
await checkResults(expectedResults, mockGetClustersResult.joinCookie);
|
|
1058
|
+
|
|
1059
|
+
if (waitLongTimeout) {
|
|
1060
|
+
// we need to wait either 14 or 12 seconds to get to the 15s timeout (depending on how much we waited earlier)
|
|
1061
|
+
clock.tick(waitShortTimeout === 'vmn' ? 14000 : 12000);
|
|
1062
|
+
|
|
1063
|
+
// we check the results again after the long timeout - they should be the same
|
|
1064
|
+
await checkResults(expectedResults, mockGetClustersResult.joinCookie);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
// now check events emitted by Reachability class
|
|
1068
|
+
assert.equal(receivedEvents['done'], 1);
|
|
1069
|
+
|
|
1070
|
+
// if we've mocked at least one event for any protocol, check that we received
|
|
1071
|
+
// firstResultAvailable event for that protocol
|
|
1072
|
+
if (mockResultReadyEvents.filter((event) => event.protocol === 'udp').length > 0) {
|
|
1073
|
+
assert.equal(receivedEvents['firstResultAvailable']['udp'], 1);
|
|
1074
|
+
}
|
|
1075
|
+
if (mockResultReadyEvents.filter((event) => event.protocol === 'tcp').length > 0) {
|
|
1076
|
+
assert.equal(receivedEvents['firstResultAvailable']['tcp'], 1);
|
|
1077
|
+
}
|
|
1078
|
+
if (mockResultReadyEvents.filter((event) => event.protocol === 'xtls').length > 0) {
|
|
1079
|
+
assert.equal(receivedEvents['firstResultAvailable']['xtls'], 1);
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// finally, check the metrics
|
|
1083
|
+
assert.calledWith(
|
|
1084
|
+
Metrics.sendBehavioralMetric,
|
|
1085
|
+
'js_sdk_reachability_completed',
|
|
1086
|
+
expectedMetrics
|
|
1087
|
+
);
|
|
1088
|
+
})
|
|
1089
|
+
);
|
|
1090
|
+
|
|
1091
|
+
it('keeps updating reachability results after the 3s public cloud timeout expires', async () => {
|
|
1092
|
+
webex.config.meetings.experimental = {
|
|
1093
|
+
enableTcpReachability: true,
|
|
1094
|
+
enableTlsReachability: true,
|
|
1095
|
+
};
|
|
1096
|
+
|
|
433
1097
|
const reachability = new Reachability(webex);
|
|
434
1098
|
|
|
435
|
-
const
|
|
1099
|
+
const mockGetClustersResult = {
|
|
436
1100
|
clusters: {
|
|
437
|
-
|
|
438
|
-
udp: '
|
|
1101
|
+
clusterA: {
|
|
1102
|
+
udp: ['udp-urlA'],
|
|
1103
|
+
tcp: ['tcp-urlA'],
|
|
1104
|
+
xtls: ['xtls-urlA'],
|
|
1105
|
+
isVideoMesh: false,
|
|
1106
|
+
},
|
|
1107
|
+
clusterB: {
|
|
1108
|
+
udp: ['udp-urlB'],
|
|
1109
|
+
tcp: ['tcp-urlB'],
|
|
1110
|
+
xtls: ['xtls-urlB'],
|
|
1111
|
+
isVideoMesh: false,
|
|
439
1112
|
},
|
|
440
1113
|
},
|
|
441
|
-
};
|
|
442
|
-
const getClustersResult = {
|
|
443
|
-
clusters: {clusterId: 'cluster'},
|
|
444
1114
|
joinCookie: {id: 'id'},
|
|
445
1115
|
};
|
|
446
1116
|
|
|
447
|
-
reachability.reachabilityRequest.getClusters = sinon.stub().returns(
|
|
448
|
-
(reachability as any).performReachabilityChecks = sinon.stub().returns(reachabilityResults);
|
|
1117
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns(mockGetClustersResult);
|
|
449
1118
|
|
|
450
|
-
const
|
|
1119
|
+
const resultPromise = reachability.gatherReachability();
|
|
451
1120
|
|
|
452
|
-
|
|
1121
|
+
await testUtils.flushPromises();
|
|
453
1122
|
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
1123
|
+
// trigger some mock result events from ClusterReachability instances
|
|
1124
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeResult('udp', {
|
|
1125
|
+
result: 'reachable',
|
|
1126
|
+
clientMediaIPs: ['1.2.3.4'],
|
|
1127
|
+
latencyInMilliseconds: 11,
|
|
1128
|
+
});
|
|
1129
|
+
mockClusterReachabilityInstances['clusterB'].emitFakeResult('udp', {
|
|
1130
|
+
result: 'reachable',
|
|
1131
|
+
clientMediaIPs: ['10.20.30.40'],
|
|
1132
|
+
latencyInMilliseconds: 22,
|
|
1133
|
+
});
|
|
1134
|
+
|
|
1135
|
+
clock.tick(3000);
|
|
1136
|
+
await resultPromise;
|
|
1137
|
+
|
|
1138
|
+
// check that the reachability results contain the 2 results from above
|
|
1139
|
+
await checkResults(
|
|
1140
|
+
{
|
|
1141
|
+
clusterA: {
|
|
1142
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 11},
|
|
1143
|
+
tcp: {result: 'unreachable'},
|
|
1144
|
+
xtls: {result: 'unreachable'},
|
|
1145
|
+
isVideoMesh: false,
|
|
1146
|
+
},
|
|
1147
|
+
clusterB: {
|
|
1148
|
+
udp: {result: 'reachable', clientMediaIPs: ['10.20.30.40'], latencyInMilliseconds: 22},
|
|
1149
|
+
tcp: {result: 'unreachable'},
|
|
1150
|
+
xtls: {result: 'unreachable'},
|
|
1151
|
+
isVideoMesh: false,
|
|
1152
|
+
},
|
|
1153
|
+
},
|
|
1154
|
+
mockGetClustersResult.joinCookie
|
|
461
1155
|
);
|
|
462
1156
|
|
|
463
|
-
|
|
464
|
-
|
|
1157
|
+
// now simulate some more "late" results
|
|
1158
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeResult('tcp', {
|
|
1159
|
+
result: 'reachable',
|
|
1160
|
+
latencyInMilliseconds: 101,
|
|
1161
|
+
});
|
|
1162
|
+
mockClusterReachabilityInstances['clusterB'].emitFakeResult('xtls', {
|
|
1163
|
+
result: 'reachable',
|
|
1164
|
+
latencyInMilliseconds: 102,
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
// and wait for the final overall timeout
|
|
1168
|
+
clock.tick(12000);
|
|
1169
|
+
|
|
1170
|
+
// the reachability results should include all results from above (including the late ones)
|
|
1171
|
+
await checkResults(
|
|
1172
|
+
{
|
|
1173
|
+
clusterA: {
|
|
1174
|
+
udp: {result: 'reachable', clientMediaIPs: ['1.2.3.4'], latencyInMilliseconds: 11},
|
|
1175
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 101},
|
|
1176
|
+
xtls: {result: 'unreachable'},
|
|
1177
|
+
isVideoMesh: false,
|
|
1178
|
+
},
|
|
1179
|
+
clusterB: {
|
|
1180
|
+
udp: {result: 'reachable', clientMediaIPs: ['10.20.30.40'], latencyInMilliseconds: 22},
|
|
1181
|
+
tcp: {result: 'unreachable'},
|
|
1182
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 102},
|
|
1183
|
+
isVideoMesh: false,
|
|
1184
|
+
},
|
|
1185
|
+
},
|
|
1186
|
+
mockGetClustersResult.joinCookie
|
|
1187
|
+
);
|
|
465
1188
|
});
|
|
466
1189
|
|
|
467
|
-
it('
|
|
1190
|
+
it('handles clientMediaIpsUpdated event by updating clientMediaIps in results', async () => {
|
|
1191
|
+
webex.config.meetings.experimental = {
|
|
1192
|
+
enableTcpReachability: true,
|
|
1193
|
+
enableTlsReachability: true,
|
|
1194
|
+
};
|
|
1195
|
+
|
|
468
1196
|
const reachability = new Reachability(webex);
|
|
469
1197
|
|
|
470
|
-
const
|
|
1198
|
+
const mockGetClustersResult = {
|
|
471
1199
|
clusters: {
|
|
472
|
-
|
|
473
|
-
udp: '
|
|
1200
|
+
clusterA: {
|
|
1201
|
+
udp: ['udp-urlA'],
|
|
1202
|
+
tcp: ['tcp-urlA'],
|
|
1203
|
+
xtls: ['xtls-urlA'],
|
|
1204
|
+
isVideoMesh: false,
|
|
474
1205
|
},
|
|
475
1206
|
},
|
|
476
|
-
};
|
|
477
|
-
const getClustersResult = {
|
|
478
|
-
clusters: {clusterId: 'cluster'},
|
|
479
1207
|
joinCookie: {id: 'id'},
|
|
480
1208
|
};
|
|
481
1209
|
|
|
1210
|
+
reachability.reachabilityRequest.getClusters = sinon.stub().returns(mockGetClustersResult);
|
|
1211
|
+
|
|
1212
|
+
const resultPromise = reachability.gatherReachability();
|
|
1213
|
+
|
|
1214
|
+
await testUtils.flushPromises();
|
|
1215
|
+
|
|
1216
|
+
// trigger a mock result event
|
|
1217
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeResult('udp', {
|
|
1218
|
+
result: 'reachable',
|
|
1219
|
+
clientMediaIPs: ['64.103.40.20'],
|
|
1220
|
+
latencyInMilliseconds: 11,
|
|
1221
|
+
});
|
|
1222
|
+
// followed by some updates to client media IPs
|
|
1223
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeClientMediaIpUpdate('udp', '64.103.40.21');
|
|
1224
|
+
mockClusterReachabilityInstances['clusterA'].emitFakeClientMediaIpUpdate('udp', '64.103.40.22');
|
|
1225
|
+
|
|
1226
|
+
// wait for the final overall timeout
|
|
1227
|
+
clock.tick(15000);
|
|
1228
|
+
await resultPromise;
|
|
1229
|
+
|
|
1230
|
+
// check that the reachability results contain all the client media ips
|
|
1231
|
+
await checkResults(
|
|
1232
|
+
{
|
|
1233
|
+
clusterA: {
|
|
1234
|
+
udp: {
|
|
1235
|
+
result: 'reachable',
|
|
1236
|
+
clientMediaIPs: ['64.103.40.20', '64.103.40.21', '64.103.40.22'],
|
|
1237
|
+
latencyInMilliseconds: 11,
|
|
1238
|
+
},
|
|
1239
|
+
tcp: {result: 'unreachable'},
|
|
1240
|
+
xtls: {result: 'unreachable'},
|
|
1241
|
+
isVideoMesh: false,
|
|
1242
|
+
},
|
|
1243
|
+
},
|
|
1244
|
+
mockGetClustersResult.joinCookie
|
|
1245
|
+
);
|
|
1246
|
+
});
|
|
1247
|
+
|
|
1248
|
+
it('keeps the stored reachability from previous call to gatherReachability if getClusters fails', async () => {
|
|
1249
|
+
const reachability = new Reachability(webex);
|
|
1250
|
+
|
|
482
1251
|
reachability.reachabilityRequest.getClusters = sinon.stub().throws();
|
|
483
1252
|
|
|
484
1253
|
const result = await reachability.gatherReachability();
|
|
485
1254
|
|
|
486
1255
|
assert.empty(result);
|
|
487
1256
|
|
|
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);
|
|
1257
|
+
await checkResults({old: 'results'}, {old: 'joinCookie'});
|
|
499
1258
|
});
|
|
500
1259
|
|
|
501
1260
|
it('keeps the stored reachability from previous call to gatherReachability if performReachabilityChecks fails', async () => {
|
|
502
1261
|
const reachability = new Reachability(webex);
|
|
503
1262
|
|
|
504
|
-
const reachabilityResults = {
|
|
505
|
-
clusters: {
|
|
506
|
-
clusterId: {
|
|
507
|
-
udp: 'testUDP',
|
|
508
|
-
},
|
|
509
|
-
},
|
|
510
|
-
};
|
|
511
1263
|
const getClustersResult = {
|
|
512
1264
|
clusters: {clusterId: 'cluster'},
|
|
513
|
-
joinCookie: {id: 'id'},
|
|
1265
|
+
joinCookie: {id: 'cookie id'},
|
|
514
1266
|
};
|
|
515
1267
|
|
|
516
1268
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
@@ -520,17 +1272,7 @@ describe('gatherReachability', () => {
|
|
|
520
1272
|
|
|
521
1273
|
assert.empty(result);
|
|
522
1274
|
|
|
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);
|
|
1275
|
+
await checkResults({old: 'results'}, {id: 'cookie id'});
|
|
534
1276
|
});
|
|
535
1277
|
|
|
536
1278
|
it('starts ClusterReachability on each media cluster', async () => {
|
|
@@ -561,14 +1303,10 @@ describe('gatherReachability', () => {
|
|
|
561
1303
|
|
|
562
1304
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
563
1305
|
|
|
564
|
-
const
|
|
565
|
-
const clusterReachabilityCtorStub = sinon
|
|
566
|
-
.stub(ClusterReachabilityModule, 'ClusterReachability')
|
|
567
|
-
.callsFake(() => ({
|
|
568
|
-
start: startStub,
|
|
569
|
-
}));
|
|
1306
|
+
const promise = reachability.gatherReachability();
|
|
570
1307
|
|
|
571
|
-
await
|
|
1308
|
+
await simulateTimeout();
|
|
1309
|
+
await promise;
|
|
572
1310
|
|
|
573
1311
|
assert.calledTwice(clusterReachabilityCtorStub);
|
|
574
1312
|
assert.calledWith(clusterReachabilityCtorStub, 'cluster 1', {
|
|
@@ -585,7 +1323,8 @@ describe('gatherReachability', () => {
|
|
|
585
1323
|
isVideoMesh: true,
|
|
586
1324
|
});
|
|
587
1325
|
|
|
588
|
-
assert.
|
|
1326
|
+
assert.calledOnce(mockClusterReachabilityInstances['cluster 1'].start);
|
|
1327
|
+
assert.calledOnce(mockClusterReachabilityInstances['cluster 2'].start);
|
|
589
1328
|
});
|
|
590
1329
|
|
|
591
1330
|
it('does not do TCP reachability if it is disabled in config', async () => {
|
|
@@ -610,13 +1349,9 @@ describe('gatherReachability', () => {
|
|
|
610
1349
|
|
|
611
1350
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
612
1351
|
|
|
613
|
-
const
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
start: sinon.stub().resolves({}),
|
|
617
|
-
}));
|
|
618
|
-
|
|
619
|
-
await reachability.gatherReachability();
|
|
1352
|
+
const promise = reachability.gatherReachability();
|
|
1353
|
+
await simulateTimeout();
|
|
1354
|
+
await promise;
|
|
620
1355
|
|
|
621
1356
|
assert.calledOnceWithExactly(clusterReachabilityCtorStub, 'cluster name', {
|
|
622
1357
|
isVideoMesh: false,
|
|
@@ -648,13 +1383,10 @@ describe('gatherReachability', () => {
|
|
|
648
1383
|
|
|
649
1384
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
650
1385
|
|
|
651
|
-
const
|
|
652
|
-
.stub(ClusterReachabilityModule, 'ClusterReachability')
|
|
653
|
-
.callsFake(() => ({
|
|
654
|
-
start: sinon.stub().resolves({}),
|
|
655
|
-
}));
|
|
1386
|
+
const promise = reachability.gatherReachability();
|
|
656
1387
|
|
|
657
|
-
await
|
|
1388
|
+
await simulateTimeout();
|
|
1389
|
+
await promise;
|
|
658
1390
|
|
|
659
1391
|
assert.calledOnceWithExactly(clusterReachabilityCtorStub, 'cluster name', {
|
|
660
1392
|
isVideoMesh: false,
|
|
@@ -686,13 +1418,10 @@ describe('gatherReachability', () => {
|
|
|
686
1418
|
|
|
687
1419
|
reachability.reachabilityRequest.getClusters = sinon.stub().returns(getClustersResult);
|
|
688
1420
|
|
|
689
|
-
const
|
|
690
|
-
.stub(ClusterReachabilityModule, 'ClusterReachability')
|
|
691
|
-
.callsFake(() => ({
|
|
692
|
-
start: sinon.stub().resolves({}),
|
|
693
|
-
}));
|
|
1421
|
+
const promise = reachability.gatherReachability();
|
|
694
1422
|
|
|
695
|
-
await
|
|
1423
|
+
await simulateTimeout();
|
|
1424
|
+
await promise;
|
|
696
1425
|
|
|
697
1426
|
assert.calledOnceWithExactly(clusterReachabilityCtorStub, 'cluster name', {
|
|
698
1427
|
isVideoMesh: false,
|
|
@@ -1033,3 +1762,310 @@ describe('getReachabilityMetrics', () => {
|
|
|
1033
1762
|
);
|
|
1034
1763
|
});
|
|
1035
1764
|
});
|
|
1765
|
+
|
|
1766
|
+
class TestReachability extends Reachability {
|
|
1767
|
+
constructor(webex: object) {
|
|
1768
|
+
super(webex);
|
|
1769
|
+
}
|
|
1770
|
+
|
|
1771
|
+
public testGetStatistics(
|
|
1772
|
+
results: Array<ClusterReachabilityModule.ClusterReachabilityResult & {isVideoMesh: boolean}>,
|
|
1773
|
+
protocol: 'udp' | 'tcp' | 'xtls',
|
|
1774
|
+
isVideoMesh: boolean
|
|
1775
|
+
) {
|
|
1776
|
+
return this.getStatistics(results, protocol, isVideoMesh);
|
|
1777
|
+
}
|
|
1778
|
+
|
|
1779
|
+
public testSendMetric() {
|
|
1780
|
+
return this.sendMetric();
|
|
1781
|
+
}
|
|
1782
|
+
|
|
1783
|
+
public setFakeClusterReachability(fakeClusterReachability) {
|
|
1784
|
+
this.clusterReachability = fakeClusterReachability;
|
|
1785
|
+
}
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
describe('getStatistics', () => {
|
|
1789
|
+
let webex;
|
|
1790
|
+
let reachability;
|
|
1791
|
+
|
|
1792
|
+
beforeEach(() => {
|
|
1793
|
+
webex = new MockWebex();
|
|
1794
|
+
reachability = new TestReachability(webex);
|
|
1795
|
+
});
|
|
1796
|
+
|
|
1797
|
+
it('takes values from the correct protocol', () => {
|
|
1798
|
+
const results = [
|
|
1799
|
+
{
|
|
1800
|
+
udp: {
|
|
1801
|
+
result: 'reachable',
|
|
1802
|
+
latencyInMilliseconds: 10,
|
|
1803
|
+
},
|
|
1804
|
+
tcp: {
|
|
1805
|
+
result: 'reachable',
|
|
1806
|
+
latencyInMilliseconds: 1010,
|
|
1807
|
+
},
|
|
1808
|
+
xtls: {
|
|
1809
|
+
result: 'reachable',
|
|
1810
|
+
latencyInMilliseconds: 2010,
|
|
1811
|
+
},
|
|
1812
|
+
isVideoMesh: false,
|
|
1813
|
+
},
|
|
1814
|
+
{
|
|
1815
|
+
udp: {
|
|
1816
|
+
result: 'reachable',
|
|
1817
|
+
latencyInMilliseconds: 20,
|
|
1818
|
+
},
|
|
1819
|
+
tcp: {
|
|
1820
|
+
result: 'reachable',
|
|
1821
|
+
latencyInMilliseconds: 1020,
|
|
1822
|
+
},
|
|
1823
|
+
xtls: {
|
|
1824
|
+
result: 'reachable',
|
|
1825
|
+
latencyInMilliseconds: 2020,
|
|
1826
|
+
},
|
|
1827
|
+
isVideoMesh: false,
|
|
1828
|
+
},
|
|
1829
|
+
{
|
|
1830
|
+
udp: {
|
|
1831
|
+
result: 'reachable',
|
|
1832
|
+
latencyInMilliseconds: 30,
|
|
1833
|
+
},
|
|
1834
|
+
tcp: {
|
|
1835
|
+
result: 'reachable',
|
|
1836
|
+
latencyInMilliseconds: 1030,
|
|
1837
|
+
},
|
|
1838
|
+
xtls: {
|
|
1839
|
+
result: 'reachable',
|
|
1840
|
+
latencyInMilliseconds: 2030,
|
|
1841
|
+
},
|
|
1842
|
+
isVideoMesh: false,
|
|
1843
|
+
},
|
|
1844
|
+
];
|
|
1845
|
+
|
|
1846
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'udp', false), {
|
|
1847
|
+
min: 10,
|
|
1848
|
+
max: 30,
|
|
1849
|
+
average: 20,
|
|
1850
|
+
});
|
|
1851
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'tcp', false), {
|
|
1852
|
+
min: 1010,
|
|
1853
|
+
max: 1030,
|
|
1854
|
+
average: 1020,
|
|
1855
|
+
});
|
|
1856
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'xtls', false), {
|
|
1857
|
+
min: 2010,
|
|
1858
|
+
max: 2030,
|
|
1859
|
+
average: 2020,
|
|
1860
|
+
});
|
|
1861
|
+
});
|
|
1862
|
+
|
|
1863
|
+
it('filters based on isVideoMesh value', () => {
|
|
1864
|
+
const results = [
|
|
1865
|
+
{
|
|
1866
|
+
udp: {
|
|
1867
|
+
result: 'reachable',
|
|
1868
|
+
latencyInMilliseconds: 10,
|
|
1869
|
+
},
|
|
1870
|
+
isVideoMesh: true,
|
|
1871
|
+
},
|
|
1872
|
+
{
|
|
1873
|
+
udp: {
|
|
1874
|
+
result: 'reachable',
|
|
1875
|
+
latencyInMilliseconds: 20,
|
|
1876
|
+
},
|
|
1877
|
+
isVideoMesh: true,
|
|
1878
|
+
},
|
|
1879
|
+
{
|
|
1880
|
+
udp: {
|
|
1881
|
+
result: 'reachable',
|
|
1882
|
+
latencyInMilliseconds: 30,
|
|
1883
|
+
},
|
|
1884
|
+
isVideoMesh: true,
|
|
1885
|
+
},
|
|
1886
|
+
{
|
|
1887
|
+
udp: {
|
|
1888
|
+
result: 'reachable',
|
|
1889
|
+
latencyInMilliseconds: 100,
|
|
1890
|
+
},
|
|
1891
|
+
isVideoMesh: false,
|
|
1892
|
+
},
|
|
1893
|
+
{
|
|
1894
|
+
udp: {
|
|
1895
|
+
result: 'reachable',
|
|
1896
|
+
latencyInMilliseconds: 200,
|
|
1897
|
+
},
|
|
1898
|
+
isVideoMesh: false,
|
|
1899
|
+
},
|
|
1900
|
+
];
|
|
1901
|
+
|
|
1902
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'udp', true), {
|
|
1903
|
+
min: 10,
|
|
1904
|
+
max: 30,
|
|
1905
|
+
average: 20,
|
|
1906
|
+
});
|
|
1907
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'udp', false), {
|
|
1908
|
+
min: 100,
|
|
1909
|
+
max: 200,
|
|
1910
|
+
average: 150,
|
|
1911
|
+
});
|
|
1912
|
+
});
|
|
1913
|
+
|
|
1914
|
+
it('only takes into account "reachable" results', () => {
|
|
1915
|
+
const results = [
|
|
1916
|
+
{
|
|
1917
|
+
udp: {
|
|
1918
|
+
result: 'reachable',
|
|
1919
|
+
latencyInMilliseconds: 10,
|
|
1920
|
+
},
|
|
1921
|
+
isVideoMesh: false,
|
|
1922
|
+
},
|
|
1923
|
+
{
|
|
1924
|
+
udp: {
|
|
1925
|
+
result: 'unreachable',
|
|
1926
|
+
latencyInMilliseconds: 100, // value put in here just for testing, in practice we wouldn't have any value here if it was unreachable
|
|
1927
|
+
},
|
|
1928
|
+
isVideoMesh: false,
|
|
1929
|
+
},
|
|
1930
|
+
{
|
|
1931
|
+
udp: {
|
|
1932
|
+
result: 'reachable',
|
|
1933
|
+
latencyInMilliseconds: 20,
|
|
1934
|
+
},
|
|
1935
|
+
isVideoMesh: false,
|
|
1936
|
+
},
|
|
1937
|
+
{
|
|
1938
|
+
udp: {
|
|
1939
|
+
result: 'untested',
|
|
1940
|
+
latencyInMilliseconds: 200, // value put in here just for testing, in practice we wouldn't have any value here if it was untested
|
|
1941
|
+
},
|
|
1942
|
+
isVideoMesh: false,
|
|
1943
|
+
},
|
|
1944
|
+
];
|
|
1945
|
+
|
|
1946
|
+
assert.deepEqual(reachability.testGetStatistics(results, 'udp', false), {
|
|
1947
|
+
min: 10,
|
|
1948
|
+
max: 20,
|
|
1949
|
+
average: 15,
|
|
1950
|
+
});
|
|
1951
|
+
});
|
|
1952
|
+
|
|
1953
|
+
it('handles the case when results are empty', () => {
|
|
1954
|
+
assert.deepEqual(reachability.testGetStatistics([], 'udp', false), {
|
|
1955
|
+
min: -1,
|
|
1956
|
+
max: -1,
|
|
1957
|
+
average: -1,
|
|
1958
|
+
});
|
|
1959
|
+
});
|
|
1960
|
+
|
|
1961
|
+
it('handles the case when results are empty after filtering', () => {
|
|
1962
|
+
const fakeResults = [
|
|
1963
|
+
{
|
|
1964
|
+
udp: {
|
|
1965
|
+
result: 'untested', // it will get filtered out because of this value
|
|
1966
|
+
latencyInMilliseconds: 10,
|
|
1967
|
+
},
|
|
1968
|
+
tcp: {
|
|
1969
|
+
result: 'reachable',
|
|
1970
|
+
latencyInMilliseconds: 10, // it will get filtered out because of the tcp protocol
|
|
1971
|
+
},
|
|
1972
|
+
isVideoMesh: false,
|
|
1973
|
+
},
|
|
1974
|
+
{
|
|
1975
|
+
udp: {
|
|
1976
|
+
result: 'reachable',
|
|
1977
|
+
latencyInMilliseconds: 10,
|
|
1978
|
+
},
|
|
1979
|
+
isVideoMesh: true, // it will get filtered out because of this value
|
|
1980
|
+
},
|
|
1981
|
+
];
|
|
1982
|
+
|
|
1983
|
+
assert.deepEqual(reachability.testGetStatistics(fakeResults, 'udp', false), {
|
|
1984
|
+
min: -1,
|
|
1985
|
+
max: -1,
|
|
1986
|
+
average: -1,
|
|
1987
|
+
});
|
|
1988
|
+
});
|
|
1989
|
+
});
|
|
1990
|
+
|
|
1991
|
+
describe('sendMetric', () => {
|
|
1992
|
+
let webex;
|
|
1993
|
+
let reachability;
|
|
1994
|
+
|
|
1995
|
+
beforeEach(() => {
|
|
1996
|
+
webex = new MockWebex();
|
|
1997
|
+
reachability = new TestReachability(webex);
|
|
1998
|
+
|
|
1999
|
+
sinon.stub(Metrics, 'sendBehavioralMetric');
|
|
2000
|
+
});
|
|
2001
|
+
|
|
2002
|
+
it('works as expected', async () => {
|
|
2003
|
+
// setup stub for getStatistics to return values that show what parameters it was called with,
|
|
2004
|
+
// this way we can verify that the correct results of calls to getStatistics are placed
|
|
2005
|
+
// in correct data fields when sendBehavioralMetric() is called
|
|
2006
|
+
sinon.stub(reachability, 'getStatistics').callsFake((results, protocol, isVideoMesh) => {
|
|
2007
|
+
return {results, protocol, isVideoMesh};
|
|
2008
|
+
});
|
|
2009
|
+
|
|
2010
|
+
// setup fake clusterReachability results
|
|
2011
|
+
reachability.setFakeClusterReachability({
|
|
2012
|
+
cluster1: {
|
|
2013
|
+
getResult: sinon.stub().returns({result: 'result 1'}),
|
|
2014
|
+
isVideoMesh: true,
|
|
2015
|
+
},
|
|
2016
|
+
cluster2: {
|
|
2017
|
+
getResult: sinon.stub().returns({result: 'result 2'}),
|
|
2018
|
+
isVideoMesh: false,
|
|
2019
|
+
},
|
|
2020
|
+
cluster3: {
|
|
2021
|
+
getResult: sinon.stub().returns({result: 'result 3'}),
|
|
2022
|
+
isVideoMesh: false,
|
|
2023
|
+
},
|
|
2024
|
+
});
|
|
2025
|
+
|
|
2026
|
+
await reachability.sendMetric();
|
|
2027
|
+
|
|
2028
|
+
// each call to getStatistics should be made with all the results from all fake clusterReachability:
|
|
2029
|
+
const expectedResults = [
|
|
2030
|
+
{
|
|
2031
|
+
result: 'result 1',
|
|
2032
|
+
isVideoMesh: true,
|
|
2033
|
+
},
|
|
2034
|
+
{
|
|
2035
|
+
result: 'result 2',
|
|
2036
|
+
isVideoMesh: false,
|
|
2037
|
+
},
|
|
2038
|
+
{
|
|
2039
|
+
result: 'result 3',
|
|
2040
|
+
isVideoMesh: false,
|
|
2041
|
+
},
|
|
2042
|
+
];
|
|
2043
|
+
|
|
2044
|
+
assert.calledWith(Metrics.sendBehavioralMetric, 'js_sdk_reachability_completed', {
|
|
2045
|
+
vmn: {
|
|
2046
|
+
udp: {
|
|
2047
|
+
results: expectedResults,
|
|
2048
|
+
protocol: 'udp',
|
|
2049
|
+
isVideoMesh: true,
|
|
2050
|
+
},
|
|
2051
|
+
},
|
|
2052
|
+
public: {
|
|
2053
|
+
udp: {
|
|
2054
|
+
results: expectedResults,
|
|
2055
|
+
protocol: 'udp',
|
|
2056
|
+
isVideoMesh: false,
|
|
2057
|
+
},
|
|
2058
|
+
tcp: {
|
|
2059
|
+
results: expectedResults,
|
|
2060
|
+
protocol: 'tcp',
|
|
2061
|
+
isVideoMesh: false,
|
|
2062
|
+
},
|
|
2063
|
+
xtls: {
|
|
2064
|
+
results: expectedResults,
|
|
2065
|
+
protocol: 'xtls',
|
|
2066
|
+
isVideoMesh: false,
|
|
2067
|
+
},
|
|
2068
|
+
},
|
|
2069
|
+
});
|
|
2070
|
+
});
|
|
2071
|
+
});
|