@webex/plugin-meetings 2.33.2 → 2.35.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.
@@ -0,0 +1,305 @@
1
+ import {assert} from '@webex/test-helper-chai';
2
+ import sinon from 'sinon';
3
+ import MediaProperties from '@webex/plugin-meetings/src/media/properties';
4
+ import MediaUtil from '@webex/plugin-meetings/src/media/util';
5
+ import testUtils from '../../../utils/testUtils';
6
+ import {PC_BAIL_TIMEOUT} from '@webex/plugin-meetings/src/constants';
7
+ import {Defer} from '@webex/common';
8
+
9
+ describe('MediaProperties', () => {
10
+ let mediaProperties;
11
+ let mockPc;
12
+ let clock;
13
+
14
+ beforeEach(() => {
15
+ clock = sinon.useFakeTimers();
16
+
17
+ mockPc = {
18
+ getStats: sinon.stub().resolves([]),
19
+ addEventListener: sinon.stub(),
20
+ removeEventListener: sinon.stub(),
21
+ iceConnectionState: 'connected',
22
+ };
23
+
24
+ sinon.stub(MediaUtil, 'createPeerConnection').returns(mockPc);
25
+
26
+ mediaProperties = new MediaProperties();
27
+ });
28
+
29
+ afterEach(() => {
30
+ clock.restore();
31
+ sinon.restore();
32
+ });
33
+ describe('waitForIceConnectedState', () => {
34
+ it('resolves immediately if ice state is connected', async () => {
35
+ mockPc.iceConnectionState = 'connected';
36
+
37
+ await mediaProperties.waitForIceConnectedState();
38
+ });
39
+ it('resolves immediately if ice state is completed', async () => {
40
+ mockPc.iceConnectionState = 'completed';
41
+
42
+ await mediaProperties.waitForIceConnectedState();
43
+ });
44
+ it('rejects after timeout if ice state does not reach connected/completed', async () => {
45
+ mockPc.iceConnectionState = 'connecting';
46
+
47
+ let promiseResolved = false;
48
+ let promiseRejected = false;
49
+
50
+ mediaProperties
51
+ .waitForIceConnectedState()
52
+ .then(() => {
53
+ promiseResolved = true;
54
+ })
55
+ .catch(() => {
56
+ promiseRejected = true;
57
+ });
58
+
59
+ assert.equal(promiseResolved, false);
60
+ assert.equal(promiseRejected, false);
61
+
62
+ await clock.tickAsync(PC_BAIL_TIMEOUT);
63
+ await testUtils.flushPromises();
64
+
65
+ assert.equal(promiseResolved, false);
66
+ assert.equal(promiseRejected, true);
67
+
68
+ // check that listener was registered and removed
69
+ assert.calledOnce(mockPc.addEventListener);
70
+ assert.equal(mockPc.addEventListener.getCall(0).args[0], 'iceconnectionstatechange');
71
+ const listener = mockPc.addEventListener.getCall(0).args[1];
72
+
73
+ assert.calledOnce(mockPc.removeEventListener);
74
+ assert.calledWith(mockPc.removeEventListener, 'iceconnectionstatechange', listener);
75
+ });
76
+
77
+ ['connected', 'completed'].forEach((successIceState) =>
78
+ it(`resolves when ice state reaches ${successIceState}`, async () => {
79
+ mockPc.iceConnectionState = 'connecting';
80
+
81
+ const clearTimeoutSpy = sinon.spy(clock, 'clearTimeout');
82
+
83
+ let promiseResolved = false;
84
+ let promiseRejected = false;
85
+
86
+ mediaProperties
87
+ .waitForIceConnectedState()
88
+ .then(() => {
89
+ promiseResolved = true;
90
+ })
91
+ .catch(() => {
92
+ promiseRejected = true;
93
+ });
94
+
95
+ assert.equal(promiseResolved, false);
96
+ assert.equal(promiseRejected, false);
97
+
98
+ // check the right listener was registered
99
+ assert.calledOnce(mockPc.addEventListener);
100
+ assert.equal(mockPc.addEventListener.getCall(0).args[0], 'iceconnectionstatechange');
101
+ const listener = mockPc.addEventListener.getCall(0).args[1];
102
+
103
+ // call the listener and pretend we are now connected
104
+ mockPc.iceConnectionState = successIceState;
105
+ listener();
106
+ await testUtils.flushPromises();
107
+
108
+ assert.equal(promiseResolved, true);
109
+ assert.equal(promiseRejected, false);
110
+
111
+ // check that listener was removed
112
+ assert.calledOnce(mockPc.removeEventListener);
113
+ assert.calledWith(mockPc.removeEventListener, 'iceconnectionstatechange', listener);
114
+
115
+ assert.calledOnce(clearTimeoutSpy);
116
+ })
117
+ );
118
+ });
119
+
120
+ describe('getCurrentConnectionType', () => {
121
+ it('calls waitForIceConnectedState', async () => {
122
+ const spy = sinon.stub(mediaProperties, 'waitForIceConnectedState');
123
+
124
+ await mediaProperties.getCurrentConnectionType();
125
+
126
+ assert.calledOnce(spy);
127
+ });
128
+ it('calls getStats() only after waitForIceConnectedState resolves', async () => {
129
+ const waitForIceConnectedStateResult = new Defer();
130
+
131
+ const waitForIceConnectedStateStub = sinon
132
+ .stub(mediaProperties, 'waitForIceConnectedState')
133
+ .returns(waitForIceConnectedStateResult.promise);
134
+
135
+ const result = mediaProperties.getCurrentConnectionType();
136
+
137
+ await testUtils.flushPromises();
138
+
139
+ assert.called(waitForIceConnectedStateStub);
140
+ assert.notCalled(mockPc.getStats);
141
+
142
+ waitForIceConnectedStateResult.resolve();
143
+ await testUtils.flushPromises();
144
+
145
+ assert.called(mockPc.getStats);
146
+ await result;
147
+ });
148
+ it('rejects if waitForIceConnectedState rejects', async () => {
149
+ const waitForIceConnectedStateResult = new Defer();
150
+
151
+ const waitForIceConnectedStateStub = sinon
152
+ .stub(mediaProperties, 'waitForIceConnectedState')
153
+ .returns(waitForIceConnectedStateResult.promise);
154
+
155
+ const result = mediaProperties.getCurrentConnectionType();
156
+
157
+ await testUtils.flushPromises();
158
+
159
+ assert.called(waitForIceConnectedStateStub);
160
+
161
+ waitForIceConnectedStateResult.reject(new Error('fake error'));
162
+ await testUtils.flushPromises();
163
+
164
+ assert.notCalled(mockPc.getStats);
165
+
166
+ await assert.isRejected(result);
167
+ });
168
+ it('returns "unknown" if getStats() fails', async () => {
169
+ mockPc.getStats.rejects(new Error());
170
+
171
+ const connectionType = await mediaProperties.getCurrentConnectionType();
172
+ assert.equal(connectionType, 'unknown');
173
+ });
174
+
175
+ it('returns "unknown" if getStats() returns no candidate pairs', async () => {
176
+ mockPc.getStats.resolves([{type: 'something', id: '1234'}]);
177
+
178
+ const connectionType = await mediaProperties.getCurrentConnectionType();
179
+ assert.equal(connectionType, 'unknown');
180
+ });
181
+
182
+ it('returns "unknown" if getStats() returns no successful candidate pair', async () => {
183
+ mockPc.getStats.resolves([{type: 'candidate-pair', id: '1234', state: 'inprogress'}]);
184
+
185
+ const connectionType = await mediaProperties.getCurrentConnectionType();
186
+ assert.equal(connectionType, 'unknown');
187
+ });
188
+
189
+ it('returns "unknown" if getStats() returns a successful candidate pair but local candidate is missing', async () => {
190
+ mockPc.getStats.resolves([
191
+ {type: 'candidate-pair', id: '1234', state: 'succeeded', localCandidateId: 'wrong id'},
192
+ ]);
193
+
194
+ const connectionType = await mediaProperties.getCurrentConnectionType();
195
+ assert.equal(connectionType, 'unknown');
196
+ });
197
+
198
+ it('returns "UDP" if getStats() returns a successful candidate pair with udp local candidate', async () => {
199
+ mockPc.getStats.resolves([
200
+ {
201
+ type: 'candidate-pair',
202
+ id: 'some candidate pair id',
203
+ state: 'succeeded',
204
+ localCandidateId: 'local candidate id',
205
+ },
206
+ {type: 'local-candidate', id: 'some other candidate id', protocol: 'tcp'},
207
+ {type: 'local-candidate', id: 'local candidate id', protocol: 'udp'},
208
+ ]);
209
+
210
+ const connectionType = await mediaProperties.getCurrentConnectionType();
211
+ assert.equal(connectionType, 'UDP');
212
+ });
213
+
214
+ it('returns "TCP" if getStats() returns a successful candidate pair with tcp local candidate', async () => {
215
+ mockPc.getStats.resolves([
216
+ {
217
+ type: 'candidate-pair',
218
+ id: 'some candidate pair id',
219
+ state: 'succeeded',
220
+ localCandidateId: 'some candidate id',
221
+ },
222
+ {type: 'local-candidate', id: 'some other candidate id', protocol: 'udp'},
223
+ {type: 'local-candidate', id: 'some candidate id', protocol: 'tcp'},
224
+ ]);
225
+
226
+ const connectionType = await mediaProperties.getCurrentConnectionType();
227
+ assert.equal(connectionType, 'TCP');
228
+ });
229
+
230
+ [
231
+ {relayProtocol: 'tls', expectedConnectionType: 'TURN-TLS'},
232
+ {relayProtocol: 'tcp', expectedConnectionType: 'TURN-TCP'},
233
+ {relayProtocol: 'udp', expectedConnectionType: 'TURN-UDP'},
234
+ ].forEach(({relayProtocol, expectedConnectionType}) =>
235
+ it(`returns "${expectedConnectionType}" if getStats() returns a successful candidate pair with a local candidate with relayProtocol=${relayProtocol}`, async () => {
236
+ mockPc.getStats.resolves([
237
+ {
238
+ type: 'candidate-pair',
239
+ id: 'some candidate pair id',
240
+ state: 'succeeded',
241
+ localCandidateId: 'selected candidate id',
242
+ },
243
+ {
244
+ type: 'candidate-pair',
245
+ id: 'some other candidate pair id',
246
+ state: 'failed',
247
+ localCandidateId: 'some other candidate id 1',
248
+ },
249
+ {type: 'local-candidate', id: 'some other candidate id 1', protocol: 'udp'},
250
+ {type: 'local-candidate', id: 'some other candidate id 2', protocol: 'tcp'},
251
+ {
252
+ type: 'local-candidate',
253
+ id: 'selected candidate id',
254
+ protocol: 'udp',
255
+ relayProtocol,
256
+ },
257
+ ]);
258
+
259
+ const connectionType = await mediaProperties.getCurrentConnectionType();
260
+ assert.equal(connectionType, expectedConnectionType);
261
+ })
262
+ );
263
+
264
+ it('returns connection type of the first successful candidate pair', async () => {
265
+ // in real life this will never happen and all active candidate pairs will have same transport,
266
+ // but here we're simulating a situation where they have different transports and just checking
267
+ // that the code still works and just returns the first one
268
+ mockPc.getStats.resolves([
269
+ {
270
+ type: 'inbound-rtp',
271
+ id: 'whatever',
272
+ },
273
+ {
274
+ type: 'candidate-pair',
275
+ id: 'some candidate pair id',
276
+ state: 'succeeded',
277
+ localCandidateId: '1st selected candidate id',
278
+ },
279
+ {
280
+ type: 'candidate-pair',
281
+ id: 'some other candidate pair id',
282
+ state: 'succeeded',
283
+ localCandidateId: '2nd selected candidate id',
284
+ },
285
+ {type: 'local-candidate', id: 'some other candidate id 1', protocol: 'udp'},
286
+ {type: 'local-candidate', id: 'some other candidate id 2', protocol: 'tcp'},
287
+ {
288
+ type: 'local-candidate',
289
+ id: '1st selected candidate id',
290
+ protocol: 'udp',
291
+ relayProtocol: 'tls',
292
+ },
293
+ {
294
+ type: 'local-candidate',
295
+ id: '2nd selected candidate id',
296
+ protocol: 'udp',
297
+ relayProtocol: 'tcp',
298
+ },
299
+ ]);
300
+
301
+ const connectionType = await mediaProperties.getCurrentConnectionType();
302
+ assert.equal(connectionType, 'TURN-TLS');
303
+ });
304
+ });
305
+ });
@@ -42,6 +42,7 @@ import BrowserDetection from '@webex/plugin-meetings/src/common/browser-detectio
42
42
  import Metrics from '@webex/plugin-meetings/src/metrics';
