pi-mono/packages/chord/test/remote/path.test.ts
Mario Zechner e9c0ff4d84 feat(chord): Chord v2 is the package's root
src/next is src (the main export), src/next-node is src/node with short subpaths (/http, /workers, /plugins,
/plugins/runtime, /app, /surfaces, /web, /react, /tui, /tui/launcher); the foundations get /json and /watch;
Chord v1 moves to src/old (/old, /old/node) until p2 runs on v2; tests and docs move likewise (test/, docs/,
test/old, docs/old). Durable and env import the foundations from their subpaths.
2026-11-02 22:36:55 +00:00

90 lines
3.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { ChordError } from "../../src/data/index.ts";
import type { HostLink } from "../../src/remote/host-link.ts";
import { Path, type Upstream } from "../../src/remote/path.ts";
/** A holder recording where it was held and what it was told, as `name@link` and `name dropped`. */
function holder(name: string, log: string[]): Upstream {
// The path calls only `attach` and `dropped`.
return {
attach: (link: { readonly name: string }) => void log.push(`${name}@${link.name}`),
dropped: () => void log.push(`${name} dropped`),
} as unknown as Upstream;
}
/** A link known by its name; the path only passes it on. */
function linkOf(name: string): HostLink {
return { name } as unknown as HostLink;
}
describe("a path", () => {
it("asks for a link when it gets a holder without one; holds every holder on each link it gets", () => {
const log: string[] = [];
let changes = 0;
const path = new Path(() => changes++);
path.attach(holder("a", log));
path.attach(holder("b", log));
expect(changes).toBe(2);
expect(log).toEqual([]);
const first = linkOf("1");
path.connected(first);
expect(log).toEqual(["a@1", "b@1"]);
path.attach(holder("c", log));
expect(log.at(-1)).toBe("c@1");
expect(changes).toBe(2);
});
it("tells every holder when its link drops, then asks for the next; a stale link's drop is ignored", () => {
const log: string[] = [];
let changes = 0;
const path = new Path(() => changes++);
const [first, second] = [linkOf("1"), linkOf("2")];
path.connected(first);
path.attach(holder("a", log));
path.disconnected(first);
expect(log).toEqual(["a@1", "a dropped"]);
expect(changes).toBe(1);
expect(path.link).toBeUndefined();
path.connected(second);
path.disconnected(first);
expect(log).toEqual(["a@1", "a dropped", "a@2"]);
expect(changes).toBe(1);
expect(path.link).toBe(second);
});
it("reports its last holder's detach: no later link holds it", () => {
const log: string[] = [];
let changes = 0;
const path = new Path(() => changes++);
const [a, b] = [holder("a", log), holder("b", log)];
path.connected(linkOf("1"));
path.attach(a);
path.attach(b);
path.detach(a);
expect(changes).toBe(0);
path.detach(b);
expect(changes).toBe(1);
expect(path.held).toBe(false);
path.connected(linkOf("2"));
expect(log).toEqual(["a@1", "b@1"]);
});
it("says what changed: a holder attached without a link, a drop, the last release; fails its holders waiting", () => {
const events: string[] = [];
const failed: string[] = [];
const path = new Path((event) => events.push(event.kind));
const a = {
attach: () => {},
dropped: () => {},
unreachable: (error: { readonly message: string }) => void failed.push(error.message),
} as unknown as Upstream;
path.attach(a);
path.unreachable(new ChordError({ code: "unavailable", message: "no worker" }));
const link = linkOf("1");
path.connected(link);
path.disconnected(link);
path.detach(a);
expect(events).toEqual(["attached", "dropped", "released"]);
expect(failed).toEqual(["no worker"]);
});
});