UNPKG

12.9 kBJavaScriptView Raw
1// @flow
2
3const expect = require('expect');
4const { ObservedRemoveSet } = require('../src');
5const { generateValue } = require('./lib/values');
6
7
8describe('Set', () => {
9 test('Add and delete values', () => {
10 const A = generateValue();
11 const B = generateValue();
12 const set = new ObservedRemoveSet();
13 expect(set.size).toEqual(0);
14 set.add(A);
15 expect(set.has(A)).toEqual(true);
16 expect(set.has(B)).toEqual(false);
17 expect(set.size).toEqual(1);
18 set.add(B);
19 expect(set.has(A)).toEqual(true);
20 expect(set.has(B)).toEqual(true);
21 expect(set.size).toEqual(2);
22 set.delete(B);
23 expect(set.has(A)).toEqual(true);
24 expect(set.has(B)).toEqual(false);
25 expect(set.size).toEqual(1);
26 set.delete(A);
27 expect(set.has(A)).toEqual(false);
28 expect(set.has(B)).toEqual(false);
29 expect(set.size).toEqual(0);
30 set.add(A);
31 expect(set.has(A)).toEqual(true);
32 expect(set.has(B)).toEqual(false);
33 expect(set.size).toEqual(1);
34 set.add(B);
35 expect(set.has(A)).toEqual(true);
36 expect(set.has(B)).toEqual(true);
37 expect(set.size).toEqual(2);
38 expect([...set.values()]).toEqual([A, B]);
39 expect([...set]).toEqual([A, B]);
40 expect([...set.entries()]).toEqual([[A, A], [B, B]]);
41 });
42
43 test('Emit add and delete events', async () => {
44 const A = generateValue();
45 const B = generateValue();
46 const set = new ObservedRemoveSet();
47 const addAPromise = new Promise((resolve) => {
48 set.once('add', (x) => {
49 expect(x).toEqual(A);
50 resolve();
51 });
52 set.add(A);
53 });
54 const addBPromise = new Promise((resolve) => {
55 set.once('add', (x) => {
56 expect(x).toEqual(B);
57 resolve();
58 });
59 set.add(B);
60 });
61 await addAPromise;
62 await addBPromise;
63 const deleteAPromise = new Promise((resolve) => {
64 set.once('delete', (x) => {
65 expect(x).toEqual(A);
66 resolve();
67 });
68 set.delete(A);
69 });
70 const deleteBPromise = new Promise((resolve) => {
71 set.once('delete', (x) => {
72 expect(x).toEqual(B);
73 resolve();
74 });
75 set.delete(B);
76 });
77 await deleteAPromise;
78 await deleteBPromise;
79 });
80
81 test('Iterate through values', () => {
82 const A = generateValue();
83 const B = generateValue();
84 const C = generateValue();
85 const set = new ObservedRemoveSet([A, B, C]);
86 let i = 0;
87 for (const x of set) {
88 if (i === 0) {
89 expect(x).toEqual(A);
90 } else if (i === 1) {
91 expect(x).toEqual(B);
92 } else if (i === 2) {
93 expect(x).toEqual(C);
94 }
95 i += 1;
96 }
97 set.forEach((x, index) => {
98 if (index === 0) {
99 expect(x).toEqual(A);
100 } else if (index === 1) {
101 expect(x).toEqual(B);
102 } else if (index === 2) {
103 expect(x).toEqual(C);
104 }
105 });
106 });
107
108 test('Hashes objects', () => {
109 const A = [{ example: 1 }];
110 const B = [{ example: 1 }];
111 const set = new ObservedRemoveSet([A, B]);
112 expect(set.size).toEqual(1);
113 expect(set.has(A)).toEqual(true);
114 expect(set.has(B)).toEqual(true);
115 });
116
117 test('Clear values', () => {
118 const A = generateValue();
119 const B = generateValue();
120 const C = generateValue();
121 const set = new ObservedRemoveSet([A, B, C], { maxAge: -1, bufferPublishing: 0 });
122 expect(set.size).toEqual(3);
123 set.clear();
124 expect(set.size).toEqual(0);
125 expect(set.insertQueue.length).toEqual(0);
126 expect(set.deleteQueue.length).toEqual(0);
127 expect(set.deletions.size).toEqual(3);
128 set.flush();
129 expect(set.size).toEqual(0);
130 expect(set.insertQueue.length).toEqual(0);
131 expect(set.deleteQueue.length).toEqual(0);
132 expect(set.deletions.size).toEqual(0);
133 });
134
135 test('Synchronize sets', async () => {
136 const X = generateValue();
137 const Y = generateValue();
138 const Z = generateValue();
139 const alice = new ObservedRemoveSet();
140 const bob = new ObservedRemoveSet();
141 let aliceAddCount = 0;
142 let bobAddCount = 0;
143 let aliceDeleteCount = 0;
144 let bobDeleteCount = 0;
145 alice.on('add', () => (aliceAddCount += 1));
146 bob.on('add', () => (bobAddCount += 1));
147 alice.on('delete', () => (aliceDeleteCount += 1));
148 bob.on('delete', () => (bobDeleteCount += 1));
149 alice.on('publish', (message) => {
150 bob.process(message);
151 });
152 bob.on('publish', (message) => {
153 alice.process(message);
154 });
155 alice.add(X);
156 alice.add(Y);
157 alice.add(Z);
158 while (aliceAddCount !== 3 || bobAddCount !== 3) {
159 await new Promise((resolve) => setTimeout(resolve, 20));
160 }
161 expect([...alice]).toEqual([X, Y, Z]);
162 expect([...bob]).toEqual([X, Y, Z]);
163 bob.delete(X);
164 bob.delete(Y);
165 bob.delete(Z);
166 while (aliceDeleteCount !== 3 || bobDeleteCount !== 3) {
167 await new Promise((resolve) => setTimeout(resolve, 100));
168 }
169 expect([...alice]).toEqual([]);
170 expect([...bob]).toEqual([]);
171 });
172
173 test('Flush values', async () => {
174 const X = generateValue();
175 const Y = generateValue();
176 const Z = generateValue();
177 const set = new ObservedRemoveSet([X, Y, Z], { maxAge: 100 });
178 set.delete(X);
179 set.delete(Y);
180 set.delete(Z);
181 expect(set.deletions.size).toEqual(3);
182 set.flush();
183 expect(set.deletions.size).toEqual(3);
184 await new Promise((resolve) => setTimeout(resolve, 200));
185 set.flush();
186 expect(set.deletions.size).toEqual(0);
187 });
188
189 test('Flush deletions from add events', async () => {
190 const X = generateValue();
191 const Y = generateValue();
192 const Z = generateValue();
193 const set = new ObservedRemoveSet([X, Y, Z], { maxAge: 100 });
194 set.flush();
195 expect(set.deletions.size).toEqual(0);
196 set.add(X);
197 set.add(Y);
198 set.add(Z);
199 expect(set.deletions.size).toEqual(3);
200 await new Promise((resolve) => setTimeout(resolve, 200));
201 set.flush();
202 expect(set.deletions.size).toEqual(0);
203 });
204
205 test('Only send out delete events when values have changed', async () => {
206 const X = generateValue();
207 const set = new ObservedRemoveSet([X], { maxAge: 100 });
208 const deletePromise = new Promise((resolve, reject) => {
209 const handleDelete = () => {
210 clearTimeout(timeout);
211 reject(new Error('Delete event should not be trigged on add'));
212 };
213 const timeout = setTimeout(() => {
214 set.removeListener('delete', handleDelete);
215 resolve();
216 }, 200);
217 set.on('delete', handleDelete);
218 });
219 set.add(X);
220 await deletePromise;
221 });
222
223 test('Synchronize add and delete events', async () => {
224 const X = generateValue();
225 const Y = generateValue();
226 const alice = new ObservedRemoveSet();
227 const bob = new ObservedRemoveSet();
228 alice.on('publish', (message) => {
229 bob.process(message);
230 });
231 bob.on('publish', (message) => {
232 alice.process(message);
233 });
234 const aliceAddXPromise = new Promise((resolve) => {
235 alice.once('add', (value) => {
236 expect(value).toEqual(X);
237 resolve();
238 });
239 });
240 const aliceDeleteXPromise = new Promise((resolve) => {
241 alice.once('delete', (value) => {
242 expect(value).toEqual(X);
243 resolve();
244 });
245 });
246 bob.add(X);
247 await aliceAddXPromise;
248 bob.delete(X);
249 await aliceDeleteXPromise;
250 const bobAddYPromise = new Promise((resolve) => {
251 bob.once('add', (value) => {
252 expect(value).toEqual(Y);
253 resolve();
254 });
255 });
256 const bobDeleteYPromise = new Promise((resolve) => {
257 bob.once('delete', (value) => {
258 expect(value).toEqual(Y);
259 resolve();
260 });
261 });
262 alice.add(Y);
263 await bobAddYPromise;
264 alice.delete(Y);
265 await bobDeleteYPromise;
266 });
267
268 test('Should not emit events for remote set/delete combos on sync', async () => {
269 const X = generateValue();
270 const Y = generateValue();
271 const alice = new ObservedRemoveSet();
272 const bob = new ObservedRemoveSet();
273 alice.add(X);
274 alice.delete(X);
275 bob.add(Y);
276 bob.delete(Y);
277 await new Promise((resolve) => setTimeout(resolve, 250));
278 const bobPromise = new Promise((resolve, reject) => {
279 bob.once('add', () => {
280 reject(new Error('Bob should not receive add event'));
281 });
282 bob.once('delete', () => {
283 reject(new Error('Bob should not receive delete event'));
284 });
285 setTimeout(resolve, 500);
286 });
287 const alicePromise = new Promise((resolve, reject) => {
288 alice.once('add', () => {
289 reject(new Error('Alice should not receive add event'));
290 });
291 alice.once('delete', () => {
292 reject(new Error('Alice should not receive delete event'));
293 });
294 setTimeout(resolve, 500);
295 });
296 alice.on('publish', (message) => {
297 bob.process(message);
298 });
299 bob.on('publish', (message) => {
300 alice.process(message);
301 });
302 alice.sync();
303 bob.sync();
304 await bobPromise;
305 await alicePromise;
306 expect(alice.has(X)).toEqual(false);
307 expect(alice.has(Y)).toEqual(false);
308 expect(bob.has(X)).toEqual(false);
309 expect(bob.has(Y)).toEqual(false);
310 });
311
312 test('Synchronize mixed sets using sync', async () => {
313 const A = generateValue();
314 const B = generateValue();
315 const C = generateValue();
316 const X = generateValue();
317 const Y = generateValue();
318 const Z = generateValue();
319 const alice = new ObservedRemoveSet();
320 const bob = new ObservedRemoveSet();
321 alice.add(A);
322 bob.add(X);
323 alice.add(B);
324 bob.add(Y);
325 alice.add(C);
326 bob.add(Z);
327 let aliceAddCount = 0;
328 let bobAddCount = 0;
329 let aliceDeleteCount = 0;
330 let bobDeleteCount = 0;
331 await new Promise((resolve) => setTimeout(resolve, 100));
332 expect(new Set([...alice])).toEqual(new Set([A, B, C]));
333 expect(new Set([...bob])).toEqual(new Set([X, Y, Z]));
334 alice.on('add', () => (aliceAddCount += 1));
335 bob.on('add', () => (bobAddCount += 1));
336 alice.on('delete', () => (aliceDeleteCount += 1));
337 bob.on('delete', () => (bobDeleteCount += 1));
338 alice.on('publish', (message) => {
339 bob.process(message);
340 });
341 bob.on('publish', (message) => {
342 alice.process(message);
343 });
344 alice.sync();
345 bob.sync();
346 while (aliceAddCount !== 3 || bobAddCount !== 3) {
347 await new Promise((resolve) => setTimeout(resolve, 20));
348 }
349 expect(new Set([...alice])).toEqual(new Set([A, X, B, Y, C, Z]));
350 expect(new Set([...bob])).toEqual(new Set([A, X, B, Y, C, Z]));
351 });
352
353 test('Values should not repeat', async () => {
354 const value = generateValue();
355 const alice = new ObservedRemoveSet();
356 alice.add(value);
357 alice.add(value);
358 expect([...alice].length).toEqual(1);
359 expect([...alice.values()].length).toEqual(1);
360 expect([...alice.entries()].length).toEqual(1);
361 expect([...alice]).toEqual([value]);
362 expect([...alice.values()]).toEqual([value]);
363 expect([...alice.entries()]).toEqual([[value, value]]);
364 });
365
366 test('Synchronizes 100 asynchrous sets', async () => {
367 const A = generateValue();
368 const B = generateValue();
369 const C = generateValue();
370 const sets = [];
371 const callbacks = [];
372 const publish = (sourceId:number, message:Buffer) => {
373 for (let i = 0; i < callbacks.length; i += 1) {
374 const [targetId, callback] = callbacks[i];
375 if (targetId === sourceId) {
376 continue;
377 }
378 setTimeout(() => callback(message), Math.round(1000 * Math.random()));
379 }
380 };
381 const subscribe = (targetId: number, callback:Function) => {
382 callbacks.push([targetId, callback]);
383 };
384 const getPair = () => {
385 const setA = sets[Math.floor(Math.random() * sets.length)];
386 let setB = setA;
387 while (setB === setA) {
388 setB = sets[Math.floor(Math.random() * sets.length)];
389 }
390 return [setA, setB];
391 };
392 for (let i = 0; i < 100; i += 1) {
393 const set = new ObservedRemoveSet();
394 set.on('publish', (message) => publish(i, message));
395 subscribe(i, (message) => set.process(message));
396 sets.push(set);
397 }
398 const [alice, bob] = getPair();
399 let aliceAddCount = 0;
400 let bobAddCount = 0;
401 let aliceDeleteCount = 0;
402 let bobDeleteCount = 0;
403 alice.on('add', () => (aliceAddCount += 1));
404 bob.on('add', () => (bobAddCount += 1));
405 alice.on('delete', () => (aliceDeleteCount += 1));
406 bob.on('delete', () => (bobDeleteCount += 1));
407 alice.add(A);
408 alice.add(B);
409 alice.add(C);
410 while (aliceAddCount !== 3 || bobAddCount !== 3) {
411 await new Promise((resolve) => setTimeout(resolve, 100));
412 }
413 bob.delete(C);
414 bob.delete(B);
415 bob.delete(A);
416 while (aliceDeleteCount !== 3 || bobDeleteCount !== 3) {
417 await new Promise((resolve) => setTimeout(resolve, 100));
418 }
419 expect([...alice]).toEqual([]);
420 expect([...bob]).toEqual([]);
421 });
422});
423