From 7ebcbd149123e63a474118b0b554ec7bc5852a14 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jo=C3=A3o=20Pereira?= <77340776+joaopereira12@users.noreply.github.com> Date: Tue, 30 Jul 2024 05:40:52 +0100 Subject: [PATCH] fix: compare & merge bug GCounter & PNCounter (#73) Co-authored-by: Oak --- packages/crdt/src/builtins/GCounter/index.ts | 14 +++--- packages/crdt/tests/GCounter.test.ts | 42 ++++++++++++++++ packages/crdt/tests/PNCounter.test.ts | 51 ++++++++++++++++++++ 3 files changed, 99 insertions(+), 8 deletions(-) create mode 100644 packages/crdt/tests/GCounter.test.ts create mode 100644 packages/crdt/tests/PNCounter.test.ts diff --git a/packages/crdt/src/builtins/GCounter/index.ts b/packages/crdt/src/builtins/GCounter/index.ts index bf73bb28..e518c359 100644 --- a/packages/crdt/src/builtins/GCounter/index.ts +++ b/packages/crdt/src/builtins/GCounter/index.ts @@ -20,12 +20,7 @@ export class GCounter { } compare(peerCounter: GCounter): boolean { - for (let key in Object.keys(this.counts)) { - if (this.counts[key] > peerCounter.counts[key]) { - return false; - } - } - return true; + return (this.counts.length === peerCounter.counts.length && Object.keys(this.counts).every(key => this.counts[key] <= peerCounter.counts[key])); } merge(peerCounter: GCounter): void { @@ -34,8 +29,11 @@ export class GCounter { this.counts, peerCounter.counts, ); + Object.keys(temp).forEach((key) => { - this.counts[key] = Math.max(this.counts[key], peerCounter.counts[key]); + this.counts[key] = Math.max(this.counts[key] || 0, peerCounter.counts[key] || 0); }); + + this.globalCounter = Object.values(this.counts).reduce((a, b) => a + b, 0); } -} +} \ No newline at end of file diff --git a/packages/crdt/tests/GCounter.test.ts b/packages/crdt/tests/GCounter.test.ts new file mode 100644 index 00000000..7aecf6e6 --- /dev/null +++ b/packages/crdt/tests/GCounter.test.ts @@ -0,0 +1,42 @@ +import { describe, test, expect, beforeEach } from "vitest"; +import { GCounter } from "../src/builtins/GCounter"; + +describe("G-Counter Tests", () => { + let set1: GCounter; + + beforeEach(() => { + set1 = new GCounter({ "node1": 5, "node2": 10}); + }); + + test("Test Initial Values", () => { + expect(set1.value()).toBe(15); + }); + + test("Test Increment", () => { + set1.increment("node1", 10); + set1.increment("node2", 5); + + expect(set1.value()).toBe(30); + }); + + test("Test Compare", () => { + let set2 = new GCounter({ "node1": 5, "node2": 10}); + let set3 = new GCounter({ "node1": 5, "node2": 10, "node3": 15 }); + + expect(set1.compare(set2)).toBe(true); + set1.increment("node1", 5); + expect(set1.compare(set2)).toBe(false); + expect(set1.compare(set3)).toBe(false); + }); + + test("Test Merge", () => { + let set2 = new GCounter({ "node1": 3, "node2": 10}); + let set3 = new GCounter({ "node1": 5, "node3": 15}); + + expect(set1.counts).toEqual({"node1": 5, "node2": 10}); + set2.merge(set1); + expect(set2.counts).toEqual({"node1": 5, "node2": 10}); + set1.merge(set3); + expect(set1.counts).toEqual({"node1": 5, "node2": 10, "node3": 15}); + }); +}); \ No newline at end of file diff --git a/packages/crdt/tests/PNCounter.test.ts b/packages/crdt/tests/PNCounter.test.ts new file mode 100644 index 00000000..89c6819f --- /dev/null +++ b/packages/crdt/tests/PNCounter.test.ts @@ -0,0 +1,51 @@ +import { describe, test, expect, beforeEach, afterEach } from "vitest"; +import { PNCounter } from "../src/builtins/PNCounter"; +import { GCounter } from "../src/builtins/GCounter"; + +describe("PN-Counter Tests", () => { + let set1: PNCounter; + let set2: PNCounter; + + beforeEach(() => { + set1 = new PNCounter(new GCounter({ "node1": 5, "node2": 10, "node3": 15 }), new GCounter({ "node1": 3, "node2": 4, "node3": 3 })); + set2 = new PNCounter(new GCounter({ "node1": 5, "node2": 10, "node3": 15 }), new GCounter({ "node1": 3, "node2": 4, "node3": 3 })); + }); + + test("Test Initial Value", () => { + expect(set1.value()).toBe(20); + expect(set2.value()).toBe(20); + }); + + test("Test Increment", () => { + set1.increment("node1",10); + set2.increment("node1",20); + expect(set1.value()).toBe(30); + expect(set2.value()).toBe(40); + }); + + test("Test Decrement", () => { + set1.decrement("node1",10); + set2.decrement("node1",20); + expect(set1.value()).toBe(10); + expect(set2.value()).toBe(0); + }); + + test("Test Compare", () => { + expect(set1.compare(set2)).toBe(true); + set1.decrement("node1",10); + expect(set1.compare(set2)).toBe(false); + set2.decrement("node1",10); + expect(set1.compare(set2)).toBe(true); + }); + + test("Test Merge", () => { + set1.increment("node1",10); + set2.decrement("node2",5); + expect(set1.compare(set2)).toBe(false); + expect(set2.compare(set1)).toBe(false); + set1.merge(set2); + set2.merge(set1); + expect(set1.compare(set2)).toBe(true); + expect(set2.compare(set1)).toBe(true); + }); +}); \ No newline at end of file