Files
dev/third_party/pi/packages/chord/test/replicated-state.test.ts
alexion 6081e3e325 chore(vendor): import pi 1.3.0
Imported as a linear vendor commit so forge rebases do not replay the upstream root tree.

git-subtree-dir: third_party/pi

git-subtree-split: 71023aec1546d6d677a65669c5665c0927ce0dd0
2026-10-15 17:58:48 -04:00

352 lines
13 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import { BACKGROUND_CONTEXT } from "../src/context/index.ts";
import type { Draft, Op } from "../src/delta/index.ts";
import {
type Context,
type ReplicatedStateSource,
type ReplicatedStateSourceAttachment,
type ReplicatedStateSourceFrame,
replicatedState,
} from "../src/index.ts";
describe("transactional replicated state", () => {
it("publishes one immutable structurally shared revision", () => {
const initial = { changed: { value: 1 }, retained: { value: 2 } };
const state = replicatedState(initial);
const previous = state.value;
const deliveries: number[] = [];
state.subscribe((_value, _context, delivery) => deliveries.push(delivery.sequence));
state.change(BACKGROUND_CONTEXT, (draft) => {
draft.changed.value = 3;
draft.changed.value = 4;
});
expect(state.value).toEqual({ changed: { value: 4 }, retained: { value: 2 } });
expect(state.value).not.toBe(previous);
expect(state.value.changed).not.toBe(previous.changed);
expect(state.value.retained).toBe(previous.retained);
expect(Object.isFrozen(state.value)).toBe(false);
expect(deliveries).toEqual([0, 1]);
});
it("rolls back callback failures and revokes escaped drafts", () => {
const state = replicatedState({ nested: { value: 1 } });
const previous = state.value;
let escaped: Draft<{ value: number }> | undefined;
expect(() =>
state.change(BACKGROUND_CONTEXT, (draft) => {
escaped = draft.nested;
draft.nested.value = 2;
throw new Error("stop");
}),
).toThrow("stop");
expect(state.value).toBe(previous);
expect(() => escaped?.value).toThrow(TypeError);
});
it("rejects PromiseLike callbacks and aborts their draft", () => {
const state = replicatedState({ value: 0 });
const previous = state.value;
expect(() =>
state.change(BACKGROUND_CONTEXT, ((draft: Draft<{ value: number }>) => {
draft.value = 1;
return Promise.resolve();
}) as (draft: Draft<{ value: number }>) => void),
).toThrow(/synchronous/);
expect(state.value).toBe(previous);
});
it("rejects nested changes and replacements without losing the outer rollback", () => {
const state = replicatedState({ left: 0, right: 0 });
expect(() =>
state.change(BACKGROUND_CONTEXT, (draft) => {
draft.left = 1;
state.change(BACKGROUND_CONTEXT, (nested) => {
nested.right = 2;
});
}),
).toThrow(/reentrantly/);
expect(state.value).toEqual({ left: 0, right: 0 });
expect(() =>
state.change(BACKGROUND_CONTEXT, () => {
state.replace(BACKGROUND_CONTEXT, { left: 1, right: 2 });
}),
).toThrow(/change callback/);
expect(state.value).toEqual({ left: 0, right: 0 });
});
it("queues listener-triggered changes in sequence order", () => {
const state = replicatedState({ value: 0 });
const deliveries: Array<{ value: number; sequence: number }> = [];
const lateDeliveries: Array<{ kind: string; sequence: number }> = [];
let nested = false;
state.subscribe((_value, _context, delivery) => {
if (delivery.kind !== "update" || nested) return;
nested = true;
state.change(BACKGROUND_CONTEXT, (draft) => {
draft.value = 2;
});
state.subscribe((_value, _context, delivery) => lateDeliveries.push(delivery));
});
state.subscribe((value, _context, delivery) => {
if (delivery.kind === "update") deliveries.push({ value: value.value, sequence: delivery.sequence });
});
state.change(BACKGROUND_CONTEXT, (draft) => {
draft.value = 1;
});
expect(deliveries).toEqual([
{ value: 1, sequence: 1 },
{ value: 2, sequence: 2 },
]);
expect(lateDeliveries).toEqual([{ kind: "hydrate", sequence: 2 }]);
});
it("isolates listener failures after committing the revision", () => {
const reports: (() => void)[] = [];
const spy = vi.spyOn(globalThis, "queueMicrotask").mockImplementation((report) => {
reports.push(report);
});
try {
const state = replicatedState({ value: 0 });
const received: number[] = [];
state.subscribe((_value, _context, delivery) => {
if (delivery.kind === "update") throw new Error("listener failed");
});
state.subscribe((_value, _context, delivery) => received.push(delivery.sequence));
expect(() =>
state.change(BACKGROUND_CONTEXT, (draft) => {
draft.value = 1;
}),
).not.toThrow();
expect(state.value).toEqual({ value: 1 });
expect(received).toEqual([0, 1]);
expect(reports).toHaveLength(1);
expect(reports[0]).toThrow("listener failed");
} finally {
spy.mockRestore();
}
});
it("copies assigned values by value", () => {
const external = { value: 1 };
const state = replicatedState<{ left: { value: number } | null; right: { value: number } | null }>({
left: null,
right: null,
});
state.change(BACKGROUND_CONTEXT, (draft) => {
draft.left = external;
draft.right = external;
draft.left.value = 2;
});
expect(external.value).toBe(1);
expect(state.value).toEqual({ left: { value: 2 }, right: { value: 1 } });
expect(state.value.left).not.toBe(state.value.right);
});
it("takes immutable ownership of alias-free replacements", () => {
const state = replicatedState({ left: { value: 1 }, right: { value: 2 } });
const replacement = { left: { ...state.value.left }, right: { ...state.value.left } };
state.replace(BACKGROUND_CONTEXT, replacement);
expect(state.value).toBe(replacement);
expect(state.value.left).not.toBe(state.value.right);
state.change(BACKGROUND_CONTEXT, (draft) => {
draft.left.value = 9;
});
expect(state.value).toEqual({ left: { value: 9 }, right: { value: 1 } });
});
it("replaces atomically and ignores deeply equal replacements", () => {
const state = replicatedState({ value: { nested: 1 }, retained: { nested: 2 } });
const previous = state.value;
state.replace(BACKGROUND_CONTEXT, { value: { nested: 1 }, retained: { nested: 2 } });
expect(state.value).toBe(previous);
state.replace(BACKGROUND_CONTEXT, { ...state.value, value: { nested: 2 } });
expect(state.value).toEqual({ value: { nested: 2 }, retained: { nested: 2 } });
expect(state.value.retained).toBe(previous.retained);
});
});
type SourceValue = Readonly<{ value: number }>;
class TestSource<T> implements ReplicatedStateSource<T> {
readonly attachments = new Set<TestSourceAttachment<T>>();
onAttach: (() => void) | undefined;
#value: T;
#cursor: number;
constructor(value: T, cursor = 0) {
this.#value = value;
this.#cursor = cursor;
}
attach(): ReplicatedStateSourceAttachment<T> {
const attachment = new TestSourceAttachment(Object.freeze({ value: this.#value, cursor: this.#cursor }), () =>
this.attachments.delete(attachment),
);
this.attachments.add(attachment);
this.onAttach?.();
return attachment;
}
commit(value: T, ops: readonly Op[], context: Context = BACKGROUND_CONTEXT, cursor = this.#cursor + 1): void {
this.#value = value;
this.#cursor = cursor;
const frame = Object.freeze({ cursor, value, ops, context });
for (const attachment of [...this.attachments]) attachment.publish(frame);
}
}
class TestSourceAttachment<T> implements ReplicatedStateSourceAttachment<T> {
readonly snapshot: { readonly value: T; readonly cursor: number };
readonly #onDispose: () => void;
readonly #buffer: ReplicatedStateSourceFrame<T>[] = [];
#listener: ((frame: ReplicatedStateSourceFrame<T>) => void) | undefined;
#activated = false;
#disposed = false;
constructor(snapshot: { readonly value: T; readonly cursor: number }, onDispose: () => void) {
this.snapshot = snapshot;
this.#onDispose = onDispose;
}
activate(listener: (frame: ReplicatedStateSourceFrame<T>) => void): void {
if (this.#activated) throw new Error("attachment is already active");
this.#activated = true;
this.#listener = listener;
for (const frame of this.#buffer.splice(0)) listener(frame);
}
publish(frame: ReplicatedStateSourceFrame<T>): void {
if (this.#disposed) return;
if (this.#listener === undefined) this.#buffer.push(frame);
else this.#listener(frame);
}
dispose(): void {
if (this.#disposed) return;
this.#disposed = true;
this.#buffer.length = 0;
this.#onDispose();
}
}
describe("authoritative replicated state sources", () => {
it("captures before activation and drains queued commits in order", () => {
const initial = Object.freeze({ value: 0 });
const first = Object.freeze({ value: 1 });
const second = Object.freeze({ value: 2 });
const firstOps = Object.freeze([["s", ["value"], 1] as const]);
const secondOps = Object.freeze([["s", ["value"], 2] as const]);
const source = new TestSource<SourceValue>(initial, 10);
source.onAttach = () => {
source.commit(first, firstOps);
source.commit(second, secondOps);
};
const state = replicatedState(source);
const deliveries: Array<{ value: SourceValue; sequence: number }> = [];
state.subscribe((value, _context, delivery) => deliveries.push({ value, sequence: delivery.sequence }));
expect(state.value).toBe(second);
expect(deliveries).toEqual([{ value: second, sequence: 2 }]);
});
it("hydrates at sequence zero when attaching after existing commits", () => {
const initial = Object.freeze({ value: 0 });
const current = Object.freeze({ value: 1 });
const source = new TestSource<SourceValue>(initial, 40);
source.commit(current, Object.freeze([["s", ["value"], 1] as const]));
const state = replicatedState(source);
const deliveries: number[] = [];
state.subscribe((_value, _context, delivery) => deliveries.push(delivery.sequence));
expect(state.value).toBe(current);
expect(deliveries).toEqual([0]);
});
it("publishes exact source values without applying or re-diffing", () => {
const source = new TestSource<SourceValue>(Object.freeze({ value: 0 }));
const state = replicatedState(source);
const next = Object.freeze({ value: 1 });
// Deliberately not the operation that produces `next`: Chord must publish the source value as is.
const ops = Object.freeze([["s", ["value"], 7] as const]);
let publishedValue: SourceValue | undefined;
state.subscribe((value, _context, delivery) => {
if (delivery.kind === "update") publishedValue = value;
});
source.commit(next, ops);
expect(state.value).toBe(next);
expect(publishedValue).toBe(next);
});
it("buffers reentrant frames and skips updates covered by a late hydration", () => {
const source = new TestSource<SourceValue>(Object.freeze({ value: 0 }));
const state = replicatedState(source);
const received: number[] = [];
const late: Array<{ kind: string; sequence: number; value: number }> = [];
let nested = false;
state.subscribe((_value, _context, delivery) => {
if (delivery.kind !== "update" || delivery.sequence !== 1 || nested) return;
nested = true;
source.commit(Object.freeze({ value: 2 }), Object.freeze([["s", ["value"], 2] as const]));
state.subscribe((value, _context, delivery) => late.push({ ...delivery, value: value.value }));
});
state.subscribe((value, _context, delivery) => {
if (delivery.kind === "update") received.push(value.value);
});
source.commit(Object.freeze({ value: 1 }), Object.freeze([["s", ["value"], 1] as const]));
expect(received).toEqual([1, 2]);
expect(late).toEqual([{ kind: "hydrate", sequence: 2, value: 2 }]);
});
it("reports listener failures without throwing them into the source", () => {
const source = new TestSource<SourceValue>(Object.freeze({ value: 0 }));
const errors: Error[] = [];
const state = replicatedState(source, { onError: (error) => errors.push(error) });
const received: number[] = [];
state.subscribe((_value, _context, delivery) => {
if (delivery.kind === "update") throw new Error("listener failed");
});
state.subscribe((value, _context, delivery) => {
if (delivery.kind === "update") received.push(value.value);
});
expect(() =>
source.commit(Object.freeze({ value: 1 }), Object.freeze([["s", ["value"], 1] as const])),
).not.toThrow();
source.commit(Object.freeze({ value: 2 }), Object.freeze([["s", ["value"], 2] as const]));
expect(received).toEqual([1, 2]);
expect(errors.map((error) => error.message)).toEqual(["listener failed", "listener failed"]);
});
it("reports cursor gaps, disposes the broken attachment, and ignores later frames", () => {
const source = new TestSource<SourceValue>(Object.freeze({ value: 0 }), 5);
const errors: Error[] = [];
const state = replicatedState(source, { onError: (error) => errors.push(error) });
source.commit(Object.freeze({ value: 2 }), Object.freeze([["s", ["value"], 2] as const]), BACKGROUND_CONTEXT, 7);
expect(errors[0]?.message).toContain("expected 6, received 7");
expect(source.attachments.size).toBe(0);
expect(state.value).toEqual({ value: 0 });
source.commit(Object.freeze({ value: 3 }), Object.freeze([["s", ["value"], 3] as const]), BACKGROUND_CONTEXT, 8);
expect(state.value).toEqual({ value: 0 });
});
it("keeps attachments independent and disposes each idempotently", () => {
const source = new TestSource<SourceValue>(Object.freeze({ value: 0 }));
const first = replicatedState(source);
const second = replicatedState(source);
expect(source.attachments.size).toBe(2);
source.commit(Object.freeze({ value: 1 }), Object.freeze([["s", ["value"], 1] as const]));
expect(first.value.value).toBe(1);
expect(second.value.value).toBe(1);
first.dispose();
first.dispose();
expect(source.attachments.size).toBe(1);
source.commit(Object.freeze({ value: 2 }), Object.freeze([["s", ["value"], 2] as const]));
expect(first.value.value).toBe(1);
expect(second.value.value).toBe(2);
second.dispose();
expect(source.attachments.size).toBe(0);
});
});