Files
oh-my-pi/packages/coding-agent/test/eval/worker-core.test.ts
T
2026-07-08 18:32:05 +08:00

115 lines
3.5 KiB
TypeScript

import { describe, expect, it } from "bun:test";
import { WorkerCore } from "@oh-my-pi/pi-coding-agent/eval/js/worker-core";
import type {
SessionSnapshot,
Transport,
WorkerInbound,
WorkerOutbound,
} from "@oh-my-pi/pi-coding-agent/eval/js/worker-protocol";
interface WorkerHarness {
send(message: WorkerInbound): void;
onMessage(handler: (message: WorkerOutbound) => void): () => void;
}
function createWorkerHarness(): WorkerHarness {
const hostListeners = new Set<(message: WorkerOutbound) => void>();
const workerListeners = new Set<(message: WorkerInbound) => void>();
const transport: Transport = {
send: message => {
queueMicrotask(() => {
for (const listener of hostListeners) listener(message);
});
},
onMessage: handler => {
workerListeners.add(handler);
return () => workerListeners.delete(handler);
},
close: () => {},
};
new WorkerCore(transport);
return {
send(message) {
queueMicrotask(() => {
for (const listener of workerListeners) listener(message);
});
},
onMessage(handler) {
hostListeners.add(handler);
return () => hostListeners.delete(handler);
},
};
}
function waitForMessage(
harness: WorkerHarness,
predicate: (message: WorkerOutbound) => boolean,
): Promise<WorkerOutbound> {
const { promise, resolve } = Promise.withResolvers<WorkerOutbound>();
let unsubscribe = (): void => {};
unsubscribe = harness.onMessage(message => {
if (!predicate(message)) return;
unsubscribe();
resolve(message);
});
return promise;
}
async function initializeWorker(harness: WorkerHarness, snapshot: SessionSnapshot): Promise<void> {
const ready = waitForMessage(harness, message => message.type === "ready");
harness.send({ type: "init", snapshot });
expect((await ready).type).toBe("ready");
}
describe("WorkerCore", () => {
it("reports same-realm cwd conflicts through the worker protocol", async () => {
const first = createWorkerHarness();
const second = createWorkerHarness();
const cwd = process.cwd();
await initializeWorker(first, { cwd, sessionId: "same-realm-first", localRoots: {} });
await initializeWorker(second, { cwd, sessionId: "same-realm-second", localRoots: {} });
const gate = Promise.withResolvers<void>();
const entered = Promise.withResolvers<void>();
(globalThis as { __omp_worker_core_gate?: { entered(): void; wait: Promise<void> } }).__omp_worker_core_gate = {
entered: () => entered.resolve(),
wait: gate.promise,
};
try {
first.send({
type: "run",
runId: "hold-first-runtime",
code: "globalThis.__omp_worker_core_gate.entered(); await globalThis.__omp_worker_core_gate.wait;",
filename: "[same-realm-first].js",
snapshot: { cwd, sessionId: "same-realm-first", localRoots: {} },
});
await entered.promise;
const result = waitForMessage(
second,
message => message.type === "result" && message.runId === "overlap-second-runtime",
);
second.send({
type: "run",
runId: "overlap-second-runtime",
code: "1 + 1;",
filename: "[same-realm-second].js",
snapshot: { cwd, sessionId: "same-realm-second", localRoots: {} },
});
expect(await result).toMatchObject({
type: "result",
runId: "overlap-second-runtime",
ok: false,
error: { message: "Cannot set cwd while another same-realm JS runtime is running" },
});
} finally {
gate.resolve();
delete (globalThis as { __omp_worker_core_gate?: { entered(): void; wait: Promise<void> } })
.__omp_worker_core_gate;
first.send({ type: "close" });
second.send({ type: "close" });
}
});
});