@fluid-internal/presence-runtime 2.102.0 → 2.110.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 (85) hide show
  1. package/dist/packageVersion.d.ts +1 -1
  2. package/dist/packageVersion.js +1 -1
  3. package/dist/packageVersion.js.map +1 -1
  4. package/dist/runtime/extension/containerPresence.d.ts +1 -1
  5. package/dist/runtime/presenceDatastoreManager.d.ts +2 -2
  6. package/dist/runtime/presenceDatastoreManager.d.ts.map +1 -1
  7. package/dist/runtime/presenceDatastoreManager.js.map +1 -1
  8. package/dist/runtime/presenceManager.js.map +1 -1
  9. package/lib/packageVersion.d.ts +1 -1
  10. package/lib/packageVersion.js +1 -1
  11. package/lib/packageVersion.js.map +1 -1
  12. package/lib/runtime/extension/containerPresence.d.ts +1 -1
  13. package/lib/runtime/presenceDatastoreManager.d.ts +2 -2
  14. package/lib/runtime/presenceDatastoreManager.d.ts.map +1 -1
  15. package/lib/runtime/presenceDatastoreManager.js.map +1 -1
  16. package/lib/runtime/presenceManager.js.map +1 -1
  17. package/package.json +15 -15
  18. package/dist/runtime/test/presenceDatastoreManager.spec.js +0 -618
  19. package/dist/runtime/test/presenceDatastoreManager.spec.js.map +0 -1
  20. package/dist/runtime/test/presenceManager.spec.js +0 -651
  21. package/dist/runtime/test/presenceManager.spec.js.map +0 -1
  22. package/dist/states/test/batching.spec.js +0 -843
  23. package/dist/states/test/batching.spec.js.map +0 -1
  24. package/dist/states/test/broadcastControlsTests.js +0 -60
  25. package/dist/states/test/broadcastControlsTests.js.map +0 -1
  26. package/dist/states/test/eventing.spec.js +0 -576
  27. package/dist/states/test/eventing.spec.js.map +0 -1
  28. package/dist/states/test/latestMapValueManager.spec.js +0 -210
  29. package/dist/states/test/latestMapValueManager.spec.js.map +0 -1
  30. package/dist/states/test/latestValueManager.spec.js +0 -193
  31. package/dist/states/test/latestValueManager.spec.js.map +0 -1
  32. package/dist/states/test/mockEphemeralRuntime.js +0 -11
  33. package/dist/states/test/mockEphemeralRuntime.js.map +0 -1
  34. package/dist/states/test/notificationsManager.spec.js +0 -460
  35. package/dist/states/test/notificationsManager.spec.js.map +0 -1
  36. package/dist/states/test/presenceStates.spec.js +0 -73
  37. package/dist/states/test/presenceStates.spec.js.map +0 -1
  38. package/dist/states/test/schemaValidation/protocol.spec.js +0 -246
  39. package/dist/states/test/schemaValidation/protocol.spec.js.map +0 -1
  40. package/dist/states/test/schemaValidation/valueManagers.spec.js +0 -784
  41. package/dist/states/test/schemaValidation/valueManagers.spec.js.map +0 -1
  42. package/dist/states/test/testUtils.js +0 -21
  43. package/dist/states/test/testUtils.js.map +0 -1
  44. package/dist/test/mockEphemeralRuntime.js +0 -175
  45. package/dist/test/mockEphemeralRuntime.js.map +0 -1
  46. package/dist/test/testUtils.js +0 -262
  47. package/dist/test/testUtils.js.map +0 -1
  48. package/dist/test/utils/index.js +0 -27
  49. package/dist/test/utils/index.js.map +0 -1
  50. package/dist/utils/test/timerManager.spec.js +0 -93
  51. package/dist/utils/test/timerManager.spec.js.map +0 -1
  52. package/lib/runtime/test/presenceDatastoreManager.spec.js +0 -616
  53. package/lib/runtime/test/presenceDatastoreManager.spec.js.map +0 -1
  54. package/lib/runtime/test/presenceManager.spec.js +0 -649
  55. package/lib/runtime/test/presenceManager.spec.js.map +0 -1
  56. package/lib/states/test/batching.spec.js +0 -841
  57. package/lib/states/test/batching.spec.js.map +0 -1
  58. package/lib/states/test/broadcastControlsTests.js +0 -56
  59. package/lib/states/test/broadcastControlsTests.js.map +0 -1
  60. package/lib/states/test/eventing.spec.js +0 -574
  61. package/lib/states/test/eventing.spec.js.map +0 -1
  62. package/lib/states/test/latestMapValueManager.spec.js +0 -206
  63. package/lib/states/test/latestMapValueManager.spec.js.map +0 -1
  64. package/lib/states/test/latestValueManager.spec.js +0 -189
  65. package/lib/states/test/latestValueManager.spec.js.map +0 -1
  66. package/lib/states/test/mockEphemeralRuntime.js +0 -6
  67. package/lib/states/test/mockEphemeralRuntime.js.map +0 -1
  68. package/lib/states/test/notificationsManager.spec.js +0 -456
  69. package/lib/states/test/notificationsManager.spec.js.map +0 -1
  70. package/lib/states/test/presenceStates.spec.js +0 -69
  71. package/lib/states/test/presenceStates.spec.js.map +0 -1
  72. package/lib/states/test/schemaValidation/protocol.spec.js +0 -244
  73. package/lib/states/test/schemaValidation/protocol.spec.js.map +0 -1
  74. package/lib/states/test/schemaValidation/valueManagers.spec.js +0 -782
  75. package/lib/states/test/schemaValidation/valueManagers.spec.js.map +0 -1
  76. package/lib/states/test/testUtils.js +0 -6
  77. package/lib/states/test/testUtils.js.map +0 -1
  78. package/lib/test/mockEphemeralRuntime.js +0 -171
  79. package/lib/test/mockEphemeralRuntime.js.map +0 -1
  80. package/lib/test/testUtils.js +0 -251
  81. package/lib/test/testUtils.js.map +0 -1
  82. package/lib/test/utils/index.js +0 -8
  83. package/lib/test/utils/index.js.map +0 -1
  84. package/lib/utils/test/timerManager.spec.js +0 -91
  85. package/lib/utils/test/timerManager.spec.js.map +0 -1