43
43
  import {trigger, eventType} from '@webex/plugin-meetings/src/metrics/config';
44
44
  import BEHAVIORAL_METRICS from '@webex/plugin-meetings/src/metrics/constants';
45
+ import {IceGatheringFailed} from '@webex/plugin-meetings/src/common/errors/webex-errors';
45
46
 
46
47
  import locus from '../fixture/locus';
47
48
  import {
@@ -910,6 +911,8 @@ describe('plugin-meetings', () => {
910
911
 
911
912
  beforeEach(() => {
912
913
  meeting.mediaProperties.setMediaDirection = sinon.stub().returns(true);
914
+ meeting.mediaProperties.waitForIceConnectedState = sinon.stub().resolves();
915
+ meeting.mediaProperties.getCurrentConnectionType = sinon.stub().resolves('udp');
913
916
  meeting.audio = muteStateStub;
914
917
  meeting.video = muteStateStub;
915
918
  Media.attachMedia = sinon.stub().returns(Promise.resolve([test1, test2]));
@@ -920,7 +923,7 @@ describe('plugin-meetings', () => {
920
923
  meeting.mediaProperties.peerConnection.connectionState = CONSTANTS.CONNECTION_STATE.CONNECTED;
921
924
  resolve();
922
925
  }));
923
- meeting.roap.doTurnDiscovery = sinon.stub().resolves();
926
+ meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: {}, turnDiscoverySkippedReason: undefined});
924
927
  PeerConnectionManager.setContentSlides = sinon.stub().returns(true);
925
928
  });
