@webex/plugin-meetings 3.8.0-next.49 → 3.8.0-next.50
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/meeting/index.js +39 -18
- package/dist/meeting/index.js.map +1 -1
- package/dist/meetings/util.js +14 -0
- package/dist/meetings/util.js.map +1 -1
- package/dist/reachability/clusterReachability.js +23 -31
- package/dist/reachability/clusterReachability.js.map +1 -1
- package/dist/reachability/index.js +41 -1
- package/dist/reachability/index.js.map +1 -1
- package/dist/types/meeting/index.d.ts +1 -0
- package/dist/types/reachability/clusterReachability.d.ts +2 -6
- package/dist/types/reachability/index.d.ts +8 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +3 -3
- package/src/meeting/index.ts +31 -0
- package/src/meetings/util.ts +18 -0
- package/src/reachability/clusterReachability.ts +26 -25
- package/src/reachability/index.ts +54 -0
- package/test/unit/spec/meeting/index.js +16 -0
- package/test/unit/spec/reachability/clusterReachability.ts +41 -55
- package/test/unit/spec/reachability/index.ts +35 -0
@@ -49,6 +49,7 @@ export class ClusterReachability extends EventsScope {
|
|
49
49
|
private srflxIceCandidates: RTCIceCandidate[] = [];
|
50
50
|
public readonly isVideoMesh: boolean;
|
51
51
|
public readonly name;
|
52
|
+
public readonly reachedSubnets: Set<string> = new Set();
|
52
53
|
|
53
54
|
/**
|
54
55
|
* Constructor for ClusterReachability
|
@@ -234,27 +235,13 @@ export class ClusterReachability extends EventsScope {
|
|
234
235
|
*/
|
235
236
|
private registerIceGatheringStateChangeListener() {
|
236
237
|
this.pc.onicegatheringstatechange = () => {
|
237
|
-
|
238
|
-
|
239
|
-
if (this.pc.iceConnectionState === COMPLETE) {
|
238
|
+
if (this.pc.iceGatheringState === ICE_GATHERING_STATE.COMPLETE) {
|
240
239
|
this.closePeerConnection();
|
241
240
|
this.finishReachabilityCheck();
|
242
241
|
}
|
243
242
|
};
|
244
243
|
}
|
245
244
|
|
246
|
-
/**
|
247
|
-
* Checks if we have the results for all the protocols (UDP and TCP)
|
248
|
-
*
|
249
|
-
* @returns {boolean} true if we have all results, false otherwise
|
250
|
-
*/
|
251
|
-
private haveWeGotAllResults(): boolean {
|
252
|
-
return ['udp', 'tcp', 'xtls'].every(
|
253
|
-
(protocol) =>
|
254
|
-
this.result[protocol].result === 'reachable' || this.result[protocol].result === 'untested'
|
255
|
-
);
|
256
|
-
}
|
257
|
-
|
258
245
|
/**
|
259
246
|
* Saves the latency in the result for the given protocol and marks it as reachable,
|
260
247
|
* emits the "resultReady" event if this is the first result for that protocol,
|
@@ -264,9 +251,15 @@ export class ClusterReachability extends EventsScope {
|
|
264
251
|
* @param {string} protocol
|
265
252
|
* @param {number} latency
|
266
253
|
* @param {string|null} [publicIp]
|
254
|
+
* @param {string|null} [serverIp]
|
267
255
|
* @returns {void}
|
268
256
|
*/
|
269
|
-
private saveResult(
|
257
|
+
private saveResult(
|
258
|
+
protocol: 'udp' | 'tcp' | 'xtls',
|
259
|
+
latency: number,
|
260
|
+
publicIp?: string | null,
|
261
|
+
serverIp?: string | null
|
262
|
+
) {
|
270
263
|
const result = this.result[protocol];
|
271
264
|
|
272
265
|
if (result.latencyInMilliseconds === undefined) {
|
@@ -294,6 +287,10 @@ export class ClusterReachability extends EventsScope {
|
|
294
287
|
} else {
|
295
288
|
this.addPublicIP(protocol, publicIp);
|
296
289
|
}
|
290
|
+
|
291
|
+
if (serverIp) {
|
292
|
+
this.reachedSubnets.add(serverIp);
|
293
|
+
}
|
297
294
|
}
|
298
295
|
|
299
296
|
/**
|
@@ -351,21 +348,25 @@ export class ClusterReachability extends EventsScope {
|
|
351
348
|
|
352
349
|
if (e.candidate) {
|
353
350
|
if (e.candidate.type === CANDIDATE_TYPES.SERVER_REFLEXIVE) {
|
354
|
-
|
351
|
+
let serverIp = null;
|
352
|
+
if ('url' in e.candidate) {
|
353
|
+
const stunServerUrlRegex = /stun:([\d.]+):\d+/;
|
354
|
+
|
355
|
+
const match = (e.candidate as any).url.match(stunServerUrlRegex);
|
356
|
+
if (match) {
|
357
|
+
// eslint-disable-next-line prefer-destructuring
|
358
|
+
serverIp = match[1];
|
359
|
+
}
|
360
|
+
}
|
361
|
+
|
362
|
+
this.saveResult('udp', latencyInMilliseconds, e.candidate.address, serverIp);
|
355
363
|
|
356
364
|
this.determineNatType(e.candidate);
|
357
365
|
}
|
358
366
|
|
359
367
|
if (e.candidate.type === CANDIDATE_TYPES.RELAY) {
|
360
368
|
const protocol = e.candidate.port === TURN_TLS_PORT ? 'xtls' : 'tcp';
|
361
|
-
this.saveResult(protocol, latencyInMilliseconds);
|
362
|
-
// we don't add public IP for TCP, because in the case of relay candidates
|
363
|
-
// e.candidate.address is the TURN server address, not the client's public IP
|
364
|
-
}
|
365
|
-
|
366
|
-
if (this.haveWeGotAllResults()) {
|
367
|
-
this.closePeerConnection();
|
368
|
-
this.finishReachabilityCheck();
|
369
|
+
this.saveResult(protocol, latencyInMilliseconds, null, e.candidate.address);
|
369
370
|
}
|
370
371
|
}
|
371
372
|
};
|
@@ -138,6 +138,60 @@ export default class Reachability extends EventsScope {
|
|
138
138
|
}
|
139
139
|
}
|
140
140
|
|
141
|
+
/**
|
142
|
+
* Checks if the given subnet is reachable
|
143
|
+
* @param {string} mediaServerIp - media server ip
|
144
|
+
* @returns {boolean | null} true if reachable, false if not reachable, null if mediaServerIp is not provided
|
145
|
+
* @public
|
146
|
+
* @memberof Reachability
|
147
|
+
*/
|
148
|
+
public isSubnetReachable(mediaServerIp?: string): boolean | null {
|
149
|
+
if (!mediaServerIp) {
|
150
|
+
LoggerProxy.logger.error(`Reachability:index#isSubnetReachable --> mediaServerIp is null`);
|
151
|
+
|
152
|
+
return null;
|
153
|
+
}
|
154
|
+
|
155
|
+
const subnetFirstOctet = mediaServerIp.split('.')[0];
|
156
|
+
|
157
|
+
LoggerProxy.logger.info(
|
158
|
+
`Reachability:index#isSubnetReachable --> Looking for subnet: ${subnetFirstOctet}.X.X.X`
|
159
|
+
);
|
160
|
+
|
161
|
+
const matchingReachedClusters = Object.values(this.clusterReachability).reduce(
|
162
|
+
(acc, cluster) => {
|
163
|
+
const reachedSubnetsArray = Array.from(cluster.reachedSubnets);
|
164
|
+
|
165
|
+
let logMessage = `Reachability:index#isSubnetReachable --> Cluster ${cluster.name} reached [`;
|
166
|
+
for (let i = 0; i < reachedSubnetsArray.length; i += 1) {
|
167
|
+
const subnet = reachedSubnetsArray[i];
|
168
|
+
const reachedSubnetFirstOctet = subnet.split('.')[0];
|
169
|
+
|
170
|
+
if (subnetFirstOctet === reachedSubnetFirstOctet) {
|
171
|
+
acc.add(cluster.name);
|
172
|
+
}
|
173
|
+
|
174
|
+
logMessage += `${subnet}`;
|
175
|
+
if (i < reachedSubnetsArray.length - 1) {
|
176
|
+
logMessage += ',';
|
177
|
+
}
|
178
|
+
}
|
179
|
+
logMessage += `]`;
|
180
|
+
|
181
|
+
LoggerProxy.logger.info(logMessage);
|
182
|
+
|
183
|
+
return acc;
|
184
|
+
},
|
185
|
+
new Set<string>()
|
186
|
+
);
|
187
|
+
|
188
|
+
LoggerProxy.logger.info(
|
189
|
+
`Reachability:index#isSubnetReachable --> Found ${matchingReachedClusters.size} clusters that use the subnet ${subnetFirstOctet}.X.X.X`
|
190
|
+
);
|
191
|
+
|
192
|
+
return matchingReachedClusters.size > 0;
|
193
|
+
}
|
194
|
+
|
141
195
|
/**
|
142
196
|
* Gets a list of media clusters from the backend and performs reachability checks on all the clusters
|
143
197
|
* @param {string} trigger - explains the reason for starting reachability
|
@@ -253,6 +253,7 @@ describe('plugin-meetings', () => {
|
|
253
253
|
getReachabilityResults: sinon.stub().resolves(undefined),
|
254
254
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
255
255
|
stopReachability: sinon.stub(),
|
256
|
+
isSubnetReachable: sinon.stub().returns(true),
|
256
257
|
};
|
257
258
|
webex.internal.llm.on = sinon.stub();
|
258
259
|
webex.internal.newMetrics.callDiagnosticLatencies = new CallDiagnosticLatencies(
|
@@ -2128,6 +2129,7 @@ describe('plugin-meetings', () => {
|
|
2128
2129
|
someReachabilityMetric2: 'some value2',
|
2129
2130
|
}),
|
2130
2131
|
stopReachability: sinon.stub(),
|
2132
|
+
isSubnetReachable: sinon.stub().returns(false),
|
2131
2133
|
};
|
2132
2134
|
|
2133
2135
|
const forceRtcMetricsSend = sinon.stub().resolves();
|
@@ -2183,6 +2185,7 @@ describe('plugin-meetings', () => {
|
|
2183
2185
|
someReachabilityMetric1: 'some value1',
|
2184
2186
|
someReachabilityMetric2: 'some value2',
|
2185
2187
|
selectedCandidatePairChanges: 2,
|
2188
|
+
isSubnetReachable: false,
|
2186
2189
|
numTransports: 1,
|
2187
2190
|
iceCandidatesCount: 0,
|
2188
2191
|
}
|
@@ -2229,6 +2232,7 @@ describe('plugin-meetings', () => {
|
|
2229
2232
|
signalingState: 'unknown',
|
2230
2233
|
connectionState: 'unknown',
|
2231
2234
|
iceConnectionState: 'unknown',
|
2235
|
+
isSubnetReachable: true,
|
2232
2236
|
})
|
2233
2237
|
);
|
2234
2238
|
|
@@ -2243,6 +2247,7 @@ describe('plugin-meetings', () => {
|
|
2243
2247
|
someReachabilityMetric1: 'some value1',
|
2244
2248
|
someReachabilityMetric2: 'some value2',
|
2245
2249
|
}),
|
2250
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2246
2251
|
};
|
2247
2252
|
|
2248
2253
|
meeting.waitForRemoteSDPAnswer = sinon.stub().rejects();
|
@@ -2293,6 +2298,7 @@ describe('plugin-meetings', () => {
|
|
2293
2298
|
selectedCandidatePairChanges: 2,
|
2294
2299
|
numTransports: 1,
|
2295
2300
|
iceCandidatesCount: 0,
|
2301
|
+
isSubnetReachable: true,
|
2296
2302
|
}
|
2297
2303
|
);
|
2298
2304
|
});
|
@@ -2350,6 +2356,7 @@ describe('plugin-meetings', () => {
|
|
2350
2356
|
signalingState: 'have-local-offer',
|
2351
2357
|
connectionState: 'connecting',
|
2352
2358
|
iceConnectionState: 'checking',
|
2359
|
+
isSubnetReachable: true,
|
2353
2360
|
})
|
2354
2361
|
);
|
2355
2362
|
|
@@ -2407,6 +2414,7 @@ describe('plugin-meetings', () => {
|
|
2407
2414
|
signalingState: 'have-local-offer',
|
2408
2415
|
connectionState: 'connecting',
|
2409
2416
|
iceConnectionState: 'checking',
|
2417
|
+
isSubnetReachable: true,
|
2410
2418
|
})
|
2411
2419
|
);
|
2412
2420
|
|
@@ -2744,6 +2752,7 @@ describe('plugin-meetings', () => {
|
|
2744
2752
|
isWebexMediaBackendUnreachable: sinon.stub().resolves(false),
|
2745
2753
|
getReachabilityMetrics: sinon.stub().resolves(),
|
2746
2754
|
stopReachability: sinon.stub(),
|
2755
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2747
2756
|
};
|
2748
2757
|
const MOCK_CLIENT_ERROR_CODE = 2004;
|
2749
2758
|
const generateClientErrorCodeForIceFailureStub = sinon
|
@@ -2923,6 +2932,7 @@ describe('plugin-meetings', () => {
|
|
2923
2932
|
selectedCandidatePairChanges: 2,
|
2924
2933
|
numTransports: 1,
|
2925
2934
|
iceCandidatesCount: 0,
|
2935
|
+
isSubnetReachable: true,
|
2926
2936
|
},
|
2927
2937
|
]);
|
2928
2938
|
|
@@ -2953,6 +2963,7 @@ describe('plugin-meetings', () => {
|
|
2953
2963
|
.resolves(false),
|
2954
2964
|
getReachabilityMetrics: sinon.stub().resolves({}),
|
2955
2965
|
stopReachability: sinon.stub(),
|
2966
|
+
isSubnetReachable: sinon.stub().returns(true),
|
2956
2967
|
};
|
2957
2968
|
const getErrorPayloadForClientErrorCodeStub =
|
2958
2969
|
(webex.internal.newMetrics.callDiagnosticMetrics.getErrorPayloadForClientErrorCode =
|
@@ -3120,6 +3131,7 @@ describe('plugin-meetings', () => {
|
|
3120
3131
|
retriedWithTurnServer: true,
|
3121
3132
|
isJoinWithMediaRetry: false,
|
3122
3133
|
iceCandidatesCount: 0,
|
3134
|
+
isSubnetReachable: true,
|
3123
3135
|
},
|
3124
3136
|
]);
|
3125
3137
|
meeting.roap.doTurnDiscovery;
|
@@ -3248,6 +3260,7 @@ describe('plugin-meetings', () => {
|
|
3248
3260
|
someReachabilityMetric2: 'some value2',
|
3249
3261
|
}),
|
3250
3262
|
stopReachability: sinon.stub(),
|
3263
|
+
isSubnetReachable: sinon.stub().returns(true),
|
3251
3264
|
};
|
3252
3265
|
meeting.iceCandidatesCount = 3;
|
3253
3266
|
meeting.iceCandidateErrors.set('701_error', 3);
|
@@ -3275,6 +3288,7 @@ describe('plugin-meetings', () => {
|
|
3275
3288
|
iceCandidatesCount: 3,
|
3276
3289
|
'701_error': 3,
|
3277
3290
|
'701_turn_host_lookup_received_error': 1,
|
3291
|
+
isSubnetReachable: true,
|
3278
3292
|
}
|
3279
3293
|
);
|
3280
3294
|
|
@@ -3337,6 +3351,7 @@ describe('plugin-meetings', () => {
|
|
3337
3351
|
iceConnectionState: 'unknown',
|
3338
3352
|
selectedCandidatePairChanges: 2,
|
3339
3353
|
numTransports: 1,
|
3354
|
+
isSubnetReachable: true,
|
3340
3355
|
iceCandidatesCount: 0,
|
3341
3356
|
}
|
3342
3357
|
);
|
@@ -3398,6 +3413,7 @@ describe('plugin-meetings', () => {
|
|
3398
3413
|
numTransports: 1,
|
3399
3414
|
'701_error': 2,
|
3400
3415
|
'701_turn_host_lookup_received_error': 1,
|
3416
|
+
isSubnetReachable: true,
|
3401
3417
|
iceCandidatesCount: 0,
|
3402
3418
|
}
|
3403
3419
|
);
|
@@ -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.
|
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.
|
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
|
});
|
@@ -2686,3 +2686,38 @@ describe('sendMetric', () => {
|
|
2686
2686
|
});
|
2687
2687
|
});
|
2688
2688
|
});
|
2689
|
+
|
2690
|
+
describe('isSubnetReachable', () => {
|
2691
|
+
let webex;
|
2692
|
+
let reachability;
|
2693
|
+
|
2694
|
+
beforeEach(() => {
|
2695
|
+
webex = new MockWebex();
|
2696
|
+
reachability = new TestReachability(webex);
|
2697
|
+
|
2698
|
+
reachability.setFakeClusterReachability({
|
2699
|
+
cluster1: {
|
2700
|
+
reachedSubnets: new Set(['1.2.3.4', '2.3.4.5']),
|
2701
|
+
},
|
2702
|
+
cluster2: {
|
2703
|
+
reachedSubnets: new Set(['3.4.5.6', '4.5.6.7']),
|
2704
|
+
},
|
2705
|
+
});
|
2706
|
+
});
|
2707
|
+
|
2708
|
+
afterEach(() => {
|
2709
|
+
sinon.restore();
|
2710
|
+
});
|
2711
|
+
|
2712
|
+
it('returns true if the subnet is reachable', () => {
|
2713
|
+
assert(reachability.isSubnetReachable('1.2.3.4'));
|
2714
|
+
});
|
2715
|
+
|
2716
|
+
it(`returns false if the subnet is unreachable`, () => {
|
2717
|
+
assert(!reachability.isSubnetReachable('11.2.3.4'));
|
2718
|
+
});
|
2719
|
+
|
2720
|
+
it('returns null if the subnet is not provided', () => {
|
2721
|
+
assert.isNull(reachability.isSubnetReachable(undefined));
|
2722
|
+
});
|
2723
|
+
});
|