@webex/plugin-meetings 3.3.0 → 3.3.1-next.10
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/constants.js +4 -2
- package/dist/constants.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/mediaQualityMetrics/config.js +20 -16
- package/dist/mediaQualityMetrics/config.js.map +1 -1
- package/dist/meeting/index.js +30 -13
- package/dist/meeting/index.js.map +1 -1
- package/dist/meetings/index.js +6 -1
- package/dist/meetings/index.js.map +1 -1
- package/dist/reachability/index.js +82 -9
- package/dist/reachability/index.js.map +1 -1
- package/dist/statsAnalyzer/index.js +77 -27
- package/dist/statsAnalyzer/index.js.map +1 -1
- package/dist/statsAnalyzer/mqaUtil.js +46 -7
- package/dist/statsAnalyzer/mqaUtil.js.map +1 -1
- package/dist/types/constants.d.ts +2 -1
- package/dist/types/mediaQualityMetrics/config.d.ts +14 -2
- package/dist/types/meeting/index.d.ts +8 -0
- package/dist/types/reachability/index.d.ts +11 -0
- package/dist/types/statsAnalyzer/index.d.ts +14 -6
- package/dist/types/statsAnalyzer/mqaUtil.d.ts +17 -4
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/constants.ts +2 -1
- package/src/mediaQualityMetrics/config.ts +22 -10
- package/src/meeting/index.ts +29 -14
- package/src/meetings/index.ts +7 -2
- package/src/reachability/index.ts +57 -0
- package/src/statsAnalyzer/index.ts +82 -22
- package/src/statsAnalyzer/mqaUtil.ts +68 -4
- package/test/unit/spec/meeting/index.js +28 -8
- package/test/unit/spec/meetings/index.js +38 -15
- package/test/unit/spec/reachability/index.ts +266 -0
- package/test/unit/spec/stats-analyzer/index.js +630 -314
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import {mean, max} from 'lodash';
|
|
4
4
|
|
|
5
|
-
import {STATS} from '../constants';
|
|
5
|
+
import {MQA_INTERVAL, STATS} from '../constants';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Get the totals of a certain value from a certain media type.
|
|
@@ -28,6 +28,7 @@ export const getAudioReceiverMqa = ({
|
|
|
28
28
|
statsResults,
|
|
29
29
|
lastMqaDataSent,
|
|
30
30
|
baseMediaType,
|
|
31
|
+
isMultistream,
|
|
31
32
|
}) => {
|
|
32
33
|
const sendrecvType = STATS.RECEIVE_DIRECTION;
|
|
33
34
|
|
|
@@ -52,6 +53,7 @@ export const getAudioReceiverMqa = ({
|
|
|
52
53
|
statsResults[Object.keys(statsResults).find((mediaType) => mediaType.includes(baseMediaType))]
|
|
53
54
|
?.direction || 'inactive';
|
|
54
55
|
audioReceiver.common.common.isMain = !baseMediaType.includes('-share');
|
|
56
|
+
audioReceiver.common.common.multistreamEnabled = isMultistream;
|
|
55
57
|
audioReceiver.common.transportType = statsResults.connectionType.local.transport;
|
|
56
58
|
|
|
57
59
|
// add rtpPacket info inside common as also for call analyzer
|
|
@@ -127,7 +129,13 @@ export const getAudioReceiverStreamMqa = ({
|
|
|
127
129
|
((statsResults[mediaType][sendrecvType].totalBytesReceived - lastBytesReceived) * 8) / 60 || 0;
|
|
128
130
|
};
|
|
129
131
|
|
|
130
|
-
export const getAudioSenderMqa = ({
|
|
132
|
+
export const getAudioSenderMqa = ({
|
|
133
|
+
audioSender,
|
|
134
|
+
statsResults,
|
|
135
|
+
lastMqaDataSent,
|
|
136
|
+
baseMediaType,
|
|
137
|
+
isMultistream,
|
|
138
|
+
}) => {
|
|
131
139
|
const sendrecvType = STATS.SEND_DIRECTION;
|
|
132
140
|
|
|
133
141
|
const getLastTotalValue = (value: string) =>
|
|
@@ -152,6 +160,7 @@ export const getAudioSenderMqa = ({audioSender, statsResults, lastMqaDataSent, b
|
|
|
152
160
|
statsResults[Object.keys(statsResults).find((mediaType) => mediaType.includes(baseMediaType))]
|
|
153
161
|
?.direction || 'inactive';
|
|
154
162
|
audioSender.common.common.isMain = !baseMediaType.includes('-share');
|
|
163
|
+
audioSender.common.common.multistreamEnabled = isMultistream;
|
|
155
164
|
audioSender.common.transportType = statsResults.connectionType.local.transport;
|
|
156
165
|
|
|
157
166
|
audioSender.common.maxRemoteJitter = max(meanRemoteJitter) * 1000 || 0;
|
|
@@ -166,8 +175,8 @@ export const getAudioSenderMqa = ({audioSender, statsResults, lastMqaDataSent, b
|
|
|
166
175
|
baseMediaType,
|
|
167
176
|
'availableOutgoingBitrate'
|
|
168
177
|
);
|
|
169
|
-
// Calculate based on how much packets lost of received compated to how to the client sent
|
|
170
178
|
|
|
179
|
+
// Calculate based on how much packets lost of received compated to how to the client sent
|
|
171
180
|
const totalPacketsLostForaMin = totalPacketsLostOnReceiver - lastPacketsLostTotal;
|
|
172
181
|
audioSender.common.remoteLossRate =
|
|
173
182
|
totalPacketsSent - lastPacketsSent > 0
|
|
@@ -225,6 +234,7 @@ export const getVideoReceiverMqa = ({
|
|
|
225
234
|
statsResults,
|
|
226
235
|
lastMqaDataSent,
|
|
227
236
|
baseMediaType,
|
|
237
|
+
isMultistream,
|
|
228
238
|
}) => {
|
|
229
239
|
const sendrecvType = STATS.RECEIVE_DIRECTION;
|
|
230
240
|
|
|
@@ -237,10 +247,16 @@ export const getVideoReceiverMqa = ({
|
|
|
237
247
|
const lastPacketsLost = getLastTotalValue('totalPacketsLost');
|
|
238
248
|
const lastBytesReceived = getLastTotalValue('totalBytesReceived');
|
|
239
249
|
|
|
250
|
+
const lastRtxPacketsReceived = getLastTotalValue('totalRtxPacketsReceived');
|
|
251
|
+
const lastRtxBytesReceived = getLastTotalValue('totalRtxBytesReceived');
|
|
252
|
+
|
|
240
253
|
const packetsLost = getTotalValue('totalPacketsLost');
|
|
241
254
|
const totalPacketsReceived = getTotalValue('totalPacketsReceived');
|
|
242
255
|
const totalBytesReceived = getTotalValue('totalBytesReceived');
|
|
243
256
|
|
|
257
|
+
const totalRtxPacketsReceived = getTotalValue('totalRtxPacketsReceived');
|
|
258
|
+
const totalRtxBytesReceived = getTotalValue('totalRtxBytesReceived');
|
|
259
|
+
|
|
244
260
|
const meanRemoteJitter = Object.keys(statsResults)
|
|
245
261
|
.filter((mt) => mt.includes(baseMediaType))
|
|
246
262
|
.reduce((acc, mt) => acc.concat(statsResults[mt][sendrecvType].meanRemoteJitter), []);
|
|
@@ -248,6 +264,7 @@ export const getVideoReceiverMqa = ({
|
|
|
248
264
|
videoReceiver.common.common.direction =
|
|
249
265
|
statsResults[Object.keys(statsResults).find((mediaType) => mediaType.includes(baseMediaType))]
|
|
250
266
|
?.direction || 'inactive';
|
|
267
|
+
videoReceiver.common.common.multistreamEnabled = isMultistream;
|
|
251
268
|
videoReceiver.common.common.isMain = !baseMediaType.includes('-share');
|
|
252
269
|
videoReceiver.common.transportType = statsResults.connectionType.local.transport;
|
|
253
270
|
|
|
@@ -266,10 +283,15 @@ export const getVideoReceiverMqa = ({
|
|
|
266
283
|
|
|
267
284
|
// Calculate the outgoing bitrate
|
|
268
285
|
const totalBytesReceivedInaMin = totalBytesReceived - lastBytesReceived;
|
|
286
|
+
const totalRtxBytesReceivedInaMin = totalRtxBytesReceived - lastRtxBytesReceived;
|
|
269
287
|
|
|
270
288
|
videoReceiver.common.rtpBitrate = totalBytesReceivedInaMin
|
|
271
289
|
? (totalBytesReceivedInaMin * 8) / 60
|
|
272
290
|
: 0;
|
|
291
|
+
videoReceiver.common.rtxPackets = totalRtxPacketsReceived - lastRtxPacketsReceived;
|
|
292
|
+
videoReceiver.common.rtxBitrate = totalRtxBytesReceivedInaMin
|
|
293
|
+
? (totalRtxBytesReceivedInaMin * 8) / 60
|
|
294
|
+
: 0;
|
|
273
295
|
};
|
|
274
296
|
|
|
275
297
|
export const getVideoReceiverStreamMqa = ({
|
|
@@ -336,9 +358,23 @@ export const getVideoReceiverStreamMqa = ({
|
|
|
336
358
|
statsResults[mediaType][sendrecvType].keyFramesDecoded - lastKeyFramesDecoded || 0;
|
|
337
359
|
videoReceiverStream.requestedKeyFrames =
|
|
338
360
|
statsResults[mediaType][sendrecvType].totalPliCount - lastPliCount || 0;
|
|
361
|
+
|
|
362
|
+
videoReceiverStream.isActiveSpeaker =
|
|
363
|
+
statsResults[mediaType][sendrecvType].isActiveSpeaker ||
|
|
364
|
+
((statsResults[mediaType][sendrecvType].lastActiveSpeakerTimestamp ?? 0) > 0 &&
|
|
365
|
+
performance.now() +
|
|
366
|
+
performance.timeOrigin -
|
|
367
|
+
(statsResults[mediaType][sendrecvType].lastActiveSpeakerTimestamp ?? 0) <
|
|
368
|
+
MQA_INTERVAL);
|
|
339
369
|
};
|
|
340
370
|
|
|
341
|
-
export const getVideoSenderMqa = ({
|
|
371
|
+
export const getVideoSenderMqa = ({
|
|
372
|
+
videoSender,
|
|
373
|
+
statsResults,
|
|
374
|
+
lastMqaDataSent,
|
|
375
|
+
baseMediaType,
|
|
376
|
+
isMultistream,
|
|
377
|
+
}) => {
|
|
342
378
|
const sendrecvType = STATS.SEND_DIRECTION;
|
|
343
379
|
|
|
344
380
|
const getLastTotalValue = (value: string) =>
|
|
@@ -349,15 +385,20 @@ export const getVideoSenderMqa = ({videoSender, statsResults, lastMqaDataSent, b
|
|
|
349
385
|
const lastPacketsSent = getLastTotalValue('totalPacketsSent');
|
|
350
386
|
const lastBytesSent = getLastTotalValue('totalBytesSent');
|
|
351
387
|
const lastPacketsLostTotal = getLastTotalValue('totalPacketsLostOnReceiver');
|
|
388
|
+
const lastRtxPacketsSent = getLastTotalValue('totalRtxPacketsSent');
|
|
389
|
+
const lastRtxBytesSent = getLastTotalValue('totalRtxBytesSent');
|
|
352
390
|
|
|
353
391
|
const totalPacketsLostOnReceiver = getTotalValue('totalPacketsLostOnReceiver');
|
|
354
392
|
const totalPacketsSent = getTotalValue('totalPacketsSent');
|
|
355
393
|
const totalBytesSent = getTotalValue('totalBytesSent');
|
|
356
394
|
const availableOutgoingBitrate = getTotalValue('availableOutgoingBitrate');
|
|
395
|
+
const totalRtxPacketsSent = getTotalValue('totalRtxPacketsSent');
|
|
396
|
+
const totalRtxBytesSent = getTotalValue('totalRtxBytesSent');
|
|
357
397
|
|
|
358
398
|
videoSender.common.common.direction =
|
|
359
399
|
statsResults[Object.keys(statsResults).find((mediaType) => mediaType.includes(baseMediaType))]
|
|
360
400
|
?.direction || 'inactive';
|
|
401
|
+
videoSender.common.common.multistreamEnabled = isMultistream;
|
|
361
402
|
videoSender.common.common.isMain = !baseMediaType.includes('-share');
|
|
362
403
|
videoSender.common.transportType = statsResults.connectionType.local.transport;
|
|
363
404
|
|
|
@@ -389,8 +430,11 @@ export const getVideoSenderMqa = ({videoSender, statsResults, lastMqaDataSent, b
|
|
|
389
430
|
|
|
390
431
|
// Calculate the outgoing bitrate
|
|
391
432
|
const totalBytesSentInaMin = totalBytesSent - lastBytesSent;
|
|
433
|
+
const totalRtxBytesSentInaMin = totalRtxBytesSent - lastRtxBytesSent;
|
|
392
434
|
|
|
393
435
|
videoSender.common.rtpBitrate = totalBytesSentInaMin ? (totalBytesSentInaMin * 8) / 60 : 0;
|
|
436
|
+
videoSender.common.rtxPackets = totalRtxPacketsSent - lastRtxPacketsSent;
|
|
437
|
+
videoSender.common.rtxBitrate = totalRtxBytesSentInaMin ? (totalRtxBytesSentInaMin * 8) / 60 : 0;
|
|
394
438
|
};
|
|
395
439
|
|
|
396
440
|
export const getVideoSenderStreamMqa = ({
|
|
@@ -443,3 +487,23 @@ export const getVideoSenderStreamMqa = ({
|
|
|
443
487
|
videoSenderStream.requestedFrameSize =
|
|
444
488
|
statsResults[mediaType][sendrecvType].requestedFrameSize || 0;
|
|
445
489
|
};
|
|
490
|
+
|
|
491
|
+
/**
|
|
492
|
+
* Checks if stream stats should be updated based on request status and elapsed time.
|
|
493
|
+
*
|
|
494
|
+
* @param {Object} statsResults - Stats results object.
|
|
495
|
+
* @param {string} mediaType - Media type (e.g., 'audio', 'video').
|
|
496
|
+
* @param {string} direction - Stats direction (e.g., 'send', 'receive').
|
|
497
|
+
* @returns {boolean} Whether stats should be updated.
|
|
498
|
+
*/
|
|
499
|
+
export const isStreamRequested = (
|
|
500
|
+
statsResults: any,
|
|
501
|
+
mediaType: string,
|
|
502
|
+
direction: string
|
|
503
|
+
): boolean => {
|
|
504
|
+
const now = performance.timeOrigin + performance.now();
|
|
505
|
+
const lastUpdateTimestamp = statsResults[mediaType][direction]?.lastRequestedUpdateTimestamp;
|
|
506
|
+
const isRequested = statsResults[mediaType][direction]?.isRequested;
|
|
507
|
+
|
|
508
|
+
return isRequested || (lastUpdateTimestamp && now - lastUpdateTimestamp < MQA_INTERVAL);
|
|
509
|
+
};
|
|
@@ -1302,6 +1302,31 @@ describe('plugin-meetings', () => {
|
|
|
1302
1302
|
);
|
|
1303
1303
|
});
|
|
1304
1304
|
});
|
|
1305
|
+
|
|
1306
|
+
describe('#handleLLMOnline', () => {
|
|
1307
|
+
beforeEach(() => {
|
|
1308
|
+
webex.internal.llm.off = sinon.stub();
|
|
1309
|
+
});
|
|
1310
|
+
|
|
1311
|
+
it('turns off llm online, emits transcription connected events', () => {
|
|
1312
|
+
meeting.handleLLMOnline();
|
|
1313
|
+
assert.calledOnceWithExactly(
|
|
1314
|
+
webex.internal.llm.off,
|
|
1315
|
+
'online',
|
|
1316
|
+
meeting.handleLLMOnline
|
|
1317
|
+
);
|
|
1318
|
+
assert.calledWith(
|
|
1319
|
+
TriggerProxy.trigger,
|
|
1320
|
+
sinon.match.instanceOf(Meeting),
|
|
1321
|
+
{
|
|
1322
|
+
file: 'meeting/index',
|
|
1323
|
+
function: 'handleLLMOnline',
|
|
1324
|
+
},
|
|
1325
|
+
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED
|
|
1326
|
+
);
|
|
1327
|
+
});
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1305
1330
|
describe('#join', () => {
|
|
1306
1331
|
let sandbox = null;
|
|
1307
1332
|
let setCorrelationIdSpy;
|
|
@@ -1351,15 +1376,10 @@ describe('plugin-meetings', () => {
|
|
|
1351
1376
|
assert.calledOnce(MeetingUtil.joinMeeting);
|
|
1352
1377
|
assert.calledOnce(meeting.setLocus);
|
|
1353
1378
|
assert.equal(result, joinMeetingResult);
|
|
1354
|
-
|
|
1355
1379
|
assert.calledWith(
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
file: 'meeting/index',
|
|
1360
|
-
function: 'join',
|
|
1361
|
-
},
|
|
1362
|
-
EVENT_TRIGGERS.MEETING_TRANSCRIPTION_CONNECTED
|
|
1380
|
+
webex.internal.llm.on,
|
|
1381
|
+
'online',
|
|
1382
|
+
meeting.handleLLMOnline
|
|
1363
1383
|
);
|
|
1364
1384
|
});
|
|
1365
1385
|
|
|
@@ -18,6 +18,7 @@ import TriggerProxy from '@webex/plugin-meetings/src/common/events/trigger-proxy
|
|
|
18
18
|
import LoggerProxy from '@webex/plugin-meetings/src/common/logs/logger-proxy';
|
|
19
19
|
import LoggerConfig from '@webex/plugin-meetings/src/common/logs/logger-config';
|
|
20
20
|
import Meeting, {CallStateForMetrics} from '@webex/plugin-meetings/src/meeting';
|
|
21
|
+
import {Services} from '@webex/webex-core';
|
|
21
22
|
import MeetingUtil from '@webex/plugin-meetings/src/meeting/util';
|
|
22
23
|
import Meetings from '@webex/plugin-meetings/src/meetings';
|
|
23
24
|
import MeetingCollection from '@webex/plugin-meetings/src/meetings/collection';
|
|
@@ -75,6 +76,8 @@ describe('plugin-meetings', () => {
|
|
|
75
76
|
let test1;
|
|
76
77
|
let test2;
|
|
77
78
|
let locusInfo;
|
|
79
|
+
let services;
|
|
80
|
+
let catalog;
|
|
78
81
|
|
|
79
82
|
describe('meetings index', () => {
|
|
80
83
|
beforeEach(() => {
|
|
@@ -93,9 +96,13 @@ describe('plugin-meetings', () => {
|
|
|
93
96
|
device: Device,
|
|
94
97
|
mercury: Mercury,
|
|
95
98
|
meetings: Meetings,
|
|
99
|
+
services: Services,
|
|
96
100
|
},
|
|
97
101
|
});
|
|
98
102
|
|
|
103
|
+
services = webex.internal.services;
|
|
104
|
+
catalog = services._getCatalog();
|
|
105
|
+
|
|
99
106
|
Object.assign(webex, {
|
|
100
107
|
logging: logger,
|
|
101
108
|
});
|
|
@@ -161,6 +168,7 @@ describe('plugin-meetings', () => {
|
|
|
161
168
|
],
|
|
162
169
|
})
|
|
163
170
|
),
|
|
171
|
+
_getCatalog: sinon.stub().returns(catalog),
|
|
164
172
|
fetchClientRegionInfo: sinon.stub().returns(Promise.resolve()),
|
|
165
173
|
},
|
|
166
174
|
metrics: {
|
|
@@ -1917,34 +1925,34 @@ describe('plugin-meetings', () => {
|
|
|
1917
1925
|
let loggerProxySpy;
|
|
1918
1926
|
|
|
1919
1927
|
it('should call request.getMeetingPreferences to get the preferred webex site ', async () => {
|
|
1928
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), []);
|
|
1920
1929
|
assert.isDefined(webex.meetings.preferredWebexSite);
|
|
1921
1930
|
await webex.meetings.fetchUserPreferredWebexSite();
|
|
1922
1931
|
|
|
1923
1932
|
assert.equal(webex.meetings.preferredWebexSite, 'go.webex.com');
|
|
1933
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), [
|
|
1934
|
+
'go.webex.com',
|
|
1935
|
+
]);
|
|
1924
1936
|
});
|
|
1925
1937
|
|
|
1926
1938
|
const setup = ({user} = {}) => {
|
|
1927
1939
|
loggerProxySpy = sinon.spy(LoggerProxy.logger, 'error');
|
|
1940
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), []);
|
|
1928
1941
|
|
|
1929
1942
|
Object.assign(webex.internal, {
|
|
1930
|
-
services: {
|
|
1931
|
-
getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
|
|
1932
|
-
},
|
|
1933
1943
|
user: {
|
|
1934
1944
|
get: sinon.stub().returns(Promise.resolve(user)),
|
|
1935
1945
|
},
|
|
1936
1946
|
});
|
|
1947
|
+
|
|
1948
|
+
Object.assign(webex.internal.services, {
|
|
1949
|
+
getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
|
|
1950
|
+
});
|
|
1937
1951
|
};
|
|
1938
1952
|
|
|
1939
1953
|
it('should not fail if UserPreferred info is not fetched ', async () => {
|
|
1940
1954
|
setup();
|
|
1941
1955
|
|
|
1942
|
-
Object.assign(webex.internal, {
|
|
1943
|
-
services: {
|
|
1944
|
-
getMeetingPreferences: sinon.stub().returns(Promise.resolve({})),
|
|
1945
|
-
},
|
|
1946
|
-
});
|
|
1947
|
-
|
|
1948
1956
|
await webex.meetings.fetchUserPreferredWebexSite().then(() => {
|
|
1949
1957
|
assert.equal(webex.meetings.preferredWebexSite, '');
|
|
1950
1958
|
});
|
|
@@ -1952,6 +1960,7 @@ describe('plugin-meetings', () => {
|
|
|
1952
1960
|
loggerProxySpy,
|
|
1953
1961
|
'Failed to fetch preferred site from user - no site will be set'
|
|
1954
1962
|
);
|
|
1963
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']);
|
|
1955
1964
|
});
|
|
1956
1965
|
|
|
1957
1966
|
it('should fall back to fetching the site from the user', async () => {
|
|
@@ -1968,6 +1977,10 @@ describe('plugin-meetings', () => {
|
|
|
1968
1977
|
await webex.meetings.fetchUserPreferredWebexSite();
|
|
1969
1978
|
|
|
1970
1979
|
assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
|
|
1980
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), [
|
|
1981
|
+
'',
|
|
1982
|
+
'site.webex.com',
|
|
1983
|
+
]);
|
|
1971
1984
|
assert.notCalled(loggerProxySpy);
|
|
1972
1985
|
});
|
|
1973
1986
|
|
|
@@ -1989,6 +2002,7 @@ describe('plugin-meetings', () => {
|
|
|
1989
2002
|
loggerProxySpy,
|
|
1990
2003
|
'Failed to fetch preferred site from user - no site will be set'
|
|
1991
2004
|
);
|
|
2005
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']);
|
|
1992
2006
|
});
|
|
1993
2007
|
}
|
|
1994
2008
|
);
|
|
@@ -2005,6 +2019,7 @@ describe('plugin-meetings', () => {
|
|
|
2005
2019
|
loggerProxySpy,
|
|
2006
2020
|
'Failed to fetch preferred site from user - no site will be set'
|
|
2007
2021
|
);
|
|
2022
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']);
|
|
2008
2023
|
});
|
|
2009
2024
|
|
|
2010
2025
|
it('should fall back to fetching the site from the user', async () => {
|
|
@@ -2022,6 +2037,10 @@ describe('plugin-meetings', () => {
|
|
|
2022
2037
|
|
|
2023
2038
|
assert.equal(webex.meetings.preferredWebexSite, 'site.webex.com');
|
|
2024
2039
|
assert.notCalled(loggerProxySpy);
|
|
2040
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), [
|
|
2041
|
+
'',
|
|
2042
|
+
'site.webex.com',
|
|
2043
|
+
]);
|
|
2025
2044
|
});
|
|
2026
2045
|
|
|
2027
2046
|
forEach(
|
|
@@ -2042,6 +2061,7 @@ describe('plugin-meetings', () => {
|
|
|
2042
2061
|
loggerProxySpy,
|
|
2043
2062
|
'Failed to fetch preferred site from user - no site will be set'
|
|
2044
2063
|
);
|
|
2064
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']);
|
|
2045
2065
|
});
|
|
2046
2066
|
}
|
|
2047
2067
|
);
|
|
@@ -2058,6 +2078,7 @@ describe('plugin-meetings', () => {
|
|
|
2058
2078
|
loggerProxySpy,
|
|
2059
2079
|
'Failed to fetch preferred site from user - no site will be set'
|
|
2060
2080
|
);
|
|
2081
|
+
assert.deepEqual(webex.internal.services._getCatalog().getAllowedDomains(), ['']);
|
|
2061
2082
|
});
|
|
2062
2083
|
});
|
|
2063
2084
|
});
|
|
@@ -2344,12 +2365,14 @@ describe('plugin-meetings', () => {
|
|
|
2344
2365
|
sessionType: 'MAIN',
|
|
2345
2366
|
};
|
|
2346
2367
|
newLocus.self.state = 'JOINED';
|
|
2347
|
-
newLocus.self.devices = [
|
|
2348
|
-
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2368
|
+
newLocus.self.devices = [
|
|
2369
|
+
{
|
|
2370
|
+
intent: {
|
|
2371
|
+
reason: 'ON_HOLD_LOBBY',
|
|
2372
|
+
type: 'WAIT',
|
|
2373
|
+
},
|
|
2374
|
+
},
|
|
2375
|
+
];
|
|
2353
2376
|
LoggerProxy.logger.log = sinon.stub();
|
|
2354
2377
|
const result = webex.meetings.isNeedHandleLocusDTO(meeting, newLocus);
|
|
2355
2378
|
assert.equal(result, true);
|
|
@@ -141,6 +141,272 @@ describe('isAnyPublicClusterReachable', () => {
|
|
|
141
141
|
});
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
+
|
|
145
|
+
describe('isWebexMediaBackendUnreachable', () => {
|
|
146
|
+
let webex;
|
|
147
|
+
|
|
148
|
+
beforeEach(() => {
|
|
149
|
+
webex = new MockWebex();
|
|
150
|
+
|
|
151
|
+
sinon.stub(MeetingUtil, 'getIpVersion').returns(IP_VERSION.unknown);
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
afterEach(() => {
|
|
155
|
+
sinon.restore();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const runCheck = async (mockStorage: any, expectedValue: boolean) => {
|
|
159
|
+
if (mockStorage) {
|
|
160
|
+
await webex.boundedStorage.put(
|
|
161
|
+
'Reachability',
|
|
162
|
+
'reachability.result',
|
|
163
|
+
JSON.stringify(mockStorage)
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
const reachability = new Reachability(webex);
|
|
167
|
+
|
|
168
|
+
const result = await reachability.isWebexMediaBackendUnreachable();
|
|
169
|
+
|
|
170
|
+
assert.equal(result, expectedValue);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
[
|
|
174
|
+
{
|
|
175
|
+
title: 'no clusters at all',
|
|
176
|
+
mockStorage: {},
|
|
177
|
+
expectedResult: false,
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
title: 'clusters without results',
|
|
181
|
+
mockStorage: {a: {}, b: {}},
|
|
182
|
+
expectedResult: false,
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
title: 'all clusters untested',
|
|
186
|
+
mockStorage: {
|
|
187
|
+
a: {udp: 'untested'},
|
|
188
|
+
b: {udp: 'untested', tcp: 'untested'},
|
|
189
|
+
},
|
|
190
|
+
expectedResult: false,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
title: 'one cluster with udp reachable',
|
|
194
|
+
mockStorage: {x: {udp: {result: 'reachable'}, tcp: {result: 'unreachable'}}},
|
|
195
|
+
expectedResult: false,
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
title: 'one cluster with tcp reachable',
|
|
199
|
+
mockStorage: {x: {tcp: {result: 'reachable'}}},
|
|
200
|
+
expectedResult: false,
|
|
201
|
+
},
|
|
202
|
+
{
|
|
203
|
+
title: 'one cluster with xtls reachable',
|
|
204
|
+
mockStorage: {x: {xtls: {result: 'reachable'}}, y: {xtls: {result: 'unreachable'}}},
|
|
205
|
+
expectedResult: false,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
title: 'multiple clusters with various protocols reachable',
|
|
209
|
+
mockStorage: {
|
|
210
|
+
a: {udp: {result: 'reachable'}, tcp: {result: 'reachable'}},
|
|
211
|
+
b: {udp: {result: 'unreachable'}, tcp: {result: 'reachable'}},
|
|
212
|
+
c: {tcp: {result: 'reachable'}},
|
|
213
|
+
d: {xtls: {result: 'reachable'}},
|
|
214
|
+
},
|
|
215
|
+
expectedResult: false,
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
title: 'multiple clusters with all protocols unreachable',
|
|
219
|
+
mockStorage: {
|
|
220
|
+
a: {
|
|
221
|
+
udp: {result: 'unreachable'},
|
|
222
|
+
tcp: {result: 'unreachable'},
|
|
223
|
+
xtls: {result: 'unreachable'},
|
|
224
|
+
},
|
|
225
|
+
b: {
|
|
226
|
+
udp: {result: 'unreachable'},
|
|
227
|
+
tcp: {result: 'unreachable'},
|
|
228
|
+
xtls: {result: 'unreachable'},
|
|
229
|
+
},
|
|
230
|
+
c: {
|
|
231
|
+
udp: {result: 'unreachable'},
|
|
232
|
+
tcp: {result: 'unreachable'},
|
|
233
|
+
xtls: {result: 'unreachable'},
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
expectedResult: true,
|
|
237
|
+
},
|
|
238
|
+
{
|
|
239
|
+
title: 'multiple clusters with UDP and TCP protocols unreachable, but TLS not tested',
|
|
240
|
+
mockStorage: {
|
|
241
|
+
a: {
|
|
242
|
+
udp: {result: 'unreachable'},
|
|
243
|
+
tcp: {result: 'unreachable'},
|
|
244
|
+
xtls: {result: 'untested'},
|
|
245
|
+
},
|
|
246
|
+
b: {
|
|
247
|
+
udp: {result: 'unreachable'},
|
|
248
|
+
tcp: {result: 'unreachable'},
|
|
249
|
+
xtls: {result: 'untested'},
|
|
250
|
+
},
|
|
251
|
+
c: {
|
|
252
|
+
udp: {result: 'unreachable'},
|
|
253
|
+
tcp: {result: 'unreachable'},
|
|
254
|
+
xtls: {result: 'untested'},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
expectedResult: false,
|
|
258
|
+
},
|
|
259
|
+
{
|
|
260
|
+
title: 'multiple clusters with UDP and TCP protocols unreachable, but TLS missing',
|
|
261
|
+
mockStorage: {
|
|
262
|
+
a: {
|
|
263
|
+
udp: {result: 'unreachable'},
|
|
264
|
+
tcp: {result: 'unreachable'},
|
|
265
|
+
},
|
|
266
|
+
b: {
|
|
267
|
+
udp: {result: 'unreachable'},
|
|
268
|
+
tcp: {result: 'unreachable'},
|
|
269
|
+
},
|
|
270
|
+
c: {
|
|
271
|
+
udp: {result: 'unreachable'},
|
|
272
|
+
tcp: {result: 'unreachable'},
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
expectedResult: false,
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
title: 'multiple clusters with UDP and TLS protocols unreachable, but TCP not tested',
|
|
279
|
+
mockStorage: {
|
|
280
|
+
a: {
|
|
281
|
+
udp: {result: 'unreachable'},
|
|
282
|
+
tcp: {result: 'untested'},
|
|
283
|
+
xtls: {result: 'unreachable'},
|
|
284
|
+
},
|
|
285
|
+
b: {
|
|
286
|
+
udp: {result: 'unreachable'},
|
|
287
|
+
tcp: {result: 'untested'},
|
|
288
|
+
xtls: {result: 'unreachable'},
|
|
289
|
+
},
|
|
290
|
+
c: {
|
|
291
|
+
udp: {result: 'unreachable'},
|
|
292
|
+
tcp: {result: 'untested'},
|
|
293
|
+
xtls: {result: 'unreachable'},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
expectedResult: false,
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
title: 'multiple clusters with UDP and TLS protocols unreachable, but TCP missing',
|
|
300
|
+
mockStorage: {
|
|
301
|
+
a: {
|
|
302
|
+
udp: {result: 'unreachable'},
|
|
303
|
+
xtls: {result: 'unreachable'},
|
|
304
|
+
},
|
|
305
|
+
b: {
|
|
306
|
+
udp: {result: 'unreachable'},
|
|
307
|
+
xtls: {result: 'unreachable'},
|
|
308
|
+
},
|
|
309
|
+
c: {
|
|
310
|
+
udp: {result: 'unreachable'},
|
|
311
|
+
xtls: {result: 'unreachable'},
|
|
312
|
+
},
|
|
313
|
+
},
|
|
314
|
+
expectedResult: false,
|
|
315
|
+
},
|
|
316
|
+
{
|
|
317
|
+
title: 'multiple clusters with all protocols unreachable, some untested',
|
|
318
|
+
mockStorage: {
|
|
319
|
+
a: {
|
|
320
|
+
udp: {result: 'unreachable'},
|
|
321
|
+
tcp: {result: 'unreachable'},
|
|
322
|
+
xtls: {result: 'unreachable'},
|
|
323
|
+
},
|
|
324
|
+
b: {udp: {result: 'unreachable'}, tcp: {result: 'untested'}, xtls: {result: 'unreachable'}},
|
|
325
|
+
c: {udp: {result: 'unreachable'}, tcp: {result: 'unreachable'}, xtls: {result: 'untested'}},
|
|
326
|
+
},
|
|
327
|
+
expectedResult: true,
|
|
328
|
+
},
|
|
329
|
+
{
|
|
330
|
+
title: 'multiple clusters with all protocols unreachable, except for 1 reachable on udp',
|
|
331
|
+
mockStorage: {
|
|
332
|
+
a: {
|
|
333
|
+
udp: {result: 'reachable'},
|
|
334
|
+
tcp: {result: 'unreachable'},
|
|
335
|
+
xtls: {result: 'unreachable'},
|
|
336
|
+
},
|
|
337
|
+
b: {
|
|
338
|
+
udp: {result: 'unreachable'},
|
|
339
|
+
tcp: {result: 'unreachable'},
|
|
340
|
+
xtls: {result: 'unreachable'},
|
|
341
|
+
},
|
|
342
|
+
c: {
|
|
343
|
+
udp: {result: 'unreachable'},
|
|
344
|
+
tcp: {result: 'unreachable'},
|
|
345
|
+
xtls: {result: 'unreachable'},
|
|
346
|
+
},
|
|
347
|
+
},
|
|
348
|
+
expectedResult: false,
|
|
349
|
+
},
|
|
350
|
+
{
|
|
351
|
+
title: 'multiple clusters with all protocols unreachable, except for 1 reachable on tcp',
|
|
352
|
+
mockStorage: {
|
|
353
|
+
a: {
|
|
354
|
+
udp: {result: 'unreachable'},
|
|
355
|
+
tcp: {result: 'unreachable'},
|
|
356
|
+
xtls: {result: 'unreachable'},
|
|
357
|
+
},
|
|
358
|
+
b: {
|
|
359
|
+
udp: {result: 'unreachable'},
|
|
360
|
+
tcp: {result: 'unreachable'},
|
|
361
|
+
xtls: {result: 'unreachable'},
|
|
362
|
+
},
|
|
363
|
+
c: {
|
|
364
|
+
udp: {result: 'unreachable'},
|
|
365
|
+
tcp: {result: 'reachable'},
|
|
366
|
+
xtls: {result: 'unreachable'},
|
|
367
|
+
},
|
|
368
|
+
},
|
|
369
|
+
expectedResult: false,
|
|
370
|
+
},
|
|
371
|
+
{
|
|
372
|
+
title: 'multiple clusters with all protocols unreachable, except for 1 reachable on xtls',
|
|
373
|
+
mockStorage: {
|
|
374
|
+
a: {
|
|
375
|
+
udp: {result: 'unreachable'},
|
|
376
|
+
tcp: {result: 'unreachable'},
|
|
377
|
+
xtls: {result: 'unreachable'},
|
|
378
|
+
},
|
|
379
|
+
b: {
|
|
380
|
+
udp: {result: 'unreachable'},
|
|
381
|
+
tcp: {result: 'unreachable'},
|
|
382
|
+
xtls: {result: 'reachable'},
|
|
383
|
+
},
|
|
384
|
+
c: {
|
|
385
|
+
udp: {result: 'unreachable'},
|
|
386
|
+
tcp: {result: 'unreachable'},
|
|
387
|
+
xtls: {result: 'unreachable'},
|
|
388
|
+
},
|
|
389
|
+
},
|
|
390
|
+
expectedResult: false,
|
|
391
|
+
},
|
|
392
|
+
{
|
|
393
|
+
title: 'multiple clusters with some missing results',
|
|
394
|
+
mockStorage: {
|
|
395
|
+
a: {udp: {result: 'unreachable'}},
|
|
396
|
+
b: {tcp: {result: 'unreachable'}},
|
|
397
|
+
c: {xtls: {result: 'unreachable'}},
|
|
398
|
+
d: {},
|
|
399
|
+
},
|
|
400
|
+
expectedResult: true,
|
|
401
|
+
},
|
|
402
|
+
].forEach(({mockStorage, expectedResult, title}) => {
|
|
403
|
+
it(`returns ${expectedResult} when ${title}`, async () => {
|
|
404
|
+
await runCheck(mockStorage, expectedResult);
|
|
405
|
+
});
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
|
|
144
410
|
describe('gatherReachability', () => {
|
|
145
411
|
let webex;
|
|
146
412
|
|