81 lines
2.5 KiB
TypeScript

import type { Role, RoleResult, StartStep, WorkflowMessage } from "@uncaged/nerve-core";
import { lastMetaForRole } from "../../lib/meta-helpers.js";
import { runTestCommand } from "../../lib/run-test-command.js";
import type { ImplementerMeta } from "../implementer/index.js";
import type { PlannerMeta } from "../planner/index.js";
export type TestCommandResult = {
command: string;
ok: boolean;
stdoutPreview: string;
stderrPreview: string;
};
export type TesterMeta = {
passed: boolean;
attempt: number;
failureReason: string | null;
testCommandResults: TestCommandResult[] | null;
};
export type BuildTesterDeps = {
nerveRoot: string;
};
export function buildTesterRole({ nerveRoot }: BuildTesterDeps): Role<TesterMeta> {
return async (_start: StartStep, messages: WorkflowMessage[]): Promise<RoleResult<TesterMeta>> => {
const plannerMeta = lastMetaForRole<PlannerMeta>(messages, "planner");
const implementerMeta = lastMetaForRole<ImplementerMeta>(messages, "implementer");
const attempt = messages.filter((message) => message.role === "tester").length + 1;
if (implementerMeta === null || !implementerMeta.implementationOk || implementerMeta.branchName === null) {
return {
content: "tester cannot continue: no successful implementer output",
meta: {
passed: false,
attempt,
failureReason: "no successful implementer output",
testCommandResults: null,
},
};
}
const commands =
plannerMeta?.testCommands !== null && plannerMeta?.testCommands !== undefined && plannerMeta.testCommands.length > 0
? plannerMeta.testCommands
: ["pnpm test"];
const testCommandResults: TestCommandResult[] = [];
for (const command of commands) {
const run = await runTestCommand(nerveRoot, command);
testCommandResults.push({
command,
ok: run.ok,
stdoutPreview: run.stdoutPreview,
stderrPreview: run.stderrPreview,
});
if (!run.ok) {
return {
content: `test failed: ${command}\n${run.reason ?? "unknown error"}`,
meta: {
passed: false,
attempt,
failureReason: `command failed: ${command} (${run.reason ?? "unknown error"})`,
testCommandResults,
},
};
}
}
return {
content: `all tests passed on branch ${implementerMeta.branchName}`,
meta: {
passed: true,
attempt,
failureReason: null,
testCommandResults,
},
};
};
}