@webex/plugin-meetings 3.8.0-web-workers-keepalive.1 → 3.8.1-next.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (168) hide show
  1. package/dist/breakouts/breakout.js +1 -1
  2. package/dist/breakouts/index.js +70 -6
  3. package/dist/breakouts/index.js.map +1 -1
  4. package/dist/common/errors/webex-errors.js +12 -2
  5. package/dist/common/errors/webex-errors.js.map +1 -1
  6. package/dist/config.js +4 -1
  7. package/dist/config.js.map +1 -1
  8. package/dist/constants.js +22 -123
  9. package/dist/constants.js.map +1 -1
  10. package/dist/controls-options-manager/enums.js +2 -0
  11. package/dist/controls-options-manager/enums.js.map +1 -1
  12. package/dist/controls-options-manager/types.js.map +1 -1
  13. package/dist/controls-options-manager/util.js +52 -0
  14. package/dist/controls-options-manager/util.js.map +1 -1
  15. package/dist/interpretation/index.js +1 -1
  16. package/dist/interpretation/siLanguage.js +1 -1
  17. package/dist/locus-info/controlsUtils.js +30 -10
  18. package/dist/locus-info/controlsUtils.js.map +1 -1
  19. package/dist/locus-info/index.js +83 -12
  20. package/dist/locus-info/index.js.map +1 -1
  21. package/dist/locus-info/selfUtils.js +432 -418
  22. package/dist/locus-info/selfUtils.js.map +1 -1
  23. package/dist/media/index.js +17 -17
  24. package/dist/media/index.js.map +1 -1
  25. package/dist/media/properties.js +94 -6
  26. package/dist/media/properties.js.map +1 -1
  27. package/dist/meeting/brbState.js +9 -2
  28. package/dist/meeting/brbState.js.map +1 -1
  29. package/dist/meeting/in-meeting-actions.js +17 -1
  30. package/dist/meeting/in-meeting-actions.js.map +1 -1
  31. package/dist/meeting/index.js +568 -328
  32. package/dist/meeting/index.js.map +1 -1
  33. package/dist/meeting/locusMediaRequest.js +0 -17
  34. package/dist/meeting/locusMediaRequest.js.map +1 -1
  35. package/dist/meeting/muteState.js +4 -4
  36. package/dist/meeting/muteState.js.map +1 -1
  37. package/dist/meeting/request.js +30 -0
  38. package/dist/meeting/request.js.map +1 -1
  39. package/dist/meeting/request.type.js.map +1 -1
  40. package/dist/meeting/util.js +9 -1
  41. package/dist/meeting/util.js.map +1 -1
  42. package/dist/meeting-info/meeting-info-v2.js +19 -13
  43. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  44. package/dist/meeting-info/utilv2.js +5 -1
  45. package/dist/meeting-info/utilv2.js.map +1 -1
  46. package/dist/meetings/index.js +76 -0
  47. package/dist/meetings/index.js.map +1 -1
  48. package/dist/meetings/util.js +14 -0
  49. package/dist/meetings/util.js.map +1 -1
  50. package/dist/member/index.js +45 -9
  51. package/dist/member/index.js.map +1 -1
  52. package/dist/member/types.js +3 -0
  53. package/dist/member/types.js.map +1 -1
  54. package/dist/member/util.js +335 -356
  55. package/dist/member/util.js.map +1 -1
  56. package/dist/members/collection.js.map +1 -1
  57. package/dist/members/index.js +137 -29
  58. package/dist/members/index.js.map +1 -1
  59. package/dist/members/request.js +38 -0
  60. package/dist/members/request.js.map +1 -1
  61. package/dist/members/util.js +36 -1
  62. package/dist/members/util.js.map +1 -1
  63. package/dist/metrics/constants.js +1 -0
  64. package/dist/metrics/constants.js.map +1 -1
  65. package/dist/reachability/clusterReachability.js +23 -31
  66. package/dist/reachability/clusterReachability.js.map +1 -1
  67. package/dist/reachability/index.js +42 -2
  68. package/dist/reachability/index.js.map +1 -1
  69. package/dist/reconnection-manager/index.js +2 -2
  70. package/dist/reconnection-manager/index.js.map +1 -1
  71. package/dist/roap/index.js.map +1 -1
  72. package/dist/roap/turnDiscovery.js +45 -27
  73. package/dist/roap/turnDiscovery.js.map +1 -1
  74. package/dist/roap/types.js +17 -0
  75. package/dist/roap/types.js.map +1 -0
  76. package/dist/types/common/errors/webex-errors.d.ts +7 -1
  77. package/dist/types/config.d.ts +2 -0
  78. package/dist/types/constants.d.ts +15 -85
  79. package/dist/types/controls-options-manager/enums.d.ts +3 -1
  80. package/dist/types/controls-options-manager/types.d.ts +7 -1
  81. package/dist/types/locus-info/index.d.ts +3 -3
  82. package/dist/types/locus-info/selfUtils.d.ts +216 -1
  83. package/dist/types/media/properties.d.ts +15 -0
  84. package/dist/types/meeting/in-meeting-actions.d.ts +16 -0
  85. package/dist/types/meeting/index.d.ts +35 -1
  86. package/dist/types/meeting/muteState.d.ts +0 -1
  87. package/dist/types/meeting/request.d.ts +12 -1
  88. package/dist/types/meeting/request.type.d.ts +6 -0
  89. package/dist/types/meeting/util.d.ts +3 -1
  90. package/dist/types/meeting-info/meeting-info-v2.d.ts +2 -1
  91. package/dist/types/meetings/index.d.ts +28 -0
  92. package/dist/types/member/index.d.ts +20 -6
  93. package/dist/types/member/types.d.ts +73 -14
  94. package/dist/types/member/util.d.ts +156 -1
  95. package/dist/types/members/collection.d.ts +6 -5
  96. package/dist/types/members/index.d.ts +32 -43
  97. package/dist/types/members/request.d.ts +26 -0
  98. package/dist/types/members/util.d.ts +27 -0
  99. package/dist/types/metrics/constants.d.ts +1 -0
  100. package/dist/types/reachability/clusterReachability.d.ts +2 -6
  101. package/dist/types/reachability/index.d.ts +8 -0
  102. package/dist/types/roap/index.d.ts +3 -2
  103. package/dist/types/roap/turnDiscovery.d.ts +5 -17
  104. package/dist/types/roap/types.d.ts +16 -0
  105. package/dist/webinar/index.js +1 -1
  106. package/package.json +24 -23
  107. package/src/breakouts/index.ts +69 -0
  108. package/src/common/errors/webex-errors.ts +8 -1
  109. package/src/config.ts +2 -0
  110. package/src/constants.ts +23 -90
  111. package/src/controls-options-manager/enums.ts +2 -0
  112. package/src/controls-options-manager/types.ts +11 -1
  113. package/src/controls-options-manager/util.ts +62 -0
  114. package/src/locus-info/controlsUtils.ts +48 -12
  115. package/src/locus-info/index.ts +88 -13
  116. package/src/locus-info/selfUtils.ts +496 -442
  117. package/src/media/index.ts +23 -21
  118. package/src/media/properties.ts +96 -0
  119. package/src/meeting/brbState.ts +11 -2
  120. package/src/meeting/in-meeting-actions.ts +32 -0
  121. package/src/meeting/index.ts +356 -87
  122. package/src/meeting/locusMediaRequest.ts +0 -18
  123. package/src/meeting/muteState.ts +4 -4
  124. package/src/meeting/request.ts +36 -1
  125. package/src/meeting/request.type.ts +7 -0
  126. package/src/meeting/util.ts +9 -1
  127. package/src/meeting-info/meeting-info-v2.ts +7 -2
  128. package/src/meeting-info/utilv2.ts +5 -0
  129. package/src/meetings/index.ts +76 -0
  130. package/src/meetings/util.ts +18 -0
  131. package/src/member/index.ts +57 -22
  132. package/src/member/types.ts +82 -16
  133. package/src/member/util.ts +357 -353
  134. package/src/members/collection.ts +4 -3
  135. package/src/members/index.ts +137 -18
  136. package/src/members/request.ts +44 -0
  137. package/src/members/util.ts +43 -1
  138. package/src/metrics/constants.ts +1 -0
  139. package/src/reachability/clusterReachability.ts +26 -25
  140. package/src/reachability/index.ts +55 -1
  141. package/src/reconnection-manager/index.ts +2 -2
  142. package/src/roap/index.ts +3 -7
  143. package/src/roap/turnDiscovery.ts +34 -39
  144. package/src/roap/types.ts +23 -0
  145. package/test/unit/spec/breakouts/index.ts +167 -95
  146. package/test/unit/spec/controls-options-manager/util.js +120 -0
  147. package/test/unit/spec/locus-info/controlsUtils.js +131 -9
  148. package/test/unit/spec/locus-info/index.js +195 -73
  149. package/test/unit/spec/locus-info/selfUtils.js +98 -24
  150. package/test/unit/spec/media/index.ts +150 -18
  151. package/test/unit/spec/media/properties.ts +130 -0
  152. package/test/unit/spec/meeting/brbState.ts +40 -2
  153. package/test/unit/spec/meeting/in-meeting-actions.ts +19 -4
  154. package/test/unit/spec/meeting/index.js +553 -36
  155. package/test/unit/spec/meeting/locusMediaRequest.ts +0 -30
  156. package/test/unit/spec/meeting/muteState.js +73 -2
  157. package/test/unit/spec/meeting/request.js +32 -1
  158. package/test/unit/spec/meeting/utils.js +79 -33
  159. package/test/unit/spec/meeting-info/meetinginfov2.js +41 -0
  160. package/test/unit/spec/meeting-info/utilv2.js +19 -0
  161. package/test/unit/spec/meetings/index.js +68 -1
  162. package/test/unit/spec/members/index.js +304 -78
  163. package/test/unit/spec/members/request.js +68 -22
  164. package/test/unit/spec/members/utils.js +75 -0
  165. package/test/unit/spec/reachability/clusterReachability.ts +41 -55
  166. package/test/unit/spec/reachability/index.ts +89 -0
  167. package/test/unit/spec/reconnection-manager/index.js +4 -4
  168. package/test/unit/spec/roap/turnDiscovery.ts +110 -28
