@webex/plugin-meetings 3.11.0-next.2 → 3.11.0-next.21
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/hashTree/hashTree.js +18 -0
- package/dist/hashTree/hashTree.js.map +1 -1
- package/dist/hashTree/hashTreeParser.js +307 -139
- package/dist/hashTree/hashTreeParser.js.map +1 -1
- package/dist/hashTree/types.js +2 -1
- package/dist/hashTree/types.js.map +1 -1
- package/dist/hashTree/utils.js +10 -0
- package/dist/hashTree/utils.js.map +1 -1
- package/dist/interpretation/index.js +1 -1
- package/dist/interpretation/siLanguage.js +1 -1
- package/dist/locus-info/index.js +55 -42
- package/dist/locus-info/index.js.map +1 -1
- package/dist/media/MediaConnectionAwaiter.js +57 -1
- package/dist/media/MediaConnectionAwaiter.js.map +1 -1
- package/dist/media/properties.js +4 -2
- package/dist/media/properties.js.map +1 -1
- package/dist/meeting/index.js +33 -22
- package/dist/meeting/index.js.map +1 -1
- package/dist/meeting/util.js +108 -2
- package/dist/meeting/util.js.map +1 -1
- package/dist/meetings/index.js +76 -26
- package/dist/meetings/index.js.map +1 -1
- package/dist/metrics/constants.js +2 -1
- package/dist/metrics/constants.js.map +1 -1
- package/dist/multistream/mediaRequestManager.js +1 -1
- package/dist/multistream/mediaRequestManager.js.map +1 -1
- package/dist/reactions/reactions.type.js.map +1 -1
- package/dist/types/hashTree/hashTree.d.ts +7 -0
- package/dist/types/hashTree/hashTreeParser.d.ts +47 -12
- package/dist/types/hashTree/types.d.ts +1 -0
- package/dist/types/hashTree/utils.d.ts +6 -0
- package/dist/types/locus-info/index.d.ts +9 -2
- package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
- package/dist/types/media/properties.d.ts +2 -1
- package/dist/types/meeting/index.d.ts +8 -5
- package/dist/types/meeting/util.d.ts +28 -0
- package/dist/types/meetings/index.d.ts +3 -1
- package/dist/types/metrics/constants.d.ts +1 -0
- package/dist/types/reactions/reactions.type.d.ts +1 -0
- package/dist/webinar/index.js +1 -1
- package/package.json +22 -22
- package/src/hashTree/hashTree.ts +17 -0
- package/src/hashTree/hashTreeParser.ts +294 -96
- package/src/hashTree/types.ts +1 -0
- package/src/hashTree/utils.ts +9 -0
- package/src/locus-info/index.ts +83 -35
- package/src/media/MediaConnectionAwaiter.ts +41 -1
- package/src/media/properties.ts +3 -1
- package/src/meeting/index.ts +24 -11
- package/src/meeting/util.ts +132 -1
- package/src/meetings/index.ts +93 -8
- package/src/metrics/constants.ts +1 -0
- package/src/multistream/mediaRequestManager.ts +1 -1
- package/src/reactions/reactions.type.ts +1 -0
- package/test/unit/spec/hashTree/hashTree.ts +66 -0
- package/test/unit/spec/hashTree/hashTreeParser.ts +942 -110
- package/test/unit/spec/locus-info/index.js +88 -17
- package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
- package/test/unit/spec/media/properties.ts +12 -3
- package/test/unit/spec/meeting/index.js +160 -2
- package/test/unit/spec/meeting/utils.js +294 -22
- package/test/unit/spec/meetings/index.js +594 -17
|
@@ -5,6 +5,9 @@ import HashTree from '@webex/plugin-meetings/src/hashTree/hashTree';
|
|
|
5
5
|
import {expect} from '@webex/test-helper-chai';
|
|
6
6
|
import sinon from 'sinon';
|
|
7
7
|
import {assert} from '@webex/test-helper-chai';
|
|
8
|
+
import {EMPTY_HASH} from '@webex/plugin-meetings/src/hashTree/constants';
|
|
9
|
+
|
|
10
|
+
const visibleDataSetsUrl = 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/visibleDataSets';
|
|
8
11
|
|
|
9
12
|
const exampleInitialLocus = {
|
|
10
13
|
dataSets: [
|
|
@@ -46,6 +49,7 @@ const exampleInitialLocus = {
|
|
|
46
49
|
},
|
|
47
50
|
dataSetNames: ['main'],
|
|
48
51
|
},
|
|
52
|
+
links: {resources: {visibleDataSets: {url: visibleDataSetsUrl}}},
|
|
49
53
|
participants: [
|
|
50
54
|
{
|
|
51
55
|
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/11941033',
|
|
@@ -62,7 +66,6 @@ const exampleInitialLocus = {
|
|
|
62
66
|
],
|
|
63
67
|
self: {
|
|
64
68
|
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/11941033',
|
|
65
|
-
visibleDataSets: ['main', 'self', 'atd-unmuted'],
|
|
66
69
|
person: {},
|
|
67
70
|
htMeta: {
|
|
68
71
|
elementId: {
|
|
@@ -76,6 +79,28 @@ const exampleInitialLocus = {
|
|
|
76
79
|
},
|
|
77
80
|
};
|
|
78
81
|
|
|
82
|
+
const exampleMetadata = {
|
|
83
|
+
htMeta: {
|
|
84
|
+
elementId: {
|
|
85
|
+
type: 'metadata',
|
|
86
|
+
id: 5,
|
|
87
|
+
version: 50,
|
|
88
|
+
},
|
|
89
|
+
dataSetNames: ['self'],
|
|
90
|
+
},
|
|
91
|
+
visibleDataSets: [
|
|
92
|
+
{name: 'main', url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main'},
|
|
93
|
+
{
|
|
94
|
+
name: 'self',
|
|
95
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
96
|
+
},
|
|
97
|
+
{
|
|
98
|
+
name: 'atd-unmuted',
|
|
99
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/atd-unmuted',
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
|
|
79
104
|
function createDataSet(name: string, leafCount: number, version = 1) {
|
|
80
105
|
return {
|
|
81
106
|
url: `https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/${name}`,
|
|
@@ -119,7 +144,6 @@ function mockSyncRequest(webexRequest: sinon.SinonStub, datasetUrl: string, resp
|
|
|
119
144
|
}
|
|
120
145
|
|
|
121
146
|
describe('HashTreeParser', () => {
|
|
122
|
-
const visibleDataSetsUrl = 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/visibleDataSets';
|
|
123
147
|
const locusUrl = 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f';
|
|
124
148
|
|
|
125
149
|
let clock;
|
|
@@ -139,9 +163,13 @@ describe('HashTreeParser', () => {
|
|
|
139
163
|
});
|
|
140
164
|
|
|
141
165
|
// Helper to create a HashTreeParser instance with common defaults
|
|
142
|
-
function createHashTreeParser(
|
|
166
|
+
function createHashTreeParser(
|
|
167
|
+
initialLocus: any = exampleInitialLocus,
|
|
168
|
+
metadata: any = exampleMetadata
|
|
169
|
+
) {
|
|
143
170
|
return new HashTreeParser({
|
|
144
171
|
initialLocus,
|
|
172
|
+
metadata,
|
|
145
173
|
webexRequest,
|
|
146
174
|
locusInfoUpdateCallback: callback,
|
|
147
175
|
debugId: 'test',
|
|
@@ -197,9 +225,50 @@ describe('HashTreeParser', () => {
|
|
|
197
225
|
body: response,
|
|
198
226
|
});
|
|
199
227
|
}
|
|
228
|
+
|
|
229
|
+
async function checkAsyncDatasetInitialization(
|
|
230
|
+
parser: HashTreeParser,
|
|
231
|
+
newDataSet: {name: string; leafCount: number; url: string}
|
|
232
|
+
) {
|
|
233
|
+
// immediately we don't have the dataset yet, so it should not be in visibleDataSets
|
|
234
|
+
// and no hash tree should exist yet
|
|
235
|
+
expect(parser.visibleDataSets.some((vds) => vds.name === newDataSet.name)).to.be.false;
|
|
236
|
+
assert.isUndefined(parser.dataSets[newDataSet.name]);
|
|
237
|
+
|
|
238
|
+
// Wait for the async initialization to complete (queued as microtask)
|
|
239
|
+
await clock.tickAsync(0);
|
|
240
|
+
|
|
241
|
+
// The visibleDataSets is updated from the metadata object data
|
|
242
|
+
expect(parser.visibleDataSets.some((vds) => vds.name === newDataSet.name)).to.be.true;
|
|
243
|
+
|
|
244
|
+
// Verify that a hash tree was created for newDataSet
|
|
245
|
+
assert.exists(parser.dataSets[newDataSet.name].hashTree);
|
|
246
|
+
assert.equal(parser.dataSets[newDataSet.name].hashTree.numLeaves, newDataSet.leafCount);
|
|
247
|
+
|
|
248
|
+
// Verify getAllDataSetsMetadata was called for async initialization
|
|
249
|
+
assert.calledWith(
|
|
250
|
+
webexRequest,
|
|
251
|
+
sinon.match({
|
|
252
|
+
method: 'GET',
|
|
253
|
+
uri: visibleDataSetsUrl,
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
|
|
257
|
+
// Verify sync request was sent for the new dataset
|
|
258
|
+
assert.calledWith(
|
|
259
|
+
webexRequest,
|
|
260
|
+
sinon.match({
|
|
261
|
+
method: 'POST',
|
|
262
|
+
uri: `${newDataSet.url}/sync`,
|
|
263
|
+
})
|
|
264
|
+
);
|
|
265
|
+
}
|
|
200
266
|
it('should correctly initialize trees from initialLocus data', () => {
|
|
201
267
|
const parser = createHashTreeParser();
|
|
202
268
|
|
|
269
|
+
// verify that visibleDataSetsUrl is read out from inside locus
|
|
270
|
+
expect(parser.visibleDataSetsUrl).to.equal(visibleDataSetsUrl);
|
|
271
|
+
|
|
203
272
|
// Check that the correct number of trees are created
|
|
204
273
|
expect(Object.keys(parser.dataSets).length).to.equal(3);
|
|
205
274
|
|
|
@@ -215,7 +284,11 @@ describe('HashTreeParser', () => {
|
|
|
215
284
|
const selfTree = parser.dataSets.self.hashTree;
|
|
216
285
|
expect(selfTree).to.be.instanceOf(HashTree);
|
|
217
286
|
const expectedSelfLeaves = new Array(1).fill(null).map(() => ({}));
|
|
218
|
-
|
|
287
|
+
// Both self (id=4) and metadata (id=5) map to the same leaf (4%1=0, 5%1=0)
|
|
288
|
+
expectedSelfLeaves[0] = {
|
|
289
|
+
self: {4: {type: 'self', id: 4, version: 100}},
|
|
290
|
+
metadata: {5: {type: 'metadata', id: 5, version: 50}},
|
|
291
|
+
};
|
|
219
292
|
expect(selfTree.leaves).to.deep.equal(expectedSelfLeaves);
|
|
220
293
|
expect(selfTree.numLeaves).to.equal(1);
|
|
221
294
|
|
|
@@ -247,7 +320,7 @@ describe('HashTreeParser', () => {
|
|
|
247
320
|
name: 'empty-set',
|
|
248
321
|
});
|
|
249
322
|
|
|
250
|
-
const parser = createHashTreeParser(modifiedLocus);
|
|
323
|
+
const parser = createHashTreeParser(modifiedLocus, exampleMetadata);
|
|
251
324
|
|
|
252
325
|
expect(Object.keys(parser.dataSets).length).to.equal(4); // main, self, atd-unmuted (now empty), empty-set
|
|
253
326
|
|
|
@@ -260,7 +333,10 @@ describe('HashTreeParser', () => {
|
|
|
260
333
|
|
|
261
334
|
const selfTree = parser.dataSets.self.hashTree;
|
|
262
335
|
const expectedSelfLeaves = new Array(1).fill(null).map(() => ({}));
|
|
263
|
-
expectedSelfLeaves[4 % 1] = {
|
|
336
|
+
expectedSelfLeaves[4 % 1] = {
|
|
337
|
+
self: {4: {type: 'self', id: 4, version: 100}},
|
|
338
|
+
metadata: {5: exampleMetadata.htMeta.elementId},
|
|
339
|
+
};
|
|
264
340
|
expect(selfTree.leaves).to.deep.equal(expectedSelfLeaves);
|
|
265
341
|
expect(selfTree.numLeaves).to.equal(1);
|
|
266
342
|
|
|
@@ -283,25 +359,34 @@ describe('HashTreeParser', () => {
|
|
|
283
359
|
// Create a parser with minimal initial data
|
|
284
360
|
const minimalInitialLocus = {
|
|
285
361
|
dataSets: [],
|
|
286
|
-
locus:
|
|
287
|
-
|
|
288
|
-
|
|
362
|
+
locus: null,
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const minimalMetadata = {
|
|
366
|
+
htMeta: {
|
|
367
|
+
elementId: {
|
|
368
|
+
type: 'metadata',
|
|
369
|
+
id: 5,
|
|
370
|
+
version: 50,
|
|
289
371
|
},
|
|
372
|
+
dataSetNames: ['self'],
|
|
290
373
|
},
|
|
374
|
+
visibleDataSets: [
|
|
375
|
+
{name: 'main', url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main'},
|
|
376
|
+
{
|
|
377
|
+
name: 'self',
|
|
378
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
379
|
+
},
|
|
380
|
+
],
|
|
291
381
|
};
|
|
292
382
|
|
|
293
|
-
const hashTreeParser = createHashTreeParser(minimalInitialLocus);
|
|
383
|
+
const hashTreeParser = createHashTreeParser(minimalInitialLocus, minimalMetadata);
|
|
294
384
|
|
|
295
385
|
// Setup the datasets that will be returned from getAllDataSetsMetadata
|
|
296
386
|
const mainDataSet = createDataSet('main', 16, 1100);
|
|
297
387
|
const selfDataSet = createDataSet('self', 1, 2100);
|
|
298
|
-
const invisibleDataSet = createDataSet('invisible', 4, 4000);
|
|
299
388
|
|
|
300
|
-
mockGetAllDataSetsMetadata(webexRequest, visibleDataSetsUrl, [
|
|
301
|
-
mainDataSet,
|
|
302
|
-
selfDataSet,
|
|
303
|
-
invisibleDataSet,
|
|
304
|
-
]);
|
|
389
|
+
mockGetAllDataSetsMetadata(webexRequest, visibleDataSetsUrl, [mainDataSet, selfDataSet]);
|
|
305
390
|
|
|
306
391
|
// Mock sync requests for visible datasets with some updated objects
|
|
307
392
|
const mainSyncResponse = {
|
|
@@ -357,15 +442,16 @@ describe('HashTreeParser', () => {
|
|
|
357
442
|
})
|
|
358
443
|
);
|
|
359
444
|
|
|
360
|
-
//
|
|
445
|
+
// verify that visibleDataSetsUrl is set on the parser
|
|
446
|
+
expect(hashTreeParser.visibleDataSetsUrl).to.equal(visibleDataSetsUrl);
|
|
447
|
+
|
|
448
|
+
// Verify all datasets returned from visibleDataSetsUrl are added to dataSets
|
|
361
449
|
expect(hashTreeParser.dataSets.main).to.exist;
|
|
362
450
|
expect(hashTreeParser.dataSets.self).to.exist;
|
|
363
|
-
expect(hashTreeParser.dataSets.invisible).to.exist;
|
|
364
451
|
|
|
365
452
|
// Verify hash trees are created only for visible datasets
|
|
366
453
|
expect(hashTreeParser.dataSets.main.hashTree).to.be.instanceOf(HashTree);
|
|
367
454
|
expect(hashTreeParser.dataSets.self.hashTree).to.be.instanceOf(HashTree);
|
|
368
|
-
expect(hashTreeParser.dataSets.invisible.hashTree).to.be.undefined;
|
|
369
455
|
|
|
370
456
|
// Verify hash trees have correct leaf counts
|
|
371
457
|
expect(hashTreeParser.dataSets.main.hashTree.numLeaves).to.equal(16);
|
|
@@ -403,15 +489,6 @@ describe('HashTreeParser', () => {
|
|
|
403
489
|
})
|
|
404
490
|
);
|
|
405
491
|
|
|
406
|
-
// Verify sync request was NOT sent for invisible dataset
|
|
407
|
-
assert.neverCalledWith(
|
|
408
|
-
webexRequest,
|
|
409
|
-
sinon.match({
|
|
410
|
-
method: 'POST',
|
|
411
|
-
uri: `${invisibleDataSet.url}/sync`,
|
|
412
|
-
})
|
|
413
|
-
);
|
|
414
|
-
|
|
415
492
|
// Verify callback was called with OBJECTS_UPDATED and correct updatedObjects list
|
|
416
493
|
assert.calledWith(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
417
494
|
updatedObjects: [
|
|
@@ -443,8 +520,6 @@ describe('HashTreeParser', () => {
|
|
|
443
520
|
// verify that sync timers are set for visible datasets
|
|
444
521
|
expect(hashTreeParser.dataSets.main.timer).to.not.be.undefined;
|
|
445
522
|
expect(hashTreeParser.dataSets.self.timer).to.not.be.undefined;
|
|
446
|
-
// and not for invisible dataset
|
|
447
|
-
expect(hashTreeParser.dataSets.invisible.timer).to.be.undefined;
|
|
448
523
|
};
|
|
449
524
|
|
|
450
525
|
describe('#initializeFromMessage', () => {
|
|
@@ -461,7 +536,7 @@ describe('HashTreeParser', () => {
|
|
|
461
536
|
|
|
462
537
|
describe('#initializeFromGetLociResponse', () => {
|
|
463
538
|
it('does nothing if url for visibleDataSets is missing from locus', async () => {
|
|
464
|
-
const parser = createHashTreeParser({dataSets: [], locus: {}});
|
|
539
|
+
const parser = createHashTreeParser({dataSets: [], locus: {}}, null);
|
|
465
540
|
|
|
466
541
|
await parser.initializeFromGetLociResponse({participants: []});
|
|
467
542
|
|
|
@@ -488,12 +563,9 @@ describe('HashTreeParser', () => {
|
|
|
488
563
|
it('updates hash trees based on provided new locus', () => {
|
|
489
564
|
const parser = createHashTreeParser();
|
|
490
565
|
|
|
491
|
-
const mainPutItemsSpy = sinon
|
|
492
|
-
|
|
493
|
-
const
|
|
494
|
-
.spy(parser.dataSets.self.hashTree, 'putItems');
|
|
495
|
-
const atdUnmutedPutItemsSpy = sinon
|
|
496
|
-
.spy(parser.dataSets['atd-unmuted'].hashTree, 'putItems');
|
|
566
|
+
const mainPutItemsSpy = sinon.spy(parser.dataSets.main.hashTree, 'putItems');
|
|
567
|
+
const selfPutItemsSpy = sinon.spy(parser.dataSets.self.hashTree, 'putItems');
|
|
568
|
+
const atdUnmutedPutItemsSpy = sinon.spy(parser.dataSets['atd-unmuted'].hashTree, 'putItems');
|
|
497
569
|
|
|
498
570
|
// Create a locus update with new htMeta information for some things
|
|
499
571
|
const locusUpdate = {
|
|
@@ -540,7 +612,6 @@ describe('HashTreeParser', () => {
|
|
|
540
612
|
],
|
|
541
613
|
self: {
|
|
542
614
|
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/11941033',
|
|
543
|
-
visibleDataSets: ['main', 'self', 'atd-unmuted'],
|
|
544
615
|
person: {},
|
|
545
616
|
htMeta: {
|
|
546
617
|
elementId: {
|
|
@@ -711,6 +782,211 @@ describe('HashTreeParser', () => {
|
|
|
711
782
|
],
|
|
712
783
|
});
|
|
713
784
|
});
|
|
785
|
+
|
|
786
|
+
it('handles metadata updates with new version', async () => {
|
|
787
|
+
const parser = createHashTreeParser();
|
|
788
|
+
|
|
789
|
+
const selfPutItemSpy = sinon.spy(parser.dataSets.self.hashTree, 'putItem');
|
|
790
|
+
|
|
791
|
+
// Create a locus update with updated metadata
|
|
792
|
+
const locusUpdate = {
|
|
793
|
+
dataSets: [createDataSet('self', 1, 2100), createDataSet('attendees', 8, 4000)],
|
|
794
|
+
locus: {
|
|
795
|
+
links: {resources: {visibleDataSets: {url: visibleDataSetsUrl}}},
|
|
796
|
+
participants: [
|
|
797
|
+
{
|
|
798
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/15',
|
|
799
|
+
person: {},
|
|
800
|
+
htMeta: {
|
|
801
|
+
elementId: {
|
|
802
|
+
type: 'participant',
|
|
803
|
+
id: 15, // new participant
|
|
804
|
+
version: 999,
|
|
805
|
+
},
|
|
806
|
+
dataSetNames: ['attendees'],
|
|
807
|
+
},
|
|
808
|
+
},
|
|
809
|
+
],
|
|
810
|
+
},
|
|
811
|
+
metadata: {
|
|
812
|
+
htMeta: {
|
|
813
|
+
elementId: {
|
|
814
|
+
type: 'metadata',
|
|
815
|
+
id: 5,
|
|
816
|
+
version: 51, // incremented version
|
|
817
|
+
},
|
|
818
|
+
dataSetNames: ['self'],
|
|
819
|
+
},
|
|
820
|
+
// new visibleDataSets: atd-unmuted removed, "attendees" and "new-dataset" added
|
|
821
|
+
visibleDataSets: [
|
|
822
|
+
{
|
|
823
|
+
name: 'main',
|
|
824
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
825
|
+
},
|
|
826
|
+
{
|
|
827
|
+
name: 'self',
|
|
828
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
829
|
+
},
|
|
830
|
+
{
|
|
831
|
+
name: 'new-dataset', // this one is not in dataSets, so will require async initialization
|
|
832
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/new-dataset',
|
|
833
|
+
},
|
|
834
|
+
{
|
|
835
|
+
name: 'attendees', // this one is in dataSets, so should be processed immediately
|
|
836
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/attendees',
|
|
837
|
+
},
|
|
838
|
+
],
|
|
839
|
+
},
|
|
840
|
+
};
|
|
841
|
+
|
|
842
|
+
// Mock the async initialization of the new dataset
|
|
843
|
+
const newDataSet = createDataSet('new-dataset', 4, 5000);
|
|
844
|
+
mockGetAllDataSetsMetadata(webexRequest, visibleDataSetsUrl, [newDataSet]);
|
|
845
|
+
mockSyncRequest(webexRequest, newDataSet.url, {
|
|
846
|
+
dataSets: [newDataSet],
|
|
847
|
+
visibleDataSetsUrl,
|
|
848
|
+
locusUrl,
|
|
849
|
+
locusStateElements: [],
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
// Call handleLocusUpdate
|
|
853
|
+
parser.handleLocusUpdate(locusUpdate);
|
|
854
|
+
|
|
855
|
+
// Verify putItem was called on self hash tree with metadata
|
|
856
|
+
assert.calledOnceWithExactly(selfPutItemSpy, {type: 'metadata', id: 5, version: 51});
|
|
857
|
+
|
|
858
|
+
console.log(
|
|
859
|
+
'callback calls',
|
|
860
|
+
callback.getCalls().map((call) => JSON.stringify(call.args, null, 2))
|
|
861
|
+
);
|
|
862
|
+
// Verify callback was called with metadata object and removed dataset objects
|
|
863
|
+
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
864
|
+
updatedObjects: [
|
|
865
|
+
// updated metadata object:
|
|
866
|
+
{
|
|
867
|
+
htMeta: {
|
|
868
|
+
elementId: {
|
|
869
|
+
type: 'metadata',
|
|
870
|
+
id: 5,
|
|
871
|
+
version: 51,
|
|
872
|
+
},
|
|
873
|
+
dataSetNames: ['self'],
|
|
874
|
+
},
|
|
875
|
+
data: {
|
|
876
|
+
htMeta: {
|
|
877
|
+
elementId: {
|
|
878
|
+
type: 'metadata',
|
|
879
|
+
id: 5,
|
|
880
|
+
version: 51,
|
|
881
|
+
},
|
|
882
|
+
dataSetNames: ['self'],
|
|
883
|
+
},
|
|
884
|
+
visibleDataSets: [
|
|
885
|
+
{
|
|
886
|
+
name: 'main',
|
|
887
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
888
|
+
},
|
|
889
|
+
{
|
|
890
|
+
name: 'self',
|
|
891
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
892
|
+
},
|
|
893
|
+
{
|
|
894
|
+
name: 'new-dataset',
|
|
895
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/new-dataset',
|
|
896
|
+
},
|
|
897
|
+
{
|
|
898
|
+
name: 'attendees',
|
|
899
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/attendees',
|
|
900
|
+
},
|
|
901
|
+
],
|
|
902
|
+
},
|
|
903
|
+
},
|
|
904
|
+
// removed participant from a removed dataset 'atd-unmuted':
|
|
905
|
+
{
|
|
906
|
+
htMeta: {
|
|
907
|
+
elementId: {
|
|
908
|
+
type: 'participant',
|
|
909
|
+
id: 14,
|
|
910
|
+
version: 300,
|
|
911
|
+
},
|
|
912
|
+
dataSetNames: ['atd-unmuted'],
|
|
913
|
+
},
|
|
914
|
+
data: null,
|
|
915
|
+
},
|
|
916
|
+
// new participant from a new data set 'attendees':
|
|
917
|
+
{
|
|
918
|
+
htMeta: {
|
|
919
|
+
elementId: {
|
|
920
|
+
type: 'participant',
|
|
921
|
+
id: 15,
|
|
922
|
+
version: 999,
|
|
923
|
+
},
|
|
924
|
+
dataSetNames: ['attendees'],
|
|
925
|
+
},
|
|
926
|
+
data: {
|
|
927
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/15',
|
|
928
|
+
person: {},
|
|
929
|
+
htMeta: {
|
|
930
|
+
elementId: {
|
|
931
|
+
type: 'participant',
|
|
932
|
+
id: 15,
|
|
933
|
+
version: 999,
|
|
934
|
+
},
|
|
935
|
+
dataSetNames: ['attendees'],
|
|
936
|
+
},
|
|
937
|
+
},
|
|
938
|
+
},
|
|
939
|
+
],
|
|
940
|
+
});
|
|
941
|
+
|
|
942
|
+
// verify also that an async initialization was done for
|
|
943
|
+
await checkAsyncDatasetInitialization(parser, newDataSet);
|
|
944
|
+
});
|
|
945
|
+
|
|
946
|
+
it('handles metadata updates with same version (no callback)', () => {
|
|
947
|
+
const parser = createHashTreeParser();
|
|
948
|
+
|
|
949
|
+
const selfPutItemSpy = sinon.spy(parser.dataSets.self.hashTree, 'putItem');
|
|
950
|
+
|
|
951
|
+
// Create a locus update with metadata that has the same version and same visibleDataSets
|
|
952
|
+
const locusUpdate = {
|
|
953
|
+
dataSets: [createDataSet('self', 1, 2100)],
|
|
954
|
+
locus: {},
|
|
955
|
+
metadata: {
|
|
956
|
+
htMeta: {
|
|
957
|
+
elementId: {
|
|
958
|
+
type: 'metadata',
|
|
959
|
+
id: 5,
|
|
960
|
+
version: 50, // same version as initial
|
|
961
|
+
},
|
|
962
|
+
dataSetNames: ['self'],
|
|
963
|
+
},
|
|
964
|
+
visibleDataSets: [
|
|
965
|
+
{
|
|
966
|
+
name: 'main',
|
|
967
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
968
|
+
},
|
|
969
|
+
{
|
|
970
|
+
name: 'self',
|
|
971
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
972
|
+
},
|
|
973
|
+
{
|
|
974
|
+
name: 'atd-unmuted',
|
|
975
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/atd-unmuted',
|
|
976
|
+
},
|
|
977
|
+
],
|
|
978
|
+
},
|
|
979
|
+
};
|
|
980
|
+
|
|
981
|
+
// Call handleLocusUpdate
|
|
982
|
+
parser.handleLocusUpdate(locusUpdate);
|
|
983
|
+
|
|
984
|
+
// Verify putItem was called on self hash tree
|
|
985
|
+
assert.calledOnceWithExactly(selfPutItemSpy, {type: 'metadata', id: 5, version: 50});
|
|
986
|
+
|
|
987
|
+
// Verify callback was NOT called because version didn't change
|
|
988
|
+
assert.notCalled(callback);
|
|
989
|
+
});
|
|
714
990
|
});
|
|
715
991
|
|
|
716
992
|
describe('#handleMessage', () => {
|
|
@@ -910,13 +1186,6 @@ describe('HashTreeParser', () => {
|
|
|
910
1186
|
// Verify callback was called with OBJECTS_UPDATED and all updated objects
|
|
911
1187
|
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
912
1188
|
updatedObjects: [
|
|
913
|
-
{
|
|
914
|
-
htMeta: {
|
|
915
|
-
elementId: {type: 'self', id: 4, version: 101},
|
|
916
|
-
dataSetNames: ['self'],
|
|
917
|
-
},
|
|
918
|
-
data: {person: {name: 'updated self name'}},
|
|
919
|
-
},
|
|
920
1189
|
{
|
|
921
1190
|
htMeta: {
|
|
922
1191
|
elementId: {type: 'locus', id: 0, version: 201},
|
|
@@ -924,10 +1193,6 @@ describe('HashTreeParser', () => {
|
|
|
924
1193
|
},
|
|
925
1194
|
data: {info: {id: 'updated-locus-info'}},
|
|
926
1195
|
},
|
|
927
|
-
// self updates appear twice, because they are processed twice in HashTreeParser.parseMessage()
|
|
928
|
-
// (first for checking for visibleDataSets changes and again with the rest of updates in the main part of parseMessage())
|
|
929
|
-
// this is only temporary until SPARK-744859 is done and having them twice here is not harmful
|
|
930
|
-
// so keeping it like this for now
|
|
931
1196
|
{
|
|
932
1197
|
htMeta: {
|
|
933
1198
|
elementId: {type: 'self', id: 4, version: 101},
|
|
@@ -1179,6 +1444,9 @@ describe('HashTreeParser', () => {
|
|
|
1179
1444
|
sinon.match({
|
|
1180
1445
|
method: 'GET',
|
|
1181
1446
|
uri: `${mainDataSetUrl}/hashtree`,
|
|
1447
|
+
qs: {
|
|
1448
|
+
rootHash: hashTree.getRootHash(),
|
|
1449
|
+
},
|
|
1182
1450
|
})
|
|
1183
1451
|
);
|
|
1184
1452
|
|
|
@@ -1186,6 +1454,7 @@ describe('HashTreeParser', () => {
|
|
|
1186
1454
|
assert.calledWith(webexRequest, {
|
|
1187
1455
|
method: 'POST',
|
|
1188
1456
|
uri: `${mainDataSetUrl}/sync`,
|
|
1457
|
+
qs: {rootHash: hashTree.getRootHash()},
|
|
1189
1458
|
body: {
|
|
1190
1459
|
leafCount: 16,
|
|
1191
1460
|
leafDataEntries: [
|
|
@@ -1239,10 +1508,17 @@ describe('HashTreeParser', () => {
|
|
|
1239
1508
|
assert.calledWith(webexRequest, {
|
|
1240
1509
|
method: 'POST',
|
|
1241
1510
|
uri: `${parser.dataSets.self.url}/sync`,
|
|
1511
|
+
qs: {rootHash: parser.dataSets.self.hashTree.getRootHash()},
|
|
1242
1512
|
body: {
|
|
1243
1513
|
leafCount: 1,
|
|
1244
1514
|
leafDataEntries: [
|
|
1245
|
-
{
|
|
1515
|
+
{
|
|
1516
|
+
leafIndex: 0,
|
|
1517
|
+
elementIds: [
|
|
1518
|
+
{type: 'self', id: 4, version: 102},
|
|
1519
|
+
{type: 'metadata', id: 5, version: 50},
|
|
1520
|
+
],
|
|
1521
|
+
},
|
|
1246
1522
|
],
|
|
1247
1523
|
},
|
|
1248
1524
|
});
|
|
@@ -1257,7 +1533,7 @@ describe('HashTreeParser', () => {
|
|
|
1257
1533
|
// Stub updateItems on self hash tree to return true
|
|
1258
1534
|
sinon.stub(parser.dataSets.self.hashTree, 'updateItems').returns([true]);
|
|
1259
1535
|
|
|
1260
|
-
// Send a message with
|
|
1536
|
+
// Send a message with Metadata object that has a new visibleDataSets list
|
|
1261
1537
|
const message = {
|
|
1262
1538
|
dataSets: [createDataSet('self', 1, 2100), createDataSet('attendees', 8, 4000)],
|
|
1263
1539
|
visibleDataSetsUrl,
|
|
@@ -1266,14 +1542,31 @@ describe('HashTreeParser', () => {
|
|
|
1266
1542
|
{
|
|
1267
1543
|
htMeta: {
|
|
1268
1544
|
elementId: {
|
|
1269
|
-
type: '
|
|
1270
|
-
id:
|
|
1271
|
-
version:
|
|
1545
|
+
type: 'metadata' as const,
|
|
1546
|
+
id: 5,
|
|
1547
|
+
version: 51,
|
|
1272
1548
|
},
|
|
1273
1549
|
dataSetNames: ['self'],
|
|
1274
1550
|
},
|
|
1275
1551
|
data: {
|
|
1276
|
-
visibleDataSets: [
|
|
1552
|
+
visibleDataSets: [
|
|
1553
|
+
{
|
|
1554
|
+
name: 'main',
|
|
1555
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
name: 'self',
|
|
1559
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
name: 'atd-unmuted',
|
|
1563
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/atd-unmuted',
|
|
1564
|
+
},
|
|
1565
|
+
{
|
|
1566
|
+
name: 'attendees',
|
|
1567
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/attendees',
|
|
1568
|
+
},
|
|
1569
|
+
], // added 'attendees'
|
|
1277
1570
|
},
|
|
1278
1571
|
},
|
|
1279
1572
|
],
|
|
@@ -1282,31 +1575,65 @@ describe('HashTreeParser', () => {
|
|
|
1282
1575
|
await parser.handleMessage(message, 'add visible dataset');
|
|
1283
1576
|
|
|
1284
1577
|
// Verify that 'attendees' was added to visibleDataSets
|
|
1285
|
-
|
|
1578
|
+
expect(parser.visibleDataSets.some((vds) => vds.name === 'attendees')).to.be.true;
|
|
1286
1579
|
|
|
1287
1580
|
// Verify that a hash tree was created for 'attendees'
|
|
1288
1581
|
assert.exists(parser.dataSets.attendees.hashTree);
|
|
1289
1582
|
assert.equal(parser.dataSets.attendees.hashTree.numLeaves, 8);
|
|
1290
1583
|
|
|
1291
|
-
// Verify callback was called with the
|
|
1584
|
+
// Verify callback was called with the metadata update (appears twice - processed once for visible dataset changes, once in main loop)
|
|
1292
1585
|
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
1293
1586
|
updatedObjects: [
|
|
1294
1587
|
{
|
|
1295
1588
|
htMeta: {
|
|
1296
|
-
elementId: {type: '
|
|
1589
|
+
elementId: {type: 'metadata', id: 5, version: 51},
|
|
1297
1590
|
dataSetNames: ['self'],
|
|
1298
1591
|
},
|
|
1299
1592
|
data: {
|
|
1300
|
-
visibleDataSets: [
|
|
1593
|
+
visibleDataSets: [
|
|
1594
|
+
{
|
|
1595
|
+
name: 'main',
|
|
1596
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
1597
|
+
},
|
|
1598
|
+
{
|
|
1599
|
+
name: 'self',
|
|
1600
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
1601
|
+
},
|
|
1602
|
+
{
|
|
1603
|
+
name: 'atd-unmuted',
|
|
1604
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/atd-unmuted',
|
|
1605
|
+
},
|
|
1606
|
+
{
|
|
1607
|
+
name: 'attendees',
|
|
1608
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/attendees',
|
|
1609
|
+
},
|
|
1610
|
+
],
|
|
1301
1611
|
},
|
|
1302
1612
|
},
|
|
1303
1613
|
{
|
|
1304
1614
|
htMeta: {
|
|
1305
|
-
elementId: {type: '
|
|
1615
|
+
elementId: {type: 'metadata', id: 5, version: 51},
|
|
1306
1616
|
dataSetNames: ['self'],
|
|
1307
1617
|
},
|
|
1308
1618
|
data: {
|
|
1309
|
-
visibleDataSets: [
|
|
1619
|
+
visibleDataSets: [
|
|
1620
|
+
{
|
|
1621
|
+
name: 'main',
|
|
1622
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
1623
|
+
},
|
|
1624
|
+
{
|
|
1625
|
+
name: 'self',
|
|
1626
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
1627
|
+
},
|
|
1628
|
+
{
|
|
1629
|
+
name: 'atd-unmuted',
|
|
1630
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/atd-unmuted',
|
|
1631
|
+
},
|
|
1632
|
+
{
|
|
1633
|
+
name: 'attendees',
|
|
1634
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/attendees',
|
|
1635
|
+
},
|
|
1636
|
+
],
|
|
1310
1637
|
},
|
|
1311
1638
|
},
|
|
1312
1639
|
],
|
|
@@ -1320,7 +1647,7 @@ describe('HashTreeParser', () => {
|
|
|
1320
1647
|
// Stub updateItems on self hash tree to return true
|
|
1321
1648
|
sinon.stub(parser.dataSets.self.hashTree, 'updateItems').returns([true]);
|
|
1322
1649
|
|
|
1323
|
-
// Send a message with
|
|
1650
|
+
// Send a message with Metadata object that has a new visibleDataSets list (adding 'new-dataset')
|
|
1324
1651
|
// but WITHOUT providing info about the new dataset in dataSets array
|
|
1325
1652
|
const message = {
|
|
1326
1653
|
dataSets: [createDataSet('self', 1, 2100)],
|
|
@@ -1330,14 +1657,31 @@ describe('HashTreeParser', () => {
|
|
|
1330
1657
|
{
|
|
1331
1658
|
htMeta: {
|
|
1332
1659
|
elementId: {
|
|
1333
|
-
type: '
|
|
1334
|
-
id:
|
|
1335
|
-
version:
|
|
1660
|
+
type: 'metadata' as const,
|
|
1661
|
+
id: 5,
|
|
1662
|
+
version: 51,
|
|
1336
1663
|
},
|
|
1337
1664
|
dataSetNames: ['self'],
|
|
1338
1665
|
},
|
|
1339
1666
|
data: {
|
|
1340
|
-
visibleDataSets: [
|
|
1667
|
+
visibleDataSets: [
|
|
1668
|
+
{
|
|
1669
|
+
name: 'main',
|
|
1670
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
1671
|
+
},
|
|
1672
|
+
{
|
|
1673
|
+
name: 'self',
|
|
1674
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
1675
|
+
},
|
|
1676
|
+
{
|
|
1677
|
+
name: 'atd-unmuted',
|
|
1678
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/atd-unmuted',
|
|
1679
|
+
},
|
|
1680
|
+
{
|
|
1681
|
+
name: 'new-dataset',
|
|
1682
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/new-dataset',
|
|
1683
|
+
},
|
|
1684
|
+
],
|
|
1341
1685
|
},
|
|
1342
1686
|
},
|
|
1343
1687
|
],
|
|
@@ -1355,38 +1699,7 @@ describe('HashTreeParser', () => {
|
|
|
1355
1699
|
|
|
1356
1700
|
await parser.handleMessage(message, 'add new dataset requiring async init');
|
|
1357
1701
|
|
|
1358
|
-
|
|
1359
|
-
// and no hash tree should exist yet
|
|
1360
|
-
assert.isFalse(parser.visibleDataSets.includes('new-dataset'));
|
|
1361
|
-
assert.isUndefined(parser.dataSets['new-dataset']);
|
|
1362
|
-
|
|
1363
|
-
// Wait for the async initialization to complete (queued as microtask)
|
|
1364
|
-
await clock.tickAsync(0);
|
|
1365
|
-
|
|
1366
|
-
// The visibleDataSets is updated from the self object data
|
|
1367
|
-
assert.include(parser.visibleDataSets, 'new-dataset');
|
|
1368
|
-
|
|
1369
|
-
// Verify that a hash tree was created for 'new-dataset'
|
|
1370
|
-
assert.exists(parser.dataSets['new-dataset'].hashTree);
|
|
1371
|
-
assert.equal(parser.dataSets['new-dataset'].hashTree.numLeaves, 4);
|
|
1372
|
-
|
|
1373
|
-
// Verify getAllDataSetsMetadata was called for async initialization
|
|
1374
|
-
assert.calledWith(
|
|
1375
|
-
webexRequest,
|
|
1376
|
-
sinon.match({
|
|
1377
|
-
method: 'GET',
|
|
1378
|
-
uri: visibleDataSetsUrl,
|
|
1379
|
-
})
|
|
1380
|
-
);
|
|
1381
|
-
|
|
1382
|
-
// Verify sync request was sent for the new dataset
|
|
1383
|
-
assert.calledWith(
|
|
1384
|
-
webexRequest,
|
|
1385
|
-
sinon.match({
|
|
1386
|
-
method: 'POST',
|
|
1387
|
-
uri: `${newDataSet.url}/sync`,
|
|
1388
|
-
})
|
|
1389
|
-
);
|
|
1702
|
+
await checkAsyncDatasetInitialization(parser, newDataSet);
|
|
1390
1703
|
});
|
|
1391
1704
|
|
|
1392
1705
|
it('handles removal of visible data set', async () => {
|
|
@@ -1406,7 +1719,7 @@ describe('HashTreeParser', () => {
|
|
|
1406
1719
|
// Stub updateItems on self hash tree to return true
|
|
1407
1720
|
sinon.stub(parser.dataSets.self.hashTree, 'updateItems').returns([true]);
|
|
1408
1721
|
|
|
1409
|
-
// Send a message with
|
|
1722
|
+
// Send a message with Metadata object that has removed 'atd-unmuted' from visibleDataSets
|
|
1410
1723
|
const message = {
|
|
1411
1724
|
dataSets: [createDataSet('self', 1, 2100)],
|
|
1412
1725
|
visibleDataSetsUrl,
|
|
@@ -1415,14 +1728,23 @@ describe('HashTreeParser', () => {
|
|
|
1415
1728
|
{
|
|
1416
1729
|
htMeta: {
|
|
1417
1730
|
elementId: {
|
|
1418
|
-
type: '
|
|
1419
|
-
id:
|
|
1420
|
-
version:
|
|
1731
|
+
type: 'metadata' as const,
|
|
1732
|
+
id: 5,
|
|
1733
|
+
version: 51,
|
|
1421
1734
|
},
|
|
1422
1735
|
dataSetNames: ['self'],
|
|
1423
1736
|
},
|
|
1424
1737
|
data: {
|
|
1425
|
-
visibleDataSets: [
|
|
1738
|
+
visibleDataSets: [
|
|
1739
|
+
{
|
|
1740
|
+
name: 'main',
|
|
1741
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
1742
|
+
},
|
|
1743
|
+
{
|
|
1744
|
+
name: 'self',
|
|
1745
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
1746
|
+
},
|
|
1747
|
+
], // removed 'atd-unmuted'
|
|
1426
1748
|
},
|
|
1427
1749
|
},
|
|
1428
1750
|
],
|
|
@@ -1431,7 +1753,7 @@ describe('HashTreeParser', () => {
|
|
|
1431
1753
|
await parser.handleMessage(message, 'remove visible dataset');
|
|
1432
1754
|
|
|
1433
1755
|
// Verify that 'atd-unmuted' was removed from visibleDataSets
|
|
1434
|
-
|
|
1756
|
+
expect(parser.visibleDataSets.some((vds) => vds.name === 'atd-unmuted')).to.be.false;
|
|
1435
1757
|
|
|
1436
1758
|
// Verify that the hash tree for 'atd-unmuted' was deleted
|
|
1437
1759
|
assert.isUndefined(parser.dataSets['atd-unmuted'].hashTree);
|
|
@@ -1439,16 +1761,25 @@ describe('HashTreeParser', () => {
|
|
|
1439
1761
|
// Verify that the timer was cleared
|
|
1440
1762
|
assert.isUndefined(parser.dataSets['atd-unmuted'].timer);
|
|
1441
1763
|
|
|
1442
|
-
// Verify callback was called with
|
|
1764
|
+
// Verify callback was called with the metadata update and the removed objects (metadata appears twice - processed once for dataset changes, once in main loop)
|
|
1443
1765
|
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
1444
1766
|
updatedObjects: [
|
|
1445
1767
|
{
|
|
1446
1768
|
htMeta: {
|
|
1447
|
-
elementId: {type: '
|
|
1769
|
+
elementId: {type: 'metadata', id: 5, version: 51},
|
|
1448
1770
|
dataSetNames: ['self'],
|
|
1449
1771
|
},
|
|
1450
1772
|
data: {
|
|
1451
|
-
visibleDataSets: [
|
|
1773
|
+
visibleDataSets: [
|
|
1774
|
+
{
|
|
1775
|
+
name: 'main',
|
|
1776
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
1777
|
+
},
|
|
1778
|
+
{
|
|
1779
|
+
name: 'self',
|
|
1780
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
1781
|
+
},
|
|
1782
|
+
],
|
|
1452
1783
|
},
|
|
1453
1784
|
},
|
|
1454
1785
|
{
|
|
@@ -1460,11 +1791,20 @@ describe('HashTreeParser', () => {
|
|
|
1460
1791
|
},
|
|
1461
1792
|
{
|
|
1462
1793
|
htMeta: {
|
|
1463
|
-
elementId: {type: '
|
|
1794
|
+
elementId: {type: 'metadata', id: 5, version: 51},
|
|
1464
1795
|
dataSetNames: ['self'],
|
|
1465
1796
|
},
|
|
1466
1797
|
data: {
|
|
1467
|
-
visibleDataSets: [
|
|
1798
|
+
visibleDataSets: [
|
|
1799
|
+
{
|
|
1800
|
+
name: 'main',
|
|
1801
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/datasets/main',
|
|
1802
|
+
},
|
|
1803
|
+
{
|
|
1804
|
+
name: 'self',
|
|
1805
|
+
url: 'https://locus-a.wbx2.com/locus/api/v1/loci/97d64a5f/participant/713e9f99/datasets/self',
|
|
1806
|
+
},
|
|
1807
|
+
],
|
|
1468
1808
|
},
|
|
1469
1809
|
},
|
|
1470
1810
|
],
|
|
@@ -1489,7 +1829,7 @@ describe('HashTreeParser', () => {
|
|
|
1489
1829
|
});
|
|
1490
1830
|
|
|
1491
1831
|
// Verify attendees is NOT in visibleDataSets
|
|
1492
|
-
|
|
1832
|
+
expect(parser.visibleDataSets.some((vds) => vds.name === 'attendees')).to.be.false;
|
|
1493
1833
|
|
|
1494
1834
|
// Send a message with attendees data
|
|
1495
1835
|
const message = {
|
|
@@ -1521,4 +1861,496 @@ describe('HashTreeParser', () => {
|
|
|
1521
1861
|
});
|
|
1522
1862
|
});
|
|
1523
1863
|
});
|
|
1864
|
+
|
|
1865
|
+
describe('#callLocusInfoUpdateCallback filtering', () => {
|
|
1866
|
+
// Helper to setup parser with initial objects and reset callback history
|
|
1867
|
+
async function setupParserWithObjects(locusStateElements: any[]) {
|
|
1868
|
+
const parser = createHashTreeParser();
|
|
1869
|
+
|
|
1870
|
+
if (locusStateElements.length > 0) {
|
|
1871
|
+
// Determine which datasets to include based on the objects' dataSetNames
|
|
1872
|
+
const dataSetNames = new Set<string>();
|
|
1873
|
+
locusStateElements.forEach((element) => {
|
|
1874
|
+
element.htMeta?.dataSetNames?.forEach((name) => dataSetNames.add(name));
|
|
1875
|
+
});
|
|
1876
|
+
|
|
1877
|
+
const dataSets = [];
|
|
1878
|
+
if (dataSetNames.has('main')) dataSets.push(createDataSet('main', 16, 1100));
|
|
1879
|
+
if (dataSetNames.has('self')) dataSets.push(createDataSet('self', 1, 2100));
|
|
1880
|
+
if (dataSetNames.has('atd-unmuted')) dataSets.push(createDataSet('atd-unmuted', 16, 3100));
|
|
1881
|
+
|
|
1882
|
+
const setupMessage = {
|
|
1883
|
+
dataSets,
|
|
1884
|
+
visibleDataSetsUrl,
|
|
1885
|
+
locusUrl,
|
|
1886
|
+
locusStateElements,
|
|
1887
|
+
};
|
|
1888
|
+
|
|
1889
|
+
await parser.handleMessage(setupMessage, 'setup');
|
|
1890
|
+
}
|
|
1891
|
+
|
|
1892
|
+
callback.resetHistory();
|
|
1893
|
+
return parser;
|
|
1894
|
+
}
|
|
1895
|
+
|
|
1896
|
+
it('filters out updates when a dataset has a higher version', async () => {
|
|
1897
|
+
const parser = await setupParserWithObjects([
|
|
1898
|
+
{
|
|
1899
|
+
htMeta: {
|
|
1900
|
+
elementId: {type: 'locus' as const, id: 5, version: 100},
|
|
1901
|
+
dataSetNames: ['main'],
|
|
1902
|
+
},
|
|
1903
|
+
data: {existingField: 'existing'},
|
|
1904
|
+
},
|
|
1905
|
+
]);
|
|
1906
|
+
|
|
1907
|
+
// Try to update with an older version (90)
|
|
1908
|
+
const updateMessage = {
|
|
1909
|
+
dataSets: [createDataSet('main', 16, 1101)],
|
|
1910
|
+
visibleDataSetsUrl,
|
|
1911
|
+
locusUrl,
|
|
1912
|
+
locusStateElements: [
|
|
1913
|
+
{
|
|
1914
|
+
htMeta: {
|
|
1915
|
+
elementId: {type: 'locus' as const, id: 5, version: 90},
|
|
1916
|
+
dataSetNames: ['main'],
|
|
1917
|
+
},
|
|
1918
|
+
data: {someField: 'value'},
|
|
1919
|
+
},
|
|
1920
|
+
],
|
|
1921
|
+
};
|
|
1922
|
+
|
|
1923
|
+
await parser.handleMessage(updateMessage, 'update with older version');
|
|
1924
|
+
|
|
1925
|
+
// Callback should not be called because the update was filtered out
|
|
1926
|
+
assert.notCalled(callback);
|
|
1927
|
+
});
|
|
1928
|
+
|
|
1929
|
+
it('allows updates when version is newer than existing', async () => {
|
|
1930
|
+
const parser = await setupParserWithObjects([
|
|
1931
|
+
{
|
|
1932
|
+
htMeta: {
|
|
1933
|
+
elementId: {type: 'locus' as const, id: 5, version: 100},
|
|
1934
|
+
dataSetNames: ['main'],
|
|
1935
|
+
},
|
|
1936
|
+
data: {existingField: 'existing'},
|
|
1937
|
+
},
|
|
1938
|
+
]);
|
|
1939
|
+
|
|
1940
|
+
// Try to update with a newer version (110)
|
|
1941
|
+
const updateMessage = {
|
|
1942
|
+
dataSets: [createDataSet('main', 16, 1101)],
|
|
1943
|
+
visibleDataSetsUrl,
|
|
1944
|
+
locusUrl,
|
|
1945
|
+
locusStateElements: [
|
|
1946
|
+
{
|
|
1947
|
+
htMeta: {
|
|
1948
|
+
elementId: {type: 'locus' as const, id: 5, version: 110},
|
|
1949
|
+
dataSetNames: ['main'],
|
|
1950
|
+
},
|
|
1951
|
+
data: {someField: 'new value'},
|
|
1952
|
+
},
|
|
1953
|
+
],
|
|
1954
|
+
};
|
|
1955
|
+
|
|
1956
|
+
await parser.handleMessage(updateMessage, 'update with newer version');
|
|
1957
|
+
|
|
1958
|
+
// Callback should be called with the update
|
|
1959
|
+
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
1960
|
+
updatedObjects: [
|
|
1961
|
+
{
|
|
1962
|
+
htMeta: {
|
|
1963
|
+
elementId: {type: 'locus', id: 5, version: 110},
|
|
1964
|
+
dataSetNames: ['main'],
|
|
1965
|
+
},
|
|
1966
|
+
data: {someField: 'new value'},
|
|
1967
|
+
},
|
|
1968
|
+
],
|
|
1969
|
+
});
|
|
1970
|
+
});
|
|
1971
|
+
|
|
1972
|
+
it('filters out removal when object still exists in any dataset', async () => {
|
|
1973
|
+
const parser = await setupParserWithObjects([
|
|
1974
|
+
{
|
|
1975
|
+
htMeta: {
|
|
1976
|
+
elementId: {type: 'participant' as const, id: 10, version: 50},
|
|
1977
|
+
dataSetNames: ['main', 'atd-unmuted'],
|
|
1978
|
+
},
|
|
1979
|
+
data: {name: 'participant'},
|
|
1980
|
+
},
|
|
1981
|
+
]);
|
|
1982
|
+
|
|
1983
|
+
// Try to remove the object from main only (it still exists in atd-unmuted)
|
|
1984
|
+
const removalMessage = {
|
|
1985
|
+
dataSets: [createDataSet('main', 16, 1101)],
|
|
1986
|
+
visibleDataSetsUrl,
|
|
1987
|
+
locusUrl,
|
|
1988
|
+
locusStateElements: [
|
|
1989
|
+
{
|
|
1990
|
+
htMeta: {
|
|
1991
|
+
elementId: {type: 'participant' as const, id: 10, version: 50},
|
|
1992
|
+
dataSetNames: ['main'],
|
|
1993
|
+
},
|
|
1994
|
+
data: null, // removal
|
|
1995
|
+
},
|
|
1996
|
+
],
|
|
1997
|
+
};
|
|
1998
|
+
|
|
1999
|
+
await parser.handleMessage(removalMessage, 'removal from one dataset');
|
|
2000
|
+
|
|
2001
|
+
// Callback should not be called because object still exists in atd-unmuted
|
|
2002
|
+
assert.notCalled(callback);
|
|
2003
|
+
});
|
|
2004
|
+
|
|
2005
|
+
it('allows removal when object does not exist in any dataset', async () => {
|
|
2006
|
+
const parser = await setupParserWithObjects([]);
|
|
2007
|
+
|
|
2008
|
+
// Stub updateItems to return true (simulating that the removal was "applied")
|
|
2009
|
+
sinon.stub(parser.dataSets.main.hashTree, 'updateItems').returns([true]);
|
|
2010
|
+
|
|
2011
|
+
// Try to remove an object that doesn't exist anywhere
|
|
2012
|
+
const removalMessage = {
|
|
2013
|
+
dataSets: [createDataSet('main', 16, 1100)],
|
|
2014
|
+
visibleDataSetsUrl,
|
|
2015
|
+
locusUrl,
|
|
2016
|
+
locusStateElements: [
|
|
2017
|
+
{
|
|
2018
|
+
htMeta: {
|
|
2019
|
+
elementId: {type: 'participant' as const, id: 99, version: 10},
|
|
2020
|
+
dataSetNames: ['main'],
|
|
2021
|
+
},
|
|
2022
|
+
data: null, // removal
|
|
2023
|
+
},
|
|
2024
|
+
],
|
|
2025
|
+
};
|
|
2026
|
+
|
|
2027
|
+
await parser.handleMessage(removalMessage, 'removal of non-existent object');
|
|
2028
|
+
|
|
2029
|
+
// Callback should be called with the removal
|
|
2030
|
+
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
2031
|
+
updatedObjects: [
|
|
2032
|
+
{
|
|
2033
|
+
htMeta: {
|
|
2034
|
+
elementId: {type: 'participant', id: 99, version: 10},
|
|
2035
|
+
dataSetNames: ['main'],
|
|
2036
|
+
},
|
|
2037
|
+
data: null,
|
|
2038
|
+
},
|
|
2039
|
+
],
|
|
2040
|
+
});
|
|
2041
|
+
});
|
|
2042
|
+
|
|
2043
|
+
it('filters out removal when object exists in another dataset with newer version', async () => {
|
|
2044
|
+
const parser = createHashTreeParser();
|
|
2045
|
+
|
|
2046
|
+
// Setup: Add object to main with version 40
|
|
2047
|
+
await parser.handleMessage(
|
|
2048
|
+
{
|
|
2049
|
+
dataSets: [createDataSet('main', 16, 1100)],
|
|
2050
|
+
visibleDataSetsUrl,
|
|
2051
|
+
locusUrl,
|
|
2052
|
+
locusStateElements: [
|
|
2053
|
+
{
|
|
2054
|
+
htMeta: {
|
|
2055
|
+
elementId: {type: 'participant' as const, id: 10, version: 40},
|
|
2056
|
+
dataSetNames: ['main'],
|
|
2057
|
+
},
|
|
2058
|
+
data: {name: 'participant v40'},
|
|
2059
|
+
},
|
|
2060
|
+
],
|
|
2061
|
+
},
|
|
2062
|
+
'setup main'
|
|
2063
|
+
);
|
|
2064
|
+
|
|
2065
|
+
// Add object to atd-unmuted with version 50
|
|
2066
|
+
await parser.handleMessage(
|
|
2067
|
+
{
|
|
2068
|
+
dataSets: [createDataSet('atd-unmuted', 16, 3100)],
|
|
2069
|
+
visibleDataSetsUrl,
|
|
2070
|
+
locusUrl,
|
|
2071
|
+
locusStateElements: [
|
|
2072
|
+
{
|
|
2073
|
+
htMeta: {
|
|
2074
|
+
elementId: {type: 'participant' as const, id: 10, version: 50},
|
|
2075
|
+
dataSetNames: ['atd-unmuted'],
|
|
2076
|
+
},
|
|
2077
|
+
data: {name: 'participant v50'},
|
|
2078
|
+
},
|
|
2079
|
+
],
|
|
2080
|
+
},
|
|
2081
|
+
'setup atd-unmuted'
|
|
2082
|
+
);
|
|
2083
|
+
callback.resetHistory();
|
|
2084
|
+
|
|
2085
|
+
// Try to remove with version 40 from main
|
|
2086
|
+
const removalMessage = {
|
|
2087
|
+
dataSets: [createDataSet('main', 16, 1101)],
|
|
2088
|
+
visibleDataSetsUrl,
|
|
2089
|
+
locusUrl,
|
|
2090
|
+
locusStateElements: [
|
|
2091
|
+
{
|
|
2092
|
+
htMeta: {
|
|
2093
|
+
elementId: {type: 'participant' as const, id: 10, version: 40},
|
|
2094
|
+
dataSetNames: ['main'],
|
|
2095
|
+
},
|
|
2096
|
+
data: null, // removal
|
|
2097
|
+
},
|
|
2098
|
+
],
|
|
2099
|
+
};
|
|
2100
|
+
|
|
2101
|
+
await parser.handleMessage(removalMessage, 'removal with older version');
|
|
2102
|
+
|
|
2103
|
+
// Callback should not be called because object still exists with newer version
|
|
2104
|
+
assert.notCalled(callback);
|
|
2105
|
+
});
|
|
2106
|
+
|
|
2107
|
+
it('filters mixed updates correctly - some pass, some filtered', async () => {
|
|
2108
|
+
const parser = await setupParserWithObjects([
|
|
2109
|
+
{
|
|
2110
|
+
htMeta: {
|
|
2111
|
+
elementId: {type: 'participant' as const, id: 1, version: 100},
|
|
2112
|
+
dataSetNames: ['main'],
|
|
2113
|
+
},
|
|
2114
|
+
data: {name: 'participant 1'},
|
|
2115
|
+
},
|
|
2116
|
+
{
|
|
2117
|
+
htMeta: {
|
|
2118
|
+
elementId: {type: 'participant' as const, id: 2, version: 50},
|
|
2119
|
+
dataSetNames: ['atd-unmuted'],
|
|
2120
|
+
},
|
|
2121
|
+
data: {name: 'participant 2'},
|
|
2122
|
+
},
|
|
2123
|
+
]);
|
|
2124
|
+
|
|
2125
|
+
// Send mixed updates
|
|
2126
|
+
const mixedMessage = {
|
|
2127
|
+
dataSets: [createDataSet('main', 16, 1101)],
|
|
2128
|
+
visibleDataSetsUrl,
|
|
2129
|
+
locusUrl,
|
|
2130
|
+
locusStateElements: [
|
|
2131
|
+
{
|
|
2132
|
+
htMeta: {
|
|
2133
|
+
elementId: {type: 'participant' as const, id: 1, version: 110}, // newer version - should pass
|
|
2134
|
+
dataSetNames: ['main'],
|
|
2135
|
+
},
|
|
2136
|
+
data: {name: 'updated'},
|
|
2137
|
+
},
|
|
2138
|
+
{
|
|
2139
|
+
htMeta: {
|
|
2140
|
+
elementId: {type: 'participant' as const, id: 1, version: 90}, // older version - should be filtered
|
|
2141
|
+
dataSetNames: ['main'],
|
|
2142
|
+
},
|
|
2143
|
+
data: {name: 'old'},
|
|
2144
|
+
},
|
|
2145
|
+
{
|
|
2146
|
+
htMeta: {
|
|
2147
|
+
elementId: {type: 'participant' as const, id: 3, version: 10}, // new object - should pass
|
|
2148
|
+
dataSetNames: ['main'],
|
|
2149
|
+
},
|
|
2150
|
+
data: {name: 'new'},
|
|
2151
|
+
},
|
|
2152
|
+
{
|
|
2153
|
+
htMeta: {
|
|
2154
|
+
elementId: {type: 'participant' as const, id: 2, version: 50}, // removal but exists in atd-unmuted - should be filtered
|
|
2155
|
+
dataSetNames: ['main'],
|
|
2156
|
+
},
|
|
2157
|
+
data: null,
|
|
2158
|
+
},
|
|
2159
|
+
],
|
|
2160
|
+
};
|
|
2161
|
+
|
|
2162
|
+
await parser.handleMessage(mixedMessage, 'mixed updates');
|
|
2163
|
+
|
|
2164
|
+
// Callback should be called with only the valid updates (participant 1 v110 and participant 3 v10)
|
|
2165
|
+
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.OBJECTS_UPDATED, {
|
|
2166
|
+
updatedObjects: [
|
|
2167
|
+
{
|
|
2168
|
+
htMeta: {
|
|
2169
|
+
elementId: {type: 'participant', id: 1, version: 110},
|
|
2170
|
+
dataSetNames: ['main'],
|
|
2171
|
+
},
|
|
2172
|
+
data: {name: 'updated'},
|
|
2173
|
+
},
|
|
2174
|
+
{
|
|
2175
|
+
htMeta: {
|
|
2176
|
+
elementId: {type: 'participant', id: 3, version: 10},
|
|
2177
|
+
dataSetNames: ['main'],
|
|
2178
|
+
},
|
|
2179
|
+
data: {name: 'new'},
|
|
2180
|
+
},
|
|
2181
|
+
],
|
|
2182
|
+
});
|
|
2183
|
+
});
|
|
2184
|
+
|
|
2185
|
+
it('does not call callback when all updates are filtered out', async () => {
|
|
2186
|
+
const parser = await setupParserWithObjects([
|
|
2187
|
+
{
|
|
2188
|
+
htMeta: {
|
|
2189
|
+
elementId: {type: 'locus' as const, id: 5, version: 100},
|
|
2190
|
+
dataSetNames: ['main'],
|
|
2191
|
+
},
|
|
2192
|
+
data: {existingField: 'existing'},
|
|
2193
|
+
},
|
|
2194
|
+
]);
|
|
2195
|
+
|
|
2196
|
+
// Try to update with older versions (all should be filtered)
|
|
2197
|
+
const updateMessage = {
|
|
2198
|
+
dataSets: [createDataSet('main', 16, 1101)],
|
|
2199
|
+
visibleDataSetsUrl,
|
|
2200
|
+
locusUrl,
|
|
2201
|
+
locusStateElements: [
|
|
2202
|
+
{
|
|
2203
|
+
htMeta: {
|
|
2204
|
+
elementId: {type: 'locus' as const, id: 5, version: 80},
|
|
2205
|
+
dataSetNames: ['main'],
|
|
2206
|
+
},
|
|
2207
|
+
data: {someField: 'value'},
|
|
2208
|
+
},
|
|
2209
|
+
{
|
|
2210
|
+
htMeta: {
|
|
2211
|
+
elementId: {type: 'locus' as const, id: 5, version: 90},
|
|
2212
|
+
dataSetNames: ['main'],
|
|
2213
|
+
},
|
|
2214
|
+
data: {someField: 'another value'},
|
|
2215
|
+
},
|
|
2216
|
+
],
|
|
2217
|
+
};
|
|
2218
|
+
|
|
2219
|
+
await parser.handleMessage(updateMessage, 'all filtered updates');
|
|
2220
|
+
|
|
2221
|
+
// Callback should not be called at all
|
|
2222
|
+
assert.notCalled(callback);
|
|
2223
|
+
});
|
|
2224
|
+
|
|
2225
|
+
it('checks all visible datasets when filtering', async () => {
|
|
2226
|
+
const parser = createHashTreeParser();
|
|
2227
|
+
|
|
2228
|
+
// Setup: Add same object to multiple datasets with different versions
|
|
2229
|
+
await parser.handleMessage(
|
|
2230
|
+
{
|
|
2231
|
+
dataSets: [createDataSet('main', 16, 1100)],
|
|
2232
|
+
visibleDataSetsUrl,
|
|
2233
|
+
locusUrl,
|
|
2234
|
+
locusStateElements: [
|
|
2235
|
+
{
|
|
2236
|
+
htMeta: {
|
|
2237
|
+
elementId: {type: 'participant' as const, id: 10, version: 100},
|
|
2238
|
+
dataSetNames: ['main'],
|
|
2239
|
+
},
|
|
2240
|
+
data: {name: 'v100'},
|
|
2241
|
+
},
|
|
2242
|
+
],
|
|
2243
|
+
},
|
|
2244
|
+
'setup main'
|
|
2245
|
+
);
|
|
2246
|
+
|
|
2247
|
+
await parser.handleMessage(
|
|
2248
|
+
{
|
|
2249
|
+
dataSets: [createDataSet('self', 1, 2100)],
|
|
2250
|
+
visibleDataSetsUrl,
|
|
2251
|
+
locusUrl,
|
|
2252
|
+
locusStateElements: [
|
|
2253
|
+
{
|
|
2254
|
+
htMeta: {
|
|
2255
|
+
elementId: {type: 'participant' as const, id: 10, version: 120}, // highest
|
|
2256
|
+
dataSetNames: ['self'],
|
|
2257
|
+
},
|
|
2258
|
+
data: {name: 'v120'},
|
|
2259
|
+
},
|
|
2260
|
+
],
|
|
2261
|
+
},
|
|
2262
|
+
'setup self'
|
|
2263
|
+
);
|
|
2264
|
+
|
|
2265
|
+
await parser.handleMessage(
|
|
2266
|
+
{
|
|
2267
|
+
dataSets: [createDataSet('atd-unmuted', 16, 3100)],
|
|
2268
|
+
visibleDataSetsUrl,
|
|
2269
|
+
locusUrl,
|
|
2270
|
+
locusStateElements: [
|
|
2271
|
+
{
|
|
2272
|
+
htMeta: {
|
|
2273
|
+
elementId: {type: 'participant' as const, id: 10, version: 110},
|
|
2274
|
+
dataSetNames: ['atd-unmuted'],
|
|
2275
|
+
},
|
|
2276
|
+
data: {name: 'v110'},
|
|
2277
|
+
},
|
|
2278
|
+
],
|
|
2279
|
+
},
|
|
2280
|
+
'setup atd-unmuted'
|
|
2281
|
+
);
|
|
2282
|
+
callback.resetHistory();
|
|
2283
|
+
|
|
2284
|
+
// Try to update with version 115 (newer than main and atd-unmuted, but older than self)
|
|
2285
|
+
const updateMessage = {
|
|
2286
|
+
dataSets: [createDataSet('main', 16, 1101)],
|
|
2287
|
+
visibleDataSetsUrl,
|
|
2288
|
+
locusUrl,
|
|
2289
|
+
locusStateElements: [
|
|
2290
|
+
{
|
|
2291
|
+
htMeta: {
|
|
2292
|
+
elementId: {type: 'participant' as const, id: 10, version: 115},
|
|
2293
|
+
dataSetNames: ['main'],
|
|
2294
|
+
},
|
|
2295
|
+
data: {name: 'update'},
|
|
2296
|
+
},
|
|
2297
|
+
],
|
|
2298
|
+
};
|
|
2299
|
+
|
|
2300
|
+
await parser.handleMessage(updateMessage, 'update with v115');
|
|
2301
|
+
|
|
2302
|
+
// Should be filtered out because self dataset has version 120
|
|
2303
|
+
assert.notCalled(callback);
|
|
2304
|
+
});
|
|
2305
|
+
|
|
2306
|
+
it('does not call callback for empty locusStateElements', async () => {
|
|
2307
|
+
const parser = await setupParserWithObjects([]);
|
|
2308
|
+
|
|
2309
|
+
const emptyMessage = {
|
|
2310
|
+
dataSets: [createDataSet('main', 16, 1100)],
|
|
2311
|
+
visibleDataSetsUrl,
|
|
2312
|
+
locusUrl,
|
|
2313
|
+
locusStateElements: [],
|
|
2314
|
+
};
|
|
2315
|
+
|
|
2316
|
+
await parser.handleMessage(emptyMessage, 'empty elements');
|
|
2317
|
+
|
|
2318
|
+
assert.notCalled(callback);
|
|
2319
|
+
});
|
|
2320
|
+
|
|
2321
|
+
it('always calls callback for MEETING_ENDED regardless of filtering', async () => {
|
|
2322
|
+
const parser = await setupParserWithObjects([
|
|
2323
|
+
{
|
|
2324
|
+
htMeta: {
|
|
2325
|
+
elementId: {type: 'locus' as const, id: 0, version: 100},
|
|
2326
|
+
dataSetNames: ['main'],
|
|
2327
|
+
},
|
|
2328
|
+
data: {info: 'data'},
|
|
2329
|
+
},
|
|
2330
|
+
]);
|
|
2331
|
+
|
|
2332
|
+
// Send roster drop message (SELF object with no data) to trigger MEETING_ENDED
|
|
2333
|
+
const rosterDropMessage = {
|
|
2334
|
+
dataSets: [createDataSet('self', 1, 2101)],
|
|
2335
|
+
visibleDataSetsUrl,
|
|
2336
|
+
locusUrl,
|
|
2337
|
+
locusStateElements: [
|
|
2338
|
+
{
|
|
2339
|
+
htMeta: {
|
|
2340
|
+
elementId: {type: 'self' as const, id: 4, version: 102},
|
|
2341
|
+
dataSetNames: ['self'],
|
|
2342
|
+
},
|
|
2343
|
+
data: undefined, // roster drop triggers MEETING_ENDED
|
|
2344
|
+
},
|
|
2345
|
+
],
|
|
2346
|
+
};
|
|
2347
|
+
|
|
2348
|
+
await parser.handleMessage(rosterDropMessage, 'roster drop message');
|
|
2349
|
+
|
|
2350
|
+
// Callback should be called with MEETING_ENDED
|
|
2351
|
+
assert.calledOnceWithExactly(callback, LocusInfoUpdateType.MEETING_ENDED, {
|
|
2352
|
+
updatedObjects: undefined,
|
|
2353
|
+
});
|
|
2354
|
+
});
|
|
2355
|
+
});
|
|
1524
2356
|
});
|