109 lines
3.4 KiB
TypeScript
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/);
|
|
});
|