UNPKG

16.9 kBJavaScriptView Raw
1// @flow
2
3const expect = require('expect');
4const uuid = require('uuid');
5const { ObservedRemoveMap } = require('../src');
6const { generateValue } = require('./lib/values');
7
8describe('Map', () => {
9 test('Set and delete values', () => {
10 const keyA = uuid.v4();
11 const keyB = uuid.v4();
12 const valueA = generateValue();
13 const valueB = generateValue();
14 const map = new ObservedRemoveMap();
15 expect(map.size).toEqual(0);
16 map.set(keyA, valueA);
17 expect(map.has(keyA)).toEqual(true);
18 expect(map.has(keyB)).toEqual(false);
19 expect(map.size).toEqual(1);
20 map.set(keyB, valueB);
21 expect(map.has(keyA)).toEqual(true);
22 expect(map.has(keyB)).toEqual(true);
23 expect(map.size).toEqual(2);
24 map.delete(keyB);
25 expect(map.has(keyA)).toEqual(true);
26 expect(map.has(keyB)).toEqual(false);
27 expect(map.size).toEqual(1);
28 map.delete(keyA);
29 expect(map.has(keyA)).toEqual(false);
30 expect(map.has(keyB)).toEqual(false);
31 expect(map.size).toEqual(0);
32 map.set(keyA, valueA);
33 expect(map.has(keyA)).toEqual(true);
34 expect(map.has(keyB)).toEqual(false);
35 expect(map.size).toEqual(1);
36 map.set(keyB, valueB);
37 expect(map.has(keyA)).toEqual(true);
38 expect(map.has(keyB)).toEqual(true);
39 expect(map.size).toEqual(2);
40 expect([...map.values()]).toEqual([valueA, valueB]);
41 expect([...map.keys()]).toEqual([keyA, keyB]);
42 expect([...map]).toEqual([[keyA, valueA], [keyB, valueB]]);
43 expect([...map.entries()]).toEqual([[keyA, valueA], [keyB, valueB]]);
44 });
45
46 test('Emit set and delete events', async () => {
47 const keyA = uuid.v4();
48 const keyB = uuid.v4();
49 const valueA = generateValue();
50 const valueB = generateValue();
51 const map = new ObservedRemoveMap();
52 const setAPromise = new Promise((resolve) => {
53 map.once('set', (k, v) => {
54 expect(k).toEqual(keyA);
55 expect(v).toEqual(valueA);
56 resolve();
57 });
58 map.set(keyA, valueA);
59 });
60 const setBPromise = new Promise((resolve) => {
61 map.once('set', (k, v) => {
62 expect(k).toEqual(keyB);
63 expect(v).toEqual(valueB);
64 resolve();
65 });
66 map.set(keyB, valueB);
67 });
68 await setAPromise;
69 await setBPromise;
70 const deleteAPromise = new Promise((resolve) => {
71 map.once('delete', (k, v) => {
72 expect(k).toEqual(keyA);
73 expect(v).toEqual(valueA);
74 resolve();
75 });
76 map.delete(keyA);
77 });
78 const deleteBPromise = new Promise((resolve) => {
79 map.once('delete', (k, v) => {
80 expect(k).toEqual(keyB);
81 expect(v).toEqual(valueB);
82 resolve();
83 });
84 map.delete(keyB);
85 });
86 await deleteAPromise;
87 await deleteBPromise;
88 });
89
90
91 test('Iterate through values', () => {
92 const keyA = uuid.v4();
93 const keyB = uuid.v4();
94 const keyC = uuid.v4();
95 const valueA = generateValue();
96 const valueB = generateValue();
97 const valueC = generateValue();
98 const map = new ObservedRemoveMap([[keyA, valueA], [keyB, valueB], [keyC, valueC]]);
99 let i = 0;
100 for (const [k, v] of map) { // eslint-disable-line no-restricted-syntax
101 if (i === 0) {
102 expect(k).toEqual(keyA);
103 expect(v).toEqual(valueA);
104 } else if (i === 1) {
105 expect(k).toEqual(keyB);
106 expect(v).toEqual(valueB);
107 } else if (i === 2) {
108 expect(k).toEqual(keyC);
109 expect(v).toEqual(valueC);
110 }
111 i += 1;
112 }
113 map.forEach((v, k) => {
114 if (k === keyA) {
115 expect(v).toEqual(valueA);
116 } else if (k === keyB) {
117 expect(v).toEqual(valueB);
118 } else if (k === keyC) {
119 expect(v).toEqual(valueC);
120 }
121 });
122 });
123
124 test('Clear values', () => {
125 const keyA = uuid.v4();
126 const keyB = uuid.v4();
127 const keyC = uuid.v4();
128 const valueA = generateValue();
129 const valueB = generateValue();
130 const valueC = generateValue();
131 const map = new ObservedRemoveMap([[keyA, valueA], [keyB, valueB], [keyC, valueC]], { maxAge: -1, bufferPublishing: 0 });
132 expect(map.size).toEqual(3);
133 map.clear();
134 expect(map.size).toEqual(0);
135 expect(map.insertQueue.length).toEqual(0);
136 expect(map.deleteQueue.length).toEqual(0);
137 expect(map.deletions.size).toEqual(3);
138 map.flush();
139 expect(map.size).toEqual(0);
140 expect(map.insertQueue.length).toEqual(0);
141 expect(map.deleteQueue.length).toEqual(0);
142 expect(map.deletions.size).toEqual(0);
143 });
144
145 test('Synchronize maps', async () => {
146 const keyX = uuid.v4();
147 const keyY = uuid.v4();
148 const keyZ = uuid.v4();
149 const valueX = generateValue();
150 const valueY = generateValue();
151 const valueZ = generateValue();
152 const alice = new ObservedRemoveMap();
153 const bob = new ObservedRemoveMap();
154 let aliceAddCount = 0;
155 let bobAddCount = 0;
156 let aliceDeleteCount = 0;
157 let bobDeleteCount = 0;
158 alice.on('set', () => (aliceAddCount += 1));
159 bob.on('set', () => (bobAddCount += 1));
160 alice.on('delete', () => (aliceDeleteCount += 1));
161 bob.on('delete', () => (bobDeleteCount += 1));
162 alice.on('publish', (message) => {
163 bob.process(message);
164 });
165 bob.on('publish', (message) => {
166 alice.process(message);
167 });
168 alice.set(keyX, valueX);
169 alice.set(keyY, valueY);
170 alice.set(keyZ, valueZ);
171 while (aliceAddCount !== 3 || bobAddCount !== 3) {
172 await new Promise((resolve) => setTimeout(resolve, 100));
173 }
174 expect(alice.get(keyX)).toEqual(valueX);
175 expect(alice.get(keyY)).toEqual(valueY);
176 expect(alice.get(keyZ)).toEqual(valueZ);
177 expect(bob.get(keyX)).toEqual(valueX);
178 expect(bob.get(keyY)).toEqual(valueY);
179 expect(bob.get(keyZ)).toEqual(valueZ);
180 expect([...alice]).toEqual([[keyX, valueX], [keyY, valueY], [keyZ, valueZ]]);
181 expect([...bob]).toEqual([[keyX, valueX], [keyY, valueY], [keyZ, valueZ]]);
182 bob.delete(keyX);
183 bob.delete(keyY);
184 bob.delete(keyZ);
185 while (aliceDeleteCount !== 3 || bobDeleteCount !== 3) {
186 await new Promise((resolve) => setTimeout(resolve, 100));
187 }
188 expect(alice.get(keyX)).toBeUndefined();
189 expect(alice.get(keyY)).toBeUndefined();
190 expect(alice.get(keyZ)).toBeUndefined();
191 expect(bob.get(keyX)).toBeUndefined();
192 expect(bob.get(keyY)).toBeUndefined();
193 expect(bob.get(keyZ)).toBeUndefined();
194 expect([...alice]).toEqual([]);
195 expect([...bob]).toEqual([]);
196 });
197
198 test('Flush deletions', async () => {
199 const keyX = uuid.v4();
200 const keyY = uuid.v4();
201 const keyZ = uuid.v4();
202 const valueX = generateValue();
203 const valueY = generateValue();
204 const valueZ = generateValue();
205 const map = new ObservedRemoveMap([[keyX, valueX], [keyY, valueY], [keyZ, valueZ]], { maxAge: 100 });
206 map.delete(keyX);
207 map.delete(keyY);
208 map.delete(keyZ);
209 expect(map.deletions.size).toEqual(3);
210 map.flush();
211 expect(map.deletions.size).toEqual(3);
212 await new Promise((resolve) => setTimeout(resolve, 200));
213 map.flush();
214 expect(map.deletions.size).toEqual(0);
215 });
216
217 test('Synchronize set and delete events', async () => {
218 const keyX = uuid.v4();
219 const keyY = uuid.v4();
220 const valueX = generateValue();
221 const valueY = generateValue();
222 const alice = new ObservedRemoveMap();
223 const bob = new ObservedRemoveMap();
224 alice.on('publish', (message) => {
225 bob.process(message);
226 });
227 bob.on('publish', (message) => {
228 alice.process(message);
229 });
230 const aliceSetXPromise = new Promise((resolve) => {
231 alice.once('set', (key, value) => {
232 expect(key).toEqual(keyX);
233 expect(value).toEqual(valueX);
234 resolve();
235 });
236 });
237 const aliceDeleteXPromise = new Promise((resolve) => {
238 alice.once('delete', (key, value) => {
239 expect(key).toEqual(keyX);
240 expect(value).toEqual(valueX);
241 resolve();
242 });
243 });
244 bob.set(keyX, valueX);
245 await aliceSetXPromise;
246 bob.delete(keyX);
247 await aliceDeleteXPromise;
248 const bobSetYPromise = new Promise((resolve) => {
249 bob.once('set', (key, value) => {
250 expect(key).toEqual(keyY);
251 expect(value).toEqual(valueY);
252 resolve();
253 });
254 });
255 const bobDeleteYPromise = new Promise((resolve) => {
256 bob.once('delete', (key, value) => {
257 expect(key).toEqual(keyY);
258 expect(value).toEqual(valueY);
259 resolve();
260 });
261 });
262 alice.set(keyY, valueY);
263 await bobSetYPromise;
264 alice.delete(keyY);
265 await bobDeleteYPromise;
266 });
267
268 test('Should not emit events for remote set/delete combos on sync', async () => {
269 const keyX = uuid.v4();
270 const keyY = uuid.v4();
271 const valueX = generateValue();
272 const valueY = generateValue();
273 const alice = new ObservedRemoveMap();
274 const bob = new ObservedRemoveMap();
275 alice.set(keyX, valueX);
276 alice.delete(keyX);
277 bob.set(keyY, valueY);
278 bob.delete(keyY);
279 await new Promise((resolve) => setTimeout(resolve, 250));
280 const bobPromise = new Promise((resolve, reject) => {
281 bob.once('set', () => {
282 reject(new Error('Bob should not receive set event'));
283 });
284 bob.once('delete', () => {
285 reject(new Error('Bob should not receive delete event'));
286 });
287 setTimeout(resolve, 500);
288 });
289 const alicePromise = new Promise((resolve, reject) => {
290 alice.once('set', () => {
291 reject(new Error('Alice should not receive set event'));
292 });
293 alice.once('delete', () => {
294 reject(new Error('Alice should not receive delete event'));
295 });
296 setTimeout(resolve, 500);
297 });
298 alice.on('publish', (message) => {
299 bob.process(message);
300 });
301 bob.on('publish', (message) => {
302 alice.process(message);
303 });
304 alice.sync();
305 bob.sync();
306 await bobPromise;
307 await alicePromise;
308 expect(alice.get(keyX)).toBeUndefined();
309 expect(alice.get(keyY)).toBeUndefined();
310 expect(bob.get(keyX)).toBeUndefined();
311 expect(bob.get(keyY)).toBeUndefined();
312 });
313
314 test('Synchronize mixed maps using sync', async () => {
315 const keyA = uuid.v4();
316 const keyB = uuid.v4();
317 const keyC = uuid.v4();
318 const keyX = uuid.v4();
319 const keyY = uuid.v4();
320 const keyZ = uuid.v4();
321 const valueA = generateValue();
322 const valueB = generateValue();
323 const valueC = generateValue();
324 const valueX = generateValue();
325 const valueY = generateValue();
326 const valueZ = generateValue();
327 const alice = new ObservedRemoveMap();
328 const bob = new ObservedRemoveMap();
329 alice.set(keyA, valueA);
330 bob.set(keyX, valueX);
331 alice.set(keyB, valueB);
332 bob.set(keyY, valueY);
333 alice.set(keyC, valueC);
334 bob.set(keyZ, valueZ);
335 let aliceAddCount = 0;
336 let bobAddCount = 0;
337 let aliceDeleteCount = 0;
338 let bobDeleteCount = 0;
339 await new Promise((resolve) => setTimeout(resolve, 100));
340 expect([...alice]).toEqual([[keyA, valueA], [keyB, valueB], [keyC, valueC]]);
341 expect([...bob]).toEqual([[keyX, valueX], [keyY, valueY], [keyZ, valueZ]]);
342 alice.on('set', () => (aliceAddCount += 1));
343 bob.on('set', () => (bobAddCount += 1));
344 alice.on('delete', () => (aliceDeleteCount += 1));
345 bob.on('delete', () => (bobDeleteCount += 1));
346 alice.on('publish', (message) => {
347 bob.process(message);
348 });
349 bob.on('publish', (message) => {
350 alice.process(message);
351 });
352 alice.sync();
353 bob.sync();
354 while (aliceAddCount !== 3 || bobAddCount !== 3) {
355 await new Promise((resolve) => setTimeout(resolve, 20));
356 }
357 expect([...alice]).toEqual(expect.arrayContaining([[keyA, valueA], [keyX, valueX], [keyB, valueB], [keyY, valueY], [keyC, valueC], [keyZ, valueZ]]));
358 expect([...bob]).toEqual(expect.arrayContaining([[keyA, valueA], [keyX, valueX], [keyB, valueB], [keyY, valueY], [keyC, valueC], [keyZ, valueZ]]));
359 });
360
361 test('Key-value pairs should not repeat', async () => {
362 const key = uuid.v4();
363 const value1 = generateValue();
364 const value2 = generateValue();
365 const alice = new ObservedRemoveMap();
366 alice.set(key, value1);
367 alice.set(key, value2);
368 expect([...alice].length).toEqual(1);
369 expect([...alice.entries()].length).toEqual(1);
370 expect([...alice.keys()].length).toEqual(1);
371 expect([...alice.values()].length).toEqual(1);
372 expect([...alice]).toEqual([[key, value2]]);
373 expect([...alice.entries()]).toEqual([[key, value2]]);
374 expect([...alice.keys()]).toEqual([key]);
375 expect([...alice.values()]).toEqual([value2]);
376 expect(alice.get(key)).toEqual(value2);
377 });
378
379 test('Synchronizes 100 asynchrous maps', async () => {
380 const keyA = uuid.v4();
381 const keyB = uuid.v4();
382 const keyC = uuid.v4();
383 const valueA = generateValue();
384 const valueB = generateValue();
385 const valueC = generateValue();
386 const maps = [];
387 const callbacks = [];
388 const publish = (sourceId:number, message:Buffer) => {
389 for (let i = 0; i < callbacks.length; i += 1) {
390 const [targetId, callback] = callbacks[i];
391 if (targetId === sourceId) {
392 continue;
393 }
394 setTimeout(() => callback(message), Math.round(1000 * Math.random()));
395 }
396 };
397 const subscribe = (targetId: number, callback:Function) => {
398 callbacks.push([targetId, callback]);
399 };
400 const getPair = () => {
401 const mapA = maps[Math.floor(Math.random() * maps.length)];
402 let mapB = mapA;
403 while (mapB === mapA) {
404 mapB = maps[Math.floor(Math.random() * maps.length)];
405 }
406 return [mapA, mapB];
407 };
408 for (let i = 0; i < 100; i += 1) {
409 const map = new ObservedRemoveMap();
410 map.on('publish', (message) => publish(i, message));
411 subscribe(i, (message) => map.process(message));
412 maps.push(map);
413 }
414 const [alice, bob] = getPair();
415 let aliceAddCount = 0;
416 let bobAddCount = 0;
417 let aliceDeleteCount = 0;
418 let bobDeleteCount = 0;
419 alice.on('set', () => (aliceAddCount += 1));
420 bob.on('set', () => (bobAddCount += 1));
421 alice.on('delete', () => (aliceDeleteCount += 1));
422 bob.on('delete', () => (bobDeleteCount += 1));
423 alice.set(keyA, valueA);
424 bob.set(keyB, valueB);
425 alice.set(keyC, valueC);
426 while (aliceAddCount !== 3 || bobAddCount !== 3) {
427 await new Promise((resolve) => setTimeout(resolve, 20));
428 }
429 bob.delete(keyC);
430 alice.delete(keyB);
431 bob.delete(keyA);
432 while (aliceDeleteCount !== 3 || bobDeleteCount !== 3) {
433 await new Promise((resolve) => setTimeout(resolve, 20));
434 }
435 expect([...alice]).toEqual([]);
436 expect([...bob]).toEqual([]);
437 });
438
439 test('Synchronize out of order sets', async () => {
440 const alice = new ObservedRemoveMap([]);
441 const bob = new ObservedRemoveMap([]);
442 const key = uuid.v4();
443 const value1 = generateValue();
444 const value2 = generateValue();
445 alice.set(key, value1);
446 const aliceDump1 = alice.dump();
447 alice.set(key, value2);
448 const aliceDump2 = alice.dump();
449 bob.process(aliceDump2);
450 expect(bob.get(key)).toEqual(value2);
451 bob.delete(key);
452 expect(bob.get(key)).toBeUndefined();
453 const bobDump1 = bob.dump();
454 alice.process(bobDump1);
455 expect(alice.get(key)).toBeUndefined();
456 bob.process(aliceDump1);
457 expect(alice.get(key)).toBeUndefined();
458 expect(bob.get(key)).toBeUndefined();
459 const bobDump2 = bob.dump();
460 alice.process(bobDump2);
461 expect(alice.get(key)).toBeUndefined();
462 expect(bob.get(key)).toBeUndefined();
463 });
464
465 test('Affirm values if synchronization happens twice', async () => {
466 const keyA = uuid.v4();
467 const keyB = uuid.v4();
468 const valueA = generateValue();
469 const valueB = generateValue();
470 const alice = new ObservedRemoveMap([[keyA, valueA]]);
471 const bob = new ObservedRemoveMap([[keyB, valueB]]);
472 const setAPromise = new Promise((resolve) => {
473 bob.once('set', (k, v) => {
474 expect(k).toEqual(keyA);
475 expect(v).toEqual(valueA);
476 resolve();
477 });
478 });
479 const setBPromise = new Promise((resolve) => {
480 alice.once('set', (k, v) => {
481 expect(k).toEqual(keyB);
482 expect(v).toEqual(valueB);
483 resolve();
484 });
485 });
486 const aliceDump = alice.dump();
487 const bobDump = bob.dump();
488 alice.process(bobDump);
489 bob.process(aliceDump);
490 await setAPromise;
491 await setBPromise;
492 const affirmAPromise = new Promise((resolve) => {
493 bob.once('affirm', (k, v) => {
494 expect(k).toEqual(keyA);
495 expect(v).toEqual(valueA);
496 resolve();
497 });
498 });
499 const affirmBPromise = new Promise((resolve) => {
500 alice.once('affirm', (k, v) => {
501 expect(k).toEqual(keyB);
502 expect(v).toEqual(valueB);
503 resolve();
504 });
505 });
506 alice.process(bobDump);
507 bob.process(aliceDump);
508 await affirmAPromise;
509 await affirmBPromise;
510 });
511});
512