@webex/plugin-meetings 3.11.0 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (170) hide show
  1. package/dist/aiEnableRequest/index.js +184 -0
  2. package/dist/aiEnableRequest/index.js.map +1 -0
  3. package/dist/aiEnableRequest/utils.js +36 -0
  4. package/dist/aiEnableRequest/utils.js.map +1 -0
  5. package/dist/annotation/index.js +14 -5
  6. package/dist/annotation/index.js.map +1 -1
  7. package/dist/breakouts/breakout.js +1 -1
  8. package/dist/breakouts/index.js +1 -1
  9. package/dist/config.js +5 -1
  10. package/dist/config.js.map +1 -1
  11. package/dist/constants.js +28 -6
  12. package/dist/constants.js.map +1 -1
  13. package/dist/hashTree/constants.js +3 -1
  14. package/dist/hashTree/constants.js.map +1 -1
  15. package/dist/hashTree/hashTree.js +18 -0
  16. package/dist/hashTree/hashTree.js.map +1 -1
  17. package/dist/hashTree/hashTreeParser.js +709 -380
  18. package/dist/hashTree/hashTreeParser.js.map +1 -1
  19. package/dist/hashTree/types.js +4 -2
  20. package/dist/hashTree/types.js.map +1 -1
  21. package/dist/hashTree/utils.js +10 -0
  22. package/dist/hashTree/utils.js.map +1 -1
  23. package/dist/index.js +11 -2
  24. package/dist/index.js.map +1 -1
  25. package/dist/interceptors/constant.js +12 -0
  26. package/dist/interceptors/constant.js.map +1 -0
  27. package/dist/interceptors/dataChannelAuthToken.js +290 -0
  28. package/dist/interceptors/dataChannelAuthToken.js.map +1 -0
  29. package/dist/interceptors/index.js +7 -0
  30. package/dist/interceptors/index.js.map +1 -1
  31. package/dist/interceptors/utils.js +27 -0
  32. package/dist/interceptors/utils.js.map +1 -0
  33. package/dist/interpretation/index.js +2 -2
  34. package/dist/interpretation/index.js.map +1 -1
  35. package/dist/interpretation/siLanguage.js +1 -1
  36. package/dist/locus-info/controlsUtils.js +5 -3
  37. package/dist/locus-info/controlsUtils.js.map +1 -1
  38. package/dist/locus-info/index.js +217 -79
  39. package/dist/locus-info/index.js.map +1 -1
  40. package/dist/locus-info/selfUtils.js +1 -0
  41. package/dist/locus-info/selfUtils.js.map +1 -1
  42. package/dist/locus-info/types.js.map +1 -1
  43. package/dist/media/MediaConnectionAwaiter.js +57 -1
  44. package/dist/media/MediaConnectionAwaiter.js.map +1 -1
  45. package/dist/media/properties.js +4 -2
  46. package/dist/media/properties.js.map +1 -1
  47. package/dist/meeting/in-meeting-actions.js +7 -1
  48. package/dist/meeting/in-meeting-actions.js.map +1 -1
  49. package/dist/meeting/index.js +1082 -861
  50. package/dist/meeting/index.js.map +1 -1
  51. package/dist/meeting/request.js +50 -0
  52. package/dist/meeting/request.js.map +1 -1
  53. package/dist/meeting/request.type.js.map +1 -1
  54. package/dist/meeting/util.js +133 -3
  55. package/dist/meeting/util.js.map +1 -1
  56. package/dist/meetings/index.js +100 -45
  57. package/dist/meetings/index.js.map +1 -1
  58. package/dist/member/index.js +10 -0
  59. package/dist/member/index.js.map +1 -1
  60. package/dist/member/util.js +10 -0
  61. package/dist/member/util.js.map +1 -1
  62. package/dist/metrics/constants.js +2 -1
  63. package/dist/metrics/constants.js.map +1 -1
  64. package/dist/multistream/mediaRequestManager.js +9 -60
  65. package/dist/multistream/mediaRequestManager.js.map +1 -1
  66. package/dist/multistream/remoteMediaManager.js +11 -0
  67. package/dist/multistream/remoteMediaManager.js.map +1 -1
  68. package/dist/reachability/index.js +18 -10
  69. package/dist/reachability/index.js.map +1 -1
  70. package/dist/reactions/reactions.type.js.map +1 -1
  71. package/dist/reconnection-manager/index.js +0 -1
  72. package/dist/reconnection-manager/index.js.map +1 -1
  73. package/dist/types/aiEnableRequest/index.d.ts +5 -0
  74. package/dist/types/aiEnableRequest/utils.d.ts +2 -0
  75. package/dist/types/config.d.ts +3 -0
  76. package/dist/types/constants.d.ts +23 -1
  77. package/dist/types/hashTree/constants.d.ts +1 -0
  78. package/dist/types/hashTree/hashTree.d.ts +7 -0
  79. package/dist/types/hashTree/hashTreeParser.d.ts +99 -14
  80. package/dist/types/hashTree/types.d.ts +3 -0
  81. package/dist/types/hashTree/utils.d.ts +6 -0
  82. package/dist/types/index.d.ts +1 -0
  83. package/dist/types/interceptors/constant.d.ts +5 -0
  84. package/dist/types/interceptors/dataChannelAuthToken.d.ts +43 -0
  85. package/dist/types/interceptors/index.d.ts +2 -1
  86. package/dist/types/interceptors/utils.d.ts +1 -0
  87. package/dist/types/locus-info/index.d.ts +21 -2
  88. package/dist/types/locus-info/types.d.ts +1 -0
  89. package/dist/types/media/MediaConnectionAwaiter.d.ts +10 -1
  90. package/dist/types/media/properties.d.ts +2 -1
  91. package/dist/types/meeting/in-meeting-actions.d.ts +6 -0
  92. package/dist/types/meeting/index.d.ts +38 -6
  93. package/dist/types/meeting/request.d.ts +16 -1
  94. package/dist/types/meeting/request.type.d.ts +5 -0
  95. package/dist/types/meeting/util.d.ts +31 -0
  96. package/dist/types/meetings/index.d.ts +4 -2
  97. package/dist/types/member/index.d.ts +1 -0
  98. package/dist/types/member/util.d.ts +5 -0
  99. package/dist/types/metrics/constants.d.ts +1 -0
  100. package/dist/types/multistream/mediaRequestManager.d.ts +0 -23
  101. package/dist/types/reactions/reactions.type.d.ts +1 -0
  102. package/dist/types/webinar/utils.d.ts +6 -0
  103. package/dist/webinar/index.js +260 -90
  104. package/dist/webinar/index.js.map +1 -1
  105. package/dist/webinar/utils.js +25 -0
  106. package/dist/webinar/utils.js.map +1 -0
  107. package/package.json +24 -23
  108. package/src/aiEnableRequest/README.md +84 -0
  109. package/src/aiEnableRequest/index.ts +170 -0
  110. package/src/aiEnableRequest/utils.ts +25 -0
  111. package/src/annotation/index.ts +27 -7
  112. package/src/config.ts +3 -0
  113. package/src/constants.ts +29 -1
  114. package/src/hashTree/constants.ts +1 -0
  115. package/src/hashTree/hashTree.ts +17 -0
  116. package/src/hashTree/hashTreeParser.ts +627 -249
  117. package/src/hashTree/types.ts +4 -0
  118. package/src/hashTree/utils.ts +9 -0
  119. package/src/index.ts +8 -1
  120. package/src/interceptors/constant.ts +6 -0
  121. package/src/interceptors/dataChannelAuthToken.ts +170 -0
  122. package/src/interceptors/index.ts +2 -1
  123. package/src/interceptors/utils.ts +16 -0
  124. package/src/interpretation/index.ts +2 -2
  125. package/src/locus-info/controlsUtils.ts +11 -0
  126. package/src/locus-info/index.ts +231 -61
  127. package/src/locus-info/selfUtils.ts +1 -0
  128. package/src/locus-info/types.ts +1 -0
  129. package/src/media/MediaConnectionAwaiter.ts +41 -1
  130. package/src/media/properties.ts +3 -1
  131. package/src/meeting/in-meeting-actions.ts +12 -0
  132. package/src/meeting/index.ts +205 -44
  133. package/src/meeting/request.ts +42 -0
  134. package/src/meeting/request.type.ts +6 -0
  135. package/src/meeting/util.ts +160 -2
  136. package/src/meetings/index.ts +135 -41
  137. package/src/member/index.ts +10 -0
  138. package/src/member/util.ts +12 -0
  139. package/src/metrics/constants.ts +1 -0
  140. package/src/multistream/mediaRequestManager.ts +4 -54
  141. package/src/multistream/remoteMediaManager.ts +13 -0
  142. package/src/reachability/index.ts +9 -0
  143. package/src/reactions/reactions.type.ts +1 -0
  144. package/src/reconnection-manager/index.ts +0 -1
  145. package/src/webinar/index.ts +162 -5
  146. package/src/webinar/utils.ts +16 -0
  147. package/test/unit/spec/aiEnableRequest/index.ts +981 -0
  148. package/test/unit/spec/aiEnableRequest/utils.ts +130 -0
  149. package/test/unit/spec/annotation/index.ts +69 -7
  150. package/test/unit/spec/hashTree/hashTree.ts +66 -0
  151. package/test/unit/spec/hashTree/hashTreeParser.ts +1869 -189
  152. package/test/unit/spec/interceptors/dataChannelAuthToken.ts +210 -0
  153. package/test/unit/spec/interceptors/utils.ts +75 -0
  154. package/test/unit/spec/locus-info/controlsUtils.js +29 -0
  155. package/test/unit/spec/locus-info/index.js +383 -46
  156. package/test/unit/spec/media/MediaConnectionAwaiter.ts +41 -1
  157. package/test/unit/spec/media/properties.ts +12 -3
  158. package/test/unit/spec/meeting/in-meeting-actions.ts +8 -2
  159. package/test/unit/spec/meeting/index.js +716 -115
  160. package/test/unit/spec/meeting/request.js +70 -0
  161. package/test/unit/spec/meeting/utils.js +438 -26
  162. package/test/unit/spec/meetings/index.js +652 -31
  163. package/test/unit/spec/member/index.js +28 -4
  164. package/test/unit/spec/member/util.js +65 -27
  165. package/test/unit/spec/multistream/mediaRequestManager.ts +2 -85
  166. package/test/unit/spec/multistream/remoteMediaManager.ts +30 -0
  167. package/test/unit/spec/reachability/index.ts +23 -0
  168. package/test/unit/spec/reconnection-manager/index.js +4 -8
  169. package/test/unit/spec/webinar/index.ts +348 -36
  170. package/test/unit/spec/webinar/utils.ts +39 -0