926
929
 
@@ -961,6 +964,39 @@ describe('plugin-meetings', () => {
961
964
  await meeting.addMedia().catch((err) => {
962
965
  assert.exists(err);
963
966
  assert.isNull(meeting.statsAnalyzer);
967
+ assert(Metrics.sendBehavioralMetric.calledOnce);
968
+ assert.calledWith(
969
+ Metrics.sendBehavioralMetric,
970
+ BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
971
+ correlation_id: meeting.correlationId,
972
+ locus_id: meeting.locusUrl.split('/').pop(),
973
+ reason: err.message,
974
+ stack: err.stack,
975
+ code: err.code,
976
+ turnDiscoverySkippedReason: undefined,
977
+ turnServerUsed: true
978
+ }
979
+ );
980
+ });
981
+ });
982
+
983
+ it('checks metrics called with skipped reason config', async () => {
984
+ meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: 'config'});
985
+ meeting.meetingState = 'ACTIVE';
986
+ await meeting.addMedia().catch((err) => {
987
+ assert.exists(err);
988
+ assert(Metrics.sendBehavioralMetric.calledOnce);
989
+ assert.calledWith(
990
+ Metrics.sendBehavioralMetric,
991
+ BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
992
+ correlation_id: meeting.correlationId,
993
+ locus_id: meeting.locusUrl.split('/').pop(),
994
+ reason: err.message,
995
+ stack: err.stack,
996
+ turnDiscoverySkippedReason: 'config',
997
+ turnServerUsed: false
998
+ }
999
+ );
964
1000
  });
