@webex/plugin-meetings 3.0.0 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/config.d.ts +1 -0
- package/dist/config.js +2 -1
- package/dist/config.js.map +1 -1
- package/dist/constants.d.ts +5 -4
- package/dist/constants.js +8 -4
- package/dist/constants.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/interpretation/index.js +16 -2
- package/dist/interpretation/index.js.map +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/mediaSharesUtils.js +15 -1
- package/dist/locus-info/mediaSharesUtils.js.map +1 -1
- package/dist/locus-info/selfUtils.js +5 -0
- package/dist/locus-info/selfUtils.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.d.ts +61 -0
- package/dist/media/MediaConnectionAwaiter.js +163 -0
- package/dist/media/MediaConnectionAwaiter.js.map +1 -0
- package/dist/media/index.js +4 -1
- package/dist/media/index.js.map +1 -1
- package/dist/media/properties.js +4 -24
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.d.ts +26 -7
- package/dist/meeting/index.js +893 -677
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/muteState.d.ts +2 -8
- package/dist/meeting/muteState.js +37 -25
- package/dist/meeting/muteState.js.map +1 -1
- package/dist/meeting/request.d.ts +3 -0
- package/dist/meeting/request.js +32 -23
- package/dist/meeting/request.js.map +1 -1
- package/dist/meeting/util.js +1 -0
- package/dist/meeting/util.js.map +1 -1
- package/dist/meeting-info/utilv2.js +4 -1
- package/dist/meeting-info/utilv2.js.map +1 -1
- package/dist/meetings/index.d.ts +8 -0
- package/dist/meetings/index.js +20 -0
- package/dist/meetings/index.js.map +1 -1
- package/dist/multistream/mediaRequestManager.d.ts +2 -1
- package/dist/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/multistream/remoteMediaGroup.d.ts +2 -0
- package/dist/multistream/remoteMediaGroup.js +16 -2
- package/dist/multistream/remoteMediaGroup.js.map +1 -1
- package/dist/multistream/remoteMediaManager.d.ts +15 -0
- package/dist/multistream/remoteMediaManager.js +179 -65
- package/dist/multistream/remoteMediaManager.js.map +1 -1
- package/dist/multistream/sendSlotManager.d.ts +9 -1
- package/dist/multistream/sendSlotManager.js +22 -0
- package/dist/multistream/sendSlotManager.js.map +1 -1
- package/dist/reachability/clusterReachability.d.ts +1 -0
- package/dist/reachability/clusterReachability.js +29 -15
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.d.ts +4 -0
- package/dist/reachability/index.js +18 -2
- package/dist/reachability/index.js.map +1 -1
- package/dist/reachability/request.js +12 -10
- package/dist/reachability/request.js.map +1 -1
- package/dist/reachability/util.d.ts +7 -0
- package/dist/reachability/util.js +19 -0
- package/dist/reachability/util.js.map +1 -1
- package/dist/reconnection-manager/index.js +2 -1
- package/dist/reconnection-manager/index.js.map +1 -1
- package/dist/roap/index.d.ts +10 -2
- package/dist/roap/index.js +15 -0
- package/dist/roap/index.js.map +1 -1
- package/dist/roap/request.js +3 -3
- package/dist/roap/request.js.map +1 -1
- package/dist/roap/turnDiscovery.d.ts +64 -17
- package/dist/roap/turnDiscovery.js +307 -126
- package/dist/roap/turnDiscovery.js.map +1 -1
- package/dist/statsAnalyzer/index.js +53 -30
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/config.ts +1 -0
- package/src/constants.ts +7 -3
- package/src/index.ts +1 -0
- package/src/interpretation/index.ts +18 -1
- package/src/locus-info/mediaSharesUtils.ts +16 -0
- package/src/locus-info/selfUtils.ts +5 -0
- package/src/media/MediaConnectionAwaiter.ts +174 -0
- package/src/media/index.ts +3 -1
- package/src/media/properties.ts +6 -31
- package/src/meeting/index.ts +321 -106
- package/src/meeting/muteState.ts +34 -20
- package/src/meeting/request.ts +18 -2
- package/src/meeting/util.ts +1 -0
- package/src/meeting-info/utilv2.ts +2 -1
- package/src/meetings/index.ts +18 -0
- package/src/multistream/mediaRequestManager.ts +4 -1
- package/src/multistream/remoteMediaGroup.ts +19 -0
- package/src/multistream/remoteMediaManager.ts +101 -16
- package/src/multistream/sendSlotManager.ts +28 -0
- package/src/reachability/clusterReachability.ts +20 -5
- package/src/reachability/index.ts +24 -1
- package/src/reachability/request.ts +15 -11
- package/src/reachability/util.ts +21 -0
- package/src/reconnection-manager/index.ts +1 -1
- package/src/roap/index.ts +25 -3
- package/src/roap/request.ts +3 -3
- package/src/roap/turnDiscovery.ts +244 -78
- package/src/statsAnalyzer/index.ts +63 -27
- package/test/integration/spec/journey.js +14 -14
- package/test/integration/spec/space-meeting.js +1 -1
- package/test/unit/spec/interpretation/index.ts +39 -3
- package/test/unit/spec/locus-info/index.js +28 -19
- package/test/unit/spec/locus-info/mediaSharesUtils.ts +9 -0
- package/test/unit/spec/locus-info/selfUtils.js +42 -12
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +344 -0
- package/test/unit/spec/media/index.ts +89 -78
- package/test/unit/spec/media/properties.ts +16 -70
- package/test/unit/spec/meeting/index.js +638 -139
- package/test/unit/spec/meeting/muteState.js +219 -67
- package/test/unit/spec/meeting/request.js +21 -0
- package/test/unit/spec/meeting/utils.js +6 -1
- package/test/unit/spec/meeting-info/utilv2.js +6 -0
- package/test/unit/spec/meetings/index.js +40 -20
- package/test/unit/spec/multistream/mediaRequestManager.ts +20 -2
- package/test/unit/spec/multistream/remoteMediaGroup.ts +79 -1
- package/test/unit/spec/multistream/remoteMediaManager.ts +199 -1
- package/test/unit/spec/multistream/sendSlotManager.ts +50 -18
- package/test/unit/spec/reachability/clusterReachability.ts +86 -22
- package/test/unit/spec/reachability/index.ts +197 -60
- package/test/unit/spec/reachability/request.js +15 -7
- package/test/unit/spec/reachability/util.ts +32 -2
- package/test/unit/spec/reconnection-manager/index.js +28 -0
- package/test/unit/spec/roap/index.ts +61 -6
- package/test/unit/spec/roap/turnDiscovery.ts +298 -16
- package/test/unit/spec/stats-analyzer/index.js +179 -0
- package/dist/member/member.types.d.ts +0 -11
- package/dist/member/member.types.js +0 -17
- package/dist/member/member.types.js.map +0 -1
- package/src/member/member.types.ts +0 -13
- /package/test/unit/spec/locus-info/{lib/selfConstant.js → selfConstant.js} +0 -0
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import {assert} from '@webex/test-helper-chai';
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
convertStunUrlToTurn,
|
|
5
|
+
convertStunUrlToTurnTls,
|
|
6
|
+
} from '@webex/plugin-meetings/src/reachability/util';
|
|
4
7
|
|
|
5
8
|
describe('plugin-meetings/src/reachability/util', () => {
|
|
6
9
|
describe('#convertStunUrlToTurn()', () => {
|
|
@@ -34,7 +37,34 @@ describe('plugin-meetings/src/reachability/util', () => {
|
|
|
34
37
|
});
|
|
35
38
|
|
|
36
39
|
it('show fail if stunUrl is not a STUN url', () => {
|
|
37
|
-
assert.throws(
|
|
40
|
+
assert.throws(
|
|
41
|
+
() => convertStunUrlToTurn('http://webex.com', 'tcp'),
|
|
42
|
+
'Not a STUN URL: http://webex.com'
|
|
43
|
+
);
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('#convertStunUrlToTurnTls()', () => {
|
|
48
|
+
it(`should convert to a turns url`, () => {
|
|
49
|
+
const turnsUrl = convertStunUrlToTurnTls(
|
|
50
|
+
'stun:external-media91.public.wjfkm-a-10.prod.infra.webex.com:443'
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
assert.equal(
|
|
54
|
+
turnsUrl,
|
|
55
|
+
'turns:external-media91.public.wjfkm-a-10.prod.infra.webex.com:443?transport=tcp'
|
|
56
|
+
);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
it('show fail if stunUrl is not a valid url', () => {
|
|
60
|
+
assert.throws(() => convertStunUrlToTurn('not a url', 'tcp'), 'Invalid URL: not a url');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('show fail if stunUrl is not a STUN url', () => {
|
|
64
|
+
assert.throws(
|
|
65
|
+
() => convertStunUrlToTurn('http://webex.com', 'tcp'),
|
|
66
|
+
'Not a STUN URL: http://webex.com'
|
|
67
|
+
);
|
|
38
68
|
});
|
|
39
69
|
});
|
|
40
70
|
});
|
|
@@ -144,6 +144,34 @@ describe('plugin-meetings', () => {
|
|
|
144
144
|
});
|
|
145
145
|
});
|
|
146
146
|
|
|
147
|
+
// this can happen when we land on a video mesh node
|
|
148
|
+
it('does not use TURN server if TURN url is an empty string', async () => {
|
|
149
|
+
const rm = new ReconnectionManager(fakeMeeting);
|
|
150
|
+
|
|
151
|
+
fakeMeeting.roap.doTurnDiscovery.resolves({
|
|
152
|
+
turnServerInfo: {
|
|
153
|
+
url: '',
|
|
154
|
+
username: 'whatever',
|
|
155
|
+
password: 'whatever',
|
|
156
|
+
},
|
|
157
|
+
turnDiscoverySkippedReason: undefined,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
await rm.reconnect();
|
|
161
|
+
|
|
162
|
+
assert.calledOnce(fakeMeeting.roap.doTurnDiscovery);
|
|
163
|
+
assert.calledWith(fakeMeeting.roap.doTurnDiscovery, fakeMeeting, true, true);
|
|
164
|
+
assert.calledOnce(fakeMediaConnection.reconnect);
|
|
165
|
+
assert.calledWith(fakeMediaConnection.reconnect, []);
|
|
166
|
+
|
|
167
|
+
assert.calledWith(fakeMeeting.webex.internal.newMetrics.submitClientEvent, {
|
|
168
|
+
name: 'client.media.reconnecting',
|
|
169
|
+
options: {
|
|
170
|
+
meetingId: rm.meeting.id,
|
|
171
|
+
},
|
|
172
|
+
});
|
|
173
|
+
});
|
|
174
|
+
|
|
147
175
|
it('does not clear previous requests and re-request media for non-multistream meetings', async () => {
|
|
148
176
|
fakeMeeting.isMultistream = false;
|
|
149
177
|
const rm = new ReconnectionManager(fakeMeeting);
|
|
@@ -13,15 +13,23 @@ import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
|
|
|
13
13
|
import { IP_VERSION } from '../../../../src/constants';
|
|
14
14
|
|
|
15
15
|
describe('Roap', () => {
|
|
16
|
+
let webex;
|
|
17
|
+
|
|
18
|
+
const RESULT = {something: 'some value'};
|
|
19
|
+
const meeting = {id: 'some meeting id'} as Meeting;
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
webex = new MockWebex({});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterEach(() => {
|
|
26
|
+
sinon.restore();
|
|
27
|
+
});
|
|
28
|
+
|
|
16
29
|
describe('doTurnDiscovery', () => {
|
|
17
30
|
[false, true].forEach(function (isReconnecting) {
|
|
18
31
|
[false, true, undefined].forEach(function (isForced) {
|
|
19
32
|
it(`calls this.turnDiscovery.doTurnDiscovery() and forwards all the arguments when isReconnecting = ${isReconnecting} and isForced = ${isForced}`, async () => {
|
|
20
|
-
const webex = new MockWebex({});
|
|
21
|
-
|
|
22
|
-
const RESULT = {something: 'some value'};
|
|
23
|
-
const meeting = {id: 'some meeting id'} as Meeting;
|
|
24
|
-
|
|
25
33
|
const doTurnDiscoveryStub = sinon
|
|
26
34
|
.stub(TurnDiscovery.prototype, 'doTurnDiscovery')
|
|
27
35
|
.resolves(RESULT);
|
|
@@ -32,11 +40,58 @@ describe('Roap', () => {
|
|
|
32
40
|
|
|
33
41
|
assert.calledOnceWithExactly(doTurnDiscoveryStub, meeting, isReconnecting, isForced);
|
|
34
42
|
assert.deepEqual(result, RESULT);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
describe('generateTurnDiscoveryRequestMessage', () => {
|
|
48
|
+
[false, true].forEach(function (isForced) {
|
|
49
|
+
it(`calls this.turnDiscovery.generateTurnDiscoveryRequestMessage with isForced=${isForced}`, async () => {
|
|
50
|
+
const generateTurnDiscoveryRequestMessageStub = sinon
|
|
51
|
+
.stub(TurnDiscovery.prototype, 'generateTurnDiscoveryRequestMessage')
|
|
52
|
+
.resolves(RESULT);
|
|
53
|
+
|
|
54
|
+
const roap = new Roap({}, {parent: webex});
|
|
35
55
|
|
|
36
|
-
|
|
56
|
+
const result = await roap.generateTurnDiscoveryRequestMessage(meeting, isForced);
|
|
57
|
+
|
|
58
|
+
assert.calledOnceWithExactly(generateTurnDiscoveryRequestMessageStub, meeting, isForced);
|
|
59
|
+
assert.deepEqual(result, RESULT);
|
|
37
60
|
});
|
|
38
61
|
});
|
|
39
62
|
});
|
|
63
|
+
|
|
64
|
+
describe('handleTurnDiscoveryHttpResponse', () => {
|
|
65
|
+
it('calls this.turnDiscovery.handleTurnDiscoveryHttpResponse', async () => {
|
|
66
|
+
const handleTurnDiscoveryHttpResponseStub = sinon
|
|
67
|
+
.stub(TurnDiscovery.prototype, 'handleTurnDiscoveryHttpResponse')
|
|
68
|
+
.resolves(RESULT);
|
|
69
|
+
|
|
70
|
+
const httpReponse = {some: 'http response'};
|
|
71
|
+
|
|
72
|
+
const roap = new Roap({}, {parent: webex});
|
|
73
|
+
|
|
74
|
+
const result = await roap.handleTurnDiscoveryHttpResponse(meeting, httpReponse);
|
|
75
|
+
|
|
76
|
+
assert.calledOnceWithExactly(handleTurnDiscoveryHttpResponseStub, meeting, httpReponse);
|
|
77
|
+
assert.deepEqual(result, RESULT);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('abortTurnDiscovery', () => {
|
|
82
|
+
it('calls this.turnDiscovery.abort', async () => {
|
|
83
|
+
const abortStub = sinon
|
|
84
|
+
.stub(TurnDiscovery.prototype, 'abort')
|
|
85
|
+
.returns(RESULT);
|
|
86
|
+
|
|
87
|
+
const roap = new Roap({}, {parent: webex});
|
|
88
|
+
|
|
89
|
+
const result = await roap.abortTurnDiscovery();
|
|
90
|
+
|
|
91
|
+
assert.calledOnceWithExactly(abortStub);
|
|
92
|
+
assert.deepEqual(result, RESULT);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
40
95
|
});
|
|
41
96
|
|
|
42
97
|
describe('sendRoapMediaRequest', () => {
|
|
@@ -121,7 +121,7 @@ describe('TurnDiscovery', () => {
|
|
|
121
121
|
});
|
|
122
122
|
|
|
123
123
|
// checks that OK roap message was sent or not sent and that the result is as expected
|
|
124
|
-
const checkResult = async (resultPromise, expectedRoapMessageSent, expectedResult) => {
|
|
124
|
+
const checkResult = async (resultPromise, expectedRoapMessageSent, expectedResult, expectedSkipReason?: string) => {
|
|
125
125
|
let turnServerInfo, turnDiscoverySkippedReason;
|
|
126
126
|
|
|
127
127
|
if (expectedRoapMessageSent === 'OK') {
|
|
@@ -150,7 +150,7 @@ describe('TurnDiscovery', () => {
|
|
|
150
150
|
}
|
|
151
151
|
|
|
152
152
|
assert.deepEqual(turnServerInfo, expectedResult);
|
|
153
|
-
assert.
|
|
153
|
+
assert.equal(turnDiscoverySkippedReason, expectedSkipReason);
|
|
154
154
|
};
|
|
155
155
|
|
|
156
156
|
it('sends TURN_DISCOVERY_REQUEST, waits for response and sends OK', async () => {
|
|
@@ -302,7 +302,7 @@ describe('TurnDiscovery', () => {
|
|
|
302
302
|
// @ts-ignore
|
|
303
303
|
mockRoapRequest.sendRoap.resetHistory();
|
|
304
304
|
|
|
305
|
-
await checkResult(result, undefined, undefined);
|
|
305
|
+
await checkResult(result, undefined, undefined, 'failure: Unexpected token o in JSON at position 1');
|
|
306
306
|
checkFailureMetricsSent();
|
|
307
307
|
});
|
|
308
308
|
|
|
@@ -366,7 +366,7 @@ describe('TurnDiscovery', () => {
|
|
|
366
366
|
// @ts-ignore
|
|
367
367
|
mockRoapRequest.sendRoap.resetHistory();
|
|
368
368
|
|
|
369
|
-
await checkResult(result, undefined, undefined);
|
|
369
|
+
await checkResult(result, undefined, undefined, 'failure: TURN_DISCOVERY_RESPONSE in http response has unexpected messageType: {"seq":"0","messageType":"ERROR"}');
|
|
370
370
|
});
|
|
371
371
|
});
|
|
372
372
|
});
|
|
@@ -494,7 +494,7 @@ describe('TurnDiscovery', () => {
|
|
|
494
494
|
assert.isUndefined(turnDiscoverySkippedReason);
|
|
495
495
|
});
|
|
496
496
|
|
|
497
|
-
it('resolves with undefined if sending the request fails', async () => {
|
|
497
|
+
it('resolves with undefined turnServerInfo if sending the request fails', async () => {
|
|
498
498
|
const td = new TurnDiscovery(mockRoapRequest);
|
|
499
499
|
|
|
500
500
|
mockRoapRequest.sendRoap = sinon.fake.rejects(new Error('fake error'));
|
|
@@ -504,11 +504,11 @@ describe('TurnDiscovery', () => {
|
|
|
504
504
|
const {turnServerInfo, turnDiscoverySkippedReason} = result;
|
|
505
505
|
|
|
506
506
|
assert.isUndefined(turnServerInfo);
|
|
507
|
-
assert.
|
|
507
|
+
assert.equal(turnDiscoverySkippedReason, 'failure: fake error');
|
|
508
508
|
checkFailureMetricsSent();
|
|
509
509
|
});
|
|
510
510
|
|
|
511
|
-
it('resolves with undefined when cluster is reachable', async () => {
|
|
511
|
+
it('resolves with undefined turnServerInfo when cluster is reachable', async () => {
|
|
512
512
|
const prev = testMeeting.webex.meetings.reachability.isAnyPublicClusterReachable;
|
|
513
513
|
testMeeting.webex.meetings.reachability.isAnyPublicClusterReachable = () =>
|
|
514
514
|
Promise.resolve(true);
|
|
@@ -523,7 +523,7 @@ describe('TurnDiscovery', () => {
|
|
|
523
523
|
testMeeting.webex.meetings.reachability.isAnyPublicClusterReachable = prev;
|
|
524
524
|
});
|
|
525
525
|
|
|
526
|
-
it("resolves with undefined if we don't get a response within 10s", async () => {
|
|
526
|
+
it("resolves with undefined turnServerInfo if we don't get a response within 10s", async () => {
|
|
527
527
|
const td = new TurnDiscovery(mockRoapRequest);
|
|
528
528
|
|
|
529
529
|
const promise = td.doTurnDiscovery(testMeeting, false);
|
|
@@ -534,11 +534,11 @@ describe('TurnDiscovery', () => {
|
|
|
534
534
|
const {turnServerInfo, turnDiscoverySkippedReason} = await promise;
|
|
535
535
|
|
|
536
536
|
assert.isUndefined(turnServerInfo);
|
|
537
|
-
assert.
|
|
537
|
+
assert.equal(turnDiscoverySkippedReason, 'failure: Timed out waiting for TURN_DISCOVERY_RESPONSE');
|
|
538
538
|
checkFailureMetricsSent();
|
|
539
539
|
});
|
|
540
540
|
|
|
541
|
-
it('resolves with undefined if the response does not have all the headers we expect', async () => {
|
|
541
|
+
it('resolves with undefined turnServerInfo if the response does not have all the headers we expect', async () => {
|
|
542
542
|
const td = new TurnDiscovery(mockRoapRequest);
|
|
543
543
|
const turnDiscoveryPromise = td.doTurnDiscovery(testMeeting, false);
|
|
544
544
|
|
|
@@ -559,11 +559,11 @@ describe('TurnDiscovery', () => {
|
|
|
559
559
|
const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
|
|
560
560
|
|
|
561
561
|
assert.isUndefined(turnServerInfo);
|
|
562
|
-
assert.
|
|
562
|
+
assert.equal(turnDiscoverySkippedReason, `failure: TURN_DISCOVERY_RESPONSE from test missing some headers: ["x-cisco-turn-url=${FAKE_TURN_URL}","x-cisco-turn-username=${FAKE_TURN_USERNAME}"]`);
|
|
563
563
|
checkFailureMetricsSent();
|
|
564
564
|
});
|
|
565
565
|
|
|
566
|
-
it('resolves with undefined if the response does not have any headers', async () => {
|
|
566
|
+
it('resolves with undefined turnServerInfo if the response does not have any headers', async () => {
|
|
567
567
|
const td = new TurnDiscovery(mockRoapRequest);
|
|
568
568
|
const turnDiscoveryPromise = td.doTurnDiscovery(testMeeting, false);
|
|
569
569
|
|
|
@@ -576,11 +576,11 @@ describe('TurnDiscovery', () => {
|
|
|
576
576
|
const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
|
|
577
577
|
|
|
578
578
|
assert.isUndefined(turnServerInfo);
|
|
579
|
-
assert.
|
|
579
|
+
assert.equal(turnDiscoverySkippedReason, 'failure: TURN_DISCOVERY_RESPONSE from test missing some headers: undefined');
|
|
580
580
|
checkFailureMetricsSent();
|
|
581
581
|
});
|
|
582
582
|
|
|
583
|
-
it('resolves with undefined if the response has empty headers array', async () => {
|
|
583
|
+
it('resolves with undefined turnServerInfo if the response has empty headers array', async () => {
|
|
584
584
|
const td = new TurnDiscovery(mockRoapRequest);
|
|
585
585
|
const turnDiscoveryPromise = td.doTurnDiscovery(testMeeting, false);
|
|
586
586
|
|
|
@@ -596,7 +596,7 @@ describe('TurnDiscovery', () => {
|
|
|
596
596
|
const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
|
|
597
597
|
|
|
598
598
|
assert.isUndefined(turnServerInfo);
|
|
599
|
-
assert.
|
|
599
|
+
assert.equal(turnDiscoverySkippedReason, 'failure: TURN_DISCOVERY_RESPONSE from test missing some headers: []');
|
|
600
600
|
checkFailureMetricsSent();
|
|
601
601
|
});
|
|
602
602
|
|
|
@@ -636,7 +636,7 @@ describe('TurnDiscovery', () => {
|
|
|
636
636
|
const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
|
|
637
637
|
|
|
638
638
|
assert.isUndefined(turnServerInfo);
|
|
639
|
-
assert.
|
|
639
|
+
assert.equal(turnDiscoverySkippedReason, 'failure: fake error');
|
|
640
640
|
checkFailureMetricsSent();
|
|
641
641
|
});
|
|
642
642
|
});
|
|
@@ -676,4 +676,286 @@ describe('TurnDiscovery', () => {
|
|
|
676
676
|
assert.notCalled(mockRoapRequest.sendRoap);
|
|
677
677
|
});
|
|
678
678
|
});
|
|
679
|
+
|
|
680
|
+
describe('generateTurnDiscoveryRequestMessage', () => {
|
|
681
|
+
let td;
|
|
682
|
+
|
|
683
|
+
beforeEach(() => {
|
|
684
|
+
td = new TurnDiscovery(mockRoapRequest);
|
|
685
|
+
sinon.stub(td, 'getSkipReason').resolves(undefined);
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
it('generates TURN_DISCOVERY_REQUEST message irrespective of skip reason when called with isForced=true', async () => {
|
|
689
|
+
td.getSkipReason.resolves('reachability');
|
|
690
|
+
|
|
691
|
+
const result = await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
692
|
+
|
|
693
|
+
assert.deepEqual(result, {
|
|
694
|
+
roapMessage: {
|
|
695
|
+
messageType: 'TURN_DISCOVERY_REQUEST',
|
|
696
|
+
version: '2',
|
|
697
|
+
seq: 0,
|
|
698
|
+
headers: ['includeAnswerInHttpResponse', 'noOkInTransaction'],
|
|
699
|
+
},
|
|
700
|
+
turnDiscoverySkippedReason: undefined,
|
|
701
|
+
});
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
it('takes into account skip reason when called with isForced=false', async () => {
|
|
705
|
+
td.getSkipReason.resolves('reachability');
|
|
706
|
+
|
|
707
|
+
const result = await td.generateTurnDiscoveryRequestMessage(testMeeting, false);
|
|
708
|
+
|
|
709
|
+
assert.deepEqual(result, {
|
|
710
|
+
roapMessage: undefined,
|
|
711
|
+
turnDiscoverySkippedReason: 'reachability',
|
|
712
|
+
});
|
|
713
|
+
});
|
|
714
|
+
|
|
715
|
+
it('generates TURN_DISCOVERY_REQUEST message if there is no skip reason when called with isForced=false', async () => {
|
|
716
|
+
const result = await td.generateTurnDiscoveryRequestMessage(testMeeting, false);
|
|
717
|
+
|
|
718
|
+
assert.deepEqual(result, {
|
|
719
|
+
roapMessage: {
|
|
720
|
+
messageType: 'TURN_DISCOVERY_REQUEST',
|
|
721
|
+
version: '2',
|
|
722
|
+
seq: 0,
|
|
723
|
+
headers: ['includeAnswerInHttpResponse', 'noOkInTransaction'],
|
|
724
|
+
},
|
|
725
|
+
turnDiscoverySkippedReason: undefined,
|
|
726
|
+
});
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
it('returns "already in progress" if TURN_DISCOVERY_REQUEST was already generated', async () => {
|
|
730
|
+
// 1st call
|
|
731
|
+
await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
732
|
+
|
|
733
|
+
// 2nd call
|
|
734
|
+
const result = await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
735
|
+
|
|
736
|
+
assert.deepEqual(result, {
|
|
737
|
+
roapMessage: undefined,
|
|
738
|
+
turnDiscoverySkippedReason: 'already in progress',
|
|
739
|
+
});
|
|
740
|
+
});
|
|
741
|
+
|
|
742
|
+
it('returns "already in progress" if doTurnDiscovery was called and not completed', async () => {
|
|
743
|
+
let promiseResolve;
|
|
744
|
+
|
|
745
|
+
// set it up so that doTurnDiscovery doesn't complete
|
|
746
|
+
mockRoapRequest.sendRoap = sinon.fake.returns(new Promise((resolve) => {
|
|
747
|
+
promiseResolve = resolve;
|
|
748
|
+
}));
|
|
749
|
+
td.doTurnDiscovery(testMeeting, false, true);
|
|
750
|
+
|
|
751
|
+
// now call generateTurnDiscoveryRequestMessage
|
|
752
|
+
const result = await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
753
|
+
|
|
754
|
+
assert.deepEqual(result, {
|
|
755
|
+
roapMessage: undefined,
|
|
756
|
+
turnDiscoverySkippedReason: 'already in progress',
|
|
757
|
+
});
|
|
758
|
+
|
|
759
|
+
// resolve the promise, just so that we don't leave it hanging
|
|
760
|
+
promiseResolve();
|
|
761
|
+
});
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
describe('handleTurnDiscoveryHttpResponse', () => {
|
|
765
|
+
let td;
|
|
766
|
+
let roapMessage;
|
|
767
|
+
|
|
768
|
+
beforeEach(() => {
|
|
769
|
+
roapMessage = {
|
|
770
|
+
seq: 1,
|
|
771
|
+
messageType: 'TURN_DISCOVERY_RESPONSE',
|
|
772
|
+
errorType: undefined,
|
|
773
|
+
errorCause: undefined,
|
|
774
|
+
headers: [
|
|
775
|
+
`x-cisco-turn-url=${FAKE_TURN_URL}`,
|
|
776
|
+
`x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
|
|
777
|
+
`x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
|
|
778
|
+
'noOkInTransaction'
|
|
779
|
+
],
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
td = new TurnDiscovery(mockRoapRequest);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
// checks if another TURN discovery can be started without any problem
|
|
786
|
+
const checkNextTurnDiscovery = async () => {
|
|
787
|
+
// after each test check that another TURN discovery can be started without any problems
|
|
788
|
+
const secondMessage = await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
789
|
+
|
|
790
|
+
assert.isDefined(secondMessage.roapMessage);
|
|
791
|
+
};
|
|
792
|
+
|
|
793
|
+
it('works as expected when called with undefined httpResponse', async () => {
|
|
794
|
+
await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
795
|
+
|
|
796
|
+
const result = await td.handleTurnDiscoveryHttpResponse(testMeeting, undefined);
|
|
797
|
+
|
|
798
|
+
assert.deepEqual(result, {
|
|
799
|
+
turnServerInfo: undefined,
|
|
800
|
+
turnDiscoverySkippedReason: 'missing http response',
|
|
801
|
+
});
|
|
802
|
+
});
|
|
803
|
+
|
|
804
|
+
[
|
|
805
|
+
{testCase: 'is missing mediaConnections', httpResponse: {}},
|
|
806
|
+
{testCase: 'is missing mediaConnections[0]', httpResponse: {mediaConnections: []}},
|
|
807
|
+
{testCase: 'is missing mediaConnections[0].remoteSdp', httpResponse: {mediaConnections: [{}]}},
|
|
808
|
+
{testCase: 'is missing roapMesssage in mediaConnections[0].remoteSdp', httpResponse: {mediaConnections: [{remoteSdp: JSON.stringify({something: "whatever"})}]}},
|
|
809
|
+
].forEach(({testCase, httpResponse}) => {
|
|
810
|
+
it(`handles httpResponse that ${testCase}`, async () => {
|
|
811
|
+
await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
812
|
+
|
|
813
|
+
const result = await td.handleTurnDiscoveryHttpResponse(testMeeting, httpResponse);
|
|
814
|
+
|
|
815
|
+
assert.deepEqual(result, {
|
|
816
|
+
turnServerInfo: undefined,
|
|
817
|
+
turnDiscoverySkippedReason: 'missing http response',
|
|
818
|
+
});
|
|
819
|
+
});
|
|
820
|
+
});
|
|
821
|
+
|
|
822
|
+
it('handles httpResponse with invalid JSON in mediaConnections[0].remoteSdp', async () => {
|
|
823
|
+
await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
824
|
+
|
|
825
|
+
const result = await td.handleTurnDiscoveryHttpResponse(testMeeting, {mediaConnections: [{remoteSdp: 'not a json'}]});
|
|
826
|
+
|
|
827
|
+
assert.deepEqual(result, {
|
|
828
|
+
turnServerInfo: undefined,
|
|
829
|
+
turnDiscoverySkippedReason: 'failure: Unexpected token o in JSON at position 1',
|
|
830
|
+
});
|
|
831
|
+
});
|
|
832
|
+
|
|
833
|
+
it('fails when called before generateTurnDiscoveryRequestMessage() was called', async () => {
|
|
834
|
+
const httpResponse = {mediaConnections: [{remoteSdp: JSON.stringify({roapMessage})}]};
|
|
835
|
+
await assert.isRejected(td.handleTurnDiscoveryHttpResponse(testMeeting, httpResponse),
|
|
836
|
+
'handleTurnDiscoveryHttpResponse() called before generateTurnDiscoveryRequestMessage()');
|
|
837
|
+
});
|
|
838
|
+
|
|
839
|
+
it('works as expected when called with valid httpResponse', async () => {
|
|
840
|
+
const httpResponse = {mediaConnections: [{remoteSdp: JSON.stringify({roapMessage})}]};
|
|
841
|
+
|
|
842
|
+
// we spy on handleTurnDiscoveryResponse and check that it's called so that we don't have to repeat
|
|
843
|
+
// all the edge case tests here, they're already covered in other tests that call handleTurnDiscoveryResponse
|
|
844
|
+
const handleTurnDiscoveryResponseSpy = sinon.spy(td, 'handleTurnDiscoveryResponse');
|
|
845
|
+
|
|
846
|
+
await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
847
|
+
const result = await td.handleTurnDiscoveryHttpResponse(testMeeting, httpResponse);
|
|
848
|
+
|
|
849
|
+
assert.deepEqual(result, {
|
|
850
|
+
turnServerInfo: {
|
|
851
|
+
url: FAKE_TURN_URL,
|
|
852
|
+
username: FAKE_TURN_USERNAME,
|
|
853
|
+
password: FAKE_TURN_PASSWORD,
|
|
854
|
+
},
|
|
855
|
+
turnDiscoverySkippedReason: undefined,
|
|
856
|
+
});
|
|
857
|
+
|
|
858
|
+
assert.calledOnceWithExactly(handleTurnDiscoveryResponseSpy, roapMessage, 'in http response');
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
it('works as expected when httpResponse is missing some headers', async () => {
|
|
862
|
+
roapMessage.headers = [
|
|
863
|
+
`x-cisco-turn-url=${FAKE_TURN_URL}`, // missing headers for username and password
|
|
864
|
+
];
|
|
865
|
+
|
|
866
|
+
const httpResponse = {mediaConnections: [{remoteSdp: JSON.stringify({roapMessage})}]};
|
|
867
|
+
|
|
868
|
+
// we spy on handleTurnDiscoveryResponse and check that it's called so that we don't have to repeat
|
|
869
|
+
// all the edge case tests here, they're already covered in other tests that call handleTurnDiscoveryResponse
|
|
870
|
+
// we test just this 1 edge case here to confirm that when handleTurnDiscoveryResponse rejects, we get the correct result
|
|
871
|
+
const handleTurnDiscoveryResponseSpy = sinon.spy(td, 'handleTurnDiscoveryResponse');
|
|
872
|
+
|
|
873
|
+
await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
874
|
+
const result = await td.handleTurnDiscoveryHttpResponse(testMeeting, httpResponse);
|
|
875
|
+
|
|
876
|
+
assert.deepEqual(result, {
|
|
877
|
+
turnServerInfo: undefined,
|
|
878
|
+
turnDiscoverySkippedReason: 'failure: TURN_DISCOVERY_RESPONSE in http response missing some headers: ["x-cisco-turn-url=turns:fakeTurnServer.com:443?transport=tcp"]',
|
|
879
|
+
});
|
|
880
|
+
assert.calledOnceWithExactly(handleTurnDiscoveryResponseSpy, roapMessage, 'in http response');
|
|
881
|
+
|
|
882
|
+
checkNextTurnDiscovery();
|
|
883
|
+
});
|
|
884
|
+
|
|
885
|
+
it('sends OK when required', async () => {
|
|
886
|
+
roapMessage.headers = [
|
|
887
|
+
`x-cisco-turn-url=${FAKE_TURN_URL}`,
|
|
888
|
+
`x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
|
|
889
|
+
`x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
|
|
890
|
+
// noOkInTransaction is missing
|
|
891
|
+
];
|
|
892
|
+
const httpResponse = {mediaConnections: [{remoteSdp: JSON.stringify({roapMessage})}]};
|
|
893
|
+
|
|
894
|
+
await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
895
|
+
const result = await td.handleTurnDiscoveryHttpResponse(testMeeting, httpResponse);
|
|
896
|
+
|
|
897
|
+
assert.deepEqual(result, {
|
|
898
|
+
turnServerInfo: {
|
|
899
|
+
url: FAKE_TURN_URL,
|
|
900
|
+
username: FAKE_TURN_USERNAME,
|
|
901
|
+
password: FAKE_TURN_PASSWORD,
|
|
902
|
+
},
|
|
903
|
+
turnDiscoverySkippedReason: undefined,
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
// check that OK was sent along with the metric for it
|
|
907
|
+
await checkRoapMessageSent('OK', 0);
|
|
908
|
+
|
|
909
|
+
assert.calledWith(
|
|
910
|
+
Metrics.sendBehavioralMetric,
|
|
911
|
+
BEHAVIORAL_METRICS.TURN_DISCOVERY_REQUIRES_OK,
|
|
912
|
+
sinon.match({
|
|
913
|
+
correlation_id: testMeeting.correlationId,
|
|
914
|
+
locus_id: FAKE_LOCUS_ID,
|
|
915
|
+
})
|
|
916
|
+
);
|
|
917
|
+
|
|
918
|
+
checkNextTurnDiscovery();
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
describe('abort', () => {
|
|
922
|
+
it('allows starting a new TURN discovery', async () => {
|
|
923
|
+
let result;
|
|
924
|
+
|
|
925
|
+
// this mock is required for doTurnDiscovery() to work
|
|
926
|
+
mockRoapRequest.sendRoap = sinon.fake.resolves({
|
|
927
|
+
mediaConnections: [
|
|
928
|
+
{
|
|
929
|
+
mediaId: '464ff97f-4bda-466a-ad06-3a22184a2274',
|
|
930
|
+
remoteSdp: `{"roapMessage": {"messageType":"TURN_DISCOVERY_RESPONSE","seq":"0","headers": ["x-cisco-turn-url=${FAKE_TURN_URL}","x-cisco-turn-username=${FAKE_TURN_USERNAME}","x-cisco-turn-password=${FAKE_TURN_PASSWORD}", "noOkInTransaction"]}}`,
|
|
931
|
+
},
|
|
932
|
+
],
|
|
933
|
+
});
|
|
934
|
+
|
|
935
|
+
result = await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
936
|
+
assert.isDefined(result.roapMessage);
|
|
937
|
+
|
|
938
|
+
td.abort();
|
|
939
|
+
|
|
940
|
+
result = await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
941
|
+
assert.isDefined(result.roapMessage);
|
|
942
|
+
|
|
943
|
+
td.abort();
|
|
944
|
+
|
|
945
|
+
// check also that doTurnDiscovery() works after abort()
|
|
946
|
+
result = await td.doTurnDiscovery(testMeeting, false);
|
|
947
|
+
});
|
|
948
|
+
|
|
949
|
+
it('does nothing when called outside of a TURN discovery', async () => {
|
|
950
|
+
let result;
|
|
951
|
+
|
|
952
|
+
// call abort() without any other calls before it - it should do nothing
|
|
953
|
+
// there is not much we can check, so afterwards we just check that we can start a new TURN discovery
|
|
954
|
+
td.abort();
|
|
955
|
+
|
|
956
|
+
result = await td.generateTurnDiscoveryRequestMessage(testMeeting, true);
|
|
957
|
+
assert.isDefined(result.roapMessage);
|
|
958
|
+
});
|
|
959
|
+
});
|
|
960
|
+
});
|
|
679
961
|
});
|