108 lines
2.8 KiB
TypeScript
108 lines
2.8 KiB
TypeScript
import assert from "node:assert/strict";
|
|
import test from "node:test";
|
|
import {
|
|
applyCanonicalFieldValue,
|
|
beginLocalFieldWrite,
|
|
createRegisterFieldState,
|
|
failLocalFieldWrite,
|
|
revisionFieldClock,
|
|
} from "../src/field-semantics.js";
|
|
|
|
test("register fields reject stale canonical values", () => {
|
|
const state = createRegisterFieldState({
|
|
value: "current",
|
|
clock: revisionFieldClock(5n),
|
|
});
|
|
|
|
beginLocalFieldWrite(state, "local:1", "current!");
|
|
|
|
const stale = applyCanonicalFieldValue(state, {
|
|
value: "old",
|
|
clock: revisionFieldClock(4n),
|
|
});
|
|
|
|
assert.equal(stale.accepted, false);
|
|
assert.equal(state.canonical.value, "current");
|
|
assert.equal(state.visible, "current!");
|
|
assert.equal(state.pending.length, 1);
|
|
});
|
|
|
|
test("register fields reconcile optimistic writes by mutation id", () => {
|
|
const state = createRegisterFieldState({
|
|
value: "a",
|
|
clock: revisionFieldClock(1n),
|
|
});
|
|
|
|
beginLocalFieldWrite(state, "local:1", "ab");
|
|
beginLocalFieldWrite(state, "local:2", "abc");
|
|
|
|
const otherWriter = applyCanonicalFieldValue(state, {
|
|
value: "z",
|
|
clock: revisionFieldClock(2n),
|
|
});
|
|
assert.equal(otherWriter.accepted, true);
|
|
assert.equal(state.canonical.value, "z");
|
|
assert.equal(state.visible, "abc");
|
|
assert.equal(state.pending.length, 2);
|
|
|
|
const firstAck = applyCanonicalFieldValue(
|
|
state,
|
|
{
|
|
value: "ab",
|
|
clock: revisionFieldClock(3n),
|
|
},
|
|
"local:1",
|
|
);
|
|
assert.equal(firstAck.accepted, true);
|
|
assert.equal(state.canonical.value, "ab");
|
|
assert.equal(state.visible, "abc");
|
|
assert.equal(state.pending.length, 1);
|
|
|
|
const secondAck = applyCanonicalFieldValue(
|
|
state,
|
|
{
|
|
value: "abc",
|
|
clock: revisionFieldClock(4n),
|
|
},
|
|
"local:2",
|
|
);
|
|
assert.equal(secondAck.accepted, true);
|
|
assert.equal(state.canonical.value, "abc");
|
|
assert.equal(state.visible, "abc");
|
|
assert.equal(state.pending.length, 0);
|
|
});
|
|
|
|
test("register fields reject unclocked values after a revision is known", () => {
|
|
const state = createRegisterFieldState({
|
|
value: "known",
|
|
clock: revisionFieldClock(9n),
|
|
});
|
|
|
|
const result = applyCanonicalFieldValue(state, {
|
|
value: "unclocked",
|
|
clock: revisionFieldClock(undefined),
|
|
});
|
|
|
|
assert.equal(result.accepted, false);
|
|
assert.equal(state.canonical.value, "known");
|
|
assert.equal(state.visible, "known");
|
|
});
|
|
|
|
test("failed optimistic writes reveal the newest accepted canonical value", () => {
|
|
const state = createRegisterFieldState({
|
|
value: "base",
|
|
clock: revisionFieldClock(1n),
|
|
});
|
|
|
|
beginLocalFieldWrite(state, "local:1", "local");
|
|
applyCanonicalFieldValue(state, {
|
|
value: "remote",
|
|
clock: revisionFieldClock(2n),
|
|
});
|
|
failLocalFieldWrite(state, "local:1");
|
|
|
|
assert.equal(state.canonical.value, "remote");
|
|
assert.equal(state.visible, "remote");
|
|
assert.equal(state.pending.length, 0);
|
|
});
|