Files
dev/third_party/pi/packages/codemode/test/portable.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

160 lines
5.6 KiB
TypeScript

import { describe, expect, it } from "vitest";
import {
type CodemodeRemote,
type CodemodeRemoteExchange,
type CodemodeRemoteRequest,
type CodemodeServeOptions,
InlineCodemodeSandbox,
loadQuickJSWasm,
RemoteCodemodeSandbox,
serveCodemodeRemote,
} from "../src/index.ts";
const wasm = loadQuickJSWasm();
/** An in-process remote that records what crosses the boundary, cloned as Workers RPC would. */
function recordingRemote(options: Partial<CodemodeServeOptions> = {}) {
const exchanges: number[] = [];
const served: Promise<void>[] = [];
const remote: CodemodeRemote = {
execute(request: CodemodeRemoteRequest, exchange: CodemodeRemoteExchange) {
const done = serveCodemodeRemote(
structuredClone(request),
async (messages) => {
exchanges.push(messages.length);
return structuredClone(await exchange(structuredClone(messages)));
},
{ wasm, ...options },
);
served.push(done);
return done;
},
};
return { remote, exchanges, served };
}
describe("InlineCodemodeSandbox", () => {
it("fails a script that computes past its interrupt budget as a timeout", async () => {
const sandbox = new InlineCodemodeSandbox({ wasm, interruptBudget: 1_000 });
for (const code of ["while (true) {}", "while (true) await null"]) {
const result = await sandbox.execute(code, { timeoutMs: Number.POSITIVE_INFINITY });
expect(result).toMatchObject({
ok: false,
error: { kind: "timeout", message: "Execution exceeded its interrupt budget of 1000" },
});
}
});
it("counts the budget across waits on tools", async () => {
const sandbox = new InlineCodemodeSandbox({
wasm,
interruptBudget: 1_000,
tools: [{ name: "tick", execute: () => 1 }],
});
const result = await sandbox.execute("for (;;) { for (let i = 0; i < 1e5; i++); await tools.tick(); }");
expect(result).toMatchObject({ ok: false, error: { kind: "timeout" } });
expect(result.calls.length).toBeGreaterThan(1);
});
});
describe("RemoteCodemodeSandbox", () => {
it("runs tools in the caller and the script on the remote", async () => {
const { remote, served } = recordingRemote();
const seen: unknown[] = [];
const sandbox = new RemoteCodemodeSandbox({
remote: () => remote,
tools: [
{
name: "add",
execute: (args) => {
seen.push(args);
return (args as { a: number }).a + 1;
},
},
],
});
const result = await sandbox.execute("text('hi'); return await tools.add({ a: 1 })", { store: { k: 1 } });
expect(result).toMatchObject({ ok: true, value: 2, output: [{ type: "text", text: "hi" }] });
expect(seen).toEqual([{ a: 1 }]);
expect(result.calls).toMatchObject([{ name: "add", status: "ok" }]);
await Promise.all(served);
});
it("batches output instead of making one exchange per line", async () => {
const { remote, exchanges } = recordingRemote();
const sandbox = new RemoteCodemodeSandbox({ remote: () => remote });
const result = await sandbox.execute("for (let i = 0; i < 5000; i++) console.log(i); return 'ok'");
expect(result).toMatchObject({ ok: true, value: "ok" });
expect(result.output).toHaveLength(5000);
expect(result.output[4999]).toEqual({ type: "text", text: "4999", console: true });
expect(exchanges.length).toBeLessThanOrEqual(6);
});
it("caps the interrupt budget and memory at the remote's limits", async () => {
const { remote } = recordingRemote({ maxInterruptBudget: 1_000, maxMemoryLimitBytes: 8 * 1024 * 1024 });
const sandbox = new RemoteCodemodeSandbox({ remote: () => remote, interruptBudget: 1e9 });
expect(await sandbox.execute("while (true) {}")).toMatchObject({
ok: false,
error: { kind: "timeout", message: "Execution exceeded its interrupt budget of 1000" },
});
const memory = await sandbox.execute(
"const parts = []; for (;;) parts.push('x'.repeat(1 << 20) + parts.length);",
);
expect(memory).toMatchObject({ ok: false });
expect(JSON.stringify(memory)).toContain("out of memory");
});
it("tells the remote to stop when the caller aborts", async () => {
const { remote, served } = recordingRemote();
let started: () => void = () => {};
const call = new Promise<void>((resolve) => {
started = resolve;
});
const sandbox = new RemoteCodemodeSandbox({
remote: () => remote,
tools: [
{
name: "hang",
execute: () => {
started();
return new Promise(() => {});
},
},
],
});
const controller = new AbortController();
const pending = sandbox.execute("await tools.hang(); return 'never'", { signal: controller.signal });
await call;
controller.abort();
expect(await pending).toMatchObject({ ok: false, error: { kind: "aborted" } });
// The remote's pending exchange is answered with "stop", so its VM is disposed and execute() returns.
await Promise.all(served);
});
it("reports a failing or vanishing remote as a sandbox error", async () => {
const failing = new RemoteCodemodeSandbox({
remote: () => ({ execute: () => Promise.reject(new Error("object reset")) }),
});
expect(await failing.execute("return 1")).toMatchObject({
ok: false,
error: { kind: "sandbox", message: "Remote sandbox failed: object reset" },
});
const silent = new RemoteCodemodeSandbox({ remote: () => ({ execute: () => Promise.resolve() }) });
expect(await silent.execute("return 1")).toMatchObject({
ok: false,
error: { kind: "sandbox", message: "Remote sandbox returned before the script settled" },
});
const unreachable = new RemoteCodemodeSandbox({
remote: () => {
throw new Error("no binding");
},
});
expect(await unreachable.execute("return 1")).toMatchObject({
ok: false,
error: { kind: "sandbox", message: "Failed to reach the remote sandbox: no binding" },
});
});
});