@@ -357,5 +357,80 @@ describe('plugin-meetings', () => {
357
357
  });
358
358
  });
359
359
  });
360
+
361
+ describe('#getMoveMemberToLobbyRequestBody', () => {
362
+ it('returns the correct options', () => {
363
+ const memberId = 'test1';
364
+ assert.deepEqual(MembersUtil.getMoveMemberToLobbyRequestBody(memberId), {
365
+ moveToLobby: {
366
+ participantIds: [memberId],
367
+ },
368
+ });
369
+ });
370
+ });
371
+
372
+ describe('#getMoveMemberToLobbyRequestParams', () => {
373
+ it('returns the correct params', () => {
374
+ const locusUrl = 'TestLocusUrl';
375
+ const memberId = 'test1';
376
+ const options = {
377
+ locusUrl: locusUrl,
378
+ memberId,
379
+ };
380
+ const body = {
381
+ moveToLobby: {participantIds: [memberId]},
382
+ };
383
+
384
+ const uri = `${options.locusUrl}/${PARTICIPANT}/${options.memberId}/${CONTROLS}`;
385
+
386
+ assert.deepEqual(MembersUtil.getMoveMemberToLobbyRequestParams(options, body), {
387
+ method: HTTP_VERBS.PATCH,
388
+ uri,
389
+ body,
390
+ });
391
+ });
392
+ });
393
+
394
+ describe('#cancelSIPInviteOptions', () => {
395
+ it('returns the correct options', () => {
396
+ const locusUrl = 'TestLocusUrl';
397
+ const memberId = 'test';
398
+ const invitee = {memberId};
399
+
400
+ assert.deepEqual(
401
+ MembersUtil.cancelSIPInviteOptions(
402
+ invitee,
403
+ locusUrl
404
+ ),
405
+ {
406
+ invitee,
407
+ locusUrl,
408
+ }
409
+ );
410
+ });
411
+ });
412
+
413
+ describe('#generateCancelSIPInviteRequestParams', () => {
414
+ it('returns the correct params', () => {
415
+ const locusUrl = 'TestLocusUrl';
416
+ const memberId = 'test';
417
+ const options = {
418
+ locusUrl,
419
+ invitee: {memberId}
420
+ };
421
+ const body = {
422
+ actionType: 'REMOVE',
423
+ invitees: [{address: options.invitee.memberId}],
424
+ };
425
+
426
+ const uri = options.locusUrl;
427
+
428
+ assert.deepEqual(MembersUtil.generateCancelSIPInviteRequestParams(options), {
429
+ method: HTTP_VERBS.PUT,
430
+ uri,
431
+ body,
432
+ });
433
+ });
434
+ });
360
435
  });