@@ -1,782 +0,0 @@
1
- /*!
2
- * Copyright (c) Microsoft Corporation and contributors. All rights reserved.
3
- * Licensed under the MIT License.
4
- */
5
- import { strict as assert } from "node:assert";
6
- import { EventAndErrorTrackingLogger } from "@fluidframework/test-utils/internal";
7
- import { describe, it, after, afterEach, before, beforeEach } from "mocha";
8
- import { useFakeTimers } from "sinon";
9
- import { StateFactory } from "@fluid-internal/presence-runtime/states";
10
- import { toOpaqueJson } from "@fluid-internal/presence-runtime/utils";
11
- import { MockEphemeralRuntime } from "../mockEphemeralRuntime.js";
12
- import { assertFinalExpectations, attendeeId1, localAttendeeId, connectionId1, initialLocalClientConnectionId, createSpiedValidator, prepareConnectedPresence, } from "../testUtils.js";
13
- const attendeeUpdate = {
14
- "clientToSessionId": {
15
- [connectionId1]: {
16
- "rev": 0,
17
- "timestamp": 0,
18
- "value": attendeeId1,
19
- },
20
- },
21
- };
22
- const latestUpdate = {
23
- "latest": {
24
- [attendeeId1]: {
25
- "rev": 1,
26
- "timestamp": 0,
27
- "value": toOpaqueJson({ x: 1, y: 1, z: 1 }),
28
- },
29
- },
30
- };
31
- const latestMapUpdate = {
32
- "latestMap": {
33
- [attendeeId1]: {
34
- "rev": 1,
35
- "items": {
36
- "key1": {
37
- "rev": 1,
38
- "timestamp": 0,
39
- "value": toOpaqueJson({ x: 1, y: 1, z: 1 }),
40
- },
41
- "key2": {
42
- "rev": 1,
43
- "timestamp": 0,
44
- "value": toOpaqueJson({ x: 2, y: 2, z: 2 }),
45
- },
46
- "invalidKey": {
47
- "rev": 1,
48
- "timestamp": 0,
49
- "value": toOpaqueJson({ x: -1, y: -1, z: -1 }),
50
- },
51
- },
52
- },
53
- },
54
- };
55
- function datastoreUpdateSignal(sendTimestamp, valueObjectName, metadata) {
56
- return {
57
- type: "Pres:DatastoreUpdate",
58
- content: {
59
- sendTimestamp,
60
- avgLatency: 20,
61
- data: {
62
- "system:presence": attendeeUpdate,
63
- "s:name:testWorkspace": {
64
- [valueObjectName]: {
65
- [attendeeId1]: metadata,
66
- },
67
- },
68
- },
69
- },
70
- clientId: connectionId1,
71
- };
72
- }
73
- /**
74
- * Runs a test against a validator by getting the value and matching the resulting data and validator call counts
75
- * against expectations.
76
- */
77
- function runValidatorTest(params) {
78
- const initialValue = params.getRemoteValue();
79
- assert.equal(params.validatorFunction.callCount, params.expectedCallCount);
80
- assert.deepEqual(initialValue, params.expectedValue);
81
- }
82
- /**
83
- * Runs a test against a validator by getting the value multiple times and verifying that the validator is not called
84
- * multiple times.
85
- */
86
- function runMultipleCallsTest(params) {
87
- // First call should invoke validator
88
- const firstValue = params.getRemoteValue();
89
- assert.equal(params.validatorFunction.callCount, 1);
90
- assert.deepEqual(firstValue, params.expectedValue);
91
- // Subsequent calls should not invoke validator when data is unchanged
92
- const secondValue = params.getRemoteValue();
93
- assert.equal(params.validatorFunction.callCount, 1);
94
- const thirdValue = params.getRemoteValue();
95
- assert.equal(params.validatorFunction.callCount, 1);
96
- assert.deepEqual(secondValue, params.expectedValue);
97
- assert.deepEqual(thirdValue, params.expectedValue);
98
- }
99
- describe("Presence/States", () => {
100
- describe("Runtime schema validation", () => {
101
- const afterCleanUp = [];
102
- const initialTime = 1000;
103
- function processUpdates(valueManagerUpdates) {
104
- const updates = { "system:presence": attendeeUpdate, ...valueManagerUpdates };
105
- processSignal([], {
106
- type: "Pres:DatastoreUpdate",
107
- content: {
108
- sendTimestamp: clock.now - 10,
109
- avgLatency: 20,
110
- data: updates,
111
- },
112
- clientId: "client1",
113
- }, false);
114
- }
115
- let clock;
116
- let logger;
117
- let presence;
118
- let processSignal;
119
- let runtime;
120
- let remoteAttendee;
121
- let validatorCallExpected;
122
- let point3DValidatorFunction;
123
- before(async () => {
124
- clock = useFakeTimers();
125
- });
126
- /**
127
- * This beforeEach sets up the runtime and presence objects. The `presence` object is owned by attendee2, while
128
- * attendee1 acts as a remote attendee for the purposes of the tests.
129
- */
130
- beforeEach(() => {
131
- logger = new EventAndErrorTrackingLogger();
132
- runtime = new MockEphemeralRuntime(logger);
133
- clock.setSystemTime(initialTime);
134
- validatorCallExpected = true;
135
- point3DValidatorFunction = createSpiedValidator((d) => {
136
- if (validatorCallExpected === "once") {
137
- validatorCallExpected = false;
138
- }
139
- else {
140
- assert(validatorCallExpected, "point3DValidatorFunction should not be called at this time");
141
- }
142
- return typeof d === "object" ? d : undefined;
143
- });
144
- // Create Presence joining session as attendeeId-2. Tests will act as attendee2
145
- ({ presence, processSignal } = prepareConnectedPresence(runtime, localAttendeeId, initialLocalClientConnectionId, clock, logger));
146
- // Pass a little time (to mimic reality)
147
- clock.tick(10);
148
- // Process remote client update signal (attendeeId-1 is then part of local client's known session).
149
- processUpdates({
150
- "s:name:testWorkspace": { ...latestUpdate, ...latestMapUpdate },
151
- });
152
- // Pass a little time (to mimic reality)
153
- clock.tick(10);
154
- // Get attendee references
155
- remoteAttendee = presence.attendees.getAttendee(attendeeId1);
156
- });
157
- afterEach(function (done) {
158
- clock.reset();
159
- point3DValidatorFunction.resetHistory();
160
- // If the test passed so far, check final expectations.
161
- if (this.currentTest?.state === "passed") {
162
- assertFinalExpectations(runtime, logger);
163
- }
164
- for (const cleanUp of afterCleanUp) {
165
- cleanUp();
166
- }
167
- afterCleanUp.length = 0;
168
- done();
169
- });
170
- after(() => {
171
- clock.restore();
172
- });
173
- describe("Latest validator", () => {
174
- let latest;
175
- /**
176
- * This beforeEach sets up the presence workspace itself and gets a reference to it. It then sets some new data as
177
- * attendee1 by processing a datastore update signal.
178
- */
179
- beforeEach(() => {
180
- // Setup workspace initialization signal
181
- runtime.signalsExpected.push([
182
- {
183
- type: "Pres:DatastoreUpdate",
184
- content: {
185
- sendTimestamp: clock.now,
186
- avgLatency: 10,
187
- data: {
188
- "system:presence": {
189
- "clientToSessionId": {
190
- [initialLocalClientConnectionId]: {
191
- "rev": 0,
192
- "timestamp": initialTime,
193
- "value": localAttendeeId,
194
- },
195
- },
196
- },
197
- "s:name:testWorkspace": {
198
- "latest": {
199
- [localAttendeeId]: {
200
- "rev": 0,
201
- "timestamp": clock.now,
202
- "value": toOpaqueJson({ x: 0, y: 0, z: 0 }),
203
- },
204
- },
205
- },
206
- },
207
- },
208
- },
209
- ]);
210
- const stateWorkspace = presence.states.getWorkspace("name:testWorkspace", {
211
- latest: StateFactory.latest({
212
- local: { x: 0, y: 0, z: 0 },
213
- validator: point3DValidatorFunction,
214
- settings: { allowableUpdateLatencyMs: 0 },
215
- }),
216
- });
217
- latest = stateWorkspace.states.latest;
218
- // Process a valid update signal with Point3D data
219
- processSignal([], {
220
- type: "Pres:DatastoreUpdate",
221
- content: {
222
- sendTimestamp: clock.now - 10,
223
- avgLatency: 20,
224
- data: {
225
- "system:presence": attendeeUpdate,
226
- "s:name:testWorkspace": {
227
- "latest": {
228
- [attendeeId1]: {
229
- "rev": 2,
230
- "timestamp": clock.now - 10,
231
- "value": toOpaqueJson({ x: 10, y: 20, z: 30 }),
232
- },
233
- },
234
- },
235
- },
236
- },
237
- clientId: "client1",
238
- }, false);
239
- });
240
- describe("is not called", () => {
241
- it("by .getRemote()", () => {
242
- // Calling getRemote should not invoke the validator (only a value read will).
243
- latest.getRemote(remoteAttendee);
244
- assert.equal(point3DValidatorFunction.callCount, 0);
245
- });
246
- it("by .getRemotes()", () => {
247
- for (const _ of latest.getRemotes()) {
248
- assert.equal(point3DValidatorFunction.callCount, 0);
249
- }
250
- });
251
- it("when accessing .local", () => {
252
- assert.equal(point3DValidatorFunction.callCount, 0, "initial call count is wrong");
253
- assert.deepEqual(latest.local, { x: 0, y: 0, z: 0 });
254
- assert.equal(point3DValidatorFunction.callCount, 0, "validator was called on local data");
255
- });
256
- it("when remote data is updated", () => {
257
- const remoteData = latest.getRemote(remoteAttendee);
258
- // Should call validator
259
- runValidatorTest({
260
- getRemoteValue: () => remoteData.value(),
261
- expectedCallCount: 1,
262
- expectedValue: { x: 10, y: 20, z: 30 },
263
- validatorFunction: point3DValidatorFunction,
264
- });
265
- // Send updated data from remote client
266
- const timestamp = clock.now - 15;
267
- processSignal([], datastoreUpdateSignal(timestamp, "latest", {
268
- "rev": 3,
269
- timestamp,
270
- "value": toOpaqueJson({ x: 50, y: 60, z: 70 }),
271
- }), false);
272
- // Validator is not called by remote update
273
- assert.equal(point3DValidatorFunction.callCount, 1);
274
- });
275
- });
276
- describe("is called", () => {
277
- it("on first .value() call", () => {
278
- const remoteData = latest.getRemote(remoteAttendee);
279
- runValidatorTest({
280
- getRemoteValue: () => remoteData.value(),
281
- expectedCallCount: 1,
282
- expectedValue: { x: 10, y: 20, z: 30 },
283
- validatorFunction: point3DValidatorFunction,
284
- });
285
- });
286
- it("only once for multiple .value() calls in series and .value() always returns same value", () => {
287
- const remoteData = latest.getRemote(remoteAttendee);
288
- runMultipleCallsTest({
289
- getRemoteValue: () => remoteData.value(),
290
- expectedValue: { x: 10, y: 20, z: 30 },
291
- validatorFunction: point3DValidatorFunction,
292
- });
293
- });
294
- it("on .value() call after remote data has changed", () => {
295
- // Get the remote data and read it, verify that the validator is called once.
296
- const remoteData = latest.getRemote(remoteAttendee);
297
- assert.deepEqual(remoteData.value(), { x: 10, y: 20, z: 30 });
298
- assert.equal(point3DValidatorFunction.callCount, 1, "first call count is wrong");
299
- // Send updated data from remote client
300
- const timestamp = clock.now - 15;
301
- processSignal([], datastoreUpdateSignal(timestamp, "latest", {
302
- "rev": 3,
303
- timestamp,
304
- "value": toOpaqueJson({ x: 50, y: 60, z: 70 }),
305
- }), false);
306
- // Reading the remote value should cause the validator to be called a second time since the data has been changed.
307
- const data2 = latest.getRemote(remoteAttendee);
308
- assert.deepEqual(data2.value(), { x: 50, y: 60, z: 70 }, "updated remote value is wrong");
309
- assert.equal(point3DValidatorFunction.callCount, 2, "validator should be called twice");
310
- });
311
- it("on .value() call when remote data changes from valid to invalid", () => {
312
- // Get the remote data and read it, verify that the validator is called once.
313
- const remoteData = latest.getRemote(remoteAttendee);
314
- assert.deepEqual(remoteData.value(), { x: 10, y: 20, z: 30 });
315
- assert.equal(point3DValidatorFunction.callCount, 1, "first call count is wrong");
316
- // Send invalid data from remote client
317
- const timestamp = clock.now - 15;
318
- processSignal([], datastoreUpdateSignal(timestamp, "latest", {
319
- "rev": 3,
320
- timestamp,
321
- "value": toOpaqueJson("invalid"),
322
- }), false);
323
- // Reading the remote value should cause the validator to be called a second time and return undefined
324
- const data2 = latest.getRemote(remoteAttendee);
325
- assert.equal(data2.value(), undefined, "invalid data should return undefined");
326
- assert.equal(point3DValidatorFunction.callCount, 2, "validator should be called twice");
327
- });
328
- it("on .value() call when remote data changes from invalid to valid", () => {
329
- // First send invalid data
330
- let timestamp = clock.now - 15;
331
- processSignal([], datastoreUpdateSignal(timestamp, "latest", {
332
- "rev": 3,
333
- timestamp,
334
- "value": toOpaqueJson("invalid"),
335
- }), false);
336
- // Get the remote data and read it, verify that the validator is called once and returns undefined
337
- const remoteData = latest.getRemote(remoteAttendee);
338
- assert.equal(remoteData.value(), undefined);
339
- assert.equal(point3DValidatorFunction.callCount, 1, "first call count is wrong");
340
- // Send valid data from remote client
341
- timestamp += 10;
342
- processSignal([], datastoreUpdateSignal(timestamp, "latest", {
343
- "rev": 4,
344
- timestamp,
345
- "value": toOpaqueJson({ x: 100, y: 200, z: 300 }),
346
- }), false);
347
- // Reading the remote value should cause the validator to be called a second time and return valid data
348
- const data2 = latest.getRemote(remoteAttendee);
349
- assert.deepEqual(data2.value(), { x: 100, y: 200, z: 300 }, "valid data should be returned");
350
- assert.equal(point3DValidatorFunction.callCount, 2, "validator should be called twice");
351
- });
352
- it("on .value() call when remote data changes from invalid to invalid", () => {
353
- // First send invalid data
354
- let timestamp = clock.now - 15;
355
- processSignal([], datastoreUpdateSignal(timestamp, "latest", {
356
- "rev": 3,
357
- timestamp,
358
- "value": toOpaqueJson("invalid"),
359
- }), false);
360
- // Get the remote data and read it, verify that the validator is called once and returns undefined
361
- const remoteData = latest.getRemote(remoteAttendee);
362
- assert.equal(remoteData.value(), undefined);
363
- assert.equal(point3DValidatorFunction.callCount, 1, "first call count is wrong");
364
- // Send different invalid data from remote client
365
- timestamp += 10;
366
- processSignal([], datastoreUpdateSignal(timestamp, "latest", {
367
- "rev": 4,
368
- timestamp,
369
- "value": toOpaqueJson("also-invalid"),
370
- }), false);
371
- // Reading the remote value should cause the validator to be called a second time and still return undefined
372
- const data2 = latest.getRemote(remoteAttendee);
373
- assert.equal(data2.value(), undefined, "invalid data should return undefined");
374
- assert.equal(point3DValidatorFunction.callCount, 2, "validator should be called twice");
375
- });
376
- });
377
- });
378
- describe("LatestMap with validation", () => {
379
- let latestMap;
380
- let keyValidatorCallExpectedCount;
381
- function keyValidatorFunction(key) {
382
- assert(keyValidatorCallExpectedCount > 0, "keyValidatorFunction should not be called at this time");
383
- keyValidatorCallExpectedCount--;
384
- return key.startsWith("key");
385
- }
386
- /**
387
- * This beforeEach sets up the presence workspace itself and gets a
388
- * reference to it.
389
- */
390
- beforeEach(() => {
391
- keyValidatorCallExpectedCount = 0;
392
- // workspace setup's initialization signal
393
- runtime.signalsExpected.push([
394
- {
395
- "type": "Pres:DatastoreUpdate",
396
- "content": {
397
- "sendTimestamp": clock.now,
398
- "avgLatency": 10,
399
- "data": {
400
- "system:presence": {
401
- "clientToSessionId": {
402
- [initialLocalClientConnectionId]: {
403
- "rev": 0,
404
- "timestamp": initialTime,
405
- "value": localAttendeeId,
406
- },
407
- },
408
- },
409
- "s:name:testWorkspace": {
410
- "latestMap": {
411
- [localAttendeeId]: {
412
- "rev": 0,
413
- "items": {
414
- "key1": {
415
- "rev": 0,
416
- "timestamp": clock.now,
417
- "value": toOpaqueJson({ "x": 0, "y": 0, "z": 0 }),
418
- },
419
- "key2": {
420
- "rev": 0,
421
- "timestamp": clock.now,
422
- "value": toOpaqueJson({ "x": 0, "y": 0, "z": 0 }),
423
- },
424
- },
425
- },
426
- },
427
- },
428
- },
429
- },
430
- },
431
- ]);
432
- const stateWorkspace = presence.states.getWorkspace("name:testWorkspace", {
433
- latestMap: StateFactory.latestMap({
434
- local: { "key1": { x: 0, y: 0, z: 0 }, "key2": { x: 0, y: 0, z: 0 } },
435
- validator: point3DValidatorFunction,
436
- keyValidator: keyValidatorFunction,
437
- settings: { allowableUpdateLatencyMs: 0 },
438
- }),
439
- });
440
- latestMap = stateWorkspace.states.latestMap;
441
- });
442
- function sendInvalidData(key = "key1") {
443
- keyValidatorCallExpectedCount = 1;
444
- const timestamp = clock.now - 15;
445
- processSignal([], datastoreUpdateSignal(timestamp, "latestMap", {
446
- "rev": 2,
447
- "items": {
448
- [key]: {
449
- "rev": 2,
450
- timestamp,
451
- "value": toOpaqueJson("invalid"),
452
- },
453
- },
454
- }), false);
455
- assert.equal(keyValidatorCallExpectedCount, 0);
456
- }
457
- describe("no validator is called", () => {
458
- beforeEach(() => {
459
- validatorCallExpected = false;
460
- });
461
- it("when reading local key value", () => {
462
- // Act
463
- const localData = latestMap.local.get("key1");
464
- // Verify
465
- assert.equal(point3DValidatorFunction.callCount, 0);
466
- // double check local data
467
- assert.deepEqual(localData, { x: 0, y: 0, z: 0 });
468
- });
469
- it("when writing local key value", () => {
470
- // Setup
471
- runtime.signalsExpected.push([
472
- {
473
- "type": "Pres:DatastoreUpdate",
474
- "content": {
475
- "sendTimestamp": clock.now,
476
- "avgLatency": 10,
477
- "data": {
478
- "system:presence": {
479
- "clientToSessionId": {
480
- [initialLocalClientConnectionId]: {
481
- "rev": 0,
482
- "timestamp": initialTime,
483
- "value": localAttendeeId,
484
- },
485
- },
486
- },
487
- "s:name:testWorkspace": {
488
- "latestMap": {
489
- [localAttendeeId]: {
490
- "rev": 1,
491
- "items": {
492
- "key1": {
493
- "rev": 1,
494
- "timestamp": clock.now,
495
- "value": toOpaqueJson({ "x": 0, "y": 1, "z": 2 }),
496
- },
497
- },
498
- },
499
- },
500
- },
501
- },
502
- },
503
- },
504
- ]);
505
- // Act
506
- latestMap.local.set("key1", { x: 0, y: 1, z: 2 });
507
- // Verify
508
- assert.equal(point3DValidatorFunction.callCount, 0);
509
- });
510
- });
511
- it("update with invalid key invokes key validator per key and does not raise events", () => {
512
- // Setup
513
- for (const event of [
514
- "remoteUpdated",
515
- "remoteItemUpdated",
516
- "remoteItemRemoved",
517
- ]) {
518
- afterCleanUp.push(latestMap.events.on(event, () => {
519
- assert.fail(`${event} event should not be raised for invalid key`);
520
- }));
521
- }
522
- keyValidatorCallExpectedCount = 1;
523
- // Act
524
- sendInvalidData("invalidKey");
525
- // Verify
526
- assert.equal(keyValidatorCallExpectedCount, 0); // all expected calls were made
527
- assert.equal(point3DValidatorFunction.callCount, 0);
528
- });
529
- /**
530
- * Sets validator call expectations and gets remote value data.
531
- * @returns `latestMap.getRemote(remoteAttendee)`
532
- * @remarks
533
- * `validatorCallExpected` will be `false` and `keyValidatorCallExpectedCount`
534
- * will be `0` after the call.
535
- */
536
- function getRemoteValue() {
537
- validatorCallExpected = false;
538
- keyValidatorCallExpectedCount = 3;
539
- const remoteData = latestMap.getRemote(remoteAttendee);
540
- assert.equal(keyValidatorCallExpectedCount, 0);
541
- return remoteData;
542
- }
543
- it(".getRemote call invokes key validator per key but not value validator", () => {
544
- // Setup and Act
545
- getRemoteValue();
546
- // Verify
547
- assert.equal(keyValidatorCallExpectedCount, 0); // all expected calls were made
548
- assert.equal(point3DValidatorFunction.callCount, 0);
549
- });
550
- describe("remote value map", () => {
551
- it(".get() does not call any validator", () => {
552
- // Setup
553
- const remoteData = getRemoteValue();
554
- // Act
555
- remoteData.get("key1");
556
- // Verify
557
- assert.equal(point3DValidatorFunction.callCount, 0);
558
- });
559
- it(".forEach() iteration does not call any validator", () => {
560
- // Setup
561
- const remoteData = getRemoteValue();
562
- let counter = 0;
563
- const expectedValues = [
564
- ["key1", { x: 1, y: 1, z: 1 }],
565
- ["key2", { x: 2, y: 2, z: 2 }],
566
- ];
567
- // Act
568
- // eslint-disable-next-line unicorn/no-array-for-each -- forEach is being tested here
569
- remoteData.forEach((_value, key) => {
570
- // Verify
571
- assert.equal(point3DValidatorFunction.callCount, 0, "call count is wrong");
572
- assert.equal(key, expectedValues[counter][0]);
573
- counter++;
574
- });
575
- // Make sure forEach iterated through all keys
576
- assert.equal(counter, expectedValues.length, "counter should match expected values length");
577
- });
578
- describe("value validator is called", () => {
579
- for (const { desc, setup, expectedValue } of [
580
- {
581
- desc: "for valid value",
582
- setup: () => { },
583
- expectedValue: { x: 1, y: 1, z: 1 },
584
- },
585
- {
586
- desc: "for invalid value",
587
- setup: sendInvalidData,
588
- expectedValue: undefined,
589
- },
590
- ]) {
591
- it(`once when key.value() is called ${desc}`, () => {
592
- // Setup
593
- setup();
594
- const remoteData = getRemoteValue();
595
- validatorCallExpected = "once";
596
- // Act
597
- runValidatorTest({
598
- getRemoteValue: () => remoteData.get("key1")?.value(),
599
- expectedCallCount: 1,
600
- expectedValue,
601
- validatorFunction: point3DValidatorFunction,
602
- });
603
- });
604
- it(`only once for multiple key.value() calls in series and .value() always returned same result ${desc}`, () => {
605
- // Setup
606
- setup();
607
- const remoteData = getRemoteValue();
608
- validatorCallExpected = "once";
609
- // Act
610
- runMultipleCallsTest({
611
- getRemoteValue: () => remoteData.get("key1")?.value(),
612
- expectedValue,
613
- validatorFunction: point3DValidatorFunction,
614
- });
615
- });
616
- it(`exactly once for each value's .value() calls in .forEach() and .value() always returns same result ${desc}`, () => {
617
- // Setup
618
- setup();
619
- const remoteData = getRemoteValue();
620
- let counter = 0;
621
- const expectedValues = [
622
- // only key1 value might be altered by the setup function
623
- ["key1", expectedValue],
624
- // key2 value will always be the valid
625
- ["key2", { x: 2, y: 2, z: 2 }],
626
- ];
627
- // Act
628
- // eslint-disable-next-line unicorn/no-array-for-each -- forEach is being tested here
629
- remoteData.forEach((value, key) => {
630
- assert.equal(key, expectedValues[counter][0]);
631
- // reset call expectations/count for each iteration
632
- validatorCallExpected = "once";
633
- point3DValidatorFunction.callCount = 0;
634
- // Act - first call
635
- const valueData = value?.value();
636
- // Verify
637
- assert.equal(point3DValidatorFunction.callCount, 1, "validator was not called");
638
- // double check value
639
- assert.deepEqual(valueData, expectedValues[counter][1], `value at key "${key}" is wrong`);
640
- // Act - second call
641
- // Access value a second time; should not affect validator call count
642
- const valueDataRedux = value?.value();
643
- // Verify
644
- assert.equal(point3DValidatorFunction.callCount, 1, "validator was called a second time");
645
- assert.strictEqual(valueDataRedux, valueData, `value at key "${key}" is wrong`);
646
- counter++;
647
- });
648
- // Make sure forEach iterated through all keys
649
- assert.equal(counter, expectedValues.length, "counter should match expected values length");
650
- });
651
- }
652
- for (const { desc, setup, expectedInitialValue, newData, expectedValue } of [
653
- {
654
- desc: "from valid to different valid value",
655
- setup: () => { },
656
- expectedInitialValue: { x: 1, y: 1, z: 1 },
657
- newData: { x: 4, y: 4, z: 4 },
658
- expectedValue: { x: 4, y: 4, z: 4 },
659
- },
660
- {
661
- desc: "from valid to same valid value",
662
- setup: () => { },
663
- expectedInitialValue: { x: 1, y: 1, z: 1 },
664
- newData: { x: 1, y: 1, z: 1 },
665
- expectedValue: { x: 1, y: 1, z: 1 },
666
- },
667
- {
668
- desc: "from valid to invalid value",
669
- setup: () => { },
670
- expectedInitialValue: { x: 1, y: 1, z: 1 },
671
- newData: "invalid",
672
- expectedValue: undefined,
673
- },
674
- {
675
- desc: "from invalid to valid value",
676
- setup: sendInvalidData,
677
- expectedInitialValue: undefined,
678
- newData: { x: 4, y: 4, z: 4 },
679
- expectedValue: { x: 4, y: 4, z: 4 },
680
- },
681
- {
682
- desc: "from invalid to invalid value",
683
- setup: sendInvalidData,
684
- expectedInitialValue: undefined,
685
- newData: "invalid",
686
- expectedValue: undefined,
687
- },
688
- ]) {
689
- it(`during .value() call after remote key data has changed ${desc}`, () => {
690
- // Setup
691
- setup();
692
- const originalMap = getRemoteValue();
693
- validatorCallExpected = "once";
694
- // Get the remote data and read it, expect that the validator is called once.
695
- const key1Value = originalMap.get("key1")?.value();
696
- assert.equal(point3DValidatorFunction.callCount, 1, "first call count is wrong");
697
- assert.deepEqual(key1Value, expectedInitialValue);
698
- // Reset call count for next verification
699
- point3DValidatorFunction.callCount = 0;
700
- // Process updated key data from remote client
701
- keyValidatorCallExpectedCount = 1;
702
- const timestamp = clock.now - 15;
703
- processSignal([], datastoreUpdateSignal(timestamp, "latestMap", {
704
- "rev": 3,
705
- "items": {
706
- "key1": {
707
- "rev": 3,
708
- timestamp,
709
- "value": toOpaqueJson(newData),
710
- },
711
- },
712
- }), false);
713
- assert.equal(keyValidatorCallExpectedCount, 0);
714
- // Verify no call yet
715
- assert.equal(point3DValidatorFunction.callCount, 0, "call count after update is wrong");
716
- // Reading the remote value should cause the validator to be
717
- // called since the data has been changed.
718
- const updatedMap = getRemoteValue();
719
- validatorCallExpected = "once";
720
- // Act - read updated key value
721
- const updatedKey1Value = updatedMap.get("key1")?.value();
722
- // Verify
723
- assert.equal(point3DValidatorFunction.callCount, 1, "validator should be called twice");
724
- assert.deepEqual(updatedKey1Value, expectedValue, "updated remote key value is wrong");
725
- });
726
- }
727
- });
728
- });
729
- // Note this block is after "value validator is called" as it is only valid when those tests are passing.
730
- describe("no validator is called", () => {
731
- it("during .value() call for unchanged keys", () => {
732
- // Setup
733
- keyValidatorCallExpectedCount = 3;
734
- const originalMap = latestMap.getRemote(remoteAttendee);
735
- assert.equal(keyValidatorCallExpectedCount, 0);
736
- validatorCallExpected = "once";
737
- // Read key1 value - should call validator once
738
- const key1Value = originalMap.get("key1")?.value();
739
- assert.equal(point3DValidatorFunction.callCount, 1, "validator should be called once for key1");
740
- assert.deepEqual(key1Value, { x: 1, y: 1, z: 1 });
741
- // Update key2 (different key) with new data, keeping key1 unchanged
742
- keyValidatorCallExpectedCount = 1;
743
- const timestamp = clock.now - 15;
744
- processSignal([], datastoreUpdateSignal(timestamp, "latestMap", {
745
- "rev": 2,
746
- "items": {
747
- "key2": {
748
- "rev": 2,
749
- timestamp,
750
- "value": toOpaqueJson({ "x": 4, "y": 4, "z": 4 }),
751
- },
752
- },
753
- }), false);
754
- assert.equal(keyValidatorCallExpectedCount, 0);
755
- const updatedMap = getRemoteValue();
756
- const key1Redux = updatedMap.get("key1");
757
- assert.ok(key1Redux);
758
- assert.equal(keyValidatorCallExpectedCount, 0);
759
- assert.equal(point3DValidatorFunction.callCount, 1);
760
- // Act & Verify
761
- // Read key1 value (again) but from updated map - should NOT call validator again since key1 data hasn't changed
762
- const key1ValueRedux = key1Redux.value();
763
- assert.equal(point3DValidatorFunction.callCount, 1, "validator should still be called only once for key1");
764
- assert.strictEqual(key1ValueRedux, key1Value, "key1 value should remain unchanged");
765
- // Test verification
766
- validatorCallExpected = true;
767
- point3DValidatorFunction.callCount = 0;
768
- // key2 should have been updated; so, reading key2 value from
769
- // original map and updated map should both result in call
770
- // count increases. Also check for updated value.
771
- let key2Value = originalMap.get("key2")?.value();
772
- assert.equal(point3DValidatorFunction.callCount, 1, "validator should be called once for original key2 value read");
773
- assert.deepEqual(key2Value, { "x": 2, "y": 2, "z": 2 }, "key2 value from original map should be unchanged");
774
- key2Value = updatedMap.get("key2")?.value();
775
- assert.equal(point3DValidatorFunction.callCount, 2, "validator should be called second time for updated key2 value read");
776
- assert.deepEqual(key2Value, { "x": 4, "y": 4, "z": 4 }, "key2 should have updated value");
777
- });
778
- });
779
- });
780
- });
781
- });
782
- //# sourceMappingURL=valueManagers.spec.js.map