UNPKG

crdt.js

Version:

Conflict-Free Replicated Data Types JavaScript Library

339 lines (302 loc) 10.3 kB
'use strict' const assert = require('assert') const { TwoPSet, GSet, CmRDTSet } = require('../src') describe('2P-Set', () => { describe('Instance', () => { describe('constructor', () => { it('creates a set', () => { const crdt = new TwoPSet() assert.notEqual(crdt, null) }) it('has two GSets', () => { const crdt = new TwoPSet() assert.notEqual(crdt._added, null) assert.equal(crdt._added instanceof GSet, true) assert.notEqual(crdt._removed, null) assert.equal(crdt._removed instanceof GSet, true) }) it('is a CmRDT Set', () => { const crdt = new TwoPSet() assert.equal(crdt instanceof CmRDTSet, true) }) it('creates a set from values', () => { const crdt = new TwoPSet(['A', 'B']) assert.notEqual(crdt, null) assert.equal(crdt._added instanceof GSet, true) assert.deepEqual(new Set(crdt._added.values()), new Set(['B', 'A'])) }) }) describe('values', () => { it('is an Iterator', () => { const crdt = new TwoPSet() assert.equal(crdt.values().toString(), '[object Set Iterator]') }) it('returns an Iterator', () => { const crdt = new TwoPSet() crdt.add('A') crdt.add('B') const iterator = crdt.values() assert.equal(iterator.next().value, 'A') assert.equal(iterator.next().value, 'B') }) }) describe('add', () => { it('adds an element to the set', () => { const crdt = new TwoPSet() crdt.add('A') assert.deepEqual(new Set(crdt.values()), new Set(['A'])) }) it('adds three elements to the set', () => { const crdt = new TwoPSet() crdt.add('A') crdt.add('B') crdt.add('C') assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C'])) }) it('contains only unique values', () => { const crdt = new TwoPSet() crdt.add(1) crdt.add('A') crdt.add('A') crdt.add(1) crdt.add('A') const obj = { hello: 'ABC' } crdt.add(obj) crdt.add(obj) crdt.add({ hello: 'ABCD' }) const expectedResult = [ 'A', 1, { hello: 'ABC' }, { hello: 'ABCD' }, ] assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) }) it('has internally a set for added elements', () => { const addedValues = ['A', 'B', 'C', 0, 1, 2, 3, 4] const removedValues = ['A', 1, 2, 3] const expectedResult = ['B', 'C', 0, 4] const crdt = new TwoPSet(addedValues, removedValues) assert.equal(crdt._added instanceof GSet, true) assert.deepEqual(new Set(crdt._added.values()), new Set(addedValues)) assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) }) }) describe('remove', () => { it('removes an element from the set', () => { const crdt = new TwoPSet() crdt.add('A') crdt.remove('A') assert.deepEqual(new Set(crdt.values()), new Set([])) }) it('element can only be removed if it is present in the set', () => { const crdt = new TwoPSet() crdt.remove('A') crdt.add('A') assert.deepEqual(new Set(crdt._removed.values()), new Set([])) assert.deepEqual(new Set(crdt.values()), new Set(['A'])) }) it('has internally a set for removed elements', () => { const addedValues = ['A', 'B', 'C', 0, 1, 2, 3, 4] const removedValues = ['A', 1, 2, 3] const expectedResult = ['B', 'C', 0, 4] const crdt = new TwoPSet(addedValues, removedValues) assert.equal(crdt._removed instanceof GSet, true) assert.deepEqual(new Set(crdt._removed.values()), new Set(removedValues)) assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) }) it('adds removed elements to the internal removed set', () => { const crdt = new TwoPSet() crdt.add('A') crdt.add('B') crdt.remove('A') crdt.remove('B') crdt.remove('A') const expectedResult = ['A', 'B'] assert.deepEqual(new Set(crdt._removed.values()), new Set(expectedResult)) }) }) describe('merge', () => { it('merges two sets with same id', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() crdt1.add('A') crdt2.add('B') crdt2.add('C') crdt2.add('D') crdt2.remove('D') crdt1.merge(crdt2) assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C']) }) it('merges two sets with same values', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() crdt1.add('A') crdt2.add('B') crdt2.add('A') crdt2.remove('B') crdt1.merge(crdt2) assert.deepEqual(crdt1.toArray(), ['A']) }) it('merges two sets with removed values', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() crdt1.add('A') crdt1.remove('A') crdt2.remove('A') crdt2.add('A') crdt1.add('AAA') crdt2.add('AAA') crdt1.add('A') crdt1.merge(crdt2) assert.deepEqual(crdt1.toArray(), ['AAA']) }) it('merge four different sets', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() const crdt3 = new TwoPSet() const crdt4 = new TwoPSet() crdt1.add('A') crdt2.add('B') crdt3.add('C') crdt4.add('D') crdt2.add('BB') crdt2.remove('BB') crdt1.merge(crdt2) crdt1.merge(crdt3) crdt1.remove('C') crdt1.merge(crdt4) assert.deepEqual(crdt1.toArray(), ['A', 'B', 'D']) }) it('doesn\'t overwrite other\'s values on merge', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() crdt1.add('A') crdt2.add('C') crdt2.add('B') crdt2.remove('C') crdt1.merge(crdt2) crdt1.add('AA') crdt2.add('CC') crdt2.add('BB') crdt2.remove('CC') crdt1.merge(crdt2) assert.deepEqual(crdt1.toArray(), ['A', 'B', 'AA', 'BB']) assert.deepEqual(crdt2.toArray(), ['B', 'BB']) }) }) describe('has', () => { it('returns true if given element is in the set', () => { const crdt = new TwoPSet() crdt.add('A') crdt.add('B') crdt.add(1) crdt.add(13) crdt.remove(13) const obj = { hello: 'world' } crdt.add(obj) assert.equal(crdt.has('A'), true) assert.equal(crdt.has('B'), true) assert.equal(crdt.has(1), true) assert.equal(crdt.has(13), false) assert.equal(crdt.has(obj), true) }) it('returns false if given element is not in the set', () => { const crdt = new TwoPSet() crdt.add('A') crdt.add('B') crdt.add('nothere') crdt.remove('nothere') assert.equal(crdt.has('nothere'), false) }) }) describe('hasAll', () => { it('returns true if all given elements are in the set', () => { const crdt = new TwoPSet() crdt.add('A') crdt.add('B') crdt.add('C') crdt.remove('C') crdt.add('D') assert.equal(crdt.hasAll(['D', 'A', 'B']), true) }) it('returns false if any of the given elements are not in the set', () => { const crdt = new TwoPSet() crdt.add('A') crdt.add('B') crdt.add('C') crdt.remove('C') crdt.add('D') assert.equal(crdt.hasAll(['D', 'A', 'C', 'B']), false) }) }) describe('toArray', () => { it('returns the values of the set as an Array', () => { const crdt = new TwoPSet() const array = crdt.toArray() assert.equal(Array.isArray(array), true) }) it('returns values', () => { const crdt = new TwoPSet() crdt.add('A') crdt.add('B') crdt.add('C') crdt.remove('C') const array = crdt.toArray() assert.equal(array.length, 2) assert.equal(array[0], 'A') assert.equal(array[1], 'B') }) }) describe('toJSON', () => { it('returns the set as JSON object', () => { const crdt = new TwoPSet() crdt.add('A') assert.equal(crdt.toJSON().values.added.length, 1) assert.equal(crdt.toJSON().values.added[0], 'A') assert.equal(crdt.toJSON().values.removed.length, 0) crdt.remove('A') assert.equal(crdt.toJSON().values.removed.length, 1) assert.equal(crdt.toJSON().values.removed[0], 'A') }) it('returns a JSON object after a merge', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() crdt1.add('A') crdt2.add('B') crdt2.remove('B') crdt1.merge(crdt2) crdt2.merge(crdt1) assert.equal(crdt1.toJSON().values.added.length, 2) assert.equal(crdt1.toJSON().values.added[0], 'A') assert.equal(crdt1.toJSON().values.added[1], 'B') assert.equal(crdt1.toJSON().values.removed.length, 1) assert.equal(crdt1.toJSON().values.removed[0], 'B') }) }) describe('isEqual', () => { it('returns true for sets with same values', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() crdt1.add('A') crdt2.add('A') assert.equal(crdt1.isEqual(crdt2), true) assert.equal(crdt2.isEqual(crdt1), true) }) it('returns true for empty sets', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() assert.equal(crdt1.isEqual(crdt2), true) assert.equal(crdt2.isEqual(crdt1), true) }) it('returns false for sets with different values', () => { const crdt1 = new TwoPSet() const crdt2 = new TwoPSet() crdt1.add('A') crdt2.add('B') assert.equal(crdt1.isEqual(crdt2), false) assert.equal(crdt2.isEqual(crdt1), false) }) }) }) })