@webex/plugin-meetings 3.10.0-next.3 → 3.10.0-next.30

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 (274) hide show
  1. package/dist/annotation/annotation.types.js.map +1 -1
  2. package/dist/annotation/constants.js.map +1 -1
  3. package/dist/annotation/index.js +19 -22
  4. package/dist/annotation/index.js.map +1 -1
  5. package/dist/breakouts/breakout.js +6 -6
  6. package/dist/breakouts/breakout.js.map +1 -1
  7. package/dist/breakouts/collection.js.map +1 -1
  8. package/dist/breakouts/edit-lock-error.js +9 -11
  9. package/dist/breakouts/edit-lock-error.js.map +1 -1
  10. package/dist/breakouts/events.js.map +1 -1
  11. package/dist/breakouts/index.js +126 -127
  12. package/dist/breakouts/index.js.map +1 -1
  13. package/dist/breakouts/request.js +6 -8
  14. package/dist/breakouts/request.js.map +1 -1
  15. package/dist/breakouts/utils.js.map +1 -1
  16. package/dist/common/browser-detection.js.map +1 -1
  17. package/dist/common/collection.js +1 -2
  18. package/dist/common/collection.js.map +1 -1
  19. package/dist/common/config.js.map +1 -1
  20. package/dist/common/errors/captcha-error.js +9 -11
  21. package/dist/common/errors/captcha-error.js.map +1 -1
  22. package/dist/common/errors/intent-to-join.js +10 -12
  23. package/dist/common/errors/intent-to-join.js.map +1 -1
  24. package/dist/common/errors/join-forbidden-error.js +10 -12
  25. package/dist/common/errors/join-forbidden-error.js.map +1 -1
  26. package/dist/common/errors/join-meeting.js +10 -12
  27. package/dist/common/errors/join-meeting.js.map +1 -1
  28. package/dist/common/errors/join-webinar-error.js +9 -11
  29. package/dist/common/errors/join-webinar-error.js.map +1 -1
  30. package/dist/common/errors/media.js +9 -11
  31. package/dist/common/errors/media.js.map +1 -1
  32. package/dist/common/errors/multistream-not-supported-error.js +9 -11
  33. package/dist/common/errors/multistream-not-supported-error.js.map +1 -1
  34. package/dist/common/errors/no-meeting-info.js +9 -11
  35. package/dist/common/errors/no-meeting-info.js.map +1 -1
  36. package/dist/common/errors/parameter.js +11 -14
  37. package/dist/common/errors/parameter.js.map +1 -1
  38. package/dist/common/errors/password-error.js +9 -11
  39. package/dist/common/errors/password-error.js.map +1 -1
  40. package/dist/common/errors/permission.js +9 -11
  41. package/dist/common/errors/permission.js.map +1 -1
  42. package/dist/common/errors/reclaim-host-role-errors.js +32 -38
  43. package/dist/common/errors/reclaim-host-role-errors.js.map +1 -1
  44. package/dist/common/errors/reconnection-not-started.js +5 -6
  45. package/dist/common/errors/reconnection-not-started.js.map +1 -1
  46. package/dist/common/errors/reconnection.js +9 -11
  47. package/dist/common/errors/reconnection.js.map +1 -1
  48. package/dist/common/errors/stats.js +9 -11
  49. package/dist/common/errors/stats.js.map +1 -1
  50. package/dist/common/errors/webex-errors.js +20 -29
  51. package/dist/common/errors/webex-errors.js.map +1 -1
  52. package/dist/common/errors/webex-meetings-error.js +9 -12
  53. package/dist/common/errors/webex-meetings-error.js.map +1 -1
  54. package/dist/common/events/events-scope.js +9 -10
  55. package/dist/common/events/events-scope.js.map +1 -1
  56. package/dist/common/events/events.js +9 -10
  57. package/dist/common/events/events.js.map +1 -1
  58. package/dist/common/events/trigger-proxy.js.map +1 -1
  59. package/dist/common/events/util.js.map +1 -1
  60. package/dist/common/logs/logger-config.js.map +1 -1
  61. package/dist/common/logs/logger-proxy.js.map +1 -1
  62. package/dist/common/logs/request.js +17 -17
  63. package/dist/common/logs/request.js.map +1 -1
  64. package/dist/common/queue.js +1 -2
  65. package/dist/common/queue.js.map +1 -1
  66. package/dist/config.js +2 -2
  67. package/dist/config.js.map +1 -1
  68. package/dist/constants.js +11 -8
  69. package/dist/constants.js.map +1 -1
  70. package/dist/controls-options-manager/constants.js.map +1 -1
  71. package/dist/controls-options-manager/enums.js.map +1 -1
  72. package/dist/controls-options-manager/index.js +1 -2
  73. package/dist/controls-options-manager/index.js.map +1 -1
  74. package/dist/controls-options-manager/types.js.map +1 -1
  75. package/dist/controls-options-manager/util.js +1 -2
  76. package/dist/controls-options-manager/util.js.map +1 -1
  77. package/dist/hashTree/constants.js +20 -0
  78. package/dist/hashTree/constants.js.map +1 -0
  79. package/dist/hashTree/hashTree.js +515 -0
  80. package/dist/hashTree/hashTree.js.map +1 -0
  81. package/dist/hashTree/hashTreeParser.js +1250 -0
  82. package/dist/hashTree/hashTreeParser.js.map +1 -0
  83. package/dist/hashTree/types.js +23 -0
  84. package/dist/hashTree/types.js.map +1 -0
  85. package/dist/hashTree/utils.js +59 -0
  86. package/dist/hashTree/utils.js.map +1 -0
  87. package/dist/index.js +1 -2
  88. package/dist/index.js.map +1 -1
  89. package/dist/interceptors/index.js.map +1 -1
  90. package/dist/interceptors/locusRetry.js +6 -8
  91. package/dist/interceptors/locusRetry.js.map +1 -1
  92. package/dist/interceptors/locusRouteToken.js +26 -12
  93. package/dist/interceptors/locusRouteToken.js.map +1 -1
  94. package/dist/interpretation/collection.js.map +1 -1
  95. package/dist/interpretation/index.js +1 -2
  96. package/dist/interpretation/index.js.map +1 -1
  97. package/dist/interpretation/siLanguage.js +1 -1
  98. package/dist/interpretation/siLanguage.js.map +1 -1
  99. package/dist/locus-info/controlsUtils.js.map +1 -1
  100. package/dist/locus-info/embeddedAppsUtils.js.map +1 -1
  101. package/dist/locus-info/fullState.js.map +1 -1
  102. package/dist/locus-info/hostUtils.js.map +1 -1
  103. package/dist/locus-info/index.js +609 -177
  104. package/dist/locus-info/index.js.map +1 -1
  105. package/dist/locus-info/infoUtils.js.map +1 -1
  106. package/dist/locus-info/mediaSharesUtils.js.map +1 -1
  107. package/dist/locus-info/parser.js +3 -4
  108. package/dist/locus-info/parser.js.map +1 -1
  109. package/dist/locus-info/selfUtils.js.map +1 -1
  110. package/dist/locus-info/types.js +7 -0
  111. package/dist/locus-info/types.js.map +1 -0
  112. package/dist/media/MediaConnectionAwaiter.js +1 -2
  113. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  114. package/dist/media/index.js +0 -2
  115. package/dist/media/index.js.map +1 -1
  116. package/dist/media/properties.js +15 -17
  117. package/dist/media/properties.js.map +1 -1
  118. package/dist/media/util.js.map +1 -1
  119. package/dist/meeting/brbState.js +8 -9
  120. package/dist/meeting/brbState.js.map +1 -1
  121. package/dist/meeting/connectionStateHandler.js +10 -13
  122. package/dist/meeting/connectionStateHandler.js.map +1 -1
  123. package/dist/meeting/in-meeting-actions.js.map +1 -1
  124. package/dist/meeting/index.js +1576 -1533
  125. package/dist/meeting/index.js.map +1 -1
  126. package/dist/meeting/locusMediaRequest.js +13 -17
  127. package/dist/meeting/locusMediaRequest.js.map +1 -1
  128. package/dist/meeting/muteState.js +11 -12
  129. package/dist/meeting/muteState.js.map +1 -1
  130. package/dist/meeting/request.js +101 -104
  131. package/dist/meeting/request.js.map +1 -1
  132. package/dist/meeting/request.type.js.map +1 -1
  133. package/dist/meeting/state.js.map +1 -1
  134. package/dist/meeting/type.js.map +1 -1
  135. package/dist/meeting/util.js +24 -23
  136. package/dist/meeting/util.js.map +1 -1
  137. package/dist/meeting/voicea-meeting.js +3 -3
  138. package/dist/meeting/voicea-meeting.js.map +1 -1
  139. package/dist/meeting-info/collection.js +7 -10
  140. package/dist/meeting-info/collection.js.map +1 -1
  141. package/dist/meeting-info/index.js +1 -2
  142. package/dist/meeting-info/index.js.map +1 -1
  143. package/dist/meeting-info/meeting-info-v2.js +135 -146
  144. package/dist/meeting-info/meeting-info-v2.js.map +1 -1
  145. package/dist/meeting-info/request.js +1 -2
  146. package/dist/meeting-info/request.js.map +1 -1
  147. package/dist/meeting-info/util.js +36 -37
  148. package/dist/meeting-info/util.js.map +1 -1
  149. package/dist/meeting-info/utilv2.js +30 -31
  150. package/dist/meeting-info/utilv2.js.map +1 -1
  151. package/dist/meetings/collection.js +6 -8
  152. package/dist/meetings/collection.js.map +1 -1
  153. package/dist/meetings/index.js +200 -148
  154. package/dist/meetings/index.js.map +1 -1
  155. package/dist/meetings/meetings.types.js.map +1 -1
  156. package/dist/meetings/request.js +6 -8
  157. package/dist/meetings/request.js.map +1 -1
  158. package/dist/meetings/util.js +36 -30
  159. package/dist/meetings/util.js.map +1 -1
  160. package/dist/member/index.js +1 -2
  161. package/dist/member/index.js.map +1 -1
  162. package/dist/member/types.js +6 -3
  163. package/dist/member/types.js.map +1 -1
  164. package/dist/member/util.js.map +1 -1
  165. package/dist/members/collection.js +1 -2
  166. package/dist/members/collection.js.map +1 -1
  167. package/dist/members/index.js +18 -21
  168. package/dist/members/index.js.map +1 -1
  169. package/dist/members/request.js +8 -11
  170. package/dist/members/request.js.map +1 -1
  171. package/dist/members/types.js.map +1 -1
  172. package/dist/members/util.js.map +1 -1
  173. package/dist/metrics/constants.js +3 -1
  174. package/dist/metrics/constants.js.map +1 -1
  175. package/dist/metrics/index.js +3 -4
  176. package/dist/metrics/index.js.map +1 -1
  177. package/dist/multistream/mediaRequestManager.js +1 -2
  178. package/dist/multistream/mediaRequestManager.js.map +1 -1
  179. package/dist/multistream/receiveSlot.js +34 -45
  180. package/dist/multistream/receiveSlot.js.map +1 -1
  181. package/dist/multistream/receiveSlotManager.js +8 -9
  182. package/dist/multistream/receiveSlotManager.js.map +1 -1
  183. package/dist/multistream/remoteMedia.js +12 -15
  184. package/dist/multistream/remoteMedia.js.map +1 -1
  185. package/dist/multistream/remoteMediaGroup.js +1 -2
  186. package/dist/multistream/remoteMediaGroup.js.map +1 -1
  187. package/dist/multistream/remoteMediaManager.js +122 -123
  188. package/dist/multistream/remoteMediaManager.js.map +1 -1
  189. package/dist/multistream/sendSlotManager.js +29 -30
  190. package/dist/multistream/sendSlotManager.js.map +1 -1
  191. package/dist/personal-meeting-room/index.js +16 -19
  192. package/dist/personal-meeting-room/index.js.map +1 -1
  193. package/dist/personal-meeting-room/request.js +7 -10
  194. package/dist/personal-meeting-room/request.js.map +1 -1
  195. package/dist/personal-meeting-room/util.js.map +1 -1
  196. package/dist/reachability/clusterReachability.js +188 -352
  197. package/dist/reachability/clusterReachability.js.map +1 -1
  198. package/dist/reachability/index.js +206 -206
  199. package/dist/reachability/index.js.map +1 -1
  200. package/dist/reachability/reachability.types.js +14 -1
  201. package/dist/reachability/reachability.types.js.map +1 -1
  202. package/dist/reachability/reachabilityPeerConnection.js +445 -0
  203. package/dist/reachability/reachabilityPeerConnection.js.map +1 -0
  204. package/dist/reachability/request.js.map +1 -1
  205. package/dist/reachability/util.js.map +1 -1
  206. package/dist/reactions/constants.js.map +1 -1
  207. package/dist/reactions/reactions.js.map +1 -1
  208. package/dist/reactions/reactions.type.js.map +1 -1
  209. package/dist/reconnection-manager/index.js +178 -176
  210. package/dist/reconnection-manager/index.js.map +1 -1
  211. package/dist/recording-controller/enums.js.map +1 -1
  212. package/dist/recording-controller/index.js +1 -2
  213. package/dist/recording-controller/index.js.map +1 -1
  214. package/dist/recording-controller/util.js.map +1 -1
  215. package/dist/roap/index.js +12 -15
  216. package/dist/roap/index.js.map +1 -1
  217. package/dist/roap/request.js +24 -26
  218. package/dist/roap/request.js.map +1 -1
  219. package/dist/roap/turnDiscovery.js +75 -76
  220. package/dist/roap/turnDiscovery.js.map +1 -1
  221. package/dist/roap/types.js.map +1 -1
  222. package/dist/transcription/index.js +4 -5
  223. package/dist/transcription/index.js.map +1 -1
  224. package/dist/types/config.d.ts +1 -0
  225. package/dist/types/constants.d.ts +26 -21
  226. package/dist/types/hashTree/constants.d.ts +8 -0
  227. package/dist/types/hashTree/hashTree.d.ts +129 -0
  228. package/dist/types/hashTree/hashTreeParser.d.ts +250 -0
  229. package/dist/types/hashTree/types.d.ts +33 -0
  230. package/dist/types/hashTree/utils.d.ts +16 -0
  231. package/dist/types/interceptors/locusRouteToken.d.ts +1 -0
  232. package/dist/types/locus-info/index.d.ts +98 -80
  233. package/dist/types/locus-info/types.d.ts +54 -0
  234. package/dist/types/meeting/index.d.ts +22 -9
  235. package/dist/types/meetings/index.d.ts +9 -2
  236. package/dist/types/metrics/constants.d.ts +2 -0
  237. package/dist/types/reachability/clusterReachability.d.ts +33 -84
  238. package/dist/types/reachability/reachability.types.d.ts +12 -1
  239. package/dist/types/reachability/reachabilityPeerConnection.d.ts +111 -0
  240. package/dist/webinar/collection.js +1 -2
  241. package/dist/webinar/collection.js.map +1 -1
  242. package/dist/webinar/index.js +148 -158
  243. package/dist/webinar/index.js.map +1 -1
  244. package/package.json +23 -22
  245. package/src/config.ts +1 -0
  246. package/src/constants.ts +13 -1
  247. package/src/hashTree/constants.ts +9 -0
  248. package/src/hashTree/hashTree.ts +463 -0
  249. package/src/hashTree/hashTreeParser.ts +1143 -0
  250. package/src/hashTree/types.ts +39 -0
  251. package/src/hashTree/utils.ts +53 -0
  252. package/src/interceptors/locusRouteToken.ts +16 -4
  253. package/src/locus-info/index.ts +625 -164
  254. package/src/locus-info/types.ts +53 -0
  255. package/src/meeting/index.ts +78 -27
  256. package/src/meeting/util.ts +1 -0
  257. package/src/meetings/index.ts +119 -59
  258. package/src/meetings/util.ts +10 -9
  259. package/src/metrics/constants.ts +2 -0
  260. package/src/reachability/clusterReachability.ts +159 -330
  261. package/src/reachability/index.ts +6 -1
  262. package/src/reachability/reachability.types.ts +15 -1
  263. package/src/reachability/reachabilityPeerConnection.ts +418 -0
  264. package/test/unit/spec/hashTree/hashTree.ts +655 -0
  265. package/test/unit/spec/hashTree/hashTreeParser.ts +1524 -0
  266. package/test/unit/spec/hashTree/utils.ts +140 -0
  267. package/test/unit/spec/interceptors/locusRouteToken.ts +27 -0
  268. package/test/unit/spec/locus-info/index.js +851 -16
  269. package/test/unit/spec/meeting/index.js +120 -20
  270. package/test/unit/spec/meeting/utils.js +77 -0
  271. package/test/unit/spec/meetings/index.js +263 -27
  272. package/test/unit/spec/meetings/utils.js +51 -1
  273. package/test/unit/spec/reachability/clusterReachability.ts +404 -137
  274. package/test/unit/spec/reachability/index.ts +3 -3
