@webex/plugin-meetings 3.12.0-next.21 → 3.12.0-next.23
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/aiEnableRequest/index.js +1 -1
- package/dist/breakouts/breakout.js +1 -1
- package/dist/breakouts/index.js +1 -1
- package/dist/hashTree/hashTreeParser.js +480 -320
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/interceptors/locusRetry.js +23 -8
- package/dist/interceptors/locusRetry.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +170 -36
- package/dist/locus-info/index.js.map +1 -1
- package/dist/meetings/index.js +130 -45
- package/dist/meetings/index.js.map +1 -1
- package/dist/types/hashTree/hashTreeParser.d.ts +40 -12
- package/dist/types/interceptors/locusRetry.d.ts +4 -4
- package/dist/types/locus-info/index.d.ts +29 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +1 -1
- package/src/hashTree/hashTreeParser.ts +182 -97
- package/src/interceptors/locusRetry.ts +25 -4
- package/src/locus-info/index.ts +176 -48
- package/src/meetings/index.ts +42 -17
- package/test/unit/spec/hashTree/hashTreeParser.ts +372 -7
- package/test/unit/spec/interceptors/locusRetry.ts +205 -4
- package/test/unit/spec/locus-info/index.js +179 -4
- package/test/unit/spec/meetings/index.js +127 -5
|
@@ -3175,7 +3175,7 @@ describe('plugin-meetings', () => {
|
|
|
3175
3175
|
const createMockParser = (state = 'active') => ({
|
|
3176
3176
|
state,
|
|
3177
3177
|
stop: sinon.stub(),
|
|
3178
|
-
|
|
3178
|
+
resumeFromMessage: sinon.stub(),
|
|
3179
3179
|
handleMessage: sinon.stub(),
|
|
3180
3180
|
});
|
|
3181
3181
|
|
|
@@ -3258,7 +3258,7 @@ describe('plugin-meetings', () => {
|
|
|
3258
3258
|
stateElementsMessage: message,
|
|
3259
3259
|
});
|
|
3260
3260
|
|
|
3261
|
-
assert.calledOnce(parserA.
|
|
3261
|
+
assert.calledOnce(parserA.resumeFromMessage);
|
|
3262
3262
|
assert.calledOnce(parserB.stop);
|
|
3263
3263
|
});
|
|
3264
3264
|
|
|
@@ -3281,7 +3281,7 @@ describe('plugin-meetings', () => {
|
|
|
3281
3281
|
stateElementsMessage: message,
|
|
3282
3282
|
});
|
|
3283
3283
|
|
|
3284
|
-
assert.notCalled(parserA.
|
|
3284
|
+
assert.notCalled(parserA.resumeFromMessage);
|
|
3285
3285
|
assert.notCalled(parserB.stop);
|
|
3286
3286
|
});
|
|
3287
3287
|
|
|
@@ -3300,7 +3300,7 @@ describe('plugin-meetings', () => {
|
|
|
3300
3300
|
stateElementsMessage: message,
|
|
3301
3301
|
});
|
|
3302
3302
|
|
|
3303
|
-
assert.notCalled(parserA.
|
|
3303
|
+
assert.notCalled(parserA.resumeFromMessage);
|
|
3304
3304
|
assert.notCalled(parserA.handleMessage);
|
|
3305
3305
|
});
|
|
3306
3306
|
|
|
@@ -3453,6 +3453,156 @@ describe('plugin-meetings', () => {
|
|
|
3453
3453
|
assert.calledOnce(locusInfo.sendClassicVsHashTreeMismatchMetric);
|
|
3454
3454
|
assert.calledOnce(mockHashTreeParser.handleLocusUpdate);
|
|
3455
3455
|
});
|
|
3456
|
+
|
|
3457
|
+
describe('parser switch via API response', () => {
|
|
3458
|
+
const deviceUrl = 'http://device-url.com';
|
|
3459
|
+
const locusUrlA = 'http://locus-url-A.com';
|
|
3460
|
+
const locusUrlB = 'http://locus-url-B.com';
|
|
3461
|
+
|
|
3462
|
+
let HashTreeParserStub;
|
|
3463
|
+
|
|
3464
|
+
const createMockApiParser = (state = 'active') => ({
|
|
3465
|
+
state,
|
|
3466
|
+
stop: sinon.stub(),
|
|
3467
|
+
resumeFromApiResponse: sinon.stub(),
|
|
3468
|
+
handleLocusUpdate: sinon.stub(),
|
|
3469
|
+
initializeFromGetLociResponse: sinon.stub(),
|
|
3470
|
+
});
|
|
3471
|
+
|
|
3472
|
+
const createLocusWithReplaces = (url, replacedLocusUrl, replacedAt) => ({
|
|
3473
|
+
url,
|
|
3474
|
+
self: {
|
|
3475
|
+
devices: [{url: deviceUrl, replaces: [{locusUrl: replacedLocusUrl, replacedAt}]}],
|
|
3476
|
+
},
|
|
3477
|
+
});
|
|
3478
|
+
|
|
3479
|
+
const createLocusWithoutReplaces = (url) => ({
|
|
3480
|
+
url,
|
|
3481
|
+
self: {devices: [{url: deviceUrl}]},
|
|
3482
|
+
});
|
|
3483
|
+
|
|
3484
|
+
beforeEach(() => {
|
|
3485
|
+
locusInfo.webex.internal.device.url = deviceUrl;
|
|
3486
|
+
HashTreeParserStub = sinon
|
|
3487
|
+
.stub(HashTreeParserModule, 'default')
|
|
3488
|
+
.returns(createMockApiParser());
|
|
3489
|
+
});
|
|
3490
|
+
|
|
3491
|
+
it('should create a new parser and initialize it when no entry exists for the locusUrl', () => {
|
|
3492
|
+
// existing parser for a different url so hashTreeParsers.size > 0
|
|
3493
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: createMockApiParser(), initializedFromHashTree: true});
|
|
3494
|
+
|
|
3495
|
+
const locus = createLocusWithReplaces(locusUrlB, locusUrlA, '2026-01-01T00:00:00Z');
|
|
3496
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
3497
|
+
|
|
3498
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3499
|
+
|
|
3500
|
+
assert.isTrue(locusInfo.hashTreeParsers.has(locusUrlB));
|
|
3501
|
+
const newEntry = locusInfo.hashTreeParsers.get(locusUrlB);
|
|
3502
|
+
assert.isFalse(newEntry.initializedFromHashTree);
|
|
3503
|
+
|
|
3504
|
+
// the stub returns the mock, so initializeFromGetLociResponse should be called on it
|
|
3505
|
+
const createdParser = HashTreeParserStub.returnValues[0];
|
|
3506
|
+
assert.calledOnceWithExactly(createdParser.initializeFromGetLociResponse, locus);
|
|
3507
|
+
assert.notCalled(locusInfo.handleLocusDelta);
|
|
3508
|
+
});
|
|
3509
|
+
|
|
3510
|
+
it('should reactivate a stopped parser when replaces info is newer', () => {
|
|
3511
|
+
const parserA = createMockApiParser('stopped');
|
|
3512
|
+
const parserB = createMockApiParser('active');
|
|
3513
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-01-01T00:00:00Z', initializedFromHashTree: true});
|
|
3514
|
+
locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
|
|
3515
|
+
|
|
3516
|
+
const locus = createLocusWithReplaces(locusUrlA, locusUrlB, '2026-02-01T00:00:00Z');
|
|
3517
|
+
|
|
3518
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3519
|
+
|
|
3520
|
+
assert.calledOnce(parserA.resumeFromApiResponse);
|
|
3521
|
+
assert.calledWithExactly(parserA.resumeFromApiResponse, locus);
|
|
3522
|
+
assert.calledOnce(parserB.stop);
|
|
3523
|
+
assert.equal(locusInfo.hashTreeParsers.get(locusUrlB).replacedAt, '2026-02-01T00:00:00Z');
|
|
3524
|
+
assert.isFalse(locusInfo.hashTreeParsers.get(locusUrlA).initializedFromHashTree);
|
|
3525
|
+
});
|
|
3526
|
+
|
|
3527
|
+
it('should not reactivate a stopped parser when replaces info is not newer', () => {
|
|
3528
|
+
const parserA = createMockApiParser('stopped');
|
|
3529
|
+
const parserB = createMockApiParser('active');
|
|
3530
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, replacedAt: '2026-03-01T00:00:00Z', initializedFromHashTree: true});
|
|
3531
|
+
locusInfo.hashTreeParsers.set(locusUrlB, {parser: parserB, initializedFromHashTree: true});
|
|
3532
|
+
|
|
3533
|
+
const locus = createLocusWithReplaces(locusUrlA, locusUrlB, '2026-01-01T00:00:00Z');
|
|
3534
|
+
|
|
3535
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3536
|
+
|
|
3537
|
+
assert.notCalled(parserA.resumeFromApiResponse);
|
|
3538
|
+
assert.notCalled(parserB.stop);
|
|
3539
|
+
});
|
|
3540
|
+
|
|
3541
|
+
it('should not reactivate a stopped parser when no replaces info is available', () => {
|
|
3542
|
+
const parserA = createMockApiParser('stopped');
|
|
3543
|
+
locusInfo.hashTreeParsers.set(locusUrlA, {parser: parserA, initializedFromHashTree: true});
|
|
3544
|
+
|
|
3545
|
+
const locus = createLocusWithoutReplaces(locusUrlA);
|
|
3546
|
+
|
|
3547
|
+
locusInfo.handleLocusAPIResponse(mockMeeting, {locus});
|
|
3548
|
+
|
|
3549
|
+
assert.notCalled(parserA.resumeFromApiResponse);
|
|
3550
|
+
});
|
|
3551
|
+
});
|
|
3552
|
+
});
|
|
3553
|
+
|
|
3554
|
+
describe('#syncAllHashTreeDatasets', () => {
|
|
3555
|
+
it('should call syncAllDatasets on each parser that has an entry', async () => {
|
|
3556
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3557
|
+
const parser2 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3558
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3559
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3560
|
+
|
|
3561
|
+
await locusInfo.syncAllHashTreeDatasets();
|
|
3562
|
+
|
|
3563
|
+
assert.calledOnce(parser1.syncAllDatasets);
|
|
3564
|
+
assert.calledOnce(parser2.syncAllDatasets);
|
|
3565
|
+
});
|
|
3566
|
+
|
|
3567
|
+
it('should skip parser entries without a parser object', async () => {
|
|
3568
|
+
const parser1 = {syncAllDatasets: sinon.stub().resolves()};
|
|
3569
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3570
|
+
locusInfo.hashTreeParsers.set('url2', {parser: undefined});
|
|
3571
|
+
|
|
3572
|
+
await locusInfo.syncAllHashTreeDatasets();
|
|
3573
|
+
|
|
3574
|
+
assert.calledOnce(parser1.syncAllDatasets);
|
|
3575
|
+
});
|
|
3576
|
+
|
|
3577
|
+
it('should await each parsers syncAllDatasets sequentially', async () => {
|
|
3578
|
+
const callOrder = [];
|
|
3579
|
+
const parser1 = {syncAllDatasets: sinon.stub().callsFake(() => {
|
|
3580
|
+
callOrder.push('start1');
|
|
3581
|
+
return new Promise((resolve) => {
|
|
3582
|
+
setTimeout(() => {
|
|
3583
|
+
callOrder.push('end1');
|
|
3584
|
+
resolve();
|
|
3585
|
+
}, 100);
|
|
3586
|
+
});
|
|
3587
|
+
})};
|
|
3588
|
+
const parser2 = {syncAllDatasets: sinon.stub().callsFake(() => {
|
|
3589
|
+
callOrder.push('start2');
|
|
3590
|
+
return Promise.resolve();
|
|
3591
|
+
})};
|
|
3592
|
+
locusInfo.hashTreeParsers.set('url1', {parser: parser1});
|
|
3593
|
+
locusInfo.hashTreeParsers.set('url2', {parser: parser2});
|
|
3594
|
+
|
|
3595
|
+
const clock = sinon.useFakeTimers();
|
|
3596
|
+
const promise = locusInfo.syncAllHashTreeDatasets();
|
|
3597
|
+
// parser1 started but parser2 not yet
|
|
3598
|
+
assert.deepEqual(callOrder, ['start1']);
|
|
3599
|
+
|
|
3600
|
+
await clock.tickAsync(100);
|
|
3601
|
+
await promise;
|
|
3602
|
+
// parser1 finished, then parser2 started and finished
|
|
3603
|
+
assert.deepEqual(callOrder, ['start1', 'end1', 'start2']);
|
|
3604
|
+
clock.restore();
|
|
3605
|
+
});
|
|
3456
3606
|
});
|
|
3457
3607
|
|
|
3458
3608
|
describe('#LocusDeltaEvents', () => {
|
|
@@ -5001,6 +5151,31 @@ describe('plugin-meetings', () => {
|
|
|
5001
5151
|
);
|
|
5002
5152
|
assert.notCalled(getTheLocusToUpdateStub);
|
|
5003
5153
|
});
|
|
5154
|
+
|
|
5155
|
+
it('should call handleLocusAPIResponse for SDK_LOCUS_FROM_SYNC_MEETINGS when hash tree parsers exist', () => {
|
|
5156
|
+
const fakeLocusUrl = 'http://locus-url.com';
|
|
5157
|
+
const fakeLocus = {url: fakeLocusUrl, fullState: {state: 'ACTIVE'}};
|
|
5158
|
+
const mockHashTreeParser = {
|
|
5159
|
+
handleMessage: sinon.stub(),
|
|
5160
|
+
handleLocusUpdate: sinon.stub(),
|
|
5161
|
+
};
|
|
5162
|
+
locusInfo.hashTreeParsers.set(fakeLocusUrl, {
|
|
5163
|
+
parser: mockHashTreeParser,
|
|
5164
|
+
initializedFromHashTree: true,
|
|
5165
|
+
});
|
|
5166
|
+
|
|
5167
|
+
sinon.stub(locusInfo, 'handleLocusDelta');
|
|
5168
|
+
|
|
5169
|
+
locusInfo.parse(mockMeeting, {
|
|
5170
|
+
eventType: LOCUSEVENT.SDK_LOCUS_FROM_SYNC_MEETINGS,
|
|
5171
|
+
locus: fakeLocus,
|
|
5172
|
+
});
|
|
5173
|
+
|
|
5174
|
+
// should route through handleLocusAPIResponse which passes unwrapped LocusDTO to parser
|
|
5175
|
+
assert.calledOnce(mockHashTreeParser.handleLocusUpdate);
|
|
5176
|
+
assert.notCalled(mockHashTreeParser.handleMessage);
|
|
5177
|
+
assert.notCalled(locusInfo.handleLocusDelta);
|
|
5178
|
+
});
|
|
5004
5179
|
});
|
|
5005
5180
|
});
|
|
5006
5181
|
|
|
@@ -91,6 +91,7 @@ describe('plugin-meetings', () => {
|
|
|
91
91
|
locusInfo = {
|
|
92
92
|
parse: sinon.stub().returns(true),
|
|
93
93
|
updateMainSessionLocusCache: sinon.stub(),
|
|
94
|
+
syncAllHashTreeDatasets: sinon.stub(),
|
|
94
95
|
};
|
|
95
96
|
webex = new MockWebex({
|
|
96
97
|
children: {
|
|
@@ -1391,7 +1392,7 @@ describe('plugin-meetings', () => {
|
|
|
1391
1392
|
it('should have #syncMeetings', () => {
|
|
1392
1393
|
assert.exists(webex.meetings.syncMeetings);
|
|
1393
1394
|
});
|
|
1394
|
-
it('should
|
|
1395
|
+
it('should skip getActiveMeetings but still call syncAllHashTreeDatasets if unverified guest', async () => {
|
|
1395
1396
|
webex.meetings.request.getActiveMeetings = sinon.stub().returns(
|
|
1396
1397
|
Promise.resolve({
|
|
1397
1398
|
loci: [
|
|
@@ -1404,13 +1405,23 @@ describe('plugin-meetings', () => {
|
|
|
1404
1405
|
webex.credentials.isUnverifiedGuest = true;
|
|
1405
1406
|
LoggerProxy.logger.info = sinon.stub();
|
|
1406
1407
|
|
|
1408
|
+
const mockLocusInfo = {
|
|
1409
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1410
|
+
};
|
|
1411
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1412
|
+
meeting1: {locusInfo: mockLocusInfo},
|
|
1413
|
+
meeting2: {locusInfo: undefined},
|
|
1414
|
+
meeting3: {},
|
|
1415
|
+
});
|
|
1416
|
+
|
|
1407
1417
|
await webex.meetings.syncMeetings();
|
|
1408
1418
|
|
|
1409
1419
|
assert.notCalled(webex.meetings.request.getActiveMeetings);
|
|
1410
1420
|
assert.calledWith(
|
|
1411
1421
|
LoggerProxy.logger.info,
|
|
1412
|
-
'Meetings:index#syncMeetings --> skipping
|
|
1422
|
+
'Meetings:index#syncMeetings --> user is unverified guest, skipping calling Locus for meeting sync'
|
|
1413
1423
|
);
|
|
1424
|
+
assert.calledOnce(mockLocusInfo.syncAllHashTreeDatasets);
|
|
1414
1425
|
});
|
|
1415
1426
|
describe('succesful requests', () => {
|
|
1416
1427
|
beforeEach(() => {
|
|
@@ -1429,6 +1440,9 @@ describe('plugin-meetings', () => {
|
|
|
1429
1440
|
webex.meetings.meetingCollection.getByKey = sinon.stub().returns({
|
|
1430
1441
|
locusInfo,
|
|
1431
1442
|
});
|
|
1443
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1444
|
+
meeting1: {locusInfo, locusUrl: url1},
|
|
1445
|
+
});
|
|
1432
1446
|
});
|
|
1433
1447
|
it('tests the sync meeting calls for existing meeting', async () => {
|
|
1434
1448
|
await webex.meetings.syncMeetings();
|
|
@@ -1436,6 +1450,7 @@ describe('plugin-meetings', () => {
|
|
|
1436
1450
|
assert.calledOnce(webex.meetings.meetingCollection.getByKey);
|
|
1437
1451
|
assert.calledOnce(locusInfo.parse);
|
|
1438
1452
|
assert.calledWith(webex.meetings.meetingCollection.getByKey, 'locusUrl', url1);
|
|
1453
|
+
assert.calledOnce(locusInfo.syncAllHashTreeDatasets);
|
|
1439
1454
|
});
|
|
1440
1455
|
});
|
|
1441
1456
|
describe('when meeting is not returned', () => {
|
|
@@ -1520,7 +1535,7 @@ describe('plugin-meetings', () => {
|
|
|
1520
1535
|
it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings is not defined', async () => {
|
|
1521
1536
|
await webex.meetings.syncMeetings();
|
|
1522
1537
|
assert.calledOnce(webex.meetings.request.getActiveMeetings);
|
|
1523
|
-
assert.
|
|
1538
|
+
assert.calledTwice(webex.meetings.meetingCollection.getAll);
|
|
1524
1539
|
assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
|
|
1525
1540
|
assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting1);
|
|
1526
1541
|
assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting2);
|
|
@@ -1532,7 +1547,7 @@ describe('plugin-meetings', () => {
|
|
|
1532
1547
|
it('destroy any meeting that has no active locus url if keepOnlyLocusMeetings === true', async () => {
|
|
1533
1548
|
await webex.meetings.syncMeetings({keepOnlyLocusMeetings: true});
|
|
1534
1549
|
assert.calledOnce(webex.meetings.request.getActiveMeetings);
|
|
1535
|
-
assert.
|
|
1550
|
+
assert.calledTwice(webex.meetings.meetingCollection.getAll);
|
|
1536
1551
|
assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
|
|
1537
1552
|
assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting1);
|
|
1538
1553
|
assert.calledWith(destroySpy, meetingCollectionMeetings.otherNonLocusMeeting2);
|
|
@@ -1544,7 +1559,7 @@ describe('plugin-meetings', () => {
|
|
|
1544
1559
|
it('destroy any LOCUS meetings that have no active locus url if keepOnlyLocusMeetings === false', async () => {
|
|
1545
1560
|
await webex.meetings.syncMeetings({keepOnlyLocusMeetings: false});
|
|
1546
1561
|
assert.calledOnce(webex.meetings.request.getActiveMeetings);
|
|
1547
|
-
assert.
|
|
1562
|
+
assert.calledTwice(webex.meetings.meetingCollection.getAll);
|
|
1548
1563
|
assert.calledWith(destroySpy, meetingCollectionMeetings.noLongerValidLocusMeeting);
|
|
1549
1564
|
assert.callCount(destroySpy, 1);
|
|
1550
1565
|
|
|
@@ -1552,6 +1567,113 @@ describe('plugin-meetings', () => {
|
|
|
1552
1567
|
});
|
|
1553
1568
|
});
|
|
1554
1569
|
});
|
|
1570
|
+
|
|
1571
|
+
describe('when globalMeetingId preserves breakout meetings', () => {
|
|
1572
|
+
let destroySpy;
|
|
1573
|
+
let cleanUpSpy;
|
|
1574
|
+
|
|
1575
|
+
beforeEach(() => {
|
|
1576
|
+
destroySpy = sinon.spy(webex.meetings, 'destroy');
|
|
1577
|
+
cleanUpSpy = sinon.stub(MeetingUtil, 'cleanUp').returns(Promise.resolve());
|
|
1578
|
+
});
|
|
1579
|
+
|
|
1580
|
+
afterEach(() => {
|
|
1581
|
+
cleanUpSpy.restore();
|
|
1582
|
+
});
|
|
1583
|
+
|
|
1584
|
+
it('should not destroy a meeting whose globalMeetingId matches an active locus', async () => {
|
|
1585
|
+
const meetingCollectionMeetings = {
|
|
1586
|
+
breakoutMeeting: {
|
|
1587
|
+
locusUrl: 'breakout-url',
|
|
1588
|
+
locusInfo: {
|
|
1589
|
+
info: {globalMeetingId: 'gmid-123'},
|
|
1590
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1591
|
+
},
|
|
1592
|
+
sendCallAnalyzerMetrics: sinon.stub(),
|
|
1593
|
+
},
|
|
1594
|
+
};
|
|
1595
|
+
|
|
1596
|
+
webex.meetings.meetingCollection.getAll = sinon
|
|
1597
|
+
.stub()
|
|
1598
|
+
.returns(meetingCollectionMeetings);
|
|
1599
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().resolves({
|
|
1600
|
+
loci: [{url: 'main-url', info: {globalMeetingId: 'gmid-123'}}],
|
|
1601
|
+
});
|
|
1602
|
+
|
|
1603
|
+
await webex.meetings.syncMeetings();
|
|
1604
|
+
|
|
1605
|
+
assert.notCalled(destroySpy);
|
|
1606
|
+
});
|
|
1607
|
+
|
|
1608
|
+
it('should destroy a meeting whose globalMeetingId does NOT match any active locus', async () => {
|
|
1609
|
+
const meetingCollectionMeetings = {
|
|
1610
|
+
breakoutMeeting: {
|
|
1611
|
+
locusUrl: 'breakout-url',
|
|
1612
|
+
locusInfo: {
|
|
1613
|
+
info: {globalMeetingId: 'gmid-other'},
|
|
1614
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1615
|
+
},
|
|
1616
|
+
sendCallAnalyzerMetrics: sinon.stub(),
|
|
1617
|
+
},
|
|
1618
|
+
};
|
|
1619
|
+
|
|
1620
|
+
webex.meetings.meetingCollection.getAll = sinon
|
|
1621
|
+
.stub()
|
|
1622
|
+
.returns(meetingCollectionMeetings);
|
|
1623
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().resolves({
|
|
1624
|
+
loci: [{url: 'main-url', info: {globalMeetingId: 'gmid-123'}}],
|
|
1625
|
+
});
|
|
1626
|
+
|
|
1627
|
+
await webex.meetings.syncMeetings();
|
|
1628
|
+
|
|
1629
|
+
assert.calledOnce(destroySpy);
|
|
1630
|
+
assert.calledWith(destroySpy, meetingCollectionMeetings.breakoutMeeting);
|
|
1631
|
+
});
|
|
1632
|
+
});
|
|
1633
|
+
|
|
1634
|
+
describe('syncAllHashTreeDatasets in syncMeetings', () => {
|
|
1635
|
+
it('should call syncAllHashTreeDatasets for multiple meetings, skipping those without locusInfo', async () => {
|
|
1636
|
+
const mockLocusInfo1 = {
|
|
1637
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1638
|
+
};
|
|
1639
|
+
const mockLocusInfo2 = {
|
|
1640
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1641
|
+
};
|
|
1642
|
+
|
|
1643
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().resolves({loci: []});
|
|
1644
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1645
|
+
meeting1: {locusInfo: mockLocusInfo1},
|
|
1646
|
+
meeting2: {locusInfo: undefined},
|
|
1647
|
+
meeting3: {locusInfo: mockLocusInfo2},
|
|
1648
|
+
meeting4: {},
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
await webex.meetings.syncMeetings({keepOnlyLocusMeetings: false});
|
|
1652
|
+
|
|
1653
|
+
assert.calledOnce(mockLocusInfo1.syncAllHashTreeDatasets);
|
|
1654
|
+
assert.calledOnce(mockLocusInfo2.syncAllHashTreeDatasets);
|
|
1655
|
+
});
|
|
1656
|
+
|
|
1657
|
+
it('should not call syncAllHashTreeDatasets when getActiveMeetings throws an error', async () => {
|
|
1658
|
+
const mockLocusInfo = {
|
|
1659
|
+
syncAllHashTreeDatasets: sinon.stub().resolves(),
|
|
1660
|
+
};
|
|
1661
|
+
|
|
1662
|
+
webex.meetings.request.getActiveMeetings = sinon.stub().rejects(new Error('network error'));
|
|
1663
|
+
webex.meetings.meetingCollection.getAll = sinon.stub().returns({
|
|
1664
|
+
meeting1: {locusInfo: mockLocusInfo},
|
|
1665
|
+
});
|
|
1666
|
+
|
|
1667
|
+
try {
|
|
1668
|
+
await webex.meetings.syncMeetings();
|
|
1669
|
+
assert.fail('should have thrown');
|
|
1670
|
+
} catch (err) {
|
|
1671
|
+
assert.equal(err.message, 'network error');
|
|
1672
|
+
}
|
|
1673
|
+
|
|
1674
|
+
assert.notCalled(mockLocusInfo.syncAllHashTreeDatasets);
|
|
1675
|
+
});
|
|
1676
|
+
});
|
|
1555
1677
|
});
|
|
1556
1678
|
describe('#fetchStaticMeetingLink', () => {
|
|
1557
1679
|
const conversationUrl = 'conv.fakeconversationurl.com';
|