@webex/plugin-meetings 3.8.0-next.5 → 3.8.0-next.50
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 +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/constants.js +14 -1
- package/dist/constants.js.map +1 -1
- package/dist/controls-options-manager/enums.js +2 -0
- package/dist/controls-options-manager/enums.js.map +1 -1
- package/dist/controls-options-manager/types.js.map +1 -1
- package/dist/controls-options-manager/util.js +52 -0
- package/dist/controls-options-manager/util.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/controlsUtils.js +28 -10
- package/dist/locus-info/controlsUtils.js.map +1 -1
- package/dist/locus-info/index.js +20 -1
- package/dist/locus-info/index.js.map +1 -1
- package/dist/media/index.js +3 -15
- package/dist/media/index.js.map +1 -1
- package/dist/meeting/in-meeting-actions.js +11 -1
- package/dist/meeting/in-meeting-actions.js.map +1 -1
- package/dist/meeting/index.js +544 -324
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/locusMediaRequest.js +26 -23
- package/dist/meeting/locusMediaRequest.js.map +1 -1
- package/dist/meeting/muteState.js +0 -2
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.js +30 -0
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/request.type.js.map +1 -1
- package/dist/meeting/util.js +27 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/meeting-info-v2.js +359 -60
- package/dist/meeting-info/meeting-info-v2.js.map +1 -1
- package/dist/meetings/index.js +69 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/meetings/util.js +14 -0
- package/dist/meetings/util.js.map +1 -1
- package/dist/member/index.js +10 -0
- package/dist/member/index.js.map +1 -1
- package/dist/member/util.js +3 -0
- package/dist/member/util.js.map +1 -1
- package/dist/metrics/constants.js +9 -0
- package/dist/metrics/constants.js.map +1 -1
- package/dist/reachability/clusterReachability.js +63 -27
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +111 -46
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/reachability.types.js +14 -0
- package/dist/reachability/reachability.types.js.map +1 -1
- package/dist/reachability/request.js +19 -3
- package/dist/reachability/request.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -2
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/recording-controller/util.js +5 -5
- package/dist/recording-controller/util.js.map +1 -1
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/turnDiscovery.js +45 -27
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/roap/types.js +17 -0
- package/dist/roap/types.js.map +1 -0
- package/dist/types/config.d.ts +1 -0
- package/dist/types/constants.d.ts +10 -0
- package/dist/types/controls-options-manager/enums.d.ts +3 -1
- package/dist/types/controls-options-manager/types.d.ts +7 -1
- package/dist/types/locus-info/index.d.ts +1 -0
- package/dist/types/meeting/in-meeting-actions.d.ts +10 -0
- package/dist/types/meeting/index.d.ts +50 -3
- package/dist/types/meeting/muteState.d.ts +0 -1
- package/dist/types/meeting/request.d.ts +12 -1
- package/dist/types/meeting/request.type.d.ts +6 -0
- package/dist/types/meeting/util.d.ts +8 -1
- package/dist/types/meeting-info/meeting-info-v2.d.ts +80 -0
- package/dist/types/meetings/index.d.ts +29 -0
- package/dist/types/member/index.d.ts +1 -0
- package/dist/types/metrics/constants.d.ts +9 -0
- package/dist/types/reachability/clusterReachability.d.ts +15 -7
- package/dist/types/reachability/index.d.ts +10 -1
- package/dist/types/reachability/reachability.types.d.ts +5 -0
- package/dist/types/roap/index.d.ts +3 -2
- package/dist/types/roap/turnDiscovery.d.ts +5 -17
- package/dist/types/roap/types.d.ts +16 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/config.ts +1 -0
- package/src/constants.ts +17 -0
- package/src/controls-options-manager/enums.ts +2 -0
- package/src/controls-options-manager/types.ts +11 -1
- package/src/controls-options-manager/util.ts +62 -0
- package/src/locus-info/controlsUtils.ts +44 -14
- package/src/locus-info/index.ts +23 -1
- package/src/media/index.ts +5 -21
- package/src/meeting/in-meeting-actions.ts +20 -0
- package/src/meeting/index.ts +351 -99
- package/src/meeting/locusMediaRequest.ts +33 -23
- package/src/meeting/muteState.ts +0 -2
- package/src/meeting/request.ts +36 -1
- package/src/meeting/request.type.ts +7 -0
- package/src/meeting/util.ts +27 -2
- package/src/meeting-info/meeting-info-v2.ts +247 -6
- package/src/meetings/index.ts +87 -1
- package/src/meetings/util.ts +18 -0
- package/src/member/index.ts +11 -0
- package/src/member/util.ts +3 -0
- package/src/metrics/constants.ts +9 -0
- package/src/reachability/clusterReachability.ts +73 -26
- package/src/reachability/index.ts +69 -0
- package/src/reachability/reachability.types.ts +6 -0
- package/src/reachability/request.ts +7 -0
- package/src/reconnection-manager/index.ts +2 -2
- package/src/recording-controller/util.ts +17 -13
- package/src/roap/index.ts +3 -7
- package/src/roap/turnDiscovery.ts +34 -39
- package/src/roap/types.ts +23 -0
- package/test/unit/spec/controls-options-manager/util.js +120 -0
- package/test/unit/spec/locus-info/controlsUtils.js +103 -9
- package/test/unit/spec/locus-info/index.js +28 -0
- package/test/unit/spec/media/index.ts +6 -16
- package/test/unit/spec/meeting/in-meeting-actions.ts +13 -4
- package/test/unit/spec/meeting/index.js +558 -145
- package/test/unit/spec/meeting/locusMediaRequest.ts +101 -88
- package/test/unit/spec/meeting/muteState.js +0 -2
- package/test/unit/spec/meeting/request.js +32 -1
- package/test/unit/spec/meeting/utils.js +123 -18
- package/test/unit/spec/meeting-info/meetinginfov2.js +443 -114
- package/test/unit/spec/meetings/index.js +96 -1
- package/test/unit/spec/member/index.js +7 -0
- package/test/unit/spec/member/util.js +24 -0
- package/test/unit/spec/reachability/clusterReachability.ts +88 -56
- package/test/unit/spec/reachability/index.ts +47 -0
- package/test/unit/spec/reachability/request.js +47 -2
- package/test/unit/spec/reconnection-manager/index.js +4 -4
- package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -285,7 +285,8 @@ describe('plugin-meetings', () => {
|
|
285
285
|
|
286
286
|
describe('failure', () => {
|
287
287
|
it('should not accept non-number input', () => {
|
288
|
-
const logUploadIntervalMultiplicationFactor =
|
288
|
+
const logUploadIntervalMultiplicationFactor =
|
289
|
+
webex.meetings.config.logUploadIntervalMultiplicationFactor;
|
289
290
|
|
290
291
|
webex.meetings._setLogUploadIntervalMultiplicationFactor('test');
|
291
292
|
assert.equal(
|
@@ -441,6 +442,19 @@ describe('plugin-meetings', () => {
|
|
441
442
|
assert.isTrue(webex.meetings.registered);
|
442
443
|
});
|
443
444
|
|
445
|
+
it('resolves even if startReachability() rejects', async () => {
|
446
|
+
webex.canAuthorize = true;
|
447
|
+
webex.meetings.registered = false;
|
448
|
+
webex.meetings.startReachability = sinon.stub().rejects(new Error('fake error'));
|
449
|
+
|
450
|
+
await webex.meetings.register();
|
451
|
+
assert.calledOnceWithExactly(webex.internal.device.register, undefined);
|
452
|
+
assert.called(webex.internal.services.getMeetingPreferences);
|
453
|
+
assert.called(webex.internal.services.fetchClientRegionInfo);
|
454
|
+
assert.called(webex.internal.mercury.connect);
|
455
|
+
assert.isTrue(webex.meetings.registered);
|
456
|
+
});
|
457
|
+
|
444
458
|
it('passes on the device registration options', async () => {
|
445
459
|
webex.canAuthorize = true;
|
446
460
|
webex.meetings.registered = false;
|
@@ -569,6 +583,24 @@ describe('plugin-meetings', () => {
|
|
569
583
|
await assert.isRejected(webex.meetings.unregister());
|
570
584
|
});
|
571
585
|
|
586
|
+
it('does not reject when device.unregister fails with statusCode 404', (done) => {
|
587
|
+
webex.meetings.registered = true;
|
588
|
+
webex.internal.device.unregister = sinon.stub().rejects({statusCode: 404});
|
589
|
+
webex.meetings.unregister().then(() => {
|
590
|
+
assert.calledWith(
|
591
|
+
TriggerProxy.trigger,
|
592
|
+
sinon.match.instanceOf(Meetings),
|
593
|
+
{
|
594
|
+
file: 'meetings',
|
595
|
+
function: 'unregister',
|
596
|
+
},
|
597
|
+
'meetings:unregistered'
|
598
|
+
);
|
599
|
+
assert.isFalse(webex.meetings.registered);
|
600
|
+
done();
|
601
|
+
});
|
602
|
+
});
|
603
|
+
|
572
604
|
it('rejects when mercury.disconnect fails', async () => {
|
573
605
|
webex.meetings.registered = true;
|
574
606
|
webex.internal.mercury.disconnect = sinon.stub().returns(Promise.reject());
|
@@ -918,6 +950,69 @@ describe('plugin-meetings', () => {
|
|
918
950
|
});
|
919
951
|
});
|
920
952
|
});
|
953
|
+
describe('#fetchStaticMeetingLink', () => {
|
954
|
+
const conversationUrl = 'conv.fakeconversationurl.com';
|
955
|
+
|
956
|
+
afterEach(() => {
|
957
|
+
sinon.restore();
|
958
|
+
});
|
959
|
+
|
960
|
+
it('should have #fetchStaticMeetingLink', () => {
|
961
|
+
assert.exists(webex.meetings.fetchStaticMeetingLink);
|
962
|
+
});
|
963
|
+
|
964
|
+
it('should call MeetingInfo#fetchStaticMeetingLink() with proper params', () => {
|
965
|
+
webex.meetings.meetingInfo.fetchStaticMeetingLink = sinon
|
966
|
+
.stub()
|
967
|
+
.resolves(conversationUrl);
|
968
|
+
|
969
|
+
return webex.meetings.fetchStaticMeetingLink(conversationUrl).then(() => {
|
970
|
+
assert.calledWith(webex.meetings.meetingInfo.fetchStaticMeetingLink, conversationUrl);
|
971
|
+
});
|
972
|
+
});
|
973
|
+
});
|
974
|
+
describe('#enableStaticMeetingLink', () => {
|
975
|
+
const conversationUrl = 'conv.fakeconversationurl.com';
|
976
|
+
|
977
|
+
afterEach(() => {
|
978
|
+
sinon.restore();
|
979
|
+
});
|
980
|
+
|
981
|
+
it('should have #enableStaticMeetingLink', () => {
|
982
|
+
assert.exists(webex.meetings.enableStaticMeetingLink);
|
983
|
+
});
|
984
|
+
|
985
|
+
it('should call MeetingInfo#enableStaticMeetingLink() with proper params', () => {
|
986
|
+
webex.meetings.meetingInfo.enableStaticMeetingLink = sinon
|
987
|
+
.stub()
|
988
|
+
.resolves(conversationUrl);
|
989
|
+
|
990
|
+
return webex.meetings.enableStaticMeetingLink(conversationUrl).then(() => {
|
991
|
+
assert.calledWith(webex.meetings.meetingInfo.enableStaticMeetingLink, conversationUrl);
|
992
|
+
});
|
993
|
+
});
|
994
|
+
});
|
995
|
+
describe('#disableStaticMeetingLink', () => {
|
996
|
+
const conversationUrl = 'conv.fakeconversationurl.com';
|
997
|
+
|
998
|
+
afterEach(() => {
|
999
|
+
sinon.restore();
|
1000
|
+
});
|
1001
|
+
|
1002
|
+
it('should have #disableStaticMeetingLink', () => {
|
1003
|
+
assert.exists(webex.meetings.disableStaticMeetingLink);
|
1004
|
+
});
|
1005
|
+
|
1006
|
+
it('should call MeetingInfo#disableStaticMeetingLink() with proper params', () => {
|
1007
|
+
webex.meetings.meetingInfo.disableStaticMeetingLink = sinon
|
1008
|
+
.stub()
|
1009
|
+
.resolves(conversationUrl);
|
1010
|
+
|
1011
|
+
return webex.meetings.disableStaticMeetingLink(conversationUrl).then(() => {
|
1012
|
+
assert.calledWith(webex.meetings.meetingInfo.disableStaticMeetingLink, conversationUrl);
|
1013
|
+
});
|
1014
|
+
});
|
1015
|
+
});
|
921
1016
|
describe('#create', () => {
|
922
1017
|
let infoOptions;
|
923
1018
|
|
@@ -50,6 +50,13 @@ describe('member', () => {
|
|
50
50
|
|
51
51
|
assert.calledOnceWithExactly(MemberUtil.canReclaimHost, participant);
|
52
52
|
});
|
53
|
+
|
54
|
+
it('checks that processParticipant calls isPresenterAssignmentProhibited', () => {
|
55
|
+
sinon.spy(MemberUtil, 'isPresenterAssignmentProhibited');
|
56
|
+
member.processParticipant(participant);
|
57
|
+
|
58
|
+
assert.calledOnceWithExactly(MemberUtil.isPresenterAssignmentProhibited, participant);
|
59
|
+
});
|
53
60
|
})
|
54
61
|
|
55
62
|
describe('#processMember', () => {
|
@@ -557,6 +557,30 @@ describe('plugin-meetings', () => {
|
|
557
557
|
testResult(false, undefined, false);
|
558
558
|
});
|
559
559
|
});
|
560
|
+
|
561
|
+
describe('MemberUtil.isPresenterAssignmentProhibited', () => {
|
562
|
+
it('returns true when isPresenterAssignmentProhibited is true', () => {
|
563
|
+
const participant = {
|
564
|
+
presenterAssignmentNotAllowed: true
|
565
|
+
};
|
566
|
+
|
567
|
+
assert.isTrue(MemberUtil.isPresenterAssignmentProhibited(participant));
|
568
|
+
});
|
569
|
+
|
570
|
+
it('returns false when isPresenterAssignmentProhibited is false', () => {
|
571
|
+
const participant = {
|
572
|
+
presenterAssignmentNotAllowed: false,
|
573
|
+
};
|
574
|
+
|
575
|
+
assert.isFalse(MemberUtil.isPresenterAssignmentProhibited(participant));
|
576
|
+
});
|
577
|
+
|
578
|
+
it('returns undefined when isPresenterAssignmentProhibited is undefined', () => {
|
579
|
+
const participant = {};
|
580
|
+
|
581
|
+
assert.isUndefined(MemberUtil.isPresenterAssignmentProhibited(participant));
|
582
|
+
});
|
583
|
+
});
|
560
584
|
});
|
561
585
|
|
562
586
|
describe('extractMediaStatus', () => {
|
@@ -9,7 +9,9 @@ import {
|
|
9
9
|
ResultEventData,
|
10
10
|
Events,
|
11
11
|
ClientMediaIpsUpdatedEventData,
|
12
|
+
NatTypeUpdatedEventData,
|
12
13
|
} from '@webex/plugin-meetings/src/reachability/clusterReachability'; // replace with actual path
|
14
|
+
import { NatType } from 'packages/@webex/plugin-meetings/dist/reachability/reachability.types';
|
13
15
|
|
14
16
|
describe('ClusterReachability', () => {
|
15
17
|
let previousRTCPeerConnection;
|
@@ -17,15 +19,17 @@ describe('ClusterReachability', () => {
|
|
17
19
|
let fakePeerConnection;
|
18
20
|
let gatherIceCandidatesSpy;
|
19
21
|
|
20
|
-
const emittedEvents: Record<Events, (ResultEventData | ClientMediaIpsUpdatedEventData)[]> = {
|
22
|
+
const emittedEvents: Record<Events, (ResultEventData | ClientMediaIpsUpdatedEventData | NatTypeUpdatedEventData)[]> = {
|
21
23
|
[Events.resultReady]: [],
|
22
24
|
[Events.clientMediaIpsUpdated]: [],
|
25
|
+
[Events.natTypeUpdated]: [],
|
23
26
|
};
|
24
27
|
const FAKE_OFFER = {type: 'offer', sdp: 'fake sdp'};
|
25
28
|
|
26
29
|
const resetEmittedEvents = () => {
|
27
30
|
emittedEvents[Events.resultReady].length = 0;
|
28
31
|
emittedEvents[Events.clientMediaIpsUpdated].length = 0;
|
32
|
+
emittedEvents[Events.natTypeUpdated].length = 0;
|
29
33
|
};
|
30
34
|
beforeEach(() => {
|
31
35
|
fakePeerConnection = {
|
@@ -56,6 +60,10 @@ describe('ClusterReachability', () => {
|
|
56
60
|
clusterReachability.on(Events.clientMediaIpsUpdated, (data: ClientMediaIpsUpdatedEventData) => {
|
57
61
|
emittedEvents[Events.clientMediaIpsUpdated].push(data);
|
58
62
|
});
|
63
|
+
|
64
|
+
clusterReachability.on(Events.natTypeUpdated, (data: NatTypeUpdatedEventData) => {
|
65
|
+
emittedEvents[Events.natTypeUpdated].push(data);
|
66
|
+
});
|
59
67
|
});
|
60
68
|
|
61
69
|
afterEach(() => {
|
@@ -166,59 +174,6 @@ describe('ClusterReachability', () => {
|
|
166
174
|
assert.deepEqual(emittedEvents[Events.clientMediaIpsUpdated], []);
|
167
175
|
});
|
168
176
|
|
169
|
-
it('resolves and has correct result as soon as it finds that all udp, tcp and tls are reachable', async () => {
|
170
|
-
const promise = clusterReachability.start();
|
171
|
-
|
172
|
-
await clock.tickAsync(100);
|
173
|
-
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp'}});
|
174
|
-
|
175
|
-
// check the right events were emitted
|
176
|
-
assert.equal(emittedEvents[Events.resultReady].length, 1);
|
177
|
-
assert.deepEqual(emittedEvents[Events.resultReady][0], {
|
178
|
-
protocol: 'udp',
|
179
|
-
result: 'reachable',
|
180
|
-
latencyInMilliseconds: 100,
|
181
|
-
clientMediaIPs: ['somePublicIp'],
|
182
|
-
});
|
183
|
-
|
184
|
-
// clientMediaIpsUpdated shouldn't be emitted, because the IP is already passed in the resultReady event
|
185
|
-
assert.equal(emittedEvents[Events.clientMediaIpsUpdated].length, 0);
|
186
|
-
|
187
|
-
await clock.tickAsync(100);
|
188
|
-
fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'someTurnRelayIp'}});
|
189
|
-
|
190
|
-
// check the right event was emitted
|
191
|
-
assert.equal(emittedEvents[Events.resultReady].length, 2);
|
192
|
-
assert.deepEqual(emittedEvents[Events.resultReady][1], {
|
193
|
-
protocol: 'tcp',
|
194
|
-
result: 'reachable',
|
195
|
-
latencyInMilliseconds: 200,
|
196
|
-
});
|
197
|
-
assert.equal(emittedEvents[Events.clientMediaIpsUpdated].length, 0);
|
198
|
-
|
199
|
-
await clock.tickAsync(100);
|
200
|
-
fakePeerConnection.onicecandidate({
|
201
|
-
candidate: {type: 'relay', address: 'someTurnRelayIp', port: 443},
|
202
|
-
});
|
203
|
-
|
204
|
-
// check the right event was emitted
|
205
|
-
assert.equal(emittedEvents[Events.resultReady].length, 3);
|
206
|
-
assert.deepEqual(emittedEvents[Events.resultReady][2], {
|
207
|
-
protocol: 'xtls',
|
208
|
-
result: 'reachable',
|
209
|
-
latencyInMilliseconds: 300,
|
210
|
-
});
|
211
|
-
assert.equal(emittedEvents[Events.clientMediaIpsUpdated].length, 0);
|
212
|
-
|
213
|
-
await promise;
|
214
|
-
|
215
|
-
assert.deepEqual(clusterReachability.getResult(), {
|
216
|
-
udp: {result: 'reachable', latencyInMilliseconds: 100, clientMediaIPs: ['somePublicIp']},
|
217
|
-
tcp: {result: 'reachable', latencyInMilliseconds: 200},
|
218
|
-
xtls: {result: 'reachable', latencyInMilliseconds: 300},
|
219
|
-
});
|
220
|
-
});
|
221
|
-
|
222
177
|
it('resolves and returns correct results when aborted before it gets any candidates', async () => {
|
223
178
|
const promise = clusterReachability.start();
|
224
179
|
|
@@ -267,7 +222,7 @@ describe('ClusterReachability', () => {
|
|
267
222
|
|
268
223
|
await testUtils.flushPromises();
|
269
224
|
|
270
|
-
fakePeerConnection.
|
225
|
+
fakePeerConnection.iceGatheringState = 'complete';
|
271
226
|
fakePeerConnection.onicegatheringstatechange();
|
272
227
|
await promise;
|
273
228
|
|
@@ -285,7 +240,7 @@ describe('ClusterReachability', () => {
|
|
285
240
|
await clock.tickAsync(30);
|
286
241
|
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1'}});
|
287
242
|
|
288
|
-
fakePeerConnection.
|
243
|
+
fakePeerConnection.iceGatheringState = 'complete';
|
289
244
|
fakePeerConnection.onicegatheringstatechange();
|
290
245
|
await promise;
|
291
246
|
|
@@ -428,6 +383,9 @@ describe('ClusterReachability', () => {
|
|
428
383
|
candidate: {type: 'relay', address: 'someTurnRelayIp', port: 443},
|
429
384
|
});
|
430
385
|
|
386
|
+
fakePeerConnection.iceGatheringState = 'complete';
|
387
|
+
fakePeerConnection.onicegatheringstatechange();
|
388
|
+
|
431
389
|
await promise;
|
432
390
|
|
433
391
|
assert.deepEqual(clusterReachability.getResult(), {
|
@@ -440,5 +398,79 @@ describe('ClusterReachability', () => {
|
|
440
398
|
xtls: {result: 'reachable', latencyInMilliseconds: 40},
|
441
399
|
});
|
442
400
|
});
|
401
|
+
|
402
|
+
it('determines correctly if symmetric-nat is detected', async () => {
|
403
|
+
const promise = clusterReachability.start();
|
404
|
+
|
405
|
+
// generate candidates with duplicate addresses
|
406
|
+
await clock.tickAsync(10);
|
407
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1', relatedPort: 3478, port: 1000}});
|
408
|
+
|
409
|
+
// check events emitted: there shouldn't be any natTypeUpdated emitted
|
410
|
+
assert.equal(emittedEvents[Events.natTypeUpdated].length, 0);
|
411
|
+
|
412
|
+
await clock.tickAsync(10);
|
413
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1', relatedPort: 3478, port: 2000}});
|
414
|
+
|
415
|
+
// should emit natTypeUpdated event
|
416
|
+
assert.equal(emittedEvents[Events.natTypeUpdated].length, 1);
|
417
|
+
assert.deepEqual(emittedEvents[Events.natTypeUpdated][0], {
|
418
|
+
natType: 'symmetric-nat',
|
419
|
+
});
|
420
|
+
|
421
|
+
// send also a relay candidate so that the reachability check finishes
|
422
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'someTurnRelayIp'}});
|
423
|
+
fakePeerConnection.onicecandidate({
|
424
|
+
candidate: {type: 'relay', address: 'someTurnRelayIp', port: 443},
|
425
|
+
});
|
426
|
+
|
427
|
+
fakePeerConnection.iceGatheringState = 'complete';
|
428
|
+
fakePeerConnection.onicegatheringstatechange();
|
429
|
+
await clock.tickAsync(10);
|
430
|
+
|
431
|
+
await promise;
|
432
|
+
|
433
|
+
assert.deepEqual(clusterReachability.getResult(), {
|
434
|
+
udp: {
|
435
|
+
result: 'reachable',
|
436
|
+
latencyInMilliseconds: 10,
|
437
|
+
clientMediaIPs: ['somePublicIp1'],
|
438
|
+
},
|
439
|
+
tcp: {result: 'reachable', latencyInMilliseconds: 20},
|
440
|
+
xtls: {result: 'reachable', latencyInMilliseconds: 20},
|
441
|
+
});
|
442
|
+
});
|
443
|
+
|
444
|
+
it('should gather correctly reached subnets', async () => {
|
445
|
+
const promise = clusterReachability.start();
|
446
|
+
|
447
|
+
await clock.tickAsync(10);
|
448
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:1.2.3.4:5004'}});
|
449
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:4.3.2.1:5004'}});
|
450
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'someTurnRelayIp'}});
|
451
|
+
|
452
|
+
clusterReachability.abort();
|
453
|
+
await promise;
|
454
|
+
|
455
|
+
assert.deepEqual(Array.from(clusterReachability.reachedSubnets), [
|
456
|
+
'1.2.3.4',
|
457
|
+
'4.3.2.1',
|
458
|
+
'someTurnRelayIp'
|
459
|
+
]);
|
460
|
+
});
|
461
|
+
|
462
|
+
it('should store only unique subnet address', async () => {
|
463
|
+
const promise = clusterReachability.start();
|
464
|
+
|
465
|
+
await clock.tickAsync(10);
|
466
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:1.2.3.4:5004'}});
|
467
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:1.2.3.4:9000'}});
|
468
|
+
fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: '1.2.3.4'}});
|
469
|
+
|
470
|
+
clusterReachability.abort();
|
471
|
+
await promise;
|
472
|
+
|
473
|
+
assert.deepEqual(Array.from(clusterReachability.reachedSubnets), ['1.2.3.4']);
|
474
|
+
});
|
443
475
|
});
|
444
476
|
});
|
@@ -538,6 +538,14 @@ describe('gatherReachability', () => {
|
|
538
538
|
assert.equal(storedResultForJoinCookie, JSON.stringify(expectedJoinCookie));
|
539
539
|
};
|
540
540
|
|
541
|
+
it('rejects if reachability is disabled in config', async () => {
|
542
|
+
webex.config.meetings.enableReachabilityChecks = false;
|
543
|
+
|
544
|
+
const reachability = new Reachability(webex);
|
545
|
+
|
546
|
+
await assert.isRejected(reachability.gatherReachability('test'), 'enableReachabilityChecks is disabled in config');
|
547
|
+
});
|
548
|
+
|
541
549
|
[
|
542
550
|
// ========================================================================
|
543
551
|
{
|
@@ -2148,6 +2156,7 @@ describe('getReachabilityMetrics', () => {
|
|
2148
2156
|
reachability_vmn_tcp_failed: 0,
|
2149
2157
|
reachability_vmn_xtls_success: 0,
|
2150
2158
|
reachability_vmn_xtls_failed: 0,
|
2159
|
+
natType: 'unknown'
|
2151
2160
|
});
|
2152
2161
|
});
|
2153
2162
|
|
@@ -2215,6 +2224,7 @@ describe('getReachabilityMetrics', () => {
|
|
2215
2224
|
reachability_vmn_tcp_failed: 1,
|
2216
2225
|
reachability_vmn_xtls_success: 0,
|
2217
2226
|
reachability_vmn_xtls_failed: 0,
|
2227
|
+
natType: 'unknown'
|
2218
2228
|
}
|
2219
2229
|
);
|
2220
2230
|
});
|
@@ -2276,6 +2286,7 @@ describe('getReachabilityMetrics', () => {
|
|
2276
2286
|
reachability_vmn_tcp_failed: 0,
|
2277
2287
|
reachability_vmn_xtls_success: 0,
|
2278
2288
|
reachability_vmn_xtls_failed: 0,
|
2289
|
+
natType: 'unknown'
|
2279
2290
|
}
|
2280
2291
|
);
|
2281
2292
|
});
|
@@ -2337,6 +2348,7 @@ describe('getReachabilityMetrics', () => {
|
|
2337
2348
|
reachability_vmn_tcp_failed: 3,
|
2338
2349
|
reachability_vmn_xtls_success: 1,
|
2339
2350
|
reachability_vmn_xtls_failed: 1,
|
2351
|
+
natType: 'unknown'
|
2340
2352
|
}
|
2341
2353
|
);
|
2342
2354
|
});
|
@@ -2674,3 +2686,38 @@ describe('sendMetric', () => {
|
|
2674
2686
|
});
|
2675
2687
|
});
|
2676
2688
|
});
|
2689
|
+
|
2690
|
+
describe('isSubnetReachable', () => {
|
2691
|
+
let webex;
|
2692
|
+
let reachability;
|
2693
|
+
|
2694
|
+
beforeEach(() => {
|
2695
|
+
webex = new MockWebex();
|
2696
|
+
reachability = new TestReachability(webex);
|
2697
|
+
|
2698
|
+
reachability.setFakeClusterReachability({
|
2699
|
+
cluster1: {
|
2700
|
+
reachedSubnets: new Set(['1.2.3.4', '2.3.4.5']),
|
2701
|
+
},
|
2702
|
+
cluster2: {
|
2703
|
+
reachedSubnets: new Set(['3.4.5.6', '4.5.6.7']),
|
2704
|
+
},
|
2705
|
+
});
|
2706
|
+
});
|
2707
|
+
|
2708
|
+
afterEach(() => {
|
2709
|
+
sinon.restore();
|
2710
|
+
});
|
2711
|
+
|
2712
|
+
it('returns true if the subnet is reachable', () => {
|
2713
|
+
assert(reachability.isSubnetReachable('1.2.3.4'));
|
2714
|
+
});
|
2715
|
+
|
2716
|
+
it(`returns false if the subnet is unreachable`, () => {
|
2717
|
+
assert(!reachability.isSubnetReachable('11.2.3.4'));
|
2718
|
+
});
|
2719
|
+
|
2720
|
+
it('returns null if the subnet is not provided', () => {
|
2721
|
+
assert.isNull(reachability.isSubnetReachable(undefined));
|
2722
|
+
});
|
2723
|
+
});
|
@@ -12,6 +12,9 @@ describe('plugin-meetings/reachability', () => {
|
|
12
12
|
let reachabilityRequest;
|
13
13
|
let webex;
|
14
14
|
|
15
|
+
let appType;
|
16
|
+
let appVersion;
|
17
|
+
|
15
18
|
beforeEach(() => {
|
16
19
|
webex = new MockWebex({
|
17
20
|
children: {
|
@@ -20,6 +23,14 @@ describe('plugin-meetings/reachability', () => {
|
|
20
23
|
},
|
21
24
|
});
|
22
25
|
|
26
|
+
webex.config.support = {
|
27
|
+
'appType': 'NetworkChecker',
|
28
|
+
'appVersion': '43.3.0.1',
|
29
|
+
}
|
30
|
+
|
31
|
+
appType = webex?.config?.support?.appType;
|
32
|
+
appVersion = webex?.config?.support?.appVersion;
|
33
|
+
|
23
34
|
webex.meetings.clientRegion = {
|
24
35
|
countryCode: 'US',
|
25
36
|
regionCode: 'WEST-COAST',
|
@@ -56,7 +67,9 @@ describe('plugin-meetings/reachability', () => {
|
|
56
67
|
|
57
68
|
previousReport = {
|
58
69
|
id: 'fake previous report',
|
59
|
-
}
|
70
|
+
};
|
71
|
+
|
72
|
+
|
60
73
|
});
|
61
74
|
|
62
75
|
afterEach(() => {
|
@@ -79,6 +92,7 @@ describe('plugin-meetings/reachability', () => {
|
|
79
92
|
'report-version': 1,
|
80
93
|
'early-call-min-clusters': true,
|
81
94
|
},
|
95
|
+
'client-environment': { components: { [appType]: appVersion } },
|
82
96
|
'previous-report': previousReport,
|
83
97
|
trigger: 'startup',
|
84
98
|
},
|
@@ -105,6 +119,7 @@ describe('plugin-meetings/reachability', () => {
|
|
105
119
|
'report-version': 1,
|
106
120
|
'early-call-min-clusters': true,
|
107
121
|
},
|
122
|
+
'client-environment': { components: { [appType]: appVersion } },
|
108
123
|
'previous-report': previousReport,
|
109
124
|
trigger: 'early-call/no-min-reached',
|
110
125
|
},
|
@@ -114,5 +129,35 @@ describe('plugin-meetings/reachability', () => {
|
|
114
129
|
assert.deepEqual(res.joinCookie, {anycastEntryPoint: "aws-eu-west-1"})
|
115
130
|
assert.notCalled(webex.internal.newMetrics.callDiagnosticLatencies.measureLatency);
|
116
131
|
});
|
132
|
+
|
133
|
+
it('sends a POST request with the correct params when appVersion is undefined', async () => {
|
134
|
+
// Setting appType & appVersion to undefined
|
135
|
+
webex.config.support.appType = undefined;
|
136
|
+
webex.config.support.appVersion = undefined;
|
137
|
+
|
138
|
+
const res = await reachabilityRequest.getClusters('startup', IP_VERSION.only_ipv4, previousReport);
|
139
|
+
const requestParams = webex.request.getCall(0).args[0];
|
140
|
+
|
141
|
+
assert.deepEqual(requestParams, {
|
142
|
+
method: 'POST',
|
143
|
+
resource: `clusters`,
|
144
|
+
api: 'calliopeDiscovery',
|
145
|
+
shouldRefreshAccessToken: false,
|
146
|
+
timeout: 3000,
|
147
|
+
body: {
|
148
|
+
ipver: IP_VERSION.only_ipv4,
|
149
|
+
'supported-options': {
|
150
|
+
'report-version': 1,
|
151
|
+
'early-call-min-clusters': true,
|
152
|
+
},
|
153
|
+
'previous-report': previousReport,
|
154
|
+
trigger: 'startup',
|
155
|
+
},
|
156
|
+
});
|
157
|
+
|
158
|
+
assert.deepEqual(res.clusters.clusterId, {udp: "testUDP", isVideoMesh: true});
|
159
|
+
assert.deepEqual(res.joinCookie, {anycastEntryPoint: "aws-eu-west-1"});
|
160
|
+
assert.calledOnceWithExactly(webex.internal.newMetrics.callDiagnosticLatencies.measureLatency, sinon.match.func, 'internal.get.cluster.time');
|
161
|
+
});
|
117
162
|
});
|
118
|
-
});
|
163
|
+
});
|
@@ -60,7 +60,7 @@ describe('plugin-meetings', () => {
|
|
60
60
|
roap: {
|
61
61
|
doTurnDiscovery: sinon.stub().resolves({
|
62
62
|
turnServerInfo: {
|
63
|
-
|
63
|
+
urls: ['fake_turn_url1', 'fake_turn_url2'],
|
64
64
|
username: 'fake_turn_username',
|
65
65
|
password: 'fake_turn_password',
|
66
66
|
},
|
@@ -137,7 +137,7 @@ describe('plugin-meetings', () => {
|
|
137
137
|
assert.calledOnce(fakeMediaConnection.reconnect);
|
138
138
|
assert.calledWith(fakeMediaConnection.reconnect, [
|
139
139
|
{
|
140
|
-
urls: '
|
140
|
+
urls: ['fake_turn_url1', 'fake_turn_url2'],
|
141
141
|
username: 'fake_turn_username',
|
142
142
|
credential: 'fake_turn_password',
|
143
143
|
},
|
@@ -152,12 +152,12 @@ describe('plugin-meetings', () => {
|
|
152
152
|
});
|
153
153
|
|
154
154
|
// this can happen when we land on a video mesh node
|
155
|
-
it('does not use TURN server if TURN
|
155
|
+
it('does not use TURN server if TURN urls is an empty array', async () => {
|
156
156
|
const rm = new ReconnectionManager(fakeMeeting);
|
157
157
|
|
158
158
|
fakeMeeting.roap.doTurnDiscovery.resolves({
|
159
159
|
turnServerInfo: {
|
160
|
-
|
160
|
+
urls: [],
|
161
161
|
username: 'whatever',
|
162
162
|
password: 'whatever',
|
163
163
|
},
|