965
1001
  });
966
1002
 
@@ -970,6 +1006,18 @@ describe('plugin-meetings', () => {
970
1006
  await meeting.addMedia().catch((err) => {
971
1007
  assert.exists(err);
972
1008
  assert.isNull(meeting.mediaProperties.peerConnection);
1009
+ assert(Metrics.sendBehavioralMetric.calledOnce);
1010
+ assert.calledWith(
1011
+ Metrics.sendBehavioralMetric,
1012
+ BEHAVIORAL_METRICS.ADD_MEDIA_FAILURE, {
1013
+ correlation_id: meeting.correlationId,
1014
+ locus_id: meeting.locusUrl.split('/').pop(),
1015
+ reason: err.message,
1016
+ stack: err.stack,
1017
+ turnDiscoverySkippedReason: undefined,
1018
+ turnServerUsed: true
1019
+ }
1020
+ );
973
1021
  });
974
1022
  });
975
1023
 
@@ -1035,6 +1083,8 @@ describe('plugin-meetings', () => {
1035
1083
  });
1036
1084
 
1037
1085
  it('should attach the media and return promise', async () => {
1086
+ meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
1087
+
1038
1088
  meeting.meetingState = 'ACTIVE';
1039
1089
  MediaUtil.createPeerConnection.resetHistory();
1040
1090
  const media = meeting.addMedia({
@@ -1065,10 +1115,14 @@ describe('plugin-meetings', () => {
1065
1115
  meeting.meetingState = 'ACTIVE';
1066
1116
  MediaUtil.createPeerConnection.resetHistory();
1067
1117
 
1118
+
1068
1119
  meeting.roap.doTurnDiscovery = sinon.stub().resolves({
1069
- url: FAKE_TURN_URL,
1070
- username: FAKE_TURN_USER,
1071
- password: FAKE_TURN_PASSWORD
1120
+ turnServerInfo: {
1121
+ url: FAKE_TURN_URL,
1122
+ username: FAKE_TURN_USER,
1123
+ password: FAKE_TURN_PASSWORD
1124
+ },
1125
+ turnServerSkippedReason: undefined
1072
1126
  });
1073
1127
  const media = meeting.addMedia({
1074
1128
  mediaSettings: {}
@@ -1087,6 +1141,7 @@ describe('plugin-meetings', () => {
1087
1141
  });
1088
1142
 
1089
1143
  it('should attach the media and return promise', async () => {
1144
+ meeting.roap.doTurnDiscovery = sinon.stub().resolves({turnServerInfo: undefined, turnDiscoverySkippedReason: undefined});
1090
1145
  meeting.meetingState = 'ACTIVE';
1091
1146
  meeting.mediaProperties.peerConnection.connectionState = 'DISCONNECTED';
1092
1147
  const media = meeting.addMedia({
@@ -1099,6 +1154,41 @@ describe('plugin-meetings', () => {
1099
1154
  });
1100
1155
  });
1101
1156
 
1157
+ it('should reject if waitForIceConnectedState() rejects', async () => {
1158
+ meeting.meetingState = 'ACTIVE';
1159
+ meeting.mediaProperties.waitForIceConnectedState.rejects(new Error('fake error'));
1160
+
1161
+ let errorThrown = false;
1162
+
1163
+ await meeting.addMedia({
1164
+ mediaSettings: {}
1165
+ })
1166
+ .catch((error) => {
1167
+ assert.equal(error.code, IceGatheringFailed.CODE);
1168
+ errorThrown = true;
1169
+ });
1170
+
1171
+ assert.isTrue(errorThrown);
1172
+ });
1173
+
1174
+ it('should send ADD_MEDIA_SUCCESS metrics', async () => {
1175
+ meeting.meetingState = 'ACTIVE';
1176
+ await meeting.addMedia({
1177
+ mediaSettings: {}
1178
+ });
1179
+
1180
+ assert.calledOnce(Metrics.sendBehavioralMetric);
1181
+ assert.calledWith(
1182
+ Metrics.sendBehavioralMetric,
1183
+ BEHAVIORAL_METRICS.ADD_MEDIA_SUCCESS,
1184
+ {
1185
+ correlation_id: meeting.correlationId,
1186
+ locus_id: meeting.locusUrl.split('/').pop(),
1187
+ connectionType: 'udp'
1188
+ }
1189
+ );
1190
+ });
1191
+
1102
1192
  describe('handles StatsAnalyzer events', () => {
1103
1193
  let prevConfigValue;
1104
1194
  let statsAnalyzerStub;
@@ -117,14 +117,15 @@ describe('TurnDiscovery', () => {
117
117
  // check that we've sent OK
118
118
  await checkRoapMessageSent('OK', 0);
119
119
 
120
- const turnInfo = await result;
120
+ const {turnServerInfo, turnDiscoverySkippedReason} = await result;
121
121
 
122
- assert.deepEqual(turnInfo, {
122
+ assert.deepEqual(turnServerInfo, {
123
123
  url: FAKE_TURN_URL,
124
124
  username: FAKE_TURN_USERNAME,
125
125
  password: FAKE_TURN_PASSWORD
126
126
  });
127
127
 
128
+ assert.isUndefined(turnDiscoverySkippedReason);
128
129
  });
129
130
 
130
131
  it('sends TURN_DISCOVERY_REQUEST with empty mediaId when isReconnecting is true', async () => {
@@ -152,13 +153,14 @@ describe('TurnDiscovery', () => {
152
153
  // check that we've sent OK
153
154
  await checkRoapMessageSent('OK', 0);
154
155
 
155
- const turnInfo = await result;
156
+ const {turnServerInfo, turnDiscoverySkippedReason} = await result;
156
157
 
157
- assert.deepEqual(turnInfo, {
158
+ assert.deepEqual(turnServerInfo, {
158
159
  url: FAKE_TURN_URL,
159
160
  username: FAKE_TURN_USERNAME,
160
161
  password: FAKE_TURN_PASSWORD
161
162
  });
163
+ assert.isUndefined(turnDiscoverySkippedReason);
162
164
  });
163
165
 
164
166
  it('ignores any extra, unexpected headers in the response', async () => {
@@ -187,13 +189,13 @@ describe('TurnDiscovery', () => {
187
189
  // check that we've sent OK and still parsed the headers we care about
188
190
  await checkRoapMessageSent('OK', 0);
189
191
 
190
- const turnInfo = await result;
191
-
192
- assert.deepEqual(turnInfo, {
192
+ const {turnServerInfo, turnDiscoverySkippedReason} = await result;
193
+ assert.deepEqual(turnServerInfo, {
193
194
  url: FAKE_TURN_URL,
194
195
  username: FAKE_TURN_USERNAME,
195
196
  password: FAKE_TURN_PASSWORD
196
197
  });
198
+ assert.isUndefined(turnDiscoverySkippedReason);
197
199
  });
198
200
 
199
201
  it('resolves with undefined if turn discovery feature is disabled in config', async () => {
@@ -203,7 +205,10 @@ describe('TurnDiscovery', () => {
203
205
 
204
206
  const result = await new TurnDiscovery(mockRoapRequest).doTurnDiscovery(testMeeting);
205
207
 
206
- assert.isUndefined(result);
208
+ const {turnServerInfo, turnDiscoverySkippedReason} = result;
209
+
210
+ assert.isUndefined(turnServerInfo);
211
+ assert.equal(turnDiscoverySkippedReason, 'config');
207
212
  assert.notCalled(mockRoapRequest.sendRoap);
208
213
  assert.notCalled(Metrics.sendBehavioralMetric);
209
214
 
@@ -218,7 +223,10 @@ describe('TurnDiscovery', () => {
218
223
 
219
224
  const result = await td.doTurnDiscovery(testMeeting, false);
220
225
 
221
- assert.isUndefined(result);
226
+ const {turnServerInfo, turnDiscoverySkippedReason} = result;
227
+
228
+ assert.isUndefined(turnServerInfo);
229
+ assert.isUndefined(turnDiscoverySkippedReason);
222
230
  checkFailureMetricsSent();
223
231
  });
224
232
 
@@ -227,7 +235,10 @@ describe('TurnDiscovery', () => {
227
235
  testMeeting.webex.meetings.reachability.isAnyClusterReachable = () => true;
228
236
  const result = await new TurnDiscovery(mockRoapRequest).doTurnDiscovery(testMeeting);
229
237
 
230
- assert.isUndefined(result);
238
+ const {turnServerInfo, turnDiscoverySkippedReason} = result;
239
+
240
+ assert.isUndefined(turnServerInfo);
241
+ assert.equal(turnDiscoverySkippedReason, 'reachability');
231
242
  assert.notCalled(mockRoapRequest.sendRoap);
232
243
  assert.notCalled(Metrics.sendBehavioralMetric);
233
244
  testMeeting.webex.meetings.reachability.isAnyClusterReachable = prev;
@@ -242,9 +253,10 @@ describe('TurnDiscovery', () => {
242
253
  await clock.tickAsync(10 * 1000);
243
254
  await testUtils.flushPromises();
244
255
 
245
- const turnInfo = await promise;
256
+ const {turnServerInfo, turnDiscoverySkippedReason} = await promise;
246
257
 
247
- assert.isUndefined(turnInfo);
258
+ assert.isUndefined(turnServerInfo);
259
+ assert.isUndefined(turnDiscoverySkippedReason);
248
260
  checkFailureMetricsSent();
249
261
  });
250
262
 
@@ -260,9 +272,10 @@ describe('TurnDiscovery', () => {
260
272
  ]
261
273
  });
262
274
  await testUtils.flushPromises();
263
- const turnInfo = await turnDiscoveryPromise;
275
+ const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
264
276
 
265
- assert.isUndefined(turnInfo);
277
+ assert.isUndefined(turnServerInfo);
278
+ assert.isUndefined(turnDiscoverySkippedReason);
266
279
  checkFailureMetricsSent();
267
280
  });
268
281
 
@@ -274,9 +287,10 @@ describe('TurnDiscovery', () => {
274
287
  td.handleTurnDiscoveryResponse({});
275
288
 
276
289
  await testUtils.flushPromises();
277
- const turnInfo = await turnDiscoveryPromise;
290
+ const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
278
291
 
279
- assert.isUndefined(turnInfo);
292
+ assert.isUndefined(turnServerInfo);
293
+ assert.isUndefined(turnDiscoverySkippedReason);
280
294
  checkFailureMetricsSent();
281
295
  });
282
296
 
@@ -288,9 +302,10 @@ describe('TurnDiscovery', () => {
288
302
  td.handleTurnDiscoveryResponse({headers: []});
289
303
 
290
304
  await testUtils.flushPromises();
291
- const turnInfo = await turnDiscoveryPromise;
305
+ const {turnServerInfo, turnDiscoverySkippedReason}= await turnDiscoveryPromise;
292
306
 
293
- assert.isUndefined(turnInfo);
307
+ assert.isUndefined(turnServerInfo);
308
+ assert.isUndefined(turnDiscoverySkippedReason);
294
309
  checkFailureMetricsSent();
295
310
  });
296
311
 
@@ -321,9 +336,10 @@ describe('TurnDiscovery', () => {
321
336
  // check that we've sent OK
322
337
  await checkRoapMessageSent('OK', 0);
323
338
 
324
- const turnInfo = await turnDiscoveryPromise;
339
+ const {turnServerInfo, turnDiscoverySkippedReason} = await turnDiscoveryPromise;
325
340
 
326
- assert.isUndefined(turnInfo);
341
+ assert.isUndefined(turnServerInfo);
342
+ assert.isUndefined(turnDiscoverySkippedReason);
327
343
  checkFailureMetricsSent();
328
344
  });
329
345
  });