123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305 |
- import {Delta, TargetNode, TargetValue} from "../lib/delta";
- import {DeltaRegistry} from "../lib/delta_registry";
- import {mockUuid,} from "../lib/util/test_helpers";
- import {assert,} from "../lib/util/assert";
- describe("Primitive Delta", () => {
- it("Delete/delete node conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const creation = registry.newNodeCreation(getId());
- assert(creation.conflictsWith.length === 0, "did not expect node creation to be conflicting with any other operation");
- const deletion1 = registry.newNodeDeletion(creation, [], []);
- assert(deletion1.conflictsWith.length === 0, "did not expect first deletion alone to be conflicting with anything");
- const deletion2 = registry.newNodeDeletion(creation, [], []);
- assert(deletion1 === deletion2, "expected deltas to be identical");
- assert(deletion1.conflictsWith.length === 0, "expected no conflicts");
- assert(deletion1.hash.equals(deletion2.hash), "deletions should have equal hash");
- });
- it("Create/create edge conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const sourceCreation = registry.newNodeCreation(getId());
- const target1Creation = registry.newNodeCreation(getId());
- const target2Creation = registry.newNodeCreation(getId());
- const edge1Creation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), target1Creation);
- assert(edge1Creation.conflictsWith.length === 0, "expected a single edge alone to not be involved in conflicts");
- const edge2Creation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), target2Creation);
- assert(edge1Creation.conflictsWith.length === 1, "expected conflict: same edge created twice, concurrently")
- assert(edge2Creation.conflictsWith.length === 1, "expected conflict: same edge created twice, concurrently")
- });
- it("Update/update edge conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const newTarget1Creation = registry.newNodeCreation(getId());
- const newTarget2Creation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- const update1 = registry.newEdgeUpdate(edgeCreation.overwrite(), newTarget1Creation);
- assert(update1.conflictsWith.length === 0, "expected no conflict with a single edge update.")
- const update2 = registry.newEdgeUpdate(edgeCreation.overwrite(),newTarget2Creation);
- assert(update1.conflictsWith.length === 1, "expected conflict between concurrent edge updates.")
- assert(update2.conflictsWith.length === 1, "expected conflict between concurrent edge updates.")
- });
- it("Delete/require (edge source) conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const sourceDeletion = registry.newNodeDeletion(sourceCreation, [], []);
- assert(sourceDeletion.conflictsWith.length === 0, "expected no conflicts so far");
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
- assert(sourceDeletion.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
- });
- it("Delete/require (edge source) conflict (reverse order)", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- // same as before, but now the 'order' of edgeCreation and sourceDeletion is reversed.
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- assert(edgeCreation.conflictsWith.length === 0, "expected no conflicts so far");
- const sourceDeletion = registry.newNodeDeletion(sourceCreation, [], []);
- assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
- assert(sourceDeletion.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
- });
- it("Require (edge source), then delete (no conflict)", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- // proper way of deleting the source of an edge: the deletion must depend on the edgeCreation
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- const edgeDeletion = registry.newEdgeUpdate(edgeCreation.overwrite(), null);
- const sourceDeletion = registry.newNodeDeletion(sourceCreation, [edgeDeletion], []);
- assert(sourceDeletion.conflictsWith.length === 0, "Since node deletion was aware of outgoing edge deletion, there should be no conflict");
- });
- it("Delete/require (edge source) conflict (3)", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- // Same as (2), really, because the additional EdgeUpdate doesn't change anything.
- // Only the earliest conflict, between EdgeCreation and source NodeDeletion, matters.
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- const newTargetCreation = registry.newNodeCreation(getId());
- const edgeUpdate = registry.newEdgeUpdate(edgeCreation.overwrite(), newTargetCreation);
- // no conflicts so far
- const sourceDeletion = registry.newNodeDeletion(sourceCreation, [], []);
- assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
- assert(sourceDeletion.conflictsWith.length === 1, "expected require/delete conflict, because edge source is concurrently deleted");
- assert(edgeUpdate.conflictsWith.length === 0, "edge update should not be involved in any conflict");
- });
- it("Update edge after deletion of source node conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- // create edge between 2 nodes, and then properly delete the source node
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- const edgeDeletion = registry.newEdgeUpdate(edgeCreation.overwrite(), null);
- const sourceDeletion = registry.newNodeDeletion(sourceCreation, [edgeDeletion], []);
- // no conflicts so far
- const newTargetCreation = registry.newNodeCreation(getId()); // no conflict
- const edgeUpdate = registry.newEdgeUpdate(edgeCreation.overwrite(), newTargetCreation);
- // console.log("edgeUpdate.conflictsWith", edgeUpdate.conflictsWith, "edgeDeletion.conflictsWith", edgeDeletion.conflictsWith);
- assert(edgeUpdate.conflictsWith.length === 2, "expected U/U conflict");
- assert(edgeUpdate.conflictsWith.some(([d])=>d===edgeDeletion), "expected U/U conflict");
- assert(edgeUpdate.conflictsWith.some(([d])=>d===sourceDeletion), "expected U/U conflict");
- assert(edgeDeletion.conflictsWith.length === 1, "expected U/U conflict");
- assert(edgeDeletion.conflictsWith.some(([d])=>d===edgeUpdate), "expected U/U conflict");
- });
- it("Delete/require (edge target) conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const targetDeletion = registry.newNodeDeletion(targetCreation, [], []);
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict");
- assert(targetDeletion.conflictsWith.length === 1, "expected require/delete conflict");
- });
- it("Delete/require (edge target) conflict (reverse order)", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- // delete target of edge, unaware that edge exists:
- const targetDeletion = registry.newNodeDeletion(targetCreation, [], []);
- assert(edgeCreation.conflictsWith.length === 1, "expected require/delete conflict");
- assert(targetDeletion.conflictsWith.length === 1, "expected require/delete conflict");
- });
- it("Require (edge target), then delete (no conflict)", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- const edgeUpdate = registry.newEdgeUpdate(edgeCreation.overwrite(), sourceCreation); // turn edge into self-edge
- // because of the edgeUpdate, 'target' is no longer the target of the edge, and can be deleted:
- const targetDeletion = registry.newNodeDeletion(targetCreation, [], [edgeUpdate]);
- // console.log(edgeCreation.conflictsWith)
- assert(edgeCreation.conflictsWith.length === 0, "expected no require/delete conflict");
- assert(targetDeletion.conflictsWith.length === 0, "expected no require/delete conflict");
- });
- it("Delete source and target of edge (no conflict)", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const sourceCreation = registry.newNodeCreation(getId());
- const targetCreation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(sourceCreation, "label"), targetCreation);
- const edgeDeletion = registry.newEdgeUpdate(edgeCreation.overwrite(), null);
- const sourceDeletion = registry.newNodeDeletion(sourceCreation, [edgeDeletion], []);
- assert(sourceDeletion.conflictsWith.length === 0, "expected no conflicts");
- const targetDeletion = registry.newNodeDeletion(targetCreation, [], [edgeDeletion]);
- assert(targetDeletion.conflictsWith.length === 0, "expected no conflicts");
- });
- it("Delete node with self-edge", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const nodeCreation = registry.newNodeCreation(getId());
- const edgeCreation = registry.newEdgeUpdate(registry.newEdgeCreation(nodeCreation, "label"), nodeCreation);
- const edgeDeletion = registry.newEdgeUpdate(edgeCreation.overwrite(), null);
- const nodeDeletion = registry.newNodeDeletion(nodeCreation, [edgeDeletion], [edgeDeletion]);
- // console.log(nodeDeletion.conflictsWith);
- assert(nodeDeletion.conflictsWith.length === 0, "expected no conflicts");
- });
- it("Read/write conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const envCreation = registry.newNodeCreation(getId());
- const xInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "x"), 1);
- const yInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "y"), 2);
-
- // so now, x == 1, y == 2
- // then, x := x + 1
- const xUpdate = registry.newEdgeUpdate(xInitial.overwrite(), 2, [xInitial.overwrite()]);
- // and concurrently, y := x + y
- const yUpdate = registry.newEdgeUpdate(yInitial.overwrite(), 3, [xInitial.overwrite(), yInitial.overwrite()]);
- assert(xUpdate.conflictsWith.length === 1 && yUpdate.conflictsWith.length === 1, "expected one conflict");
- assert(xUpdate.conflictsWith.some(([d]) => d === yUpdate), "expected one conflict");
- });
-
- it("No Read/Read conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
-
- const envCreation = registry.newNodeCreation(getId());
- const xInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "x"), 1);
-
- // so now, x == 1
-
- // then, y := x + 1
- const yInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "y"), 2, [xInitial.overwrite()]);
-
- // and concurrently, z := x + 2
- const zInitial = registry.newEdgeUpdate(registry.newEdgeCreation(envCreation, "z"), 3, [xInitial.overwrite()]);
-
- assert(yInitial.conflictsWith.length === 0 && zInitial.conflictsWith.length === 0, "expected no conflicts");
- });
- it("R*/U conflict", () => {
- const registry = new DeltaRegistry();
- const getId = mockUuid();
- const collection = registry.newNodeCreation(getId());
- const newEdgeX = registry.newEdgeCreation(collection, "x");
- assert(newEdgeX === registry.newEdgeCreation(collection, "x"), "Should return same object");
- const addItemX = registry.newEdgeUpdate(newEdgeX, "x");
- assert(newEdgeX === registry.newEdgeCreation(collection, "x"), "Should return same object");
- assert(addItemX.conflictsWith.length === 0, "No conflicts so far");
- const readAll = registry.newReadAllOutgoing(collection, [newEdgeX]);
- // No conflict so far...
- // Now we'll create our conflict:
- const newEdgeY = registry.newEdgeCreation(collection, "y");
- const addItemY = registry.newEdgeUpdate(newEdgeY, "y");
-
- console.log(collection);
- console.log("RACW:", readAll.conflictsWith)
- console.log("NIYCW:", newEdgeY.conflictsWith)
- assert(readAll.conflictsWith.length === 1
- && newEdgeY.conflictsWith.length === 1, "Expected one conflict here");
- assert(readAll.conflictsWith.some(([d]) => d === newEdgeY)
- && newEdgeY.conflictsWith.some(([d]) => d === readAll), "Expected 'addItemY' and 'readAll' to be conflicting");
- assert(newEdgeX.conflictsWith.length === 0, "Still no conflicts here");
- })
- });
|