UNPKG

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