Files
pi-subagents/src/process-runner.test.ts

109 lines
3.4 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { EventEmitter } from "node:events";
import { mkdtemp, readFile, writeFile } from "node:fs/promises";
import { join } from "node:path";
import { tmpdir } from "node:os";
import { createRunArtifacts } from "./artifacts.ts";
import { monitorRun } from "./monitor.ts";
import { createProcessSingleRunner } from "./process-runner.ts";
class FakeChild extends EventEmitter {}
test("createProcessSingleRunner launches wrapper without tmux and returns monitored result", async () => {
const cwd = await mkdtemp(join(tmpdir(), "pi-subagents-process-"));
let metaPathSeen = "";
const runSingleTask = createProcessSingleRunner({
createArtifacts: createRunArtifacts,
buildWrapperSpawn(metaPath: string) {
metaPathSeen = metaPath;
return { command: process.execPath, args: ["-e", "process.exit(0)"] };
},
spawnChild() {
const child = new FakeChild() as any;
process.nextTick(async () => {
const meta = JSON.parse(await readFile(metaPathSeen, "utf8"));
await writeFile(
meta.resultPath,
JSON.stringify(
{
runId: meta.runId,
mode: meta.mode,
task: meta.task,
requestedModel: meta.requestedModel,
resolvedModel: meta.resolvedModel,
sessionPath: meta.sessionPath,
exitCode: 0,
finalText: "done",
stdoutPath: meta.stdoutPath,
stderrPath: meta.stderrPath,
transcriptPath: meta.transcriptPath,
resultPath: meta.resultPath,
eventsPath: meta.eventsPath,
},
null,
2,
),
"utf8",
);
child.emit("close", 0);
});
return child;
},
monitorRun: (input) => monitorRun({ ...input, pollMs: 1 }),
});
const result = await runSingleTask({
cwd,
meta: {
mode: "single",
task: "inspect auth",
requestedModel: "openai/gpt-5",
resolvedModel: "openai/gpt-5",
},
});
assert.equal(result.finalText, "done");
assert.equal(result.exitCode, 0);
assert.match(result.resultPath ?? "", /\.pi\/subagents\/runs\//);
});
test("createProcessSingleRunner writes error result.json when wrapper launch fails", async () => {
const cwd = await mkdtemp(join(tmpdir(), "pi-subagents-process-"));
const runSingleTask = createProcessSingleRunner({
createArtifacts: createRunArtifacts,
buildWrapperSpawn() {
return { command: process.execPath, args: ["-e", "process.exit(0)"] };
},
spawnChild() {
const child = new FakeChild() as any;
process.nextTick(() => {
child.emit("error", new Error("spawn boom"));
});
return child;
},
monitorRun: (input) => monitorRun({ ...input, pollMs: 1 }),
});
const result = await runSingleTask({
cwd,
meta: {
mode: "single",
task: "inspect auth",
requestedModel: "openai/gpt-5",
resolvedModel: "openai/gpt-5",
},
});
assert.equal(result.exitCode, 1);
assert.equal(result.stopReason, "error");
assert.match(result.errorMessage ?? "", /spawn boom/);
const saved = JSON.parse(await readFile(result.resultPath!, "utf8"));
assert.equal(saved.exitCode, 1);
assert.equal("agent" in saved, false);
assert.match(saved.errorMessage ?? "", /spawn boom/);
});