361
436
  });
@@ -174,59 +174,6 @@ describe('ClusterReachability', () => {
174
174
  assert.deepEqual(emittedEvents[Events.clientMediaIpsUpdated], []);
175
175
  });
176
176
 
177
- it('resolves and has correct result as soon as it finds that all udp, tcp and tls are reachable', async () => {
178
- const promise = clusterReachability.start();
179
-
180
- await clock.tickAsync(100);
181
- fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp'}});
182
-
183
- // check the right events were emitted
184
- assert.equal(emittedEvents[Events.resultReady].length, 1);
185
- assert.deepEqual(emittedEvents[Events.resultReady][0], {
186
- protocol: 'udp',
187
- result: 'reachable',
188
- latencyInMilliseconds: 100,
189
- clientMediaIPs: ['somePublicIp'],
190
- });
191
-
192
- // clientMediaIpsUpdated shouldn't be emitted, because the IP is already passed in the resultReady event
193
- assert.equal(emittedEvents[Events.clientMediaIpsUpdated].length, 0);
194
-
195
- await clock.tickAsync(100);
196
- fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'someTurnRelayIp'}});
197
-
198
- // check the right event was emitted
199
- assert.equal(emittedEvents[Events.resultReady].length, 2);
200
- assert.deepEqual(emittedEvents[Events.resultReady][1], {
201
- protocol: 'tcp',
202
- result: 'reachable',
203
- latencyInMilliseconds: 200,
204
- });
205
- assert.equal(emittedEvents[Events.clientMediaIpsUpdated].length, 0);
206
-
207
- await clock.tickAsync(100);
208
- fakePeerConnection.onicecandidate({
209
- candidate: {type: 'relay', address: 'someTurnRelayIp', port: 443},
210
- });
211
-
212
- // check the right event was emitted
213
- assert.equal(emittedEvents[Events.resultReady].length, 3);
214
- assert.deepEqual(emittedEvents[Events.resultReady][2], {
215
- protocol: 'xtls',
216
- result: 'reachable',
217
- latencyInMilliseconds: 300,
218
- });
219
- assert.equal(emittedEvents[Events.clientMediaIpsUpdated].length, 0);
220
-
221
- await promise;
222
-
223
- assert.deepEqual(clusterReachability.getResult(), {
224
- udp: {result: 'reachable', latencyInMilliseconds: 100, clientMediaIPs: ['somePublicIp']},
225
- tcp: {result: 'reachable', latencyInMilliseconds: 200},
226
- xtls: {result: 'reachable', latencyInMilliseconds: 300},
227
- });
228
- });
229
-
230
177
  it('resolves and returns correct results when aborted before it gets any candidates', async () => {
231
178
  const promise = clusterReachability.start();
232
179
 
@@ -275,7 +222,7 @@ describe('ClusterReachability', () => {
275
222
 
276
223
  await testUtils.flushPromises();
277
224
 
278
- fakePeerConnection.iceConnectionState = 'complete';
225
+ fakePeerConnection.iceGatheringState = 'complete';
279
226
  fakePeerConnection.onicegatheringstatechange();
280
227
  await promise;
281
228
 
@@ -293,7 +240,7 @@ describe('ClusterReachability', () => {
293
240
  await clock.tickAsync(30);
294
241
  fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1'}});
295
242
 
296
- fakePeerConnection.iceConnectionState = 'complete';
243
+ fakePeerConnection.iceGatheringState = 'complete';
297
244
  fakePeerConnection.onicegatheringstatechange();
298
245
  await promise;
299
246
 
@@ -436,6 +383,9 @@ describe('ClusterReachability', () => {
436
383
  candidate: {type: 'relay', address: 'someTurnRelayIp', port: 443},
437
384
  });
438
385
 
386
+ fakePeerConnection.iceGatheringState = 'complete';
387
+ fakePeerConnection.onicegatheringstatechange();
388
+
439
389
  await promise;
440
390
 
441
391
  assert.deepEqual(clusterReachability.getResult(), {
@@ -474,6 +424,10 @@ describe('ClusterReachability', () => {
474
424
  candidate: {type: 'relay', address: 'someTurnRelayIp', port: 443},
475
425
  });
476
426
 
427
+ fakePeerConnection.iceGatheringState = 'complete';
428
+ fakePeerConnection.onicegatheringstatechange();
429
+ await clock.tickAsync(10);
430
+
477
431
  await promise;
478
432
 
479
433
  assert.deepEqual(clusterReachability.getResult(), {
@@ -486,5 +440,37 @@ describe('ClusterReachability', () => {
486
440
  xtls: {result: 'reachable', latencyInMilliseconds: 20},
487
441
  });
488
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
+ });
489
475
  });
490
476
  });
@@ -1955,6 +1955,7 @@ describe('gatherReachability', () => {
1955
1955
  receivedEvents[event] = receivedEvents[event] + 1 || 1;
1956
1956
  });
1957
1957
  };
1958
+
1958
1959
  it('works as expected', async () => {
1959
1960
  setListener('reachability:stopped');
1960
1961
  setListener('reachability:done');
@@ -2016,6 +2017,59 @@ describe('gatherReachability', () => {
2016
2017
  assert.equal(receivedEvents['reachability:done'], undefined);
2017
2018
  assert.equal(receivedEvents['reachability:firstResultAvailable'], undefined);
2018
2019
  });
2020
+
2021
+ it('does not fallback when no clusters were reached and min clusters were specified', async () => {
2022
+ setListener('reachability:stopped');
2023
+ setListener('reachability:done');
2024
+ setListener('reachability:firstResultAvailable');
2025
+
2026
+ const mockGetClustersResult = {
2027
+ discoveryOptions: {
2028
+ ['early-call-min-clusters']: 1,
2029
+ },
2030
+ clusters: {
2031
+ clusterA: {
2032
+ udp: [],
2033
+ tcp: [],
2034
+ xtls: [],
2035
+ isVideoMesh: false,
2036
+ },
2037
+ clusterB: {
2038
+ udp: [],
2039
+ tcp: [],
2040
+ xtls: [],
2041
+ isVideoMesh: false,
2042
+ },
2043
+ },
2044
+ joinCookie: {id: 'id'},
2045
+ };
2046
+
2047
+ reachability.reachabilityRequest.getClusters = sinon.stub().returns(mockGetClustersResult);
2048
+
2049
+ const gatherReachabilityFallbackSpy = sinon.spy(reachability, 'gatherReachabilityFallback');
2050
+
2051
+ const resultPromise = reachability.gatherReachability('test');
2052
+
2053
+ await testUtils.flushPromises();
2054
+
2055
+ reachability.stopReachability();
2056
+
2057
+ await resultPromise;
2058
+
2059
+ // simulate a lot of time passing to check that all timers were stopped and nothing else happens
2060
+ clock.tick(99000);
2061
+
2062
+ assert.calledOnceWithExactly(mockClusterReachabilityInstances['clusterA'].abort);
2063
+ assert.calledOnceWithExactly(mockClusterReachabilityInstances['clusterB'].abort);
2064
+
2065
+ assert.calledOnceWithExactly(sendMetricSpy, true);
2066
+
2067
+ assert.equal(receivedEvents['reachability:stopped'], 1);
2068
+ assert.equal(receivedEvents['reachability:done'], undefined);
2069
+ assert.equal(receivedEvents['reachability:firstResultAvailable'], undefined);
2070
+
2071
+ assert.notCalled(gatherReachabilityFallbackSpy);
2072
+ });
2019
2073
  });
2020
2074
  });
2021
2075
 
@@ -2686,3 +2740,38 @@ describe('sendMetric', () => {
2686
2740
  });
2687
2741
  });
