mirror of
https://github.com/wassname/pi-goals.git
synced 2026-09-21 13:20:15 +08:00
Borrow the provider tracker queries from cecb1e9. Keep unavailable state unknown, bind incremental views to acknowledged source entries, reset after compaction, bound serialized payloads, and recheck tracked work at sign-off.
76 lines
3.6 KiB
TypeScript
76 lines
3.6 KiB
TypeScript
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
import { describe, expect, it, vi } from "vitest";
|
|
import { GoalIntercom } from "../src/intercom.js";
|
|
import { intercomFixture } from "./intercom-fixture.js";
|
|
|
|
function setup(role: "worker" | "supervisor", entries: any[] = []) {
|
|
const fixture = intercomFixture();
|
|
const hooks = new Map<string, any>();
|
|
const ctx = { sessionManager: { getEntries: () => entries }, ui: { notify: vi.fn() } };
|
|
const api = { events: fixture.events, on: (name: string, hook: any) => hooks.set(name, hook), appendEntry: (customType: string, data: unknown) => entries.push({ type: "custom", customType, data }) };
|
|
const link = new GoalIntercom(api as unknown as ExtensionAPI);
|
|
link.configure("binding", role, ctx as any);
|
|
return { link, fixture, entries, ctx, hooks };
|
|
}
|
|
|
|
describe("pi-intercom transport", () => {
|
|
it("receives exact advice once, acknowledges it and rejects unrelated peers", async () => {
|
|
const runtime = setup("worker");
|
|
await runtime.link.waitReady();
|
|
const instruction = "Check the Modal dependency.\nKeep the local queue paused.";
|
|
const delivered = vi.fn();
|
|
runtime.link.onSteer = delivered;
|
|
const message = { binding: "binding", role: "supervisor", kind: "steer", id: "instruction", text: instruction };
|
|
runtime.fixture.receive({ ...message, binding: "other" });
|
|
runtime.fixture.receive(message, "wrong-peer");
|
|
expect(delivered).not.toHaveBeenCalled();
|
|
runtime.fixture.receive(message);
|
|
runtime.fixture.receive(message);
|
|
expect(delivered).toHaveBeenCalledExactlyOnceWith(instruction);
|
|
expect(runtime.fixture.sent.filter(message => message.kind === "received")).toHaveLength(2);
|
|
await runtime.hooks.get("session_shutdown")();
|
|
runtime.fixture.receive({ ...message, id: "late" });
|
|
expect(delivered).toHaveBeenCalledTimes(1);
|
|
});
|
|
|
|
it("restores an unacknowledged steer on reconnect and stops replay after acknowledgment", async () => {
|
|
const first = setup("supervisor");
|
|
first.link.markReady();
|
|
await first.link.waitReady();
|
|
const id = first.link.steer("Read the full output.");
|
|
await first.hooks.get("session_shutdown")();
|
|
const resumed = setup("supervisor", [...first.entries]);
|
|
resumed.link.markReady();
|
|
await resumed.link.waitReady();
|
|
expect(resumed.fixture.sent.filter(message => message.kind === "steer")).toMatchObject([{ id, text: "Read the full output." }]);
|
|
resumed.fixture.receive({ binding: "binding", role: "worker", kind: "received", id });
|
|
resumed.fixture.connect(false);
|
|
expect(resumed.link.connected).toBe(false);
|
|
expect(() => resumed.link.steer("Must not send.")).toThrow("disconnected");
|
|
resumed.fixture.connect(true);
|
|
await resumed.link.waitReady();
|
|
expect(resumed.fixture.sent.filter(message => message.kind === "steer")).toHaveLength(1);
|
|
});
|
|
|
|
it("advances the incremental overview only after acknowledgment", async () => {
|
|
const runtime = setup("worker");
|
|
await runtime.link.waitReady();
|
|
const view = runtime.link.view("The worker stopped.", "settled", "entry-1", true);
|
|
expect(runtime.link.acknowledgedEntry).toBeUndefined();
|
|
runtime.fixture.receive({ binding: "binding", role: "supervisor", kind: "received", id: view.id });
|
|
expect(runtime.link.acknowledgedEntry).toBe("entry-1");
|
|
const resumed = setup("worker", [...runtime.entries]);
|
|
expect(resumed.link.acknowledgedEntry).toBe("entry-1");
|
|
});
|
|
|
|
it("cancels a readiness wait on shutdown", async () => {
|
|
const runtime = setup("worker");
|
|
await runtime.link.waitReady();
|
|
runtime.fixture.connect(false);
|
|
const wait = runtime.link.waitReady();
|
|
const rejection = expect(wait).rejects.toThrow("Session ended");
|
|
await runtime.hooks.get("session_shutdown")();
|
|
await rejection;
|
|
});
|
|
});
|