@@ -1,17 +1,16 @@
1
1
  import {assert} from '@webex/test-helper-chai';
2
- import MockWebex from '@webex/test-helper-mock-webex';
3
2
  import sinon from 'sinon';
4
3
  import testUtils from '../../../utils/testUtils';
5
4
 
6
- // packages/@webex/plugin-meetings/test/unit/spec/reachability/clusterReachability.ts
7
5
  import {
8
6
  ClusterReachability,
9
7
  ResultEventData,
10
8
  Events,
11
9
  ClientMediaIpsUpdatedEventData,
12
10
  NatTypeUpdatedEventData,
13
- } from '@webex/plugin-meetings/src/reachability/clusterReachability'; // replace with actual path
14
- import { NatType } from 'packages/@webex/plugin-meetings/dist/reachability/reachability.types';
11
+ } from '@webex/plugin-meetings/src/reachability/clusterReachability';
12
+ import {ReachabilityPeerConnection} from '@webex/plugin-meetings/src/reachability/reachabilityPeerConnection';
13
+ import {ReachabilityPeerConnectionEvents} from '@webex/plugin-meetings/src/reachability/reachability.types';
15
14
 
16
15
  describe('ClusterReachability', () => {
17
16
  let previousRTCPeerConnection;
@@ -49,7 +48,7 @@ describe('ClusterReachability', () => {
49
48
  xtls: ['stun:xtls1.webex.com', 'stun:xtls2.webex.com:443'],
50
49
  });
51
50
 
52
- gatherIceCandidatesSpy = sinon.spy(clusterReachability, 'gatherIceCandidates');
51
+ gatherIceCandidatesSpy = sinon.spy(clusterReachability.reachabilityPeerConnection as any, 'gatherIceCandidates');
53
52
 
54
53
  resetEmittedEvents();
55
54
 
@@ -70,75 +69,341 @@ describe('ClusterReachability', () => {
70
69
  global.RTCPeerConnection = previousRTCPeerConnection;
71
70
  });
72
71
 
73
- it('should create an instance correctly', () => {
72
+ it('should create an instance correctly with provided cluster info', () => {
74
73
  assert.instanceOf(clusterReachability, ClusterReachability);
75
74
  assert.equal(clusterReachability.name, 'testName');
76
75
  assert.equal(clusterReachability.isVideoMesh, false);
77
- assert.equal(clusterReachability.numUdpUrls, 2);
78
- assert.equal(clusterReachability.numTcpUrls, 2);
76
+ assert.instanceOf(clusterReachability.reachabilityPeerConnection, ReachabilityPeerConnection);
79
77
  });
80
78
 
81
- it('should create a peer connection with the right config', () => {
82
- assert.calledOnceWithExactly(global.RTCPeerConnection, {
83
- iceServers: [
84
- {username: '', credential: '', urls: ['stun:udp1']},
85
- {username: '', credential: '', urls: ['stun:udp2']},
86
- {
87
- username: 'webexturnreachuser',
88
- credential: 'webexturnreachpwd',
89
- urls: ['turn:tcp1.webex.com?transport=tcp'],
90
- },
91
- {
92
- username: 'webexturnreachuser',
93
- credential: 'webexturnreachpwd',
94
- urls: ['turn:tcp2.webex.com:5004?transport=tcp'],
95
- },
79
+ it('should initialize reachedSubnets as empty set', () => {
80
+ assert.instanceOf(clusterReachability.reachedSubnets, Set);
81
+ assert.equal(clusterReachability.reachedSubnets.size, 0);
82
+ });
83
+
84
+ it('returns correct results before start() is called', () => {
85
+ assert.deepEqual(clusterReachability.getResult(), {
86
+ udp: {result: 'untested'},
87
+ tcp: {result: 'untested'},
88
+ xtls: {result: 'untested'},
89
+ });
90
+
91
+ // verify that no events were emitted
92
+ assert.deepEqual(emittedEvents[Events.resultReady], []);
93
+ assert.deepEqual(emittedEvents[Events.clientMediaIpsUpdated], []);
94
+ });
95
+
96
+ it('should create separate peer connections when enablePerUdpUrlReachability is true', () => {
97
+ const perUdpClusterReachability = new ClusterReachability(
98
+ 'testName',
99
+ {
100
+ isVideoMesh: false,
101
+ udp: ['stun:udp1', 'stun:udp2'],
102
+ tcp: ['stun:tcp1.webex.com'],
103
+ xtls: ['stun:xtls1.webex.com'],
104
+ },
105
+ true
106
+ );
107
+
108
+ assert.equal((perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp.length, 2);
109
+ assert.instanceOf((perUdpClusterReachability as any).reachabilityPeerConnection, ReachabilityPeerConnection);
110
+ });
111
+
112
+ describe('#event relaying', () => {
113
+ let clock;
114
+
115
+ beforeEach(() => {
116
+ clock = sinon.useFakeTimers();
117
+ });
118
+
119
+ afterEach(() => {
120
+ clock.restore();
121
+ });
122
+
123
+ it('relays resultReady event from ReachabilityPeerConnection', async () => {
124
+ const promise = clusterReachability.start();
125
+
126
+ await testUtils.flushPromises();
127
+
128
+ // Simulate RPC emitting resultReady
129
+ await clock.tickAsync(50);
130
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1'}});
131
+
132
+ // ClusterReachability should relay the event
133
+ assert.equal(emittedEvents[Events.resultReady].length, 1);
134
+ assert.deepEqual(emittedEvents[Events.resultReady][0], {
135
+ protocol: 'udp',
136
+ result: 'reachable',
137
+ latencyInMilliseconds: 50,
138
+ clientMediaIPs: ['somePublicIp1'],
139
+ });
140
+
141
+ clusterReachability.abort();
142
+ await promise;
143
+ });
144
+
145
+ it('relays clientMediaIpsUpdated event from ReachabilityPeerConnection', async () => {
146
+ const promise = clusterReachability.start();
147
+
148
+ await clock.tickAsync(10);
149
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1'}});
150
+
151
+ // First IP found - only resultReady emitted
152
+ assert.equal(emittedEvents[Events.resultReady].length, 1);
153
+ assert.equal(emittedEvents[Events.clientMediaIpsUpdated].length, 0);
154
+ resetEmittedEvents();
155
+
156
+ // New IP found - should emit clientMediaIpsUpdated
157
+ await clock.tickAsync(10);
158
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp2'}});
159
+
160
+ assert.equal(emittedEvents[Events.resultReady].length, 0);
161
+ assert.equal(emittedEvents[Events.clientMediaIpsUpdated].length, 1);
162
+ assert.deepEqual(emittedEvents[Events.clientMediaIpsUpdated][0], {
163
+ protocol: 'udp',
164
+ clientMediaIPs: ['somePublicIp1', 'somePublicIp2'],
165
+ });
166
+
167
+ clusterReachability.abort();
168
+ await promise;
169
+ });
170
+
171
+ it('relays natTypeUpdated event from ReachabilityPeerConnection', async () => {
172
+ const promise = clusterReachability.start();
173
+
174
+ await clock.tickAsync(10);
175
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1', port: 1000, relatedPort: 3478}});
176
+
177
+ // No NAT detection yet (only 1 candidate)
178
+ assert.equal(emittedEvents[Events.natTypeUpdated].length, 0);
179
+
180
+ // Second candidate with same address but different port - indicates symmetric NAT
181
+ await clock.tickAsync(10);
182
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1', port: 2000, relatedPort: 3478}});
183
+
184
+ assert.equal(emittedEvents[Events.natTypeUpdated].length, 1);
185
+ assert.deepEqual(emittedEvents[Events.natTypeUpdated][0], {
186
+ natType: 'symmetric-nat',
187
+ });
188
+
189
+ clusterReachability.abort();
190
+ await promise;
191
+ });
192
+
193
+ it('emits only the first successful UDP result when enablePerUdpUrlReachability is true', async () => {
194
+ const perUdpClusterReachability = new ClusterReachability(
195
+ 'testName',
96
196
  {
97
- username: 'webexturnreachuser',
98
- credential: 'webexturnreachpwd',
99
- urls: ['turns:xtls1.webex.com?transport=tcp'],
197
+ isVideoMesh: false,
198
+ udp: ['stun:udp1', 'stun:udp2'],
199
+ tcp: [],
200
+ xtls: [],
100
201
  },
202
+ true
203
+ );
204
+
205
+ const udpEvents: ResultEventData[] = [];
206
+ perUdpClusterReachability.on(Events.resultReady, (data: ResultEventData) => {
207
+ udpEvents.push(data);
208
+ });
209
+
210
+ const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
211
+ const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
212
+
213
+ udpRpc1.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.resultReady, {
214
+ protocol: 'udp',
215
+ result: 'reachable',
216
+ latencyInMilliseconds: 50,
217
+ clientMediaIPs: ['1.1.1.1'],
218
+ });
219
+
220
+ udpRpc2.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.resultReady, {
221
+ protocol: 'udp',
222
+ result: 'reachable',
223
+ latencyInMilliseconds: 30,
224
+ clientMediaIPs: ['2.2.2.2'],
225
+ });
226
+
227
+ assert.equal(udpEvents.length, 1);
228
+ assert.equal(udpEvents[0].latencyInMilliseconds, 50);
229
+ });
230
+ });
231
+
232
+ describe('#subnet collection', () => {
233
+ let clock;
234
+
235
+ beforeEach(() => {
236
+ clock = sinon.useFakeTimers();
237
+ });
238
+
239
+ afterEach(() => {
240
+ clock.restore();
241
+ });
242
+
243
+ it('collects reached subnets from ReachabilityPeerConnection events', async () => {
244
+ const promise = clusterReachability.start();
245
+
246
+ await clock.tickAsync(10);
247
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:192.168.1.1:5004'}});
248
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:10.0.0.1:5004'}});
249
+ fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'relay.server.ip'}});
250
+
251
+ clusterReachability.abort();
252
+ await promise;
253
+
254
+ assert.equal(clusterReachability.reachedSubnets.size, 3);
255
+ assert.isTrue(clusterReachability.reachedSubnets.has('192.168.1.1'));
256
+ assert.isTrue(clusterReachability.reachedSubnets.has('10.0.0.1'));
257
+ assert.isTrue(clusterReachability.reachedSubnets.has('relay.server.ip'));
258
+ });
259
+
260
+ it('stores only unique subnet addresses', async () => {
261
+ const promise = clusterReachability.start();
262
+
263
+ await clock.tickAsync(10);
264
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:192.168.1.1:5004'}});
265
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:192.168.1.1:9000'}});
266
+ fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: '192.168.1.1'}});
267
+
268
+ clusterReachability.abort();
269
+ await promise;
270
+
271
+ // Should have only 1 unique subnet
272
+ assert.equal(clusterReachability.reachedSubnets.size, 1);
273
+ assert.isTrue(clusterReachability.reachedSubnets.has('192.168.1.1'));
274
+ });
275
+
276
+ it('accumulates subnets from multiple candidates', async () => {
277
+ const promise = clusterReachability.start();
278
+
279
+ await clock.tickAsync(10);
280
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:192.168.1.1:5004'}});
281
+
282
+ await clock.tickAsync(10);
283
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', url: 'stun:10.0.0.1:5004'}});
284
+
285
+ await clock.tickAsync(10);
286
+ fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: '172.16.0.1'}});
287
+
288
+ clusterReachability.abort();
289
+ await promise;
290
+
291
+ assert.equal(clusterReachability.reachedSubnets.size, 3);
292
+ assert.deepEqual(Array.from(clusterReachability.reachedSubnets), ['192.168.1.1', '10.0.0.1', '172.16.0.1']);
293
+ });
294
+
295
+ it('collects reached subnets from all peer connections when enablePerUdpUrlReachability is true', async () => {
296
+ const perUdpClusterReachability = new ClusterReachability(
297
+ 'testName',
101
298
  {
102
- username: 'webexturnreachuser',
103
- credential: 'webexturnreachpwd',
104
- urls: ['turns:xtls2.webex.com:443?transport=tcp'],
299
+ isVideoMesh: false,
300
+ udp: ['stun:udp1', 'stun:udp2'],
301
+ tcp: ['stun:tcp1.webex.com'],
302
+ xtls: [],
105
303
  },
106
- ],
107
- iceCandidatePoolSize: 0,
108
- iceTransportPolicy: 'all',
304
+ true
305
+ );
306
+
307
+ const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
308
+ const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
309
+ const tcpTlsRpc = (perUdpClusterReachability as any).reachabilityPeerConnection;
310
+
311
+ udpRpc1.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
312
+ subnets: ['192.168.1.1'],
313
+ });
314
+ udpRpc2.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
315
+ subnets: ['10.0.0.1'],
316
+ });
317
+ tcpTlsRpc.emit({file: 'test', function: 'test'}, ReachabilityPeerConnectionEvents.reachedSubnets, {
318
+ subnets: ['172.16.0.1'],
319
+ });
320
+
321
+ assert.equal(perUdpClusterReachability.reachedSubnets.size, 3);
322
+ assert.isTrue(perUdpClusterReachability.reachedSubnets.has('192.168.1.1'));
323
+ assert.isTrue(perUdpClusterReachability.reachedSubnets.has('10.0.0.1'));
324
+ assert.isTrue(perUdpClusterReachability.reachedSubnets.has('172.16.0.1'));
109
325
  });
