Comparing version 0.1.3 to 0.1.4
{ | ||
"name": "crdts", | ||
"version": "0.1.3", | ||
"version": "0.1.4", | ||
"description": "A CRDT Library for JavaScript", | ||
@@ -5,0 +5,0 @@ "main": "src/index.js", |
@@ -42,15 +42,9 @@ # CRDTs | ||
``` | ||
+------------------------------------------------------------+ | ||
| || || || || | | ||
| OR-Set || LWW-Set || 2P-Set || G-Set || G-Counter | | ||
| || || || || | | ||
+-------------------------| || +------------+ | ||
| || || | | ||
| CmRDT-Set || || | | ||
| || || | | ||
+-----------------------------------------------+ | ||
| | | ||
| G-Set | | ||
| | | ||
+-----------------------------------------------+ | ||
+-----------++-----------++----------++---------++------------+ | ||
Data Type | OR-Set || LWW-Set || 2P-Set || G-Set || G-Counter | | ||
+-----------++-----------++----------++---------++------------+ | ||
Base Class | CmRDT-Set | -- | | ||
|-----------------------------------------------+-------------+ | ||
CRDT Type | Operation-Based | State-based | | ||
+-----------------------------------------------+-------------+ | ||
``` | ||
@@ -57,0 +51,0 @@ |
@@ -11,4 +11,4 @@ 'use strict' | ||
* | ||
* See base class G-Set.js for the rest of the API | ||
* https://github.com/orbitdb/crdts/blob/master/src/G-Set.js | ||
* See base class CmRDT-Set.js for the rest of the API | ||
* https://github.com/orbitdb/crdts/blob/master/src/CmRDT-Set.js | ||
* | ||
@@ -19,3 +19,3 @@ * Sources: | ||
*/ | ||
class TwoPSet extends GSet { | ||
class TwoPSet extends CRDTSet { | ||
/** | ||
@@ -39,4 +39,7 @@ * Create a new TwoPSet instance | ||
*/ | ||
get values () { | ||
// Include elements that are in the add set but not in the remove set | ||
values () { | ||
// A value is included in the set if it's present in | ||
// the add set and not present in the remove set. We can | ||
// determine this by calculating the difference between | ||
// adds and removes. | ||
const difference = GSet.difference(this._added, this._removed) | ||
@@ -43,0 +46,0 @@ return difference.values() |
'use strict' | ||
const GSet = require('./G-Set') | ||
const { OperationTuple3 } = require('./utils') | ||
@@ -9,7 +8,7 @@ | ||
* | ||
* Base Class for Operation-Based Set CRDT | ||
* Base Class for Operation-Based Set CRDT. Provides a Set interface. | ||
* | ||
* Inherits from GSet. Operations are described as: | ||
* Operations are described as: | ||
* | ||
* Operation = Tuple3(value : Any, added : Set, removed : Set) | ||
* Operation = Tuple3(value : Any, added : Set, removed : Set) | ||
* | ||
@@ -22,5 +21,6 @@ * This class is meant to be used as a base class for | ||
* Used by: | ||
* OR-Set - https://github.com/orbitdb/crdts/blob/master/src/OR-Set.js | ||
* 2P-Set - https://github.com/orbitdb/crdts/blob/master/src/2P-Set.js | ||
* LWW-Set - https://github.com/orbitdb/crdts/blob/master/LWW-Set.js | ||
* G-Set - https://github.com/orbitdb/crdts/blob/master/src/G-Set.js | ||
* OR-Set - https://github.com/orbitdb/crdts/blob/master/src/OR-Set.js | ||
* 2P-Set - https://github.com/orbitdb/crdts/blob/master/src/2P-Set.js | ||
* LWW-Set - https://github.com/orbitdb/crdts/blob/master/LWW-Set.js | ||
* | ||
@@ -31,3 +31,3 @@ * Sources: | ||
*/ | ||
class CmRDTSet extends GSet { | ||
class CmRDTSet extends Set { | ||
/** | ||
@@ -37,4 +37,2 @@ * Create a new CmRDTSet instance | ||
* | ||
* A CmRDT Set inherits from GSet (Grow-Only Set) | ||
* | ||
* The constructor should never be used directly | ||
@@ -44,7 +42,9 @@ * but rather via `super()` call in the constructor of | ||
* | ||
* @param {[Iterable]} iterable [Opetional Iterable object (eg. Array, Set) to create the GSet from] | ||
* @param {[Iterable]} iterable [Opetional Iterable object (eg. Array, Set) to create the Set from] | ||
* @param {[Object]} options [Options to pass to the Set. Currently supported: `{ compareFunc: (a, b) => true|false }`] | ||
*/ | ||
constructor (iterable, options) { | ||
super(iterable) | ||
super() | ||
// Internal cache for tracking which values have been added to the set | ||
this._values = new Set() | ||
// List of operations (adds or removes of a value) for this set as | ||
@@ -65,10 +65,10 @@ // Operation : Tuple3(value : Any, added : Set, removed : Set) | ||
*/ | ||
get values () { | ||
const contains = e => this._resolveState(e.added, e.removed, this._options.compareFunc) | ||
values () { | ||
const shouldIncludeValue = e => this._resolveValueState(e.added, e.removed, this._options.compareFunc) | ||
const getValue = e => e.value | ||
// Filter out values that should not be in this set | ||
// by using the _resolveState() function to determine | ||
// by using the _resolveValueState() function to determine | ||
// if the value should be present | ||
const state = this._operations | ||
.filter(contains) | ||
.filter(shouldIncludeValue) | ||
.map(getValue) | ||
@@ -79,2 +79,21 @@ return new Set(state).values() | ||
/** | ||
* Check if this Set has a value | ||
* @param {[Any]} value [Value to look for] | ||
* @return {Boolean} [True if value is in the Set, false if not] | ||
*/ | ||
has (value) { | ||
return new Set(this.values()).has(value) | ||
} | ||
/** | ||
* Check if this Set has all values of an input array | ||
* @param {[Array]} values [Values that should be in the Set] | ||
* @return {Boolean} [True if all values are in the Set, false if not] | ||
*/ | ||
hasAll (values) { | ||
const contains = e => this.has(e) | ||
return values.every(contains) | ||
} | ||
/** | ||
* Add a value to the Set | ||
@@ -88,12 +107,14 @@ * @override | ||
* | ||
* @param {[Any]} element [Value to add to the Set] | ||
* @param {[Any]} tag [Optional tag for this add operation, eg. a clock] | ||
* @param {[Any]} value [Value to add to the Set] | ||
* @param {[Any]} tag [Optional tag for this add operation, eg. a clock] | ||
*/ | ||
add (element, tag = 0) { | ||
if (!this._values.has(element)) { | ||
const operation = OperationTuple3.create(element, [tag], null) | ||
this._addOperation(operation) | ||
add (value, tag) { | ||
// If the value is not in the set yet | ||
if (!this._values.has(value)) { | ||
// Create an operation for the value and apply it to this set | ||
const addOperation = OperationTuple3.create(value, [tag], null) | ||
this._applyOperation(addOperation) | ||
} else { | ||
const elm = this._findElement(element) | ||
elm.added.add(tag) | ||
// If the value is in the set, add a tag to its added set | ||
this._findOperationsFor(value).map(val => val.added.add(tag)) | ||
} | ||
@@ -111,10 +132,9 @@ } | ||
* | ||
* @param {[Any]} element [Value to remove from the Set] | ||
* @param {[Any]} tag [Optional tag for this remove operation, eg. a clock] | ||
* @param {[Any]} value [Value to remove from the Set] | ||
* @param {[Any]} tag [Optional tag for this remove operation, eg. a clock] | ||
*/ | ||
remove (element, tag = 0) { | ||
if (this._values.has(element)) { | ||
const elm = this._findElement(element) | ||
elm.removed.add(tag) | ||
} | ||
remove (value, tag) { | ||
// Add a remove tag to the value's removed set, and only | ||
// apply the remove operation if the value was added previously | ||
this._findOperationsFor(value).map(e => e.removed.add(tag)) | ||
} | ||
@@ -128,11 +148,15 @@ | ||
merge (other) { | ||
other._operations.forEach(element => { | ||
const value = element.value | ||
other._operations.forEach(operation => { | ||
const value = operation.value | ||
if (!this._values.has(value)) { | ||
const operation = OperationTuple3.create(value, element.added, element.removed) | ||
this._addOperation(operation) | ||
// If we don't have the value yet, add it with all tags from other's operation | ||
const op = OperationTuple3.create(value, operation.added, operation.removed) | ||
this._applyOperation(op) | ||
} else { | ||
const elm = this._findElement(value) | ||
element.added.forEach(e => elm.added.add(e)) | ||
element.removed.forEach(e => elm.removed.add(e)) | ||
// If this set has the value | ||
this._findOperationsFor(value).map(op => { | ||
// Add all add and remove tags from other's operations to value in this set | ||
operation.added.forEach(e => op.added.add(e)) | ||
operation.removed.forEach(e => op.removed.add(e)) | ||
}) | ||
} | ||
@@ -143,3 +167,35 @@ }) | ||
/** | ||
* _resolveState function is used to determine if an element is present in a Set. | ||
* CmRDT-Set as an Object that can be JSON.stringified | ||
* @return {[Object]} [Object in the shape of `{ values: [ { value: <value>, added: [<tags>], removed: [<tags>] } ] }`] | ||
*/ | ||
toJSON () { | ||
const values = this._operations.map(e => { | ||
return { | ||
value: e.value, | ||
added: Array.from(e.added), | ||
removed: Array.from(e.removed), | ||
} | ||
}) | ||
return { values: values } | ||
} | ||
/** | ||
* Create an Array of the values of this Set | ||
* @return {[Array]} [Values of this Set as an Array] | ||
*/ | ||
toArray () { | ||
return Array.from(this.values()) | ||
} | ||
/** | ||
* Check if this Set equal another Set | ||
* @param {[type]} other [Set to compare] | ||
* @return {Boolean} [True if this Set is the same as the other Set] | ||
*/ | ||
isEqual (other) { | ||
return CmRDTSet.isEqual(this, other) | ||
} | ||
/** | ||
* _resolveValueState function is used to determine if an element is present in a Set. | ||
* | ||
@@ -159,6 +215,7 @@ * It receives a Set of add tags and a Set of remove tags for an element as arguments. | ||
*/ | ||
_resolveState (added, removed, compareFunc) { | ||
_resolveValueState (added, removed, compareFunc) { | ||
// By default, if there's an add operation present, | ||
// we include the value in the set | ||
return added.size > 0 | ||
// and there are no remove operations, we include | ||
// the value in the set | ||
return added.size > 0 && removed.size === 0 | ||
} | ||
@@ -170,3 +227,3 @@ | ||
*/ | ||
_addOperation (operationTuple3) { | ||
_applyOperation (operationTuple3) { | ||
this._operations.push(operationTuple3) | ||
@@ -177,13 +234,62 @@ this._values.add(operationTuple3.value) | ||
/** | ||
* Find a value from this set's internal cache | ||
* Find a value and its operations from this set's internal cache | ||
* @private | ||
* | ||
* Returns a value and all its operations as a OperationTuple3: | ||
* Operation : Tuple3(value : Any, added : Set, removed : Set) | ||
* | ||
* Where 'value' is the value in the set, 'added' is all add operations | ||
* and 'removed' are all remove operations for that value. | ||
* | ||
* @param {[Any]} value [Value to find] | ||
* @return {[Any]} [Value if found, undefined if value was not found] | ||
*/ | ||
_findElement (value) { | ||
const compare = e => e.value === value | ||
return this._operations.find(compare) | ||
_findOperationsFor (value) { | ||
let operations = [] | ||
if (this._values.has(value)) { | ||
const isForValue = e => e.value === value | ||
const notNull = e => e !== undefined | ||
operations = [this._operations.find(isForValue)].filter(notNull) | ||
} | ||
return operations | ||
} | ||
/** | ||
* Create Set from a json object | ||
* @param {[Object]} json [Input object to create the Set from. Needs to be: '{ values: [] }'] | ||
* @return {[Set]} [new Set instance] | ||
*/ | ||
static from (json) { | ||
return new CmRDTSet(json.values) | ||
} | ||
/** | ||
* Check if two Set are equal | ||
* | ||
* Two Set are equal if they both contain exactly | ||
* the same values. | ||
* | ||
* @param {[Set]} a [Set to compare] | ||
* @param {[Set]} b [Set to compare] | ||
* @return {Boolean} [True input Set are the same] | ||
*/ | ||
static isEqual (a, b) { | ||
return (a.toArray().length === b.toArray().length) | ||
&& a.hasAll(b.toArray()) | ||
} | ||
/** | ||
* Return the difference between the values of two Sets | ||
* | ||
* @param {[Set]} a [First Set] | ||
* @param {[Set]} b [Second Set] | ||
* @return {[Set]} [Set of values that are in Set A but not in Set B] | ||
*/ | ||
static difference (a, b) { | ||
const otherDoesntInclude = x => !b.has(x) | ||
const difference = new Set([...a.values()].filter(otherDoesntInclude)) | ||
return difference | ||
} | ||
} | ||
module.exports = CmRDTSet |
'use strict' | ||
const CRDTSet = require('./CmRDT-Set') | ||
/** | ||
@@ -8,8 +10,10 @@ * G-Set | ||
* | ||
* The G-Set works as a base class for many other Set CRDTs. | ||
* G stands for "Grow-Only" which means that values can only | ||
* ever be added to the set, they can never be removed. | ||
* | ||
* See base class CmRDT-Set.js for the rest of the API | ||
* https://github.com/orbitdb/crdts/blob/master/src/CmRDT-Set.js | ||
* | ||
* Used by: | ||
* CmRDT-Set - https://github.com/orbitdb/crdts/blob/master/src/CmRDT-Set.js | ||
* 2P-Set - https://github.com/orbitdb/crdts/blob/master/src/2P-Set.js | ||
* | ||
@@ -20,3 +24,3 @@ * Sources: | ||
*/ | ||
class GSet { | ||
class GSet extends CRDTSet { | ||
/** | ||
@@ -27,2 +31,3 @@ * Create a G-Set CRDT instance | ||
constructor (iterable) { | ||
super() | ||
this._values = new Set(iterable) | ||
@@ -35,3 +40,3 @@ } | ||
*/ | ||
get values () { | ||
values () { | ||
return this._values.values() | ||
@@ -56,3 +61,3 @@ } | ||
remove (value) { | ||
throw new Error('G-Set doesn\'t allow removing values') | ||
throw new Error(`G-Set doesn't allow removing values`) | ||
} | ||
@@ -70,23 +75,2 @@ | ||
/** | ||
* Check if this GSet has a value | ||
* @param {[Any]} value [Value to look for] | ||
* @return {Boolean} [True if value is in the GSet, false if not] | ||
*/ | ||
has (value) { | ||
return new Set(this.values).has(value) | ||
} | ||
/** | ||
* Check if this GSet has all values of an input array | ||
* @param {[Array]} values [Values that should be in the GSet] | ||
* @return {Boolean} [True if all values are in the GSet, false if not] | ||
*/ | ||
hasAll (values) { | ||
const contains = e => this.has(e) | ||
return values.length > 0 | ||
? values.every(contains) | ||
: this._values.size === 0 | ||
} | ||
/** | ||
* GSet as an Object that can be JSON.stringified | ||
@@ -102,19 +86,2 @@ * @return {[Object]} [Object in the shape of `{ values: [<values>] }`] | ||
/** | ||
* Create an Array of the values of this GSet | ||
* @return {[Array]} [Values of this GSet as an Array] | ||
*/ | ||
toArray () { | ||
return Array.from(this.values) | ||
} | ||
/** | ||
* Check if this GSet equal another GSet | ||
* @param {[type]} other [GSet to compare] | ||
* @return {Boolean} [True if this GSet is the same as the other GSet] | ||
*/ | ||
isEqual (other) { | ||
return GSet.isEqual(this, other) | ||
} | ||
/** | ||
* Create GSet from a json object | ||
@@ -127,32 +94,4 @@ * @param {[Object]} json [Input object to create the GSet from. Needs to be: '{ values: [] }'] | ||
} | ||
/** | ||
* Check if two GSets are equal | ||
* | ||
* Two GSet are equal if they both contain exactly | ||
* the same values. | ||
* | ||
* @param {[GSet]} a [GSet to compare] | ||
* @param {[GSet]} b [GSet to compare] | ||
* @return {Boolean} [True input GSets are the same] | ||
*/ | ||
static isEqual (a, b) { | ||
return (a.toArray().length === b.toArray().length) | ||
&& a.hasAll(b.toArray()) | ||
} | ||
/** | ||
* Return the difference between the values of two GSets | ||
* | ||
* @param {[GSet]} a [First GSet] | ||
* @param {[GSet]} b [Second GSet] | ||
* @return {[Set]} [Set of values that are in GSet A but not in GSet B] | ||
*/ | ||
static difference (a, b) { | ||
const otherIncludes = x => !b.has(x) | ||
const difference = new Set(a.toArray().filter(otherIncludes)) | ||
return difference | ||
} | ||
} | ||
module.exports = GSet |
// Export all supported CRDTs | ||
exports.GCounter = GCounter = require('./G-Counter.js') | ||
exports.GSet = GSet = require('./G-Set.js') | ||
exports.TwoPSet = TwoPSet = require('./2P-Set.js') | ||
exports.ORSet = ORSet = require('./OR-Set.js') | ||
exports.LWWSet = LWWSet = require('./LWW-Set.js') | ||
exports.GCounter = GCounter = require('./G-Counter') | ||
exports.CmRDTSet = CmRDTSet = require('./CmRDT-Set') | ||
exports.GSet = GSet = require('./G-Set') | ||
exports.TwoPSet = TwoPSet = require('./2P-Set') | ||
exports.ORSet = ORSet = require('./OR-Set') | ||
exports.LWWSet = LWWSet = require('./LWW-Set') | ||
exports.PNCounter = PNCounter = require('./PN-Counter.js') | ||
@@ -23,2 +24,4 @@ | ||
GCounter: GCounter, | ||
PNCounter: PNCounter, | ||
CmRDTSet: CmRDTSet, | ||
GSet: GSet, | ||
@@ -28,3 +31,2 @@ TwoPSet: TwoPSet, | ||
LWWSet: LWWSet, | ||
PNCounter: PNCounter | ||
} |
@@ -21,3 +21,3 @@ 'use strict' | ||
* | ||
* _resolveState function is used to determine if an element is present in a Set. | ||
* _resolveValueState function is used to determine if an element is present in a Set. | ||
* | ||
@@ -37,3 +37,3 @@ * It receives a Set of add tags and a Set of remove tags for an element as arguments. | ||
*/ | ||
_resolveState (added, removed, compareFunc) { | ||
_resolveValueState (added, removed, compareFunc) { | ||
// Sort both sets with the given comparison function | ||
@@ -40,0 +40,0 @@ // or use "distance" sort by default |
@@ -30,10 +30,10 @@ 'use strict' | ||
* | ||
* @param {[Any]} element [Value to remove from the Set] | ||
* @param {[Any]} tag [Optional tag for this remove operation, eg. a clock] | ||
* @param {[Any]} value [Value to remove from the Set] | ||
* @param {[Any]} tag [Optional tag for this remove operation, eg. a clock] | ||
*/ | ||
remove (element) { | ||
if (this._values.has(element)) { | ||
const elm = this._findElement(element) | ||
elm.removed = new Set(elm.added) | ||
} | ||
remove (value) { | ||
// Add all observed (known) add tags to the removed tags | ||
const removeObserved = e => e.removed = new Set([...e.added, ...e.removed]) | ||
// Create a remove operation for the value if it exists | ||
this._findOperationsFor(value).map(removeObserved) | ||
} | ||
@@ -44,3 +44,3 @@ | ||
* | ||
* _resolveState function is used to determine if an element is present in a Set. | ||
* _resolveValueState function is used to determine if an element is present in a Set. | ||
* | ||
@@ -60,23 +60,18 @@ * It receives a Set of add tags and a Set of remove tags for an element as arguments. | ||
*/ | ||
_resolveState (added, removed, compareFunc) { | ||
_resolveValueState (added, removed, compareFunc) { | ||
// Check if a tag is included in the remove set | ||
const removesDontIncludeGreater = e => { | ||
const hasMatchingRemoveOperation = addTag => { | ||
// Check if remove tags includes the add tag, ie. check for | ||
// equality for the tags using a provided comparison function | ||
if (compareFunc) { | ||
// If remove set has any elements, check it | ||
if (removed.size > 0) { | ||
// If remove set doesn't include the tag, | ||
// return true to include the value in the state | ||
return Array.from(removed).some(f => compareFunc(e, f) === -1) | ||
} | ||
return !Array.from(removed).some(removeTag => compareFunc(removeTag, addTag)) | ||
} | ||
return true | ||
} | ||
// If remove set doesn't have the tag, | ||
// return true to include the value in the state | ||
return !removed.has(e) | ||
return !removed.has(addTag) | ||
} | ||
// If the remove set doesn't include the add tag, | ||
// return true to include the value in the state | ||
return Array.from(added).filter(removesDontIncludeGreater).length > 0 | ||
return Array.from(added).filter(hasMatchingRemoveOperation).length > 0 | ||
} | ||
@@ -83,0 +78,0 @@ |
'use strict' | ||
const assert = require('assert') | ||
const { GSet, TwoPSet } = require('../src') | ||
const { TwoPSet, GSet, CmRDTSet } = require('../src') | ||
@@ -10,21 +10,24 @@ describe('2P-Set', () => { | ||
it('creates a set', () => { | ||
const gset = new TwoPSet() | ||
assert.notEqual(gset, null) | ||
assert.notEqual(gset._added, null) | ||
assert.equal(gset._added instanceof GSet, true) | ||
assert.notEqual(gset._removed, null) | ||
assert.equal(gset._removed instanceof GSet, true) | ||
const crdt = new TwoPSet() | ||
assert.notEqual(crdt, null) | ||
}) | ||
it('is a GSet', () => { | ||
const gset = new TwoPSet() | ||
assert.notEqual(gset._values, null) | ||
assert.equal(gset._values instanceof Set, true) | ||
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 gset = new TwoPSet(['A', 'B']) | ||
assert.notEqual(gset, null) | ||
assert.equal(gset._added instanceof GSet, true) | ||
assert.deepEqual(new Set(gset._added.values), new Set(['B', 'A'])) | ||
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'])) | ||
}) | ||
@@ -35,11 +38,11 @@ }) | ||
it('is an Iterator', () => { | ||
const gset = new TwoPSet() | ||
assert.equal(gset.values.toString(), '[object Set Iterator]') | ||
const crdt = new TwoPSet() | ||
assert.equal(crdt.values().toString(), '[object Set Iterator]') | ||
}) | ||
it('returns an Iterator', () => { | ||
const gset = new TwoPSet() | ||
gset.add('A') | ||
gset.add('B') | ||
const iterator = gset.values | ||
const crdt = new TwoPSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
const iterator = crdt.values() | ||
assert.equal(iterator.next().value, 'A') | ||
@@ -52,26 +55,26 @@ assert.equal(iterator.next().value, 'B') | ||
it('adds an element to the set', () => { | ||
const gset = new TwoPSet() | ||
gset.add('A') | ||
assert.deepEqual(new Set(gset.values), new Set(['A'])) | ||
const crdt = new TwoPSet() | ||
crdt.add('A') | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
it('adds three elements to the set', () => { | ||
const gset = new TwoPSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
assert.deepEqual(new Set(gset.values), new Set(['A', 'B', 'C'])) | ||
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 gset = new TwoPSet() | ||
gset.add(1) | ||
gset.add('A') | ||
gset.add('A') | ||
gset.add(1) | ||
gset.add('A') | ||
const crdt = new TwoPSet() | ||
crdt.add(1) | ||
crdt.add('A') | ||
crdt.add('A') | ||
crdt.add(1) | ||
crdt.add('A') | ||
const obj = { hello: 'ABC' } | ||
gset.add(obj) | ||
gset.add(obj) | ||
gset.add({ hello: 'ABCD' }) | ||
crdt.add(obj) | ||
crdt.add(obj) | ||
crdt.add({ hello: 'ABCD' }) | ||
@@ -85,3 +88,3 @@ const expectedResult = [ | ||
assert.deepEqual(new Set(gset.values), new Set(expectedResult)) | ||
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) | ||
}) | ||
@@ -93,6 +96,6 @@ | ||
const expectedResult = ['B', 'C', 0, 4] | ||
const gset = new TwoPSet(addedValues, removedValues) | ||
assert.equal(gset._added instanceof GSet, true) | ||
assert.deepEqual(new Set(gset._added.values), new Set(addedValues)) | ||
assert.deepEqual(new Set(gset.values), new Set(expectedResult)) | ||
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)) | ||
}) | ||
@@ -103,14 +106,14 @@ }) | ||
it('removes an element from the set', () => { | ||
const gset = new TwoPSet() | ||
gset.add('A') | ||
gset.remove('A') | ||
assert.deepEqual(new Set(gset.values), new 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 gset = new TwoPSet() | ||
gset.remove('A') | ||
gset.add('A') | ||
assert.deepEqual(new Set(gset._removed.values), new Set([])) | ||
assert.deepEqual(new Set(gset.values), new Set(['A'])) | ||
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'])) | ||
}) | ||
@@ -122,17 +125,17 @@ | ||
const expectedResult = ['B', 'C', 0, 4] | ||
const gset = new TwoPSet(addedValues, removedValues) | ||
assert.equal(gset._removed instanceof GSet, true) | ||
assert.deepEqual(new Set(gset._removed.values), new Set(removedValues)) | ||
assert.deepEqual(new Set(gset.values), new Set(expectedResult)) | ||
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 gset = new TwoPSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.remove('A') | ||
gset.remove('B') | ||
gset.remove('A') | ||
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(gset._removed.values), new Set(expectedResult)) | ||
assert.deepEqual(new Set(crdt._removed.values()), new Set(expectedResult)) | ||
}) | ||
@@ -143,73 +146,73 @@ }) | ||
it('merges two sets with same id', () => { | ||
const gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
gset2.add('C') | ||
gset2.add('D') | ||
gset2.remove('D') | ||
gset1.merge(gset2) | ||
assert.deepEqual(gset1.toArray(), ['A', 'B', 'C']) | ||
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 gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
gset2.add('A') | ||
gset2.remove('B') | ||
gset1.merge(gset2) | ||
assert.deepEqual(gset1.toArray(), ['A']) | ||
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 gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
gset1.add('A') | ||
gset1.remove('A') | ||
gset2.remove('A') | ||
gset2.add('A') | ||
gset1.add('AAA') | ||
gset2.add('AAA') | ||
gset1.add('A') | ||
gset1.merge(gset2) | ||
assert.deepEqual(gset1.toArray(), ['AAA']) | ||
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 gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
const gset3 = new TwoPSet() | ||
const gset4 = new TwoPSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
gset3.add('C') | ||
gset4.add('D') | ||
gset2.add('BB') | ||
gset2.remove('BB') | ||
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') | ||
gset1.merge(gset2) | ||
gset1.merge(gset3) | ||
gset1.remove('C') | ||
gset1.merge(gset4) | ||
crdt1.merge(crdt2) | ||
crdt1.merge(crdt3) | ||
crdt1.remove('C') | ||
crdt1.merge(crdt4) | ||
assert.deepEqual(gset1.toArray(), ['A', 'B', 'D']) | ||
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'D']) | ||
}) | ||
it('doesn\'t overwrite other\'s values on merge', () => { | ||
const gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
gset1.add('A') | ||
gset2.add('C') | ||
gset2.add('B') | ||
gset2.remove('C') | ||
gset1.merge(gset2) | ||
gset1.add('AA') | ||
gset2.add('CC') | ||
gset2.add('BB') | ||
gset2.remove('CC') | ||
gset1.merge(gset2) | ||
assert.deepEqual(gset1.toArray(), ['A', 'B', 'AA', 'BB']) | ||
assert.deepEqual(gset2.toArray(), ['B', 'BB']) | ||
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']) | ||
}) | ||
@@ -220,24 +223,24 @@ }) | ||
it('returns true if given element is in the set', () => { | ||
const gset = new TwoPSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add(1) | ||
gset.add(13) | ||
gset.remove(13) | ||
const crdt = new TwoPSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add(1) | ||
crdt.add(13) | ||
crdt.remove(13) | ||
const obj = { hello: 'world' } | ||
gset.add(obj) | ||
assert.equal(gset.has('A'), true) | ||
assert.equal(gset.has('B'), true) | ||
assert.equal(gset.has(1), true) | ||
assert.equal(gset.has(13), false) | ||
assert.equal(gset.has(obj), true) | ||
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 gset = new TwoPSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('nothere') | ||
gset.remove('nothere') | ||
assert.equal(gset.has('nothere'), false) | ||
const crdt = new TwoPSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('nothere') | ||
crdt.remove('nothere') | ||
assert.equal(crdt.has('nothere'), false) | ||
}) | ||
@@ -248,19 +251,19 @@ }) | ||
it('returns true if all given elements are in the set', () => { | ||
const gset = new TwoPSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
gset.remove('C') | ||
gset.add('D') | ||
assert.equal(gset.hasAll(['D', 'A', 'B']), true) | ||
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 gset = new TwoPSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
gset.remove('C') | ||
gset.add('D') | ||
assert.equal(gset.hasAll(['D', 'A', 'C', 'B']), false) | ||
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) | ||
}) | ||
@@ -271,4 +274,4 @@ }) | ||
it('returns the values of the set as an Array', () => { | ||
const gset = new TwoPSet() | ||
const array = gset.toArray() | ||
const crdt = new TwoPSet() | ||
const array = crdt.toArray() | ||
assert.equal(Array.isArray(array), true) | ||
@@ -278,8 +281,8 @@ }) | ||
it('returns values', () => { | ||
const gset = new TwoPSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
gset.remove('C') | ||
const array = gset.toArray() | ||
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) | ||
@@ -293,25 +296,25 @@ assert.equal(array[0], 'A') | ||
it('returns the set as JSON object', () => { | ||
const gset = new TwoPSet() | ||
gset.add('A') | ||
assert.equal(gset.toJSON().values.added.length, 1) | ||
assert.equal(gset.toJSON().values.added[0], 'A') | ||
assert.equal(gset.toJSON().values.removed.length, 0) | ||
gset.remove('A') | ||
assert.equal(gset.toJSON().values.removed.length, 1) | ||
assert.equal(gset.toJSON().values.removed[0], 'A') | ||
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 gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
gset2.remove('B') | ||
gset1.merge(gset2) | ||
gset2.merge(gset1) | ||
assert.equal(gset1.toJSON().values.added.length, 2) | ||
assert.equal(gset1.toJSON().values.added[0], 'A') | ||
assert.equal(gset1.toJSON().values.added[1], 'B') | ||
assert.equal(gset1.toJSON().values.removed.length, 1) | ||
assert.equal(gset1.toJSON().values.removed[0], 'B') | ||
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') | ||
}) | ||
@@ -322,24 +325,24 @@ }) | ||
it('returns true for sets with same values', () => { | ||
const gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
gset1.add('A') | ||
gset2.add('A') | ||
assert.equal(gset1.isEqual(gset2), true) | ||
assert.equal(gset2.isEqual(gset1), true) | ||
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 gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
assert.equal(gset1.isEqual(gset2), true) | ||
assert.equal(gset2.isEqual(gset1), true) | ||
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 gset1 = new TwoPSet() | ||
const gset2 = new TwoPSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
assert.equal(gset1.isEqual(gset2), false) | ||
assert.equal(gset2.isEqual(gset1), false) | ||
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) | ||
}) | ||
@@ -346,0 +349,0 @@ }) |
@@ -7,2 +7,3 @@ 'use strict' | ||
const { GCounter, GSet, TwoPSet, ORSet, LWWSet } = CRDTs | ||
const CmRDTSet = require('../src/CmRDT-Set') | ||
@@ -52,2 +53,35 @@ const added = [1, 2, 3] | ||
{ | ||
type: 'CmRDT-Set', | ||
class: CmRDTSet, | ||
added: added, | ||
removed: removed, | ||
diff: diff, | ||
inputData: { | ||
values: [ | ||
{ | ||
value: 'A', | ||
added: [1], | ||
removed: [], | ||
}, | ||
{ | ||
value: 'B', | ||
added: [1], | ||
removed: [1], | ||
}, | ||
{ | ||
value: 'C', | ||
added: [1, 2], | ||
removed: [2, 3], | ||
}, | ||
], | ||
}, | ||
expectedValues: ['A'], | ||
expectedValuesWithDiff: added.slice(1, added.length).concat(diff), | ||
create: (input) => new CmRDTSet(input && input.values ? input.values : []), | ||
from: (json) => new CmRDTSet(json && json.values ? json.values : []), | ||
remove: (crdt, tag) => crdt.remove(tag), | ||
isEqual: (a, b) => CmRDTSet.isEqual(a, b), | ||
difference: (a, b) => CmRDTSet.difference(a, b), | ||
}, | ||
{ | ||
type: 'OR-Set', | ||
@@ -124,16 +158,48 @@ class: ORSet, | ||
it('creates a new ' + CRDT.type + ' from a JSON object', () => { | ||
const orset1 = CRDT.create(CRDT.inputData) | ||
const orset2 = CRDT.from(CRDT.inputData) | ||
assert.deepEqual(new Set(orset2.values), new Set(CRDT.expectedValues)) | ||
const crdt1 = CRDT.create(CRDT.inputData) | ||
const crdt2 = CRDT.from(CRDT.inputData) | ||
assert.deepEqual(new Set(crdt2.values()), new Set(CRDT.expectedValues)) | ||
}) | ||
it('is a Set', () => { | ||
const crdt = CRDT.create() | ||
assert.equal(crdt instanceof Set, true) | ||
}) | ||
it('provides a JSON object and creates itself from it', () => { | ||
const crdt1 = CRDT.create() | ||
crdt1.add('A') | ||
crdt1.add('B') | ||
crdt1.add('C') | ||
const crdt2 = CRDT.class.from(crdt1.toJSON()) | ||
assert.deepEqual(new Set(crdt2.values()), new Set(crdt1.values())) | ||
}) | ||
it('toArray() returns the values in the set as an array', () => { | ||
const crdt1 = CRDT.create() | ||
crdt1.add('A') | ||
crdt1.add('B') | ||
crdt1.add('C') | ||
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C']) | ||
}) | ||
it('returns true if two Sets are equal', () => { | ||
const orset1 = CRDT.create(CRDT.inputData) | ||
const orset2 = CRDT.create(CRDT.inputData) | ||
const orset3 = CRDT.create([]) | ||
assert.equal(CRDT.isEqual(orset1, orset2), true) | ||
const isEqual = CRDT.isEqual(orset1, orset3) | ||
const crdt1 = CRDT.create(CRDT.inputData) | ||
const crdt2 = CRDT.create(CRDT.inputData) | ||
const crdt3 = CRDT.create([]) | ||
assert.equal(CRDT.isEqual(crdt1, crdt2), true) | ||
const isEqual = CRDT.isEqual(crdt1, crdt3) | ||
assert.equal(isEqual, false) | ||
}) | ||
it('returns true if set has all values', () => { | ||
const crdt1 = CRDT.create(CRDT.inputData) | ||
assert.equal(crdt1.hasAll(CRDT.expectedValues), true) | ||
}) | ||
it('returns false if set doesn\'t have all values', () => { | ||
const crdt1 = CRDT.create(CRDT.inputData) | ||
assert.equal(crdt1.hasAll(CRDT.expectedValues.concat(['extra', 1])), false) | ||
}) | ||
it('returns a Set of values from Set A that are not in Set B', () => { | ||
@@ -145,12 +211,12 @@ const addedValues = added | ||
const orset1 = CRDT.create() | ||
const orset2 = CRDT.create() | ||
const orset3 = CRDT.create() | ||
const crdt1 = CRDT.create() | ||
const crdt2 = CRDT.create() | ||
const crdt3 = CRDT.create() | ||
addedValues.concat(expectedDiff).forEach((e, idx) => orset1.add(e, idx)) | ||
removedValues.forEach(e => CRDT.remove(orset1, e, 10)) | ||
addedValues.forEach(e => orset2.add(e, 100)) | ||
addedValues.concat(expectedDiff).forEach((e, idx) => crdt1.add(e, idx)) | ||
removedValues.forEach(e => CRDT.remove(crdt1, e, 10)) | ||
addedValues.forEach(e => crdt2.add(e, 100)) | ||
assert.deepEqual(CRDT.difference(orset1, orset2), new Set(expectedDiff)) | ||
assert.deepEqual(CRDT.difference(orset1, orset3), new Set(expectedValues)) | ||
assert.deepEqual(CRDT.difference(crdt1, crdt2), new Set(expectedDiff)) | ||
assert.deepEqual(CRDT.difference(crdt1, crdt3), new Set(expectedValues)) | ||
}) | ||
@@ -157,0 +223,0 @@ }) |
@@ -7,2 +7,3 @@ 'use strict' | ||
const { GCounter, GSet, TwoPSet, ORSet, LWWSet } = CRDTs | ||
const CmRDTSet = require('../src/CmRDT-Set') | ||
@@ -25,3 +26,3 @@ const crdts = [ | ||
merge: (crdt, other) => crdt.merge(other), | ||
query: (crdt) => new Set(crdt.values), | ||
query: (crdt) => new Set(crdt.values()), | ||
getExpectedMergedValue: (values) => new Set(values), | ||
@@ -35,3 +36,3 @@ }, | ||
merge: (crdt, other) => crdt.merge(other), | ||
query: (crdt) => new Set(crdt.values), | ||
query: (crdt) => new Set(crdt.values()), | ||
getExpectedMergedValue: (values) => new Set(values), | ||
@@ -45,3 +46,3 @@ }, | ||
merge: (crdt, other) => crdt.merge(other), | ||
query: (crdt) => new Set(crdt.values), | ||
query: (crdt) => new Set(crdt.values()), | ||
getExpectedMergedValue: (values) => new Set(values), | ||
@@ -55,3 +56,3 @@ }, | ||
merge: (crdt, other) => crdt.merge(other), | ||
query: (crdt) => new Set(crdt.values), | ||
query: (crdt) => new Set(crdt.values()), | ||
getExpectedMergedValue: (values) => new Set(values), | ||
@@ -58,0 +59,0 @@ }, |
'use strict' | ||
const assert = require('assert') | ||
const { GSet } = require('../src') | ||
const { GSet, CmRDTSet } = require('../src') | ||
@@ -10,13 +10,18 @@ describe('G-Set', () => { | ||
it('creates a set', () => { | ||
const gset = new GSet() | ||
assert.notEqual(gset, null) | ||
assert.notEqual(gset._values, null) | ||
assert.equal(gset._values instanceof Set, true) | ||
const crdt = new GSet() | ||
assert.notEqual(crdt, null) | ||
assert.notEqual(crdt._values, null) | ||
assert.equal(crdt._values instanceof Set, true) | ||
}) | ||
it('is a CmRDT Set', () => { | ||
const crdt = new GSet() | ||
assert.equal(crdt instanceof CmRDTSet, true) | ||
}) | ||
it('creates a set from values', () => { | ||
const gset = new GSet(['A', 'B']) | ||
assert.notEqual(gset, null) | ||
assert.equal(gset._values instanceof Set, true) | ||
assert.deepEqual(gset._values, new Set(['B', 'A'])) | ||
const crdt = new GSet(['A', 'B']) | ||
assert.notEqual(crdt, null) | ||
assert.equal(crdt._values instanceof Set, true) | ||
assert.deepEqual(crdt._values, new Set(['B', 'A'])) | ||
}) | ||
@@ -27,11 +32,11 @@ }) | ||
it('is an Iterator', () => { | ||
const gset = new GSet() | ||
assert.equal(gset.values.toString(), '[object Set Iterator]') | ||
const crdt = new GSet() | ||
assert.equal(crdt.values().toString(), '[object Set Iterator]') | ||
}) | ||
it('returns an Iterator', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
gset.add('B') | ||
const iterator = gset.values | ||
const crdt = new GSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
const iterator = crdt.values() | ||
assert.equal(iterator.next().value, 'A') | ||
@@ -44,26 +49,26 @@ assert.equal(iterator.next().value, 'B') | ||
it('adds an element to the set', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
assert.deepEqual(new Set(gset.values), new Set(['A'])) | ||
const crdt = new GSet() | ||
crdt.add('A') | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
it('adds three elements to the set', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
assert.deepEqual(new Set(gset.values), new Set(['A', 'B', 'C'])) | ||
const crdt = new GSet() | ||
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 gset = new GSet() | ||
gset.add(1) | ||
gset.add('A') | ||
gset.add('A') | ||
gset.add(1) | ||
gset.add('A') | ||
const crdt = new GSet() | ||
crdt.add(1) | ||
crdt.add('A') | ||
crdt.add('A') | ||
crdt.add(1) | ||
crdt.add('A') | ||
const obj = { hello: 'ABC' } | ||
gset.add(obj) | ||
gset.add(obj) | ||
gset.add({ hello: 'ABCD' }) | ||
crdt.add(obj) | ||
crdt.add(obj) | ||
crdt.add({ hello: 'ABCD' }) | ||
@@ -77,54 +82,69 @@ const expectedResult = [ | ||
assert.deepEqual(new Set(gset.values), new Set(expectedResult)) | ||
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) | ||
}) | ||
}) | ||
describe('remove', () => { | ||
it('doesn\'t allow removing values', () => { | ||
let crdt, err | ||
try { | ||
crdt = new GSet() | ||
crdt.add('A') | ||
crdt.remove('A') | ||
} catch (e) { | ||
err = e.toString() | ||
} | ||
assert.equal(err, `Error: G-Set doesn't allow removing values`) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
}) | ||
describe('merge', () => { | ||
it('merges two sets with same id', () => { | ||
const gset1 = new GSet() | ||
const gset2 = new GSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
gset2.add('C') | ||
gset1.merge(gset2) | ||
assert.deepEqual(gset1.toArray(), ['A', 'B', 'C']) | ||
const crdt1 = new GSet() | ||
const crdt2 = new GSet() | ||
crdt1.add('A') | ||
crdt2.add('B') | ||
crdt2.add('C') | ||
crdt1.merge(crdt2) | ||
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C']) | ||
}) | ||
it('merges two sets with same values', () => { | ||
const gset1 = new GSet() | ||
const gset2 = new GSet() | ||
gset1.add('A') | ||
gset2.add('A') | ||
gset1.merge(gset2) | ||
assert.deepEqual(gset1.toArray(), ['A']) | ||
const crdt1 = new GSet() | ||
const crdt2 = new GSet() | ||
crdt1.add('A') | ||
crdt2.add('A') | ||
crdt1.merge(crdt2) | ||
assert.deepEqual(crdt1.toArray(), ['A']) | ||
}) | ||
it('merge four different sets', () => { | ||
const gset1 = new GSet() | ||
const gset2 = new GSet() | ||
const gset3 = new GSet() | ||
const gset4 = new GSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
gset3.add('C') | ||
gset4.add('D') | ||
const crdt1 = new GSet() | ||
const crdt2 = new GSet() | ||
const crdt3 = new GSet() | ||
const crdt4 = new GSet() | ||
crdt1.add('A') | ||
crdt2.add('B') | ||
crdt3.add('C') | ||
crdt4.add('D') | ||
gset1.merge(gset2) | ||
gset1.merge(gset3) | ||
gset1.merge(gset4) | ||
crdt1.merge(crdt2) | ||
crdt1.merge(crdt3) | ||
crdt1.merge(crdt4) | ||
assert.deepEqual(gset1.toArray(), ['A', 'B', 'C', 'D']) | ||
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'C', 'D']) | ||
}) | ||
it('doesn\'t overwrite other\'s values on merge', () => { | ||
const gset1 = new GSet() | ||
const gset2 = new GSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
gset1.merge(gset2) | ||
gset1.add('AA') | ||
gset2.add('BB') | ||
gset1.merge(gset2) | ||
assert.deepEqual(gset1.toArray(), ['A', 'B', 'AA', 'BB']) | ||
assert.deepEqual(gset2.toArray(), ['B', 'BB']) | ||
const crdt1 = new GSet() | ||
const crdt2 = new GSet() | ||
crdt1.add('A') | ||
crdt2.add('B') | ||
crdt1.merge(crdt2) | ||
crdt1.add('AA') | ||
crdt2.add('BB') | ||
crdt1.merge(crdt2) | ||
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'AA', 'BB']) | ||
assert.deepEqual(crdt2.toArray(), ['B', 'BB']) | ||
}) | ||
@@ -135,19 +155,19 @@ }) | ||
it('returns true if given element is in the set', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add(1) | ||
const crdt = new GSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add(1) | ||
const obj = { hello: 'world' } | ||
gset.add(obj) | ||
assert.equal(gset.has('A'), true) | ||
assert.equal(gset.has('B'), true) | ||
assert.equal(gset.has(1), true) | ||
assert.equal(gset.has(obj), true) | ||
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(obj), true) | ||
}) | ||
it('returns false if given element is not in the set', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
gset.add('B') | ||
assert.equal(gset.has('nothere'), false) | ||
const crdt = new GSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
assert.equal(crdt.has('nothere'), false) | ||
}) | ||
@@ -158,17 +178,17 @@ }) | ||
it('returns true if all given elements are in the set', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
gset.add('D') | ||
assert.equal(gset.hasAll(['D', 'A', 'C', 'B']), true) | ||
const crdt = new GSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('C') | ||
crdt.add('D') | ||
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B']), true) | ||
}) | ||
it('returns false if any of the given elements are not in the set', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
gset.add('D') | ||
assert.equal(gset.hasAll(['D', 'A', 'C', 'B', 'nothere']), false) | ||
const crdt = new GSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('C') | ||
crdt.add('D') | ||
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B', 'nothere']), false) | ||
}) | ||
@@ -179,4 +199,4 @@ }) | ||
it('returns the values of the set as an Array', () => { | ||
const gset = new GSet() | ||
const array = gset.toArray() | ||
const crdt = new GSet() | ||
const array = crdt.toArray() | ||
assert.equal(Array.isArray(array), true) | ||
@@ -186,6 +206,6 @@ }) | ||
it('returns values', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
gset.add('B') | ||
const array = gset.toArray() | ||
const crdt = new GSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
const array = crdt.toArray() | ||
assert.equal(array[0], 'A') | ||
@@ -198,18 +218,18 @@ assert.equal(array[1], 'B') | ||
it('returns the set as JSON object', () => { | ||
const gset = new GSet() | ||
gset.add('A') | ||
assert.equal(gset.toJSON().values.length, 1) | ||
assert.equal(gset.toJSON().values[0], 'A') | ||
const crdt = new GSet() | ||
crdt.add('A') | ||
assert.equal(crdt.toJSON().values.length, 1) | ||
assert.equal(crdt.toJSON().values[0], 'A') | ||
}) | ||
it('returns a JSON object after a merge', () => { | ||
const gset1 = new GSet() | ||
const gset2 = new GSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
gset1.merge(gset2) | ||
gset2.merge(gset1) | ||
assert.equal(gset1.toJSON().values.length, 2) | ||
assert.equal(gset1.toJSON().values[0], 'A') | ||
assert.equal(gset1.toJSON().values[1], 'B') | ||
const crdt1 = new GSet() | ||
const crdt2 = new GSet() | ||
crdt1.add('A') | ||
crdt2.add('B') | ||
crdt1.merge(crdt2) | ||
crdt2.merge(crdt1) | ||
assert.equal(crdt1.toJSON().values.length, 2) | ||
assert.equal(crdt1.toJSON().values[0], 'A') | ||
assert.equal(crdt1.toJSON().values[1], 'B') | ||
}) | ||
@@ -220,24 +240,24 @@ }) | ||
it('returns true for sets with same values', () => { | ||
const gset1 = new GSet() | ||
const gset2 = new GSet() | ||
gset1.add('A') | ||
gset2.add('A') | ||
assert.equal(gset1.isEqual(gset2), true) | ||
assert.equal(gset2.isEqual(gset1), true) | ||
const crdt1 = new GSet() | ||
const crdt2 = new GSet() | ||
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 gset1 = new GSet() | ||
const gset2 = new GSet() | ||
assert.equal(gset1.isEqual(gset2), true) | ||
assert.equal(gset2.isEqual(gset1), true) | ||
const crdt1 = new GSet() | ||
const crdt2 = new GSet() | ||
assert.equal(crdt1.isEqual(crdt2), true) | ||
assert.equal(crdt2.isEqual(crdt1), true) | ||
}) | ||
it('returns false for sets with different values', () => { | ||
const gset1 = new GSet() | ||
const gset2 = new GSet() | ||
gset1.add('A') | ||
gset2.add('B') | ||
assert.equal(gset1.isEqual(gset2), false) | ||
assert.equal(gset2.isEqual(gset1), false) | ||
const crdt1 = new GSet() | ||
const crdt2 = new GSet() | ||
crdt1.add('A') | ||
crdt2.add('B') | ||
assert.equal(crdt1.isEqual(crdt2), false) | ||
assert.equal(crdt2.isEqual(crdt1), false) | ||
}) | ||
@@ -244,0 +264,0 @@ }) |
'use strict' | ||
const assert = require('assert') | ||
const { LWWSet, GSet } = require('../src') | ||
const { LWWSet, CmRDTSet } = require('../src') | ||
const LamportClock = require('./lamport-clock') | ||
@@ -11,4 +11,4 @@ | ||
it('creates a set', () => { | ||
const lwwset = new LWWSet() | ||
assert.notEqual(lwwset, null) | ||
const crdt = new LWWSet() | ||
assert.notEqual(crdt, null) | ||
}) | ||
@@ -18,4 +18,3 @@ | ||
const gset = new LWWSet() | ||
assert.notEqual(gset._operations, null) | ||
assert.equal(gset._operations instanceof Array, true) | ||
assert.equal(gset instanceof CmRDTSet, true) | ||
}) | ||
@@ -26,11 +25,11 @@ }) | ||
it('is an Iterator', () => { | ||
const lwwset = new LWWSet() | ||
assert.equal(lwwset.values.toString(), '[object Set Iterator]') | ||
const crdt = new LWWSet() | ||
assert.equal(crdt.values().toString(), '[object Set Iterator]') | ||
}) | ||
it('returns an Iterator', () => { | ||
const lwwset = new LWWSet() | ||
lwwset.add('A') | ||
lwwset.add('B') | ||
const iterator = lwwset.values | ||
const crdt = new LWWSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
const iterator = crdt.values() | ||
assert.equal(iterator.next().value, 'A') | ||
@@ -43,30 +42,30 @@ assert.equal(iterator.next().value, 'B') | ||
it('adds an element to the set', () => { | ||
const lwwset = new LWWSet() | ||
const crdt = new LWWSet() | ||
const uid = new Date().getTime() | ||
lwwset.add('A', uid) | ||
lwwset.add('A', uid + 1) | ||
assert.deepEqual(new Set(lwwset.values), new Set(['A'])) | ||
crdt.add('A', uid) | ||
crdt.add('A', uid + 1) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
it('adds three elements to the set', () => { | ||
const lwwset = new LWWSet() | ||
lwwset.add('A') | ||
lwwset.add('B') | ||
lwwset.add('C') | ||
assert.deepEqual(new Set(lwwset.values), new Set(['A', 'B', 'C'])) | ||
const crdt = new LWWSet() | ||
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 lwwset = new LWWSet() | ||
lwwset.add(1) | ||
lwwset.add('A') | ||
lwwset.add('A') | ||
lwwset.add(1) | ||
lwwset.add('A') | ||
const crdt = new LWWSet() | ||
crdt.add(1) | ||
crdt.add('A') | ||
crdt.add('A') | ||
crdt.add(1) | ||
crdt.add('A') | ||
const obj = { hello: 'ABC' } | ||
lwwset.add(obj) | ||
lwwset.add(obj) | ||
lwwset.add({ hello: 'ABCD' }) | ||
crdt.add(obj) | ||
crdt.add(obj) | ||
crdt.add({ hello: 'ABCD' }) | ||
lwwset.add(9, 11111) | ||
crdt.add(9, 11111) | ||
@@ -81,3 +80,3 @@ const expectedResult = [ | ||
assert.deepEqual(new Set(lwwset.values), new Set(expectedResult)) | ||
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) | ||
}) | ||
@@ -89,6 +88,6 @@ }) | ||
let uid = new LamportClock('A') | ||
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
lwwset.add('A', uid) | ||
lwwset.add('A', uid.tick()) | ||
assert.deepEqual(new Set(lwwset.values), new Set(['A'])) | ||
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
crdt.add('A', uid) | ||
crdt.add('A', uid.tick()) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
@@ -98,7 +97,7 @@ | ||
let uid = new LamportClock('A') | ||
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
lwwset.add('A', uid) | ||
lwwset.add('B', uid) | ||
lwwset.add('C', uid) | ||
assert.deepEqual(new Set(lwwset.values), new Set(['A', 'B', 'C'])) | ||
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
crdt.add('A', uid) | ||
crdt.add('B', uid) | ||
crdt.add('C', uid) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C'])) | ||
}) | ||
@@ -108,14 +107,14 @@ | ||
let uid = new LamportClock('A') | ||
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
lwwset.add(1, uid.tick()) | ||
lwwset.add('A', uid.tick()) | ||
lwwset.add('A', uid.tick()) | ||
lwwset.add(1, uid.tick()) | ||
lwwset.add('A', uid.tick()) | ||
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
crdt.add(1, uid.tick()) | ||
crdt.add('A', uid.tick()) | ||
crdt.add('A', uid.tick()) | ||
crdt.add(1, uid.tick()) | ||
crdt.add('A', uid.tick()) | ||
const obj = { hello: 'ABC' } | ||
lwwset.add(obj, uid.tick()) | ||
lwwset.add(obj, uid) | ||
lwwset.add({ hello: 'ABCD' }, uid.tick()) | ||
crdt.add(obj, uid.tick()) | ||
crdt.add(obj, uid) | ||
crdt.add({ hello: 'ABCD' }, uid.tick()) | ||
lwwset.add(9, uid) | ||
crdt.add(9, uid) | ||
@@ -130,3 +129,3 @@ const expectedResult = [ | ||
assert.deepEqual(new Set(lwwset.values), new Set(expectedResult)) | ||
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) | ||
}) | ||
@@ -138,15 +137,15 @@ }) | ||
let tag = 1 | ||
const lwwset = new LWWSet() | ||
lwwset.add('A', tag) | ||
lwwset.remove('A', tag + 1) | ||
assert.deepEqual(new Set(lwwset.values), new Set([])) | ||
const crdt = new LWWSet() | ||
crdt.add('A', tag) | ||
crdt.remove('A', tag + 1) | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
it('removes an element from the set when element has multiple tags', () => { | ||
const lwwset = new LWWSet() | ||
lwwset.add('A', 1) | ||
lwwset.add('A', 2) | ||
lwwset.add('A', 3) | ||
lwwset.remove('A', 4) | ||
assert.deepEqual(new Set(lwwset.values), new Set([])) | ||
const crdt = new LWWSet() | ||
crdt.add('A', 1) | ||
crdt.add('A', 2) | ||
crdt.add('A', 3) | ||
crdt.remove('A', 4) | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
@@ -156,6 +155,6 @@ | ||
const compareFunc = (a, b) => b - a | ||
const lwwset = new LWWSet() | ||
lwwset.add('A', 2) | ||
lwwset.remove('A', 1) | ||
assert.deepEqual(new Set(lwwset.values), new Set(['A'])) | ||
const crdt = new LWWSet() | ||
crdt.add('A', 2) | ||
crdt.remove('A', 1) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
@@ -165,33 +164,33 @@ | ||
const compareFunc = (a, b) => a.time === 'bigger' ? 1 : -1 | ||
const lwwset = new LWWSet(null, { compareFunc: compareFunc }) | ||
lwwset.add('A', { time: 'bigger' }) | ||
lwwset.remove('A', { time: 'smaller' }) | ||
assert.deepEqual(new Set(lwwset.values), new Set(['A'])) | ||
const crdt = new LWWSet(null, { compareFunc: compareFunc }) | ||
crdt.add('A', { time: 'bigger' }) | ||
crdt.remove('A', { time: 'smaller' }) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
it('removes an element from the set if all add tags are in removed tags', () => { | ||
const lwwset = new LWWSet() | ||
const crdt = new LWWSet() | ||
const uid = new Date().getTime() | ||
lwwset.add('A', uid) | ||
lwwset.remove('A', uid) | ||
lwwset.add('A', uid + 1) | ||
lwwset.add('A', uid + 2) | ||
lwwset.remove('A', uid + 3) | ||
assert.deepEqual(new Set(lwwset.values), new Set([])) | ||
crdt.add('A', uid) | ||
crdt.remove('A', uid) | ||
crdt.add('A', uid + 1) | ||
crdt.add('A', uid + 2) | ||
crdt.remove('A', uid + 3) | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
it('doesn\'t remove an element from the set if add and remove were concurrent', () => { | ||
const lwwset = new LWWSet() | ||
const crdt = new LWWSet() | ||
const uid = new Date().getTime() | ||
lwwset.remove('A', uid) | ||
lwwset.add('A', uid) | ||
assert.deepEqual(new Set(lwwset.values), new Set(['A'])) | ||
crdt.remove('A', uid) | ||
crdt.add('A', uid) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
it('doesn\'t remove an element from the set if element wasn\'t in the set', () => { | ||
const lwwset = new LWWSet() | ||
const crdt = new LWWSet() | ||
const uid = new Date().getTime() | ||
lwwset.remove('A', uid - 1) | ||
lwwset.add('A', uid) | ||
assert.deepEqual(new Set(lwwset.values), new Set(['A'])) | ||
crdt.remove('A', uid - 1) | ||
crdt.add('A', uid) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
@@ -203,6 +202,6 @@ }) | ||
let tag = new LamportClock('A') | ||
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
lwwset.add('A', tag) | ||
lwwset.remove('A', tag.tick()) | ||
assert.deepEqual(new Set(lwwset.values), new Set([])) | ||
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
crdt.add('A', tag) | ||
crdt.remove('A', tag.tick()) | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
@@ -213,11 +212,11 @@ | ||
let clock2 = new LamportClock('A') | ||
const lwwset = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
const crdt = new LWWSet(null, { compareFunc: LamportClock.compare }) | ||
clock1 = clock1.tick() | ||
lwwset.add('A', clock1) | ||
crdt.add('A', clock1) | ||
clock2 = clock2.tick() | ||
lwwset.add('A', clock1) | ||
crdt.add('A', clock1) | ||
clock2 = clock2.tick() | ||
clock2 = clock2.tick() | ||
lwwset.remove('A', clock2) | ||
assert.deepEqual(new Set(lwwset.values), new Set([])) | ||
crdt.remove('A', clock2) | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
@@ -228,74 +227,74 @@ }) | ||
it('merges two sets with same id', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
orset1.add('A') | ||
orset2.add('B') | ||
orset2.add('C') | ||
orset2.add('D', 33) | ||
orset2.remove('D', 34) | ||
orset1.merge(orset2) | ||
assert.deepEqual(orset1.toArray(), ['A', 'B', 'C']) | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
lwwset1.add('A') | ||
lwwset2.add('B') | ||
lwwset2.add('C') | ||
lwwset2.add('D', 33) | ||
lwwset2.remove('D', 34) | ||
lwwset1.merge(lwwset2) | ||
assert.deepEqual(lwwset1.toArray(), ['A', 'B', 'C']) | ||
}) | ||
it('merges two sets with same values', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
orset1.add('A') | ||
orset2.add('B', 13) | ||
orset2.add('A') | ||
orset2.remove('B', 14) | ||
orset1.merge(orset2) | ||
assert.deepEqual(orset1.toArray(), ['A']) | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
lwwset1.add('A') | ||
lwwset2.add('B', 13) | ||
lwwset2.add('A') | ||
lwwset2.remove('B', 14) | ||
lwwset1.merge(lwwset2) | ||
assert.deepEqual(lwwset1.toArray(), ['A']) | ||
}) | ||
it('merges two sets with removed values', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
orset1.add('A', 1) | ||
orset1.remove('A', 2) | ||
orset2.remove('A', 3) | ||
orset2.add('A', 4) | ||
orset1.add('AAA', 5) | ||
orset2.add('AAA', 6) | ||
orset1.add('A', 7) | ||
orset1.remove('A', 8) | ||
orset1.merge(orset2) | ||
assert.deepEqual(orset1.toArray(), ['AAA']) | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
lwwset1.add('A', 1) | ||
lwwset1.remove('A', 2) | ||
lwwset2.remove('A', 3) | ||
lwwset2.add('A', 4) | ||
lwwset1.add('AAA', 5) | ||
lwwset2.add('AAA', 6) | ||
lwwset1.add('A', 7) | ||
lwwset1.remove('A', 8) | ||
lwwset1.merge(lwwset2) | ||
assert.deepEqual(lwwset1.toArray(), ['AAA']) | ||
}) | ||
it('merge four different sets', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
const orset3 = new LWWSet() | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
const lwwset3 = new LWWSet() | ||
const lwwset4 = new LWWSet() | ||
orset1.add('A', 1) | ||
orset2.add('B', 1) | ||
orset3.add('C', 1) | ||
lwwset1.add('A', 1) | ||
lwwset2.add('B', 1) | ||
lwwset3.add('C', 1) | ||
lwwset4.add('D', 1) | ||
orset2.add('BB', 2) | ||
orset2.remove('BB', 3) | ||
lwwset2.add('BB', 2) | ||
lwwset2.remove('BB', 3) | ||
orset1.merge(orset2) | ||
orset1.merge(orset3) | ||
orset1.remove('C', 2) | ||
orset1.merge(lwwset4) | ||
lwwset1.merge(lwwset2) | ||
lwwset1.merge(lwwset3) | ||
lwwset1.remove('C', 2) | ||
lwwset1.merge(lwwset4) | ||
assert.deepEqual(orset1.toArray(), ['A', 'B', 'D']) | ||
assert.deepEqual(lwwset1.toArray(), ['A', 'B', 'D']) | ||
}) | ||
it('doesn\'t overwrite other\'s values on merge', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
orset1.add('A', 1) | ||
orset2.add('C', 1) | ||
orset2.add('B', 1) | ||
orset2.remove('C', 2) | ||
orset1.merge(orset2) | ||
orset1.add('AA', 3) | ||
orset2.add('CC', 3) | ||
orset2.add('BB', 3) | ||
orset2.remove('CC', 4) | ||
orset1.merge(orset2) | ||
assert.deepEqual(orset1.toArray(), ['A', 'B', 'AA', 'BB']) | ||
assert.deepEqual(orset2.toArray(), ['B', 'BB']) | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
lwwset1.add('A', 1) | ||
lwwset2.add('C', 1) | ||
lwwset2.add('B', 1) | ||
lwwset2.remove('C', 2) | ||
lwwset1.merge(lwwset2) | ||
lwwset1.add('AA', 3) | ||
lwwset2.add('CC', 3) | ||
lwwset2.add('BB', 3) | ||
lwwset2.remove('CC', 4) | ||
lwwset1.merge(lwwset2) | ||
assert.deepEqual(lwwset1.toArray(), ['A', 'B', 'AA', 'BB']) | ||
assert.deepEqual(lwwset2.toArray(), ['B', 'BB']) | ||
}) | ||
@@ -306,24 +305,24 @@ }) | ||
it('returns true if given element is in the set', () => { | ||
const lwwset = new LWWSet() | ||
lwwset.add('A') | ||
lwwset.add('B') | ||
lwwset.add(1) | ||
lwwset.add(13, 'A') | ||
lwwset.remove(13, 'B') | ||
const crdt = new LWWSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add(1) | ||
crdt.add(13, 'A') | ||
crdt.remove(13, 'B') | ||
const obj = { hello: 'world' } | ||
lwwset.add(obj) | ||
assert.equal(lwwset.has('A'), true) | ||
assert.equal(lwwset.has('B'), true) | ||
assert.equal(lwwset.has(1), true) | ||
assert.equal(lwwset.has(13), false) | ||
assert.equal(lwwset.has(obj), true) | ||
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 lwwset = new LWWSet() | ||
lwwset.add('A') | ||
lwwset.add('B') | ||
lwwset.add('nothere', 666) | ||
lwwset.remove('nothere', 777) | ||
assert.equal(lwwset.has('nothere'), false) | ||
const crdt = new LWWSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('nothere', 666) | ||
crdt.remove('nothere', 777) | ||
assert.equal(crdt.has('nothere'), false) | ||
}) | ||
@@ -334,19 +333,19 @@ }) | ||
it('returns true if all given elements are in the set', () => { | ||
const lwwset = new LWWSet() | ||
lwwset.add('A') | ||
lwwset.add('B') | ||
lwwset.add('C') | ||
lwwset.remove('C') | ||
lwwset.add('D') | ||
assert.equal(lwwset.hasAll(['D', 'A', 'B']), true) | ||
const crdt = new LWWSet() | ||
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 lwwset = new LWWSet() | ||
lwwset.add('A') | ||
lwwset.add('B') | ||
lwwset.add('C', 1) | ||
lwwset.remove('C', 2) | ||
lwwset.add('D') | ||
assert.equal(lwwset.hasAll(['D', 'A', 'C', 'B']), false) | ||
const crdt = new LWWSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('C', 1) | ||
crdt.remove('C', 2) | ||
crdt.add('D') | ||
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B']), false) | ||
}) | ||
@@ -357,4 +356,4 @@ }) | ||
it('returns the values of the set as an Array', () => { | ||
const lwwset = new LWWSet() | ||
const array = lwwset.toArray() | ||
const crdt = new LWWSet() | ||
const array = crdt.toArray() | ||
assert.equal(Array.isArray(array), true) | ||
@@ -364,8 +363,8 @@ }) | ||
it('returns values', () => { | ||
const lwwset = new LWWSet() | ||
lwwset.add('A') | ||
lwwset.add('B') | ||
lwwset.add('C', 1) | ||
lwwset.remove('C', 2) | ||
const array = lwwset.toArray() | ||
const crdt = new LWWSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('C', 1) | ||
crdt.remove('C', 2) | ||
const array = crdt.toArray() | ||
assert.equal(array.length, 2) | ||
@@ -379,21 +378,25 @@ assert.equal(array[0], 'A') | ||
it('returns the set as JSON object', () => { | ||
const lwwset = new LWWSet() | ||
lwwset.add('A', 1) | ||
assert.equal(lwwset.toJSON().values.length, 1) | ||
assert.equal(lwwset.toJSON().values[0], 'A') | ||
lwwset.remove('A', 2) | ||
assert.equal(lwwset.toJSON().values.length, 0) | ||
const crdt = new LWWSet() | ||
crdt.add('A', 1) | ||
assert.equal(crdt.toJSON().values.length, 1) | ||
assert.equal(crdt.toJSON().values[0].value, 'A') | ||
assert.equal(crdt.toJSON().values[0].added[0], 1) | ||
crdt.remove('A', 2) | ||
assert.equal(crdt.toJSON().values.length, 1) | ||
assert.equal(crdt.toJSON().values[0].value, 'A') | ||
assert.equal(crdt.toJSON().values[0].removed[0], 2) | ||
}) | ||
it('returns a JSON object after a merge', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
orset1.add('A', 1) | ||
orset2.add('B', 1) | ||
orset2.remove('B', 2) | ||
orset1.add('C', 3) | ||
orset1.merge(orset2) | ||
assert.equal(orset1.toJSON().values.length, 2) | ||
assert.equal(orset1.toJSON().values[0], 'A') | ||
assert.equal(orset1.toJSON().values[1], 'C') | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
lwwset1.add('A', 1) | ||
lwwset2.add('B', 1) | ||
lwwset2.remove('B', 2) | ||
lwwset1.add('C', 3) | ||
lwwset1.merge(lwwset2) | ||
assert.equal(lwwset1.toJSON().values.length, 3) | ||
assert.equal(lwwset1.toJSON().values[0].value, 'A') | ||
assert.equal(lwwset1.toJSON().values[1].value, 'C') | ||
assert.equal(lwwset1.toJSON().values[2].value, 'B') | ||
}) | ||
@@ -404,24 +407,24 @@ }) | ||
it('returns true for sets with same values', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
orset1.add('A', 1) | ||
orset2.add('A', 1) | ||
assert.equal(orset1.isEqual(orset2), true) | ||
assert.equal(orset2.isEqual(orset1), true) | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
lwwset1.add('A', 1) | ||
lwwset2.add('A', 1) | ||
assert.equal(lwwset1.isEqual(lwwset2), true) | ||
assert.equal(lwwset2.isEqual(lwwset1), true) | ||
}) | ||
it('returns true for empty sets', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
assert.equal(orset1.isEqual(orset2), true) | ||
assert.equal(orset2.isEqual(orset1), true) | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
assert.equal(lwwset1.isEqual(lwwset2), true) | ||
assert.equal(lwwset2.isEqual(lwwset1), true) | ||
}) | ||
it('returns false for sets with different values', () => { | ||
const orset1 = new LWWSet() | ||
const orset2 = new LWWSet() | ||
orset1.add('A') | ||
orset2.add('B') | ||
assert.equal(orset1.isEqual(orset2), false) | ||
assert.equal(orset2.isEqual(orset1), false) | ||
const lwwset1 = new LWWSet() | ||
const lwwset2 = new LWWSet() | ||
lwwset1.add('A') | ||
lwwset2.add('B') | ||
assert.equal(lwwset1.isEqual(lwwset2), false) | ||
assert.equal(lwwset2.isEqual(lwwset1), false) | ||
}) | ||
@@ -428,0 +431,0 @@ }) |
'use strict' | ||
const assert = require('assert') | ||
const { ORSet, GSet } = require('../src') | ||
const { ORSet, CmRDTSet } = require('../src') | ||
const LamportClock = require('./lamport-clock') | ||
@@ -11,10 +11,9 @@ | ||
it('creates a set', () => { | ||
const gset = new ORSet() | ||
assert.notEqual(gset, null) | ||
const crdt = new ORSet() | ||
assert.notEqual(crdt, null) | ||
}) | ||
it('is a CmRDT Set', () => { | ||
const gset = new ORSet() | ||
assert.notEqual(gset._operations, null) | ||
assert.equal(gset._operations instanceof Array, true) | ||
const crdt = new ORSet() | ||
assert.equal(crdt instanceof CmRDTSet, true) | ||
}) | ||
@@ -25,11 +24,11 @@ }) | ||
it('is an Iterator', () => { | ||
const gset = new ORSet() | ||
assert.equal(gset.values.toString(), '[object Set Iterator]') | ||
const crdt = new ORSet() | ||
assert.equal(crdt.values().toString(), '[object Set Iterator]') | ||
}) | ||
it('returns an Iterator', () => { | ||
const gset = new ORSet() | ||
gset.add('A') | ||
gset.add('B') | ||
const iterator = gset.values | ||
const crdt = new ORSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
const iterator = crdt.values() | ||
assert.equal(iterator.next().value, 'A') | ||
@@ -42,30 +41,30 @@ assert.equal(iterator.next().value, 'B') | ||
it('adds an element to the set', () => { | ||
const gset = new ORSet() | ||
const crdt = new ORSet() | ||
const uid = new Date().getTime() | ||
gset.add('A', uid) | ||
gset.add('A', uid + 1) | ||
assert.deepEqual(new Set(gset.values), new Set(['A'])) | ||
crdt.add('A', uid) | ||
crdt.add('A', uid + 1) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
it('adds three elements to the set', () => { | ||
const gset = new ORSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
assert.deepEqual(new Set(gset.values), new Set(['A', 'B', 'C'])) | ||
const crdt = new ORSet() | ||
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 gset = new ORSet() | ||
gset.add(1) | ||
gset.add('A') | ||
gset.add('A') | ||
gset.add(1) | ||
gset.add('A') | ||
const crdt = new ORSet() | ||
crdt.add(1) | ||
crdt.add('A') | ||
crdt.add('A') | ||
crdt.add(1) | ||
crdt.add('A') | ||
const obj = { hello: 'ABC' } | ||
gset.add(obj) | ||
gset.add(obj) | ||
gset.add({ hello: 'ABCD' }) | ||
crdt.add(obj) | ||
crdt.add(obj) | ||
crdt.add({ hello: 'ABCD' }) | ||
gset.add(9, 'ok') | ||
crdt.add(9, 'ok') | ||
@@ -80,3 +79,3 @@ const expectedResult = [ | ||
assert.deepEqual(new Set(gset.values), new Set(expectedResult)) | ||
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) | ||
}) | ||
@@ -87,7 +86,7 @@ }) | ||
it('adds an element to the set', () => { | ||
let uid = new LamportClock('A') | ||
const gset = new ORSet(null, { compareFunc: LamportClock.compare }) | ||
gset.add('A', uid) | ||
gset.add('A', uid.tick()) | ||
assert.deepEqual(new Set(gset.values), new Set(['A'])) | ||
let clock = new LamportClock('A') | ||
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual }) | ||
crdt.add('A', clock) | ||
crdt.add('A', clock.tick()) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
@@ -97,7 +96,7 @@ | ||
let uid = new LamportClock('A') | ||
const gset = new ORSet(null, { compareFunc: LamportClock.compare }) | ||
gset.add('A', uid) | ||
gset.add('B', uid) | ||
gset.add('C', uid) | ||
assert.deepEqual(new Set(gset.values), new Set(['A', 'B', 'C'])) | ||
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual }) | ||
crdt.add('A', uid) | ||
crdt.add('B', uid) | ||
crdt.add('C', uid) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A', 'B', 'C'])) | ||
}) | ||
@@ -107,14 +106,14 @@ | ||
let uid = new LamportClock('A') | ||
const gset = new ORSet(null, { compareFunc: LamportClock.compare }) | ||
gset.add(1, uid.tick()) | ||
gset.add('A', uid.tick()) | ||
gset.add('A', uid.tick()) | ||
gset.add(1, uid.tick()) | ||
gset.add('A', uid.tick()) | ||
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual }) | ||
crdt.add(1, uid.tick()) | ||
crdt.add('A', uid.tick()) | ||
crdt.add('A', uid.tick()) | ||
crdt.add(1, uid.tick()) | ||
crdt.add('A', uid.tick()) | ||
const obj = { hello: 'ABC' } | ||
gset.add(obj, uid.tick()) | ||
gset.add(obj, uid) | ||
gset.add({ hello: 'ABCD' }, uid.tick()) | ||
crdt.add(obj, uid.tick()) | ||
crdt.add(obj, uid) | ||
crdt.add({ hello: 'ABCD' }, uid.tick()) | ||
gset.add(9, 'ok') | ||
crdt.add(9, 'ok') | ||
@@ -129,3 +128,3 @@ const expectedResult = [ | ||
assert.deepEqual(new Set(gset.values), new Set(expectedResult)) | ||
assert.deepEqual(new Set(crdt.values()), new Set(expectedResult)) | ||
}) | ||
@@ -137,34 +136,34 @@ }) | ||
let tag = 1 | ||
const gset = new ORSet() | ||
gset.add('A', tag) | ||
gset.remove('A', tag) | ||
assert.deepEqual(new Set(gset.values), new Set([])) | ||
const crdt = new ORSet() | ||
crdt.add('A', tag) | ||
crdt.remove('A', tag) | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
it('removes an element from the set when element has multiple tags', () => { | ||
const gset = new ORSet() | ||
gset.add('A', 1) | ||
gset.add('A', 2) | ||
gset.add('A', 3) | ||
gset.remove('A') | ||
assert.deepEqual(new Set(gset.values), new Set([])) | ||
const crdt = new ORSet() | ||
crdt.add('A', 1) | ||
crdt.add('A', 2) | ||
crdt.add('A', 3) | ||
crdt.remove('A') | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
it('removes an element from the set if all add tags are in removed tags', () => { | ||
const gset = new ORSet() | ||
const crdt = new ORSet() | ||
const uid = new Date().getTime() | ||
gset.add('A', uid) | ||
gset.remove('A') | ||
gset.add('A', uid + 1) | ||
gset.add('A', uid + 2) | ||
gset.remove('A') | ||
assert.deepEqual(new Set(gset.values), new Set([])) | ||
crdt.add('A', uid) | ||
crdt.remove('A') | ||
crdt.add('A', uid + 1) | ||
crdt.add('A', uid + 2) | ||
crdt.remove('A') | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
it('doesn\'t remove an element from the set if element wasn\'t in the set', () => { | ||
const gset = new ORSet() | ||
const crdt = new ORSet() | ||
const uid = new Date().getTime() | ||
gset.remove('A') | ||
gset.add('A', uid) | ||
assert.deepEqual(new Set(gset.values), new Set(['A'])) | ||
crdt.remove('A') | ||
crdt.add('A', uid) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
@@ -175,20 +174,68 @@ }) | ||
it('removes an element from the set', () => { | ||
let tag = new LamportClock('A') | ||
const gset = new ORSet(null, { compareFunc: LamportClock.compare }) | ||
gset.add('A', tag) | ||
gset.remove('A') | ||
assert.deepEqual(new Set(gset.values), new Set([])) | ||
let clock = new LamportClock('A') | ||
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual }) | ||
crdt.add('A', clock) | ||
clock = clock.tick() | ||
crdt.remove('A') | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
it('doesn\'t remove an element from the set if element was added with a new tag', () => { | ||
const crdt = new ORSet() | ||
const clock1 = new LamportClock('A') | ||
const clock2 = new LamportClock('B') | ||
crdt.add('A', clock1) | ||
crdt.remove('A') | ||
crdt.add('A', clock1.tick()) | ||
assert.deepEqual(new Set(crdt.values()), new Set(['A'])) | ||
}) | ||
it('removes an element from the set with the same tag', () => { | ||
let clock1 = new LamportClock('A') | ||
let clock2 = new LamportClock('A') | ||
const gset = new ORSet(null, { compareFunc: LamportClock.compare }) | ||
const crdt = new ORSet(null, { compareFunc: LamportClock.isEqual }) | ||
clock1 = clock1.tick() | ||
gset.add('A', clock1) | ||
crdt.add('A', clock1) | ||
clock2 = clock2.tick() | ||
gset.add('A', clock1) | ||
gset.remove('A') | ||
assert.deepEqual(new Set(gset.values), new Set([])) | ||
crdt.add('A', clock1) | ||
crdt.remove('A') | ||
assert.deepEqual(new Set(crdt.values()), new Set([])) | ||
}) | ||
it('doesn\'t remove an element from the set if element was added with different tags', () => { | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
const clock1 = new LamportClock('A') | ||
const clock2 = new LamportClock('B') | ||
crdt1.add('A', clock1) | ||
crdt1.remove('A') | ||
crdt2.add('A', clock2) | ||
crdt2.remove('A') | ||
assert.deepEqual(new Set(crdt1.values()), new Set([])) | ||
assert.deepEqual(new Set(crdt2.values()), new Set([])) | ||
crdt1.merge(crdt2) | ||
assert.deepEqual(new Set(crdt1.values()), new Set([])) | ||
crdt1.add('A', clock1.tick()) | ||
assert.deepEqual(new Set(crdt1.values()), new Set(['A'])) | ||
crdt2.merge(crdt1) | ||
assert.deepEqual(new Set(crdt2.values()), new Set(['A'])) | ||
}) | ||
it('doesn\'t remove an element from the set if not all add tags are observed', () => { | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
const clock1 = new LamportClock('A') | ||
const clock2 = new LamportClock('B') | ||
crdt1.add('A', clock1) | ||
crdt2.add('A', clock2) | ||
crdt2.remove('A') | ||
assert.deepEqual(new Set(crdt1.values()), new Set(['A'])) | ||
assert.deepEqual(new Set(crdt2.values()), new Set([])) | ||
crdt2.merge(crdt1) | ||
assert.deepEqual(new Set(crdt1.values()), new Set(['A'])) | ||
}) | ||
}) | ||
@@ -198,73 +245,73 @@ | ||
it('merges two sets with same id', () => { | ||
const orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
orset1.add('A') | ||
orset2.add('B') | ||
orset2.add('C') | ||
orset2.add('D') | ||
orset2.remove('D') | ||
orset1.merge(orset2) | ||
assert.deepEqual(orset1.toArray(), ['A', 'B', 'C']) | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
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 orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
orset1.add('A') | ||
orset2.add('B', 13) | ||
orset2.add('A') | ||
orset2.remove('B', 13) | ||
orset1.merge(orset2) | ||
assert.deepEqual(orset1.toArray(), ['A']) | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
crdt1.add('A') | ||
crdt2.add('B', 13) | ||
crdt2.add('A') | ||
crdt2.remove('B', 13) | ||
crdt1.merge(crdt2) | ||
assert.deepEqual(crdt1.toArray(), ['A']) | ||
}) | ||
it('merges two sets with removed values', () => { | ||
const orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
orset1.add('A', 1) | ||
orset1.remove('A', 1) | ||
orset2.add('A', 1) | ||
orset2.remove('A', 2) | ||
orset1.add('AAA') | ||
orset2.add('AAA') | ||
orset1.add('A', 1) | ||
orset1.merge(orset2) | ||
assert.deepEqual(orset1.toArray(), ['AAA']) | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
crdt1.add('A', 1) | ||
crdt1.remove('A', 1) | ||
crdt2.add('A', 1) | ||
crdt2.remove('A', 2) | ||
crdt1.add('AAA') | ||
crdt2.add('AAA') | ||
crdt1.add('A', 1) | ||
crdt1.merge(crdt2) | ||
assert.deepEqual(crdt1.toArray(), ['AAA']) | ||
}) | ||
it('merge four different sets', () => { | ||
const orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
const orset3 = new ORSet() | ||
const gset4 = new ORSet() | ||
orset1.add('A') | ||
orset2.add('B') | ||
const crdt4 = new ORSet() | ||
crdt1.add('A') | ||
crdt2.add('B') | ||
orset3.add('C', 7) | ||
gset4.add('D') | ||
orset2.add('BB', 1) | ||
orset2.remove('BB', 1) | ||
crdt4.add('D') | ||
crdt2.add('BB', 1) | ||
crdt2.remove('BB', 1) | ||
orset1.merge(orset2) | ||
orset1.merge(orset3) | ||
orset1.remove('C', 8) | ||
orset1.merge(gset4) | ||
crdt1.merge(crdt2) | ||
crdt1.merge(orset3) | ||
crdt1.remove('C', 8) | ||
crdt1.merge(crdt4) | ||
assert.deepEqual(orset1.toArray(), ['A', 'B', 'D']) | ||
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'D']) | ||
}) | ||
it('doesn\'t overwrite other\'s values on merge', () => { | ||
const orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
orset1.add('A') | ||
orset2.add('C') | ||
orset2.add('B') | ||
orset2.remove('C') | ||
orset1.merge(orset2) | ||
orset1.add('AA') | ||
orset2.add('CC', 1) | ||
orset2.add('BB') | ||
orset2.remove('CC', 1) | ||
orset1.merge(orset2) | ||
assert.deepEqual(orset1.toArray(), ['A', 'B', 'AA', 'BB']) | ||
assert.deepEqual(orset2.toArray(), ['B', 'BB']) | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
crdt1.add('A') | ||
crdt2.add('C') | ||
crdt2.add('B') | ||
crdt2.remove('C') | ||
crdt1.merge(crdt2) | ||
crdt1.add('AA') | ||
crdt2.add('CC', 1) | ||
crdt2.add('BB') | ||
crdt2.remove('CC', 1) | ||
crdt1.merge(crdt2) | ||
assert.deepEqual(crdt1.toArray(), ['A', 'B', 'AA', 'BB']) | ||
assert.deepEqual(crdt2.toArray(), ['B', 'BB']) | ||
}) | ||
@@ -275,24 +322,24 @@ }) | ||
it('returns true if given element is in the set', () => { | ||
const gset = new ORSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add(1) | ||
gset.add(13) | ||
gset.remove(13) | ||
const crdt = new ORSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add(1) | ||
crdt.add(13) | ||
crdt.remove(13) | ||
const obj = { hello: 'world' } | ||
gset.add(obj) | ||
assert.equal(gset.has('A'), true) | ||
assert.equal(gset.has('B'), true) | ||
assert.equal(gset.has(1), true) | ||
assert.equal(gset.has(13), false) | ||
assert.equal(gset.has(obj), true) | ||
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 gset = new ORSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('nothere') | ||
gset.remove('nothere') | ||
assert.equal(gset.has('nothere'), false) | ||
const crdt = new ORSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('nothere') | ||
crdt.remove('nothere') | ||
assert.equal(crdt.has('nothere'), false) | ||
}) | ||
@@ -303,19 +350,19 @@ }) | ||
it('returns true if all given elements are in the set', () => { | ||
const gset = new ORSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
gset.remove('C') | ||
gset.add('D') | ||
assert.equal(gset.hasAll(['D', 'A', 'B']), true) | ||
const crdt = new ORSet() | ||
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 gset = new ORSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C') | ||
gset.remove('C') | ||
gset.add('D') | ||
assert.equal(gset.hasAll(['D', 'A', 'C', 'B']), false) | ||
const crdt = new ORSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('C') | ||
crdt.remove('C') | ||
crdt.add('D') | ||
assert.equal(crdt.hasAll(['D', 'A', 'C', 'B']), false) | ||
}) | ||
@@ -326,4 +373,4 @@ }) | ||
it('returns the values of the set as an Array', () => { | ||
const gset = new ORSet() | ||
const array = gset.toArray() | ||
const crdt = new ORSet() | ||
const array = crdt.toArray() | ||
assert.equal(Array.isArray(array), true) | ||
@@ -333,8 +380,8 @@ }) | ||
it('returns values', () => { | ||
const gset = new ORSet() | ||
gset.add('A') | ||
gset.add('B') | ||
gset.add('C', 1) | ||
gset.remove('C', 1) | ||
const array = gset.toArray() | ||
const crdt = new ORSet() | ||
crdt.add('A') | ||
crdt.add('B') | ||
crdt.add('C', 1) | ||
crdt.remove('C', 1) | ||
const array = crdt.toArray() | ||
assert.equal(array.length, 2) | ||
@@ -348,22 +395,28 @@ assert.equal(array[0], 'A') | ||
it('returns the set as JSON object', () => { | ||
const gset = new ORSet() | ||
gset.add('A', 1) | ||
assert.equal(gset.toJSON().values.length, 1) | ||
assert.equal(gset.toJSON().values[0], 'A') | ||
gset.remove('A', 1) | ||
assert.equal(gset.toJSON().values.length, 0) | ||
const crdt = new ORSet() | ||
crdt.add('A', 1) | ||
assert.equal(crdt.toJSON().values.length, 1) | ||
assert.equal(crdt.toJSON().values[0].added.length, 1) | ||
assert.equal(crdt.toJSON().values[0].value, 'A') | ||
crdt.remove('A', 1) | ||
assert.equal(crdt.toJSON().values.length, 1) | ||
assert.equal(crdt.toJSON().values[0].removed.length, 1) | ||
assert.equal(crdt.toJSON().values[0].value, 'A') | ||
}) | ||
it('returns a JSON object after a merge', () => { | ||
const orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
orset1.add('A') | ||
orset2.add('B', 1) | ||
orset2.remove('B', 1) | ||
orset1.add('C') | ||
orset1.merge(orset2) | ||
orset2.merge(orset1) | ||
assert.equal(orset1.toJSON().values.length, 2) | ||
assert.equal(orset1.toJSON().values[0], 'A') | ||
assert.equal(orset1.toJSON().values[1], 'C') | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
crdt1.add('A') | ||
crdt2.add('B', 1) | ||
crdt2.remove('B', 1) | ||
crdt1.add('C') | ||
crdt1.merge(crdt2) | ||
crdt2.merge(crdt1) | ||
assert.equal(crdt1.toJSON().values.length, 3) | ||
assert.equal(crdt1.toJSON().values[0].value, 'A') | ||
assert.equal(crdt1.toJSON().values[1].value, 'C') | ||
assert.equal(crdt1.toJSON().values[2].value, 'B') | ||
assert.equal(crdt1.toJSON().values[2].added.length, 1) | ||
assert.equal(crdt1.toJSON().values[2].removed.length, 1) | ||
}) | ||
@@ -374,24 +427,24 @@ }) | ||
it('returns true for sets with same values', () => { | ||
const orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
orset1.add('A') | ||
orset2.add('A') | ||
assert.equal(orset1.isEqual(orset2), true) | ||
assert.equal(orset2.isEqual(orset1), true) | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
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 orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
assert.equal(orset1.isEqual(orset2), true) | ||
assert.equal(orset2.isEqual(orset1), true) | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
assert.equal(crdt1.isEqual(crdt2), true) | ||
assert.equal(crdt2.isEqual(crdt1), true) | ||
}) | ||
it('returns false for sets with different values', () => { | ||
const orset1 = new ORSet() | ||
const orset2 = new ORSet() | ||
orset1.add('A') | ||
orset2.add('B') | ||
assert.equal(orset1.isEqual(orset2), false) | ||
assert.equal(orset2.isEqual(orset1), false) | ||
const crdt1 = new ORSet() | ||
const crdt2 = new ORSet() | ||
crdt1.add('A') | ||
crdt2.add('B') | ||
assert.equal(crdt1.isEqual(crdt2), false) | ||
assert.equal(crdt2.isEqual(crdt1), false) | ||
}) | ||
@@ -398,0 +451,0 @@ }) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
98963
2842
55