mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
102 lines
3.5 KiB
TypeScript
102 lines
3.5 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import { applyImmutable, assertValidOp, assertValidWireOp, decoder, encoder, type Op } from "../src/delta/index.ts";
|
|
import { diffRevisions } from "../src/state/diff.ts";
|
|
import type { JsonValue } from "../src/types.ts";
|
|
|
|
const expectDiff = (before: JsonValue, after: JsonValue, expected: JsonValue): void => {
|
|
const operations = diffRevisions(before, after);
|
|
expect(operations).toEqual(expected);
|
|
expect(applyImmutable(before, operations)).toEqual(after);
|
|
};
|
|
|
|
describe("immutable revision diff", () => {
|
|
it("emits sets and deletes", () => {
|
|
expectDiff({ keep: 1, change: 1, remove: true }, { keep: 1, change: 2, add: 3 }, [
|
|
["s", ["change"], 2],
|
|
["s", ["add"], 3],
|
|
["d", ["remove"]],
|
|
]);
|
|
});
|
|
|
|
it("emits string append and front truncation", () => {
|
|
expectDiff({ text: "hello" }, { text: "hello world" }, [["a", ["text"], " world"]]);
|
|
expectDiff({ text: "hello world" }, { text: "world" }, [["t", ["text"], 6]]);
|
|
expectDiff({ text: "abcdefgh" }, { text: "defghxyz" }, [
|
|
["t", ["text"], 3],
|
|
["a", ["text"], "xyz"],
|
|
]);
|
|
});
|
|
|
|
it("represents array insertion, removal, and shift with splices", () => {
|
|
const a = { id: "a" };
|
|
const b = { id: "b" };
|
|
const c = { id: "c" };
|
|
expectDiff({ values: [a, b] }, { values: [a, c, b] }, [["p", ["values"], 1, 0, [c]]]);
|
|
expectDiff({ values: [a, b, c] }, { values: [b, c] }, [["p", ["values"], 0, 1, []]]);
|
|
});
|
|
|
|
it("collapses a same-length queue update to two splices", () => {
|
|
const a = { id: "a" };
|
|
const b = { id: "b" };
|
|
const c = { id: "c" };
|
|
const d = { id: "d" };
|
|
expectDiff({ values: [a, b, c] }, { values: [b, c, d] }, [
|
|
["p", ["values"], 0, 1, []],
|
|
["p", ["values"], 2, 0, [d]],
|
|
]);
|
|
});
|
|
|
|
it("emits a permutation for a pure reorder", () => {
|
|
const a = { id: "a" };
|
|
const b = { id: "b" };
|
|
const c = { id: "c" };
|
|
expectDiff({ values: [a, b, c] }, { values: [c, a, b] }, [["m", ["values"], [2, 0, 1]]]);
|
|
});
|
|
|
|
it("validates and encodes permutations", () => {
|
|
const operations: Op[] = [
|
|
["m", ["values"], [2, 0, 1]],
|
|
["m", ["values"], [1, 2, 0]],
|
|
];
|
|
for (const operation of operations) assertValidOp(operation);
|
|
const wire = encoder().encode(operations);
|
|
expect(wire).toEqual([
|
|
["m", ["values"], [2, 0, 1]],
|
|
["m", [1, 2, 0]],
|
|
]);
|
|
for (const operation of wire) assertValidWireOp(operation);
|
|
expect(decoder().decode(wire)).toEqual(operations);
|
|
expect(() => assertValidOp(["m", ["values"], [0, 0]])).toThrow(/bijection/);
|
|
});
|
|
|
|
it("emits nothing for deeply equal reconstructed values", () => {
|
|
expect(diffRevisions({ value: { nested: [1, 2] } }, { value: { nested: [1, 2] } })).toEqual([]);
|
|
expect(diffRevisions({ values: [{ id: 1 }, { id: 2 }] }, { values: [{ id: 1 }, { id: 2 }] })).toEqual([]);
|
|
expect(diffRevisions({ values: [true, true, true] }, { values: [true, true, true] })).toEqual([]);
|
|
});
|
|
|
|
it("keeps a leaf edit inside a reconstructed array narrow", () => {
|
|
expectDiff(
|
|
{
|
|
values: [
|
|
{ id: 1, label: "one" },
|
|
{ id: 2, label: "two" },
|
|
],
|
|
},
|
|
{
|
|
values: [
|
|
{ id: 1, label: "one" },
|
|
{ id: 2, label: "changed" },
|
|
],
|
|
},
|
|
[["s", ["values", 1, "label"], "changed"]],
|
|
);
|
|
});
|
|
|
|
it("falls back to a base operation when leaf operations are larger", () => {
|
|
const before = { values: Array.from({ length: 40_000 }, () => 0) };
|
|
const after = { values: Array.from({ length: 40_000 }, () => 1) };
|
|
const operations = diffRevisions(before, after);
|
|
expect(operations).toEqual([["r", after]]);
|
|
});
|
|
});
|