@@ -0,0 +1,130 @@
1
+ import {assert} from '@webex/test-helper-chai';
2
+ import {getAIEnablementApprover} from '../../../../src/aiEnableRequest/utils';
3
+ import {Meeting} from '../../../../src';
4
+
5
+ describe('AI Enable Request Utils', () => {
6
+ describe('#getAIEnablementApprover', () => {
7
+ const createMember = (id, roles, canApproveAIEnablement) => ({
8
+ id,
9
+ roles,
10
+ canApproveAIEnablement,
11
+ });
12
+
13
+ const createMeeting = (members) =>
14
+ ({
15
+ members: {
16
+ membersCollection: {
17
+ members: members.reduce((acc, member) => {
18
+ acc[member.id] = member;
19
+ return acc;
20
+ }, {}),
21
+ },
22
+ },
23
+ } as Meeting);
24
+
25
+ it('should return host id when host has moderator role and canApproveAIEnablement', () => {
26
+ const host = createMember('host-123', {moderator: true, cohost: false}, true);
27
+ const member1 = createMember('member-456', {moderator: false, cohost: false}, false);
28
+ const meeting = createMeeting([host, member1]);
29
+
30
+ const result = getAIEnablementApprover(meeting);
31
+
32
+ assert.equal(result, 'host-123');
33
+ });
34
+
35
+ it('should return null when host exists but does not have canApproveAIEnablement', () => {
36
+ const host = createMember('host-123', {moderator: true, cohost: false}, false);
37
+ const member1 = createMember('member-456', {moderator: false, cohost: false}, false);
38
+ const meeting = createMeeting([host, member1]);
39
+
40
+ const result = getAIEnablementApprover(meeting);
41
+
42
+ assert.isNull(result);
43
+ });
44
+
45
+ it('should return first cohost id (alphabetically) when host does not have capability but cohosts do', () => {
46
+ const host = createMember('host-123', {moderator: true, cohost: false}, false);
47
+ const cohost1 = createMember('cohost-zzz', {moderator: false, cohost: true}, true);
48
+ const cohost2 = createMember('cohost-aaa', {moderator: false, cohost: true}, true);
49
+ const cohost3 = createMember('cohost-mmm', {moderator: false, cohost: true}, true);
50
+ const meeting = createMeeting([host, cohost1, cohost2, cohost3]);
51
+
52
+ const result = getAIEnablementApprover(meeting);
53
+
54
+ assert.equal(result, 'cohost-aaa');
55
+ });
56
+
57
+ it('should return null when no host exists and no cohosts have capability', () => {
58
+ const member1 = createMember('member-123', {moderator: false, cohost: false}, false);
59
+ const member2 = createMember('member-456', {moderator: false, cohost: false}, false);
60
+ const meeting = createMeeting([member1, member2]);
61
+
62
+ const result = getAIEnablementApprover(meeting);
63
+
64
+ assert.isNull(result);
65
+ });
66
+
67
+ it('should return null when cohosts exist but none have canApproveAIEnablement', () => {
68
+ const host = createMember('host-123', {moderator: true, cohost: false}, false);
69
+ const cohost1 = createMember('cohost-111', {moderator: false, cohost: true}, false);
70
+ const cohost2 = createMember('cohost-222', {moderator: false, cohost: true}, false);
71
+ const meeting = createMeeting([host, cohost1, cohost2]);
72
+
73
+ const result = getAIEnablementApprover(meeting);
74
+
75
+ assert.isNull(result);
76
+ });
77
+
78
+ it('should prioritize host over cohosts even if cohosts have capability', () => {
79
+ const host = createMember('host-zzz', {moderator: true, cohost: false}, true);
80
+ const cohost1 = createMember('cohost-aaa', {moderator: false, cohost: true}, true);
81
+ const meeting = createMeeting([cohost1, host]);
82
+
83
+ const result = getAIEnablementApprover(meeting);
84
+
85
+ assert.equal(result, 'host-zzz');
86
+ });
87
+
88
+ it('should handle a single cohost with capability when no host has capability', () => {
89
+ const host = createMember('host-123', {moderator: true, cohost: false}, false);
90
+ const cohost = createMember('cohost-999', {moderator: false, cohost: true}, true);
91
+ const meeting = createMeeting([host, cohost]);
92
+
93
+ const result = getAIEnablementApprover(meeting);
94
+
95
+ assert.equal(result, 'cohost-999');
96
+ });
97
+
98
+ it('should handle empty members collection', () => {
99
+ const meeting = createMeeting([]);
100
+
101
+ const result = getAIEnablementApprover(meeting);
102
+
103
+ assert.isNull(result);
104
+ });
105
+
106
+ it('should correctly sort cohosts by id string (not numeric)', () => {
107
+ const host = createMember('host-123', {moderator: true, cohost: false}, false);
108
+ const cohost1 = createMember('cohost-20', {moderator: false, cohost: true}, true);
109
+ const cohost2 = createMember('cohost-100', {moderator: false, cohost: true}, true);
110
+ const cohost3 = createMember('cohost-3', {moderator: false, cohost: true}, true);
111
+ const meeting = createMeeting([host, cohost1, cohost2, cohost3]);
112
+
113
+ const result = getAIEnablementApprover(meeting);
114
+
115
+ // Alphabetically: "cohost-100" < "cohost-20" < "cohost-3"
116
+ assert.equal(result, 'cohost-100');
117
+ });
118
+
119
+ it('should handle members with both moderator and cohost roles', () => {
120
+ // If someone is both moderator and cohost with capability, they should be found as host
121
+ const hostCohost = createMember('host-cohost-123', {moderator: true, cohost: true}, true);
122
+ const member = createMember('member-456', {moderator: false, cohost: false}, false);
123
+ const meeting = createMeeting([hostCohost, member]);
124
+
125
+ const result = getAIEnablementApprover(meeting);
126
+
127
+ assert.equal(result, 'host-cohost-123');
128
+ });
129
+ });
130
+ });
@@ -7,6 +7,7 @@ import LLMChannel from '@webex/internal-plugin-llm';
7
7
 
