Files
boss/tests/hermes-runner.test.ts

158 lines
4.1 KiB
TypeScript

import assert from "node:assert/strict";
import { mkdtemp, realpath, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { runHermesCommandForTesting } from "../src/lib/execution/backends/hermes-runner.ts";
async function createTempScript(source: string) {
const dir = await mkdtemp(join(tmpdir(), "hermes-runner-"));
const scriptPath = join(dir, "hermes-script.mjs");
await writeFile(scriptPath, source, "utf8");
return { dir, scriptPath };
}
test("Hermes runner 会按固定 chat -q -Q 形态执行并提取正文", async () => {
const workspace = await mkdtemp(join(tmpdir(), "hermes-runner-cwd-"));
const expectedWorkspace = await realpath(workspace);
const { scriptPath } = await createTempScript(`
process.stdout.write(JSON.stringify({
argv: process.argv.slice(2),
cwd: process.cwd(),
envSource: process.env.HERMES_SESSION_SOURCE || ""
}) + "\\n");
process.stdout.write("Hermes smoke completed\\n\\n");
process.stdout.write("session_id: hermes-session-123\\n");
`);
const result = await runHermesCommandForTesting({
config: {
enabled: true,
command: process.execPath,
args: [scriptPath],
cwd: workspace,
timeoutMs: 1000,
defaultModel: "gpt-5.4",
toolsets: ["web", "terminal"],
skills: ["boss-dev"],
sourceTag: "tool",
},
payload: {
executionPrompt: "请输出链路正常",
model: "gpt-5.5",
},
});
assert.equal(result.status, "completed");
if (result.status !== "completed") {
assert.fail("expected completed");
}
const lines = result.output.split("\n");
const metadata = JSON.parse(lines[0] ?? "{}") as {
argv: string[];
cwd: string;
envSource: string;
};
assert.deepEqual(metadata.argv, [
"chat",
"-q",
"请输出链路正常",
"-Q",
"--source",
"tool",
"-m",
"gpt-5.5",
"-t",
"web,terminal",
"-s",
"boss-dev",
]);
assert.equal(metadata.cwd, expectedWorkspace);
assert.equal(metadata.envSource, "");
assert.equal(lines.at(-1), "Hermes smoke completed");
assert.equal(result.sessionId, "hermes-session-123");
});
test("Hermes runner 会把非零退出码映射成 stderr 或退出码错误", async () => {
const { scriptPath } = await createTempScript(`
process.stderr.write("hermes crashed");
process.exit(2);
`);
const result = await runHermesCommandForTesting({
config: {
enabled: true,
command: process.execPath,
args: [scriptPath],
timeoutMs: 1000,
sourceTag: "tool",
},
payload: {
executionPrompt: "anything",
},
});
assert.equal(result.status, "failed");
if (result.status !== "failed") {
assert.fail("expected failed");
}
assert.match(result.error, /hermes crashed/);
});
test("Hermes runner 在输出只有 session_id 时会视为失败", async () => {
const { scriptPath } = await createTempScript(`
process.stdout.write("session_id: hermes-session-123\\n");
`);
const result = await runHermesCommandForTesting({
config: {
enabled: true,
command: process.execPath,
args: [scriptPath],
timeoutMs: 1000,
sourceTag: "tool",
},
payload: {
executionPrompt: "anything",
},
});
assert.equal(result.status, "failed");
if (result.status !== "failed") {
assert.fail("expected failed");
}
assert.match(result.error, /EMPTY_HERMES_RESPONSE/);
});
test("Hermes runner 超时后返回 HERMES_TIMEOUT", async () => {
const { scriptPath } = await createTempScript(`
setTimeout(() => {
process.stdout.write("late response\\n");
}, 500);
`);
const result = await runHermesCommandForTesting({
config: {
enabled: true,
command: process.execPath,
args: [scriptPath],
timeoutMs: 50,
sourceTag: "tool",
},
payload: {
executionPrompt: "slow",
},
});
assert.equal(result.status, "failed");
if (result.status !== "failed") {
assert.fail("expected failed");
}
assert.match(result.error, /HERMES_TIMEOUT/);
});