110
326
  });
111
327
 
112
- it('should create a peer connection with the right config even if lists of urls are empty', () => {
113
- (global.RTCPeerConnection as any).resetHistory();
328
+ describe('#delegation', () => {
329
+ it('delegates getResult() to ReachabilityPeerConnection', () => {
330
+ const rpcGetResultStub = sinon.stub(clusterReachability.reachabilityPeerConnection, 'getResult').returns({
331
+ udp: {result: 'reachable', latencyInMilliseconds: 42},
332
+ tcp: {result: 'unreachable'},
333
+ xtls: {result: 'untested'},
334
+ });
114
335
 
115
- clusterReachability = new ClusterReachability('testName', {
116
- isVideoMesh: false,
117
- udp: [],
118
- tcp: [],
119
- xtls: [],
336
+ const result = clusterReachability.getResult();
337
+
338
+ assert.calledOnce(rpcGetResultStub);
339
+ assert.equal(result.udp.result, 'reachable');
340
+ assert.equal(result.udp.latencyInMilliseconds, 42);
120
341
  });
121
342
 
122
- assert.calledOnceWithExactly(global.RTCPeerConnection, {
123
- iceServers: [],
124
- iceCandidatePoolSize: 0,
125
- iceTransportPolicy: 'all',
343
+ it('delegates abort() to ReachabilityPeerConnection', () => {
344
+ const rpcAbortStub = sinon.stub(clusterReachability.reachabilityPeerConnection, 'abort');
345
+
346
+ clusterReachability.abort();
347
+
348
+ assert.calledOnce(rpcAbortStub);
126
349
  });
127
- });
128
350
 
129
- it('returns correct results before start() is called', () => {
130
- assert.deepEqual(clusterReachability.getResult(), {
131
- udp: {result: 'untested'},
132
- tcp: {result: 'untested'},
133
- xtls: {result: 'untested'},
351
+ it('delegates start() to ReachabilityPeerConnection and returns result', async () => {
352
+ const expectedResult = {
353
+ udp: {result: 'reachable'},
354
+ tcp: {result: 'unreachable'},
355
+ xtls: {result: 'unreachable'},
356
+ };
357
+
358
+ const rpcStartStub = sinon.stub(clusterReachability.reachabilityPeerConnection, 'start').resolves();
359
+ const rpcGetResultStub = sinon.stub(clusterReachability.reachabilityPeerConnection, 'getResult').returns(expectedResult);
360
+
361
+ const result = await clusterReachability.start();
362
+
363
+ assert.calledOnce(rpcStartStub);
364
+ assert.calledOnce(rpcGetResultStub);
365
+ assert.deepEqual(result, expectedResult);
134
366
  });
135
367
 
136
- // verify that no events were emitted
137
- assert.deepEqual(emittedEvents[Events.resultReady], []);
138
- assert.deepEqual(emittedEvents[Events.clientMediaIpsUpdated], []);
368
+ it('delegates start() and abort() to all peer connections when enablePerUdpUrlReachability is true', async () => {
369
+ const perUdpClusterReachability = new ClusterReachability(
370
+ 'testName',
371
+ {
372
+ isVideoMesh: false,
373
+ udp: ['stun:udp1', 'stun:udp2'],
374
+ tcp: ['stun:tcp1.webex.com'],
375
+ xtls: [],
376
+ },
377
+ true
378
+ );
379
+
380
+ const udpRpc1 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[0];
381
+ const udpRpc2 = (perUdpClusterReachability as any).reachabilityPeerConnectionsForUdp[1];
382
+ const tcpTlsRpc = (perUdpClusterReachability as any).reachabilityPeerConnection;
383
+
384
+ const startStub1 = sinon.stub(udpRpc1, 'start').resolves({udp: {result: 'reachable'}});
385
+ const startStub2 = sinon.stub(udpRpc2, 'start').resolves({udp: {result: 'unreachable'}});
386
+ const startStubTcp = sinon.stub(tcpTlsRpc, 'start').resolves({tcp: {result: 'reachable'}});
387
+
388
+ const abortStub1 = sinon.stub(udpRpc1, 'abort');
389
+ const abortStub2 = sinon.stub(udpRpc2, 'abort');
390
+ const abortStubTcp = sinon.stub(tcpTlsRpc, 'abort');
391
+
392
+ await perUdpClusterReachability.start();
393
+
394
+ assert.calledOnce(startStub1);
395
+ assert.calledOnce(startStub2);
396
+ assert.calledOnce(startStubTcp);
397
+
398
+ perUdpClusterReachability.abort();
399
+
400
+ assert.calledOnce(abortStub1);
401
+ assert.calledOnce(abortStub2);
402
+ assert.calledOnce(abortStubTcp);
403
+ });
139
404
  });
140
405
 
141
- describe('#start', () => {
406
+ describe('#WebRTC peer connection setup', () => {
142
407
  let clock;
143
408
 
144
409
  beforeEach(() => {
@@ -149,6 +414,37 @@ describe('ClusterReachability', () => {
149
414
  clock.restore();
150
415
  });
151
416
 
417
+ it('should create a peer connection with the right config', () => {
418
+ assert.calledOnceWithExactly(global.RTCPeerConnection, {
419
+ iceServers: [
420
+ {username: '', credential: '', urls: ['stun:udp1']},
421
+ {username: '', credential: '', urls: ['stun:udp2']},
422
+ {
423
+ username: 'webexturnreachuser',
424
+ credential: 'webexturnreachpwd',
425
+ urls: ['turn:tcp1.webex.com?transport=tcp'],
426
+ },
427
+ {
428
+ username: 'webexturnreachuser',
429
+ credential: 'webexturnreachpwd',
430
+ urls: ['turn:tcp2.webex.com:5004?transport=tcp'],
431
+ },
432
+ {
433
+ username: 'webexturnreachuser',
434
+ credential: 'webexturnreachpwd',
435
+ urls: ['turns:xtls1.webex.com?transport=tcp'],
436
+ },
437
+ {
438
+ username: 'webexturnreachuser',
439
+ credential: 'webexturnreachpwd',
440
+ urls: ['turns:xtls2.webex.com:443?transport=tcp'],
441
+ },
442
+ ],
443
+ iceCandidatePoolSize: 0,
444
+ iceTransportPolicy: 'all',
445
+ });
446
+ });
447
+
152
448
  it('should initiate the ICE gathering process', async () => {
153
449
  const promise = clusterReachability.start();
154
450
 
@@ -174,6 +470,40 @@ describe('ClusterReachability', () => {
174
470
  assert.deepEqual(emittedEvents[Events.clientMediaIpsUpdated], []);
175
471
  });
176
472
 
473
+ it('resolves when ICE gathering is completed', async () => {
474
+ const promise = clusterReachability.start();
475
+
476
+ await testUtils.flushPromises();
477
+
478
+ fakePeerConnection.iceGatheringState = 'complete';
479
+ fakePeerConnection.onicegatheringstatechange();
480
+ await promise;
481
+
482
+ assert.deepEqual(clusterReachability.getResult(), {
483
+ udp: {result: 'unreachable'},
484
+ tcp: {result: 'unreachable'},
485
+ xtls: {result: 'unreachable'},
486
+ });
487
+ });
488
+
489
+ it('resolves with the right result when ICE gathering is completed', async () => {
490
+ const promise = clusterReachability.start();
491
+
492
+ // send 1 candidate
493
+ await clock.tickAsync(30);
494
+ fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1'}});
495
+
496
+ fakePeerConnection.iceGatheringState = 'complete';
497
+ fakePeerConnection.onicegatheringstatechange();
498
+ await promise;
499
+
500
+ assert.deepEqual(clusterReachability.getResult(), {
501
+ udp: {result: 'reachable', latencyInMilliseconds: 30, clientMediaIPs: ['somePublicIp1']},
502
+ tcp: {result: 'unreachable'},
503
+ xtls: {result: 'unreachable'},
504
+ });
505
+ });
506
+
177
507
  it('resolves and returns correct results when aborted before it gets any candidates', async () => {
178
508
  const promise = clusterReachability.start();
179
509
 
@@ -216,39 +546,17 @@ describe('ClusterReachability', () => {
216
546
  xtls: {result: 'unreachable'},
217
547
  });
218
548
  });
549
+ });
219
550
 
220
- it('resolves when ICE gathering is completed', async () => {
221
- const promise = clusterReachability.start();
222
-
223
- await testUtils.flushPromises();
224
-
225
- fakePeerConnection.iceGatheringState = 'complete';
226
- fakePeerConnection.onicegatheringstatechange();
227
- await promise;
551
+ describe('#latency and candidate handling', () => {
552
+ let clock;
228
553
 
229
- assert.deepEqual(clusterReachability.getResult(), {
230
- udp: {result: 'unreachable'},
231
- tcp: {result: 'unreachable'},
232
- xtls: {result: 'unreachable'},
233
- });
554
+ beforeEach(() => {
555
+ clock = sinon.useFakeTimers();
234
556
  });
235
557
 
236
- it('resolves with the right result when ICE gathering is completed', async () => {
237
- const promise = clusterReachability.start();
238
-
239
- // send 1 candidate
240
- await clock.tickAsync(30);
241
- fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1'}});
242
-
243
- fakePeerConnection.iceGatheringState = 'complete';
244
- fakePeerConnection.onicegatheringstatechange();
245
- await promise;
246
-
247
- assert.deepEqual(clusterReachability.getResult(), {
248
- udp: {result: 'reachable', latencyInMilliseconds: 30, clientMediaIPs: ['somePublicIp1']},
249
- tcp: {result: 'unreachable'},
250
- xtls: {result: 'unreachable'},
251
- });
558
+ afterEach(() => {
559
+ clock.restore();
252
560
  });
253
561
 
254
562
  it('should store latency only for the first srflx candidate, but IPs from all of them', async () => {
@@ -257,17 +565,16 @@ describe('ClusterReachability', () => {
257
565
  await clock.tickAsync(10);
258
566
  fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp1'}});
259
567
 
260
- // generate more candidates
261
- await clock.tickAsync(10);
568
+ await clock.tickAsync(50); // total elapsed time: 60
262
569
  fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp2'}});
263
570
 
264
- await clock.tickAsync(10);
571
+ await clock.tickAsync(10); // total elapsed time: 70
265
572
  fakePeerConnection.onicecandidate({candidate: {type: 'srflx', address: 'somePublicIp3'}});
266
573
 
267
574
  clusterReachability.abort();
268
575
  await promise;
269
576
 
270
- // latency should be from only the first candidates, but the clientMediaIps should be from all UDP candidates (not TCP)
577
+ // latency should be from only the first candidates, but the clientMediaIps should be from all UDP candidates
271
578
  assert.deepEqual(clusterReachability.getResult(), {
272
579
  udp: {
273
580
  result: 'reachable',
@@ -283,19 +590,18 @@ describe('ClusterReachability', () => {
283
590
  const promise = clusterReachability.start();
284
591
 
285
592
  await clock.tickAsync(10);
286
- fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'someTurnRelayIp1'}});
287
-
288
- // generate more candidates
289
- await clock.tickAsync(10);
290
- fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'someTurnRelayIp2'}});
593
+ fakePeerConnection.onicecandidate({
594
+ candidate: {type: 'relay', address: 'relayIp1', port: 3478},
595
+ });
291
596
 
292
- await clock.tickAsync(10);
293
- fakePeerConnection.onicecandidate({candidate: {type: 'relay', address: 'someTurnRelayIp3'}});
597
+ await clock.tickAsync(50); // total elapsed time: 60
598
+ fakePeerConnection.onicecandidate({
599
+ candidate: {type: 'relay', address: 'relayIp2', port: 3478},
600
+ });
294
601
 
295
602
  clusterReachability.abort();
296
603
  await promise;
297
604
 
298
- // latency should be from only the first candidates, but the clientMediaIps should be from only from UDP candidates
299
605
  assert.deepEqual(clusterReachability.getResult(), {
300
606
  udp: {result: 'unreachable'},
301
607
  tcp: {result: 'reachable', latencyInMilliseconds: 10},
@@ -308,24 +614,17 @@ describe('ClusterReachability', () => {
308
614
 
309
615
  await clock.tickAsync(10);
310
616
  fakePeerConnection.onicecandidate({
311
- candidate: {type: 'relay', address: 'someTurnRelayIp1', port: 443},
617
+ candidate: {type: 'relay', address: 'relayIp1', port: 443},
312
618
  });
313
619
 
314
- // generate more candidates
315
- await clock.tickAsync(10);
620
+ await clock.tickAsync(50); // total elapsed time: 60
316
621
  fakePeerConnection.onicecandidate({
317
- candidate: {type: 'relay', address: 'someTurnRelayIp2', port: 443},
318
- });
319
-
320
- await clock.tickAsync(10);
321
- fakePeerConnection.onicecandidate({
322
- candidate: {type: 'relay', address: 'someTurnRelayIp3', port: 443},
622
+ candidate: {type: 'relay', address: 'relayIp2', port: 443},
323
623
  });
324
624
 
325
625
  clusterReachability.abort();
326
626
  await promise;
327
627
 
328
- // latency should be from only the first candidates, but the clientMediaIps should be from only from UDP candidates
329
628
  assert.deepEqual(clusterReachability.getResult(), {
330
629
  udp: {result: 'unreachable'},
331
630
  tcp: {result: 'unreachable'},
@@ -440,37 +739,5 @@ describe('ClusterReachability', () => {
440
739
  xtls: {result: 'reachable', latencyInMilliseconds: 20},
441
740
  });
442
741
  });
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
- });
475
742
  });
476
- });
743
+ });
@@ -1693,7 +1693,7 @@ describe('gatherReachability', () => {
1693
1693
  udp: ['testUDP1', 'testUDP2'],
1694
1694
  tcp: [], // empty list because TCP is disabled in config
1695
1695
  xtls: ['testXTLS1', 'testXTLS2'],
1696
- });
1696
+ }, undefined);
1697
1697
  });
1698
1698
 
1699
1699
  it('does not do TLS reachability if it is disabled in config', async () => {
@@ -1728,7 +1728,7 @@ describe('gatherReachability', () => {
1728
1728
  udp: ['testUDP1', 'testUDP2'],
1729
1729
  tcp: ['testTCP1', 'testTCP2'],
1730
1730
  xtls: [], // empty list because TLS is disabled in config
1731
- });
1731
+ }, undefined);
1732
1732
  });
1733
1733
 
1734
1734
  it('does not do TCP or TLS reachability if it is disabled in config', async () => {
@@ -1763,7 +1763,7 @@ describe('gatherReachability', () => {
1763
1763
  udp: ['testUDP1', 'testUDP2'],
1764
1764
  tcp: [], // empty list because TCP is disabled in config
1765
1765
  xtls: [], // empty list because TLS is disabled in config
1766
- });
1766
+ }, undefined);
1767
1767
  });
1768
1768
 
1769
1769
  it('retry of getClusters is succesfull', async () => {