2688
2742
  });
2743
+
2744
+ describe('isSubnetReachable', () => {
2745
+ let webex;
2746
+ let reachability;
2747
+
2748
+ beforeEach(() => {
2749
+ webex = new MockWebex();
2750
+ reachability = new TestReachability(webex);
2751
+
2752
+ reachability.setFakeClusterReachability({
2753
+ cluster1: {
2754
+ reachedSubnets: new Set(['1.2.3.4', '2.3.4.5']),
2755
+ },
2756
+ cluster2: {
2757
+ reachedSubnets: new Set(['3.4.5.6', '4.5.6.7']),
2758
+ },
2759
+ });
2760
+ });
2761
+
2762
+ afterEach(() => {
2763
+ sinon.restore();
2764
+ });
2765
+
2766
+ it('returns true if the subnet is reachable', () => {
2767
+ assert(reachability.isSubnetReachable('1.2.3.4'));
2768
+ });
2769
+
2770
+ it(`returns false if the subnet is unreachable`, () => {
2771
+ assert(!reachability.isSubnetReachable('11.2.3.4'));
2772
+ });
2773
+
2774
+ it('returns null if the subnet is not provided', () => {
2775
+ assert.isNull(reachability.isSubnetReachable(undefined));
2776
+ });
2777
+ });
@@ -60,7 +60,7 @@ describe('plugin-meetings', () => {
60
60
  roap: {
61
61
  doTurnDiscovery: sinon.stub().resolves({
62
62
  turnServerInfo: {
63
- url: 'fake_turn_url',
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: 'fake_turn_url',
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 url is an empty string', async () => {
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
- url: '',
160
+ urls: [],
161
161
  username: 'whatever',
162
162
  password: 'whatever',
163
163
  },
@@ -15,7 +15,8 @@ describe('TurnDiscovery', () => {
15
15
  let mockRoapRequest: RoapRequest;
16
16
  let testMeeting: any;
17
17
 
18
- const FAKE_TURN_URL = 'turns:fakeTurnServer.com:443?transport=tcp';
18
+ const FAKE_TURN_URL1 = 'turns:fakeTurnServer1.com:443?transport=tcp';
19
+ const FAKE_TURN_URL2 = 'turns:fakeTurnServer2.com:443?transport=tcp';
19
20
  const FAKE_TURN_USERNAME = 'someUsernameFromServer';
20
21
  const FAKE_TURN_PASSWORD = 'fakePasswordFromServer';
21
22
  const FAKE_LOCUS_ID = '09493311-f5d5-3e58-b491-009cc628162e';
@@ -186,7 +187,8 @@ describe('TurnDiscovery', () => {
186
187
  {
187
188
  messageType: 'TURN_DISCOVERY_RESPONSE',
188
189
  headers: [
189
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
190
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
191
+ `x-cisco-turn-url=${FAKE_TURN_URL2}`,
190
192
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
191
193
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
192
194
  ],
@@ -195,7 +197,7 @@ describe('TurnDiscovery', () => {
195
197
  );
196
198
 
197
199
  await checkResult(result, 'OK', {
198
- url: FAKE_TURN_URL,
200
+ urls: [FAKE_TURN_URL1, FAKE_TURN_URL2],
199
201
  username: FAKE_TURN_USERNAME,
200
202
  password: FAKE_TURN_PASSWORD,
201
203
  });
@@ -220,7 +222,7 @@ describe('TurnDiscovery', () => {
220
222
  {
221
223
  messageType: 'TURN_DISCOVERY_RESPONSE',
222
224
  headers: [
223
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
225
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
224
226
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
225
227
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
226
228
  'noOkInTransaction',
@@ -230,7 +232,7 @@ describe('TurnDiscovery', () => {
230
232
  );
231
233
 
232
234
  await checkResult(result, undefined, {
233
- url: FAKE_TURN_URL,
235
+ urls: [FAKE_TURN_URL1],
234
236
  username: FAKE_TURN_USERNAME,
235
237
  password: FAKE_TURN_PASSWORD,
236
238
  });
@@ -241,7 +243,7 @@ describe('TurnDiscovery', () => {
241
243
  mediaConnections: [
242
244
  {
243
245
  mediaId: '464ff97f-4bda-466a-ad06-3a22184a2274',
244
- 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"]}}`,
246
+ remoteSdp: `{"roapMessage": {"messageType":"TURN_DISCOVERY_RESPONSE","seq":"0","headers": ["x-cisco-turn-url=${FAKE_TURN_URL1}","x-cisco-turn-username=${FAKE_TURN_USERNAME}","x-cisco-turn-password=${FAKE_TURN_PASSWORD}", "noOkInTransaction"]}}`,
245
247
  },
246
248
  ],
247
249
  });
@@ -256,7 +258,7 @@ describe('TurnDiscovery', () => {
256
258
  mockRoapRequest.sendRoap.resetHistory();
257
259
 
258
260
  await checkResult(result, undefined, {
259
- url: FAKE_TURN_URL,
261
+ urls: [FAKE_TURN_URL1],
260
262
  username: FAKE_TURN_USERNAME,
261
263
  password: FAKE_TURN_PASSWORD,
262
264
  });
@@ -268,7 +270,7 @@ describe('TurnDiscovery', () => {
268
270
  mediaConnections: [
269
271
  {
270
272
  mediaId: '464ff97f-4bda-466a-ad06-3a22184a2274',
271
- 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}"]}}`,
273
+ remoteSdp: `{"roapMessage": {"messageType":"TURN_DISCOVERY_RESPONSE","seq":"0","headers": ["x-cisco-turn-url=${FAKE_TURN_URL1}","x-cisco-turn-username=${FAKE_TURN_USERNAME}","x-cisco-turn-password=${FAKE_TURN_PASSWORD}"]}}`,
272
274
  },
273
275
  ],
274
276
  };
@@ -290,7 +292,7 @@ describe('TurnDiscovery', () => {
290
292
  sendRoapPromiseResolve(sendRoapResult);
291
293
 
292
294
  await checkResult(result, 'OK', {
293
- url: FAKE_TURN_URL,
295
+ urls: [FAKE_TURN_URL1],
294
296
  username: FAKE_TURN_USERNAME,
295
297
  password: FAKE_TURN_PASSWORD,
296
298
  });
@@ -350,16 +352,17 @@ describe('TurnDiscovery', () => {
350
352
  {
351
353
  messageType: 'TURN_DISCOVERY_RESPONSE',
352
354
  headers: [
353
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
355
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
354
356
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
355
357
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
358
+ `x-cisco-turn-url=${FAKE_TURN_URL2}`,
356
359
  ],
357
360
  },
358
361
  'from test'
359
362
  );
360
363
 
361
364
  await checkResult(result, 'OK', {
362
- url: FAKE_TURN_URL,
365
+ urls: [FAKE_TURN_URL1, FAKE_TURN_URL2],
363
366
  username: FAKE_TURN_USERNAME,
364
367
  password: FAKE_TURN_PASSWORD,
365
368
  });
@@ -391,6 +394,44 @@ describe('TurnDiscovery', () => {
391
394
  'failure: TURN_DISCOVERY_RESPONSE in http response has unexpected messageType: {"seq":"0","messageType":"ERROR"}'
392
395
  );
393
396
  });
397
+
398
+ it('resets turnInfo each time TURN discovery is done', async () => {
399
+ const runCheck = async (td, turnUrl, expectedResult) => {
400
+ mockRoapRequest.sendRoap = sinon.fake.resolves({
401
+ mediaConnections: [
402
+ {
403
+ mediaId: '464ff97f-4bda-466a-ad06-3a22184a2274',
404
+ remoteSdp: `{"roapMessage": {"messageType":"TURN_DISCOVERY_RESPONSE","seq":"0","headers": ["x-cisco-turn-url=${turnUrl}","x-cisco-turn-username=${FAKE_TURN_USERNAME}","x-cisco-turn-password=${FAKE_TURN_PASSWORD}", "noOkInTransaction"]}}`,
405
+ },
406
+ ],
407
+ });
408
+
409
+ const result = td.doTurnDiscovery(testMeeting, false);
410
+
411
+ // check that TURN_DISCOVERY_REQUEST was sent
412
+ await checkRoapMessageSent('TURN_DISCOVERY_REQUEST', 0);
413
+
414
+ // @ts-ignore
415
+ mockRoapRequest.sendRoap.resetHistory();
416
+
417
+ await checkResult(result, undefined, expectedResult);
418
+ };
419
+
420
+ const td = new TurnDiscovery(mockRoapRequest);
421
+
422
+ await runCheck(td, FAKE_TURN_URL1, {
423
+ urls: [FAKE_TURN_URL1],
424
+ username: FAKE_TURN_USERNAME,
425
+ password: FAKE_TURN_PASSWORD,
426
+ });
427
+
428
+ // call it again with different turn url, the result should not have the previous url
429
+ await runCheck(td, FAKE_TURN_URL2, {
430
+ urls: [FAKE_TURN_URL2],
431
+ username: FAKE_TURN_USERNAME,
432
+ password: FAKE_TURN_PASSWORD,
433
+ });
434
+ });
394
435
  });
395
436
  });
396
437
 
@@ -415,7 +456,7 @@ describe('TurnDiscovery', () => {
415
456
  {
416
457
  messageType: 'TURN_DISCOVERY_RESPONSE',
417
458
  headers: [
418
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
459
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
419
460
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
420
461
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
421
462
  ],
@@ -428,7 +469,7 @@ describe('TurnDiscovery', () => {
428
469
 
429
470
  const {turnServerInfo, turnDiscoverySkippedReason} = await result;
430
471
  assert.deepEqual(turnServerInfo, {
431
- url: FAKE_TURN_URL,
472
+ urls: [FAKE_TURN_URL1],
432
473
  username: FAKE_TURN_USERNAME,
433
474
  password: FAKE_TURN_PASSWORD,
434
475
  });
@@ -455,7 +496,7 @@ describe('TurnDiscovery', () => {
455
496
  {
456
497
  messageType: 'TURN_DISCOVERY_RESPONSE',
457
498
  headers: [
458
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
499
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
459
500
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
460
501
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
461
502
  ],
@@ -471,7 +512,7 @@ describe('TurnDiscovery', () => {
471
512
  const {turnServerInfo, turnDiscoverySkippedReason} = await result;
472
513
 
473
514
  assert.deepEqual(turnServerInfo, {
474
- url: FAKE_TURN_URL,
515
+ urls: [FAKE_TURN_URL1],
475
516
  username: FAKE_TURN_USERNAME,
476
517
  password: FAKE_TURN_PASSWORD,
477
518
  });
@@ -493,7 +534,7 @@ describe('TurnDiscovery', () => {
493
534
  messageType: 'TURN_DISCOVERY_RESPONSE',
494
535
  headers: [
495
536
  'x-cisco-turn-unexpected-header=xxx',
496
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
537
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
497
538
  'x-cisco-some-other-header',
498
539
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
499
540
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
@@ -510,7 +551,47 @@ describe('TurnDiscovery', () => {
510
551
 
511
552
  const {turnServerInfo, turnDiscoverySkippedReason} = await result;
512
553
  assert.deepEqual(turnServerInfo, {
513
- url: FAKE_TURN_URL,
554
+ urls: [FAKE_TURN_URL1],
555
+ username: FAKE_TURN_USERNAME,
556
+ password: FAKE_TURN_PASSWORD,
557
+ });
558
+ assert.isUndefined(turnDiscoverySkippedReason);
559
+ });
560
+
561
+ // this happens when we land on video-mesh nodes (VMN) - we will get a single empty url
562
+ it('filters out any empty TURN urls', async () => {
563
+ const td = new TurnDiscovery(mockRoapRequest);
564
+ const result = td.doTurnDiscovery(testMeeting, false);
565
+
566
+ // check that TURN_DISCOVERY_REQUEST was sent
567
+ await checkRoapMessageSent('TURN_DISCOVERY_REQUEST', 0);
568
+ // @ts-ignore
569
+ mockRoapRequest.sendRoap.resetHistory();
570
+
571
+ // simulate the response with some empty urls, normally there would be just 1, but we put more just for the sake of testing
572
+ td.handleTurnDiscoveryResponse(
573
+ {
574
+ messageType: 'TURN_DISCOVERY_RESPONSE',
575
+ headers: [
576
+ 'x-cisco-turn-url=',
577
+ `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
578
+ 'x-cisco-turn-url=',
579
+ `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
580
+ 'x-cisco-turn-url=non-empty-url',
581
+ 'x-cisco-turn-url=',
582
+ ],
583
+ },
584
+ 'from test'
585
+ );
586
+
587
+ await testUtils.flushPromises();
588
+
589
+ // check that we've sent OK and still parsed the headers we care about
590
+ await checkRoapMessageSent('OK', 0);
591
+
592
+ const {turnServerInfo, turnDiscoverySkippedReason} = await result;
593
+ assert.deepEqual(turnServerInfo, {
594
+ urls: ['non-empty-url'], // empty urls should be filtered out
514
595
  username: FAKE_TURN_USERNAME,
515
596
  password: FAKE_TURN_PASSWORD,
516
597
  });
@@ -575,7 +656,7 @@ describe('TurnDiscovery', () => {
575
656
  {
576
657
  messageType: 'TURN_DISCOVERY_RESPONSE',
577
658
  headers: [
578
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
659
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
579
660
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
580
661
  ],
581
662
  },
@@ -587,7 +668,7 @@ describe('TurnDiscovery', () => {
587
668
  assert.isUndefined(turnServerInfo);
588
669
  assert.equal(
589
670
  turnDiscoverySkippedReason,
590
- `failure: TURN_DISCOVERY_RESPONSE from test missing some headers: ["x-cisco-turn-url=${FAKE_TURN_URL}","x-cisco-turn-username=${FAKE_TURN_USERNAME}"]`
671
+ `failure: TURN_DISCOVERY_RESPONSE from test missing some headers: ["x-cisco-turn-url=${FAKE_TURN_URL1}","x-cisco-turn-username=${FAKE_TURN_USERNAME}"]`
591
672
  );
592
673
  checkFailureMetricsSent();
593
674
  });
@@ -655,7 +736,7 @@ describe('TurnDiscovery', () => {
655
736
  {
656
737
  messageType: 'TURN_DISCOVERY_RESPONSE',
657
738
  headers: [
658
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
739
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
659
740
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
660
741
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
661
742
  ],
@@ -707,7 +788,7 @@ describe('TurnDiscovery', () => {
707
788
  {
708
789
  messageType: 'TURN_DISCOVERY_RESPONSE',
709
790
  headers: [
710
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
791
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
711
792
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
712
793
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
713
794
  ],
@@ -816,7 +897,8 @@ describe('TurnDiscovery', () => {
816
897
  errorType: undefined,
817
898
  errorCause: undefined,
818
899
  headers: [
819
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
900
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
901
+ `x-cisco-turn-url=${FAKE_TURN_URL2}`,
820
902
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
821
903
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
822
904
  'noOkInTransaction',
@@ -902,7 +984,7 @@ describe('TurnDiscovery', () => {
902
984
 
903
985
  assert.deepEqual(result, {
904
986
  turnServerInfo: {
905
- url: FAKE_TURN_URL,
987
+ urls: [FAKE_TURN_URL1, FAKE_TURN_URL2],
906
988
  username: FAKE_TURN_USERNAME,
907
989
  password: FAKE_TURN_PASSWORD,
908
990
  },
@@ -914,7 +996,7 @@ describe('TurnDiscovery', () => {
914
996
 
915
997
  it('works as expected when httpResponse is missing some headers', async () => {
916
998
  roapMessage.headers = [
917
- `x-cisco-turn-url=${FAKE_TURN_URL}`, // missing headers for username and password
999
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`, // missing headers for username and password
918
1000
  ];
919
1001
 
920
1002
  const httpResponse = {mediaConnections: [{remoteSdp: JSON.stringify({roapMessage})}]};
@@ -930,7 +1012,7 @@ describe('TurnDiscovery', () => {
930
1012
  assert.deepEqual(result, {
931
1013
  turnServerInfo: undefined,
932
1014
  turnDiscoverySkippedReason:
933
- 'failure: TURN_DISCOVERY_RESPONSE in http response missing some headers: ["x-cisco-turn-url=turns:fakeTurnServer.com:443?transport=tcp"]',
1015
+ 'failure: TURN_DISCOVERY_RESPONSE in http response missing some headers: ["x-cisco-turn-url=turns:fakeTurnServer1.com:443?transport=tcp"]',
934
1016
  });
935
1017
  assert.calledOnceWithExactly(handleTurnDiscoveryResponseSpy, roapMessage, 'in http response');
936
1018
 
@@ -939,7 +1021,7 @@ describe('TurnDiscovery', () => {
939
1021
 
940
1022
  it('sends OK when required', async () => {
941
1023
  roapMessage.headers = [
942
- `x-cisco-turn-url=${FAKE_TURN_URL}`,
1024
+ `x-cisco-turn-url=${FAKE_TURN_URL1}`,
943
1025
  `x-cisco-turn-username=${FAKE_TURN_USERNAME}`,
944
1026
  `x-cisco-turn-password=${FAKE_TURN_PASSWORD}`,
945
1027
  // noOkInTransaction is missing
@@ -951,7 +1033,7 @@ describe('TurnDiscovery', () => {
951
1033
 
952
1034
  assert.deepEqual(result, {
953
1035
  turnServerInfo: {
954
- url: FAKE_TURN_URL,
1036
+ urls: [FAKE_TURN_URL1],
955
1037
  username: FAKE_TURN_USERNAME,
956
1038
  password: FAKE_TURN_PASSWORD,
957
1039
  },
@@ -982,7 +1064,7 @@ describe('TurnDiscovery', () => {
982
1064
  mediaConnections: [
983
1065
  {
984
1066
  mediaId: '464ff97f-4bda-466a-ad06-3a22184a2274',
985
- 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"]}}`,
1067
+ remoteSdp: `{"roapMessage": {"messageType":"TURN_DISCOVERY_RESPONSE","seq":"0","headers": ["x-cisco-turn-url=${FAKE_TURN_URL1}","x-cisco-turn-username=${FAKE_TURN_USERNAME}","x-cisco-turn-password=${FAKE_TURN_PASSWORD}", "noOkInTransaction"]}}`,
986
1068
  },
987
1069
  ],
988
1070
  });