pi-mono/packages/chord/test/next/remote/path.test.ts
Mario Zechner 8959ad6e68 feat(chord): Chord v2 slice 10, HTTP and workers
chord/next/http: a Web fetch handler built from the registry Http (routes, wraps,
sockets, hosts, relays, CSP sources) and Authenticators, responses as descriptions,
a security baseline on by default, Chord's own Node listener as a host-lifetime
object, nodeRequest for apps mounting it, links and plugin sockets.
chord/next/workers: a forked worker per address, started on demand, stopped when
idle, never restarted by itself, plugins built by the server and shipped.
Plugins: kinds with platform and host packages, createPluginRuntime, scripts(kind),
rollout results per host, imports of other plugins by name. Kernel: RouteHandle
(retry, suspend), no route backoff, anonymous host.root, origins only from relays.
ws is an optional peer dependency.
2026-10-31 20:40:36 +00:00

90 lines
3.1 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { ChordError } from "../../../src/next/data/index.ts";
import type { HostLink } from "../../../src/next/remote/host-link.ts";
import { Path, type Upstream } from "../../../src/next/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"]);
});
});