8
8
  import AnnotationService from '../../../../src/annotation/index';
9
9
  import {ANNOTATION_RELAY_TYPES, ANNOTATION_REQUEST_TYPE, EVENT_TRIGGERS} from '../../../../src/annotation/constants';
10
+ import {LLM_PRACTICE_SESSION} from '../../../../src/constants';
10
11
 
11
12
 
12
13
  describe('live-annotation', () => {
@@ -28,6 +29,7 @@ describe('live-annotation', () => {
28
29
  annotationService = webex.internal.annotation;
29
30
  annotationService.connect = sinon.stub().resolves(true);
30
31
  annotationService.webex.internal.llm.isConnected = sinon.stub().returns(true);
32
+ annotationService.webex.internal.llm.getSocket = sinon.stub().returns(undefined);
31
33
  annotationService.webex.internal.llm.getBinding = sinon.stub().returns(undefined);
32
34
  annotationService.webex.internal.llm.getLocusUrl = sinon.stub().returns(locusUrl);
33
35
  annotationService.approvalUrl = 'url/approval';
@@ -183,7 +185,13 @@ describe('live-annotation', () => {
183
185
 
184
186
  annotationService.listenToEvents();
185
187
 
186
- assert.calledOnceWithExactly(spy, 'event:relay.event', sinon.match.func,sinon.match.object);
188
+ assert.calledWithExactly(spy.firstCall, 'event:relay.event', sinon.match.func, sinon.match.object);
189
+ assert.calledWithExactly(
190
+ spy.secondCall,
191
+ `event:relay.event:${LLM_PRACTICE_SESSION}`,
192
+ sinon.match.func,
193
+ sinon.match.object
194
+ );
187
195
  });
188
196
 
189
197
  });
@@ -226,14 +234,11 @@ describe('live-annotation', () => {
226
234
  });
227
235
 
228
236
  describe('sendStrokeData', () => {
237
+ let strokeData;
229
238
 
230
239
  beforeEach(async () => {
231
240
  annotationService.webex.internal.llm.socket = new MockWebSocket();
232
- });
233
-
234
-
235
- it('works on publish Stroke Data', async () => {
236
- const strokeData = {
241
+ strokeData = {
237
242
  content: {
238
243
  "contentsBuffer": [{
239
244
  "contentArray": [{
@@ -249,8 +254,11 @@ describe('live-annotation', () => {
249
254
  shareInstanceId: '7fa6fe07-dcb1-41ad-973d-7bcf65fab55d',
250
255
  encryptionKeyUrl: "encryptionKeyUrl",
251
256
  version: '1',
252
- } ;
257
+ };
258
+ });
259
+
253
260
 
261
+ it('works on publish Stroke Data', async () => {
254
262
  annotationService.publishEncrypted(strokeData.content, strokeData);
255
263
 
256
264
  const sendObject = {
@@ -283,6 +291,49 @@ describe('live-annotation', () => {
283
291
  assert.calledOnceWithExactly(annotationService.webex.internal.llm.socket.send, sendObject);
284
292
  });
285
293
 
294
+ it('uses the practice-session socket and binding only when the practice-session session is connected', () => {
295
+ const practiceSocket = new MockWebSocket();
296
+
297
+ annotationService.webex.internal.llm.isConnected.callsFake((sessionId) =>
298
+ sessionId === LLM_PRACTICE_SESSION
299
+ );
300
+ annotationService.webex.internal.llm.getSocket
301
+ .withArgs(LLM_PRACTICE_SESSION)
302
+ .returns(practiceSocket);
303
+ annotationService.webex.internal.llm.getBinding
304
+ .withArgs(LLM_PRACTICE_SESSION)
305
+ .returns('practice-binding');
306
+
307
+ annotationService.publishEncrypted(strokeData.content, strokeData);
308
+
309
+ assert.calledOnce(practiceSocket.send);
310
+ assert.notCalled(annotationService.webex.internal.llm.socket.send);
311
+
312
+ const sent = practiceSocket.send.getCall(0).args[0];
313
+ assert.equal(sent.recipients.route, 'practice-binding');
314
+ });
315
+
316
+ it('falls back to the default socket and binding when the practice-session socket exists but is not connected', () => {
317
+ const practiceSocket = new MockWebSocket();
318
+
319
+ annotationService.webex.internal.llm.isConnected.callsFake((sessionId) => !sessionId);
320
+ annotationService.webex.internal.llm.getSocket
321
+ .withArgs(LLM_PRACTICE_SESSION)
322
+ .returns(practiceSocket);
323
+ annotationService.webex.internal.llm.getBinding
324
+ .withArgs(LLM_PRACTICE_SESSION)
325
+ .returns('practice-binding');
326
+ annotationService.webex.internal.llm.getBinding.returns('default-binding');
327
+
328
+ annotationService.publishEncrypted(strokeData.content, strokeData);
329
+
330
+ assert.notCalled(practiceSocket.send);
331
+ assert.calledOnce(annotationService.webex.internal.llm.socket.send);
332
+
333
+ const sent = annotationService.webex.internal.llm.socket.send.getCall(0).args[0];
334
+ assert.equal(sent.recipients.route, 'default-binding');
335
+ });
336
+
286
337
  });
287
338
 
288
339
 
@@ -441,10 +492,21 @@ describe('live-annotation', () => {
441
492
  annotationService.eventDataProcessor,
442
493
  annotationService
443
494
  );
495
+ assert.calledWith(
496
+ llmOn,
497
+ `event:relay.event:${LLM_PRACTICE_SESSION}`,
498
+ annotationService.eventDataProcessor,
499
+ annotationService
500
+ );
444
501
  assert.match(annotationService.hasSubscribedToEvents, true);
445
502
 
446
503
  annotationService.deregisterEvents();
447
504
  assert.calledWith(llmOff, 'event:relay.event', annotationService.eventDataProcessor);
505
+ assert.calledWith(
506
+ llmOff,
507
+ `event:relay.event:${LLM_PRACTICE_SESSION}`,
508
+ annotationService.eventDataProcessor
509
+ );
448
510
  assert.calledWith(
449
511
  mercuryOff,
450
512
  'event:locus.approval_request',
@@ -652,4 +652,70 @@ describe('HashTree', () => {
652
652
  expect(() => hashTree.computeLeafHash(2)).to.not.throw();
653
653
  });
654
654
  });
655
+
656
+ describe('getItemVersion', () => {
657
+ it('should return version when item exists', () => {
658
+ const items: LeafDataItem[] = [
659
+ {type: 'participant', id: 1, version: 5},
660
+ {type: 'self', id: 2, version: 10},
661
+ ];
662
+ const hashTree = new HashTree(items, 4);
663
+
664
+ expect(hashTree.getItemVersion(1, 'participant')).to.equal(5);
665
+ expect(hashTree.getItemVersion(2, 'self')).to.equal(10);
666
+ });
667
+
668
+ it('should return undefined when item does not exist', () => {
669
+ const items: LeafDataItem[] = [{type: 'participant', id: 1, version: 5}];
670
+ const hashTree = new HashTree(items, 4);
671
+
672
+ expect(hashTree.getItemVersion(999, 'participant')).to.be.undefined;
673
+ });
674
+
675
+ it('should return undefined when type does not match', () => {
676
+ const items: LeafDataItem[] = [{type: 'participant', id: 1, version: 5}];
677
+ const hashTree = new HashTree(items, 4);
678
+
679
+ expect(hashTree.getItemVersion(1, 'self')).to.be.undefined;
680
+ });
681
+
682
+ it('should return undefined for tree with 0 leaves', () => {
683
+ const hashTree = new HashTree([], 0);
684
+
685
+ expect(hashTree.getItemVersion(1, 'participant')).to.be.undefined;
686
+ });
687
+
688
+ it('should return correct version when multiple items exist in same leaf', () => {
689
+ const items: LeafDataItem[] = [
690
+ {type: 'participant', id: 1, version: 3}, // leaf 1 (1 % 2 = 1)
691
+ {type: 'self', id: 3, version: 7}, // leaf 1 (3 % 2 = 1)
692
+ {type: 'locus', id: 5, version: 12}, // leaf 1 (5 % 2 = 1)
693
+ ];
694
+ const hashTree = new HashTree(items, 2);
695
+
696
+ expect(hashTree.getItemVersion(1, 'participant')).to.equal(3);
697
+ expect(hashTree.getItemVersion(3, 'self')).to.equal(7);
698
+ expect(hashTree.getItemVersion(5, 'locus')).to.equal(12);
699
+ });
700
+
701
+ it('should return updated version after item is updated', () => {
702
+ const hashTree = new HashTree([{type: 'participant', id: 1, version: 5}], 4);
703
+
704
+ expect(hashTree.getItemVersion(1, 'participant')).to.equal(5);
705
+
706
+ hashTree.putItem({type: 'participant', id: 1, version: 10});
707
+
708
+ expect(hashTree.getItemVersion(1, 'participant')).to.equal(10);
709
+ });
710
+
711
+ it('should return undefined after item is removed', () => {
712
+ const hashTree = new HashTree([{type: 'participant', id: 1, version: 5}], 4);
713
+
714
+ expect(hashTree.getItemVersion(1, 'participant')).to.equal(5);
715
+
716
+ hashTree.removeItem({type: 'participant', id: 1, version: 5});
717
+
718
+ expect(hashTree.getItemVersion(1, 'participant')).to.be.undefined;
719
+ });
720
+ });
655
721
  });