Files
OmniRoute/tests/unit/cli-helper/tool-detector.test.ts
Armin Anton” ∴ 8f390efffd feat(codex): self-contained codex app-server transport (executor + provider + sign-in) (#11205)
Merged after conflict resolution: the 5 conflicting test files were the base-red drains that #11201 already landed on the tip — kept the tip versions; the feature content is untouched. Validated on the combined batch board + this branch: codex-app-server + codex-gpt56-catalog 25/25, typecheck:core clean, docs-counts green (351 providers), provider-consistency 268/351/0. The opt-in codex-app-server transport (JSON-RPC-over-WS, turn/completed-awaited close, Responses SSE bridge) leaves the default codex path untouched. Thank you @arminanton — a 3.4k-line transport with the docs wave and tests to match!
2026-08-23 10:20:06 -03:00

116 lines
4.7 KiB
TypeScript

import { describe, it, before } from "node:test";
import assert from "node:assert";
import * as toolDetector from "../../../src/lib/cli-helper/tool-detector.ts";
// The Hermes tool detector honors a HERMES_HOME env var (#3628) and only falls
// back to the default ~/.hermes/config.yaml path when it is unset. CI runs with
// HERMES_HOME unset, but this suite can also run inside a Hermes Agent session
// that exports HERMES_HOME, which redirects the detected config path and breaks
// the ".hermes/config.yaml" assertion below. Unset it so the test is hermetic
// and matches CI regardless of the ambient runtime.
delete process.env.HERMES_HOME;
describe("tool-detector", () => {
before(() => {
// Install mock exec implementation for deterministic testing
// @ts-expect-error - internal test hook
toolDetector.__setExecFileImpl(async (cmd) => {
if (cmd === "opencode") {
return { stdout: "v1.0.0\n" };
}
if (cmd === "hermes") {
return { stdout: "v0.75.3\n" };
}
if (cmd === "openclaw") {
return { stdout: "v0.3.1\n" };
}
if (cmd === "which") {
return { stdout: "/usr/local/bin/opencode\n" };
}
throw new Error("Command not found");
});
});
describe("detectTool", () => {
it("returns null for unknown tool id", async () => {
const result = await toolDetector.detectTool("unknown-tool-xyz");
assert.strictEqual(result, null);
});
it("returns DetectedTool object for installed tool", async () => {
const result = await toolDetector.detectTool("opencode");
assert.ok(result !== null);
assert.strictEqual(result!.id, "opencode");
assert.strictEqual(result!.name, "OpenCode");
assert.strictEqual(result!.installed, true);
assert.strictEqual(result!.version, "1.0.0");
assert.ok(result!.configPath.includes(".config/opencode"));
assert.strictEqual(typeof result!.configured, "boolean");
});
it("returns DetectedTool object for Hermes with the Hermes config path", async () => {
const result = await toolDetector.detectTool("hermes");
assert.ok(result !== null);
assert.strictEqual(result!.id, "hermes");
assert.strictEqual(result!.name, "Hermes");
assert.strictEqual(result!.installed, true);
assert.strictEqual(result!.version, "0.75.3");
assert.ok(result!.configPath.includes(".hermes/config.yaml"));
assert.strictEqual(typeof result!.configured, "boolean");
});
// Regression test for #2833 — openclaw was missing from TOOLS array
it("returns DetectedTool object for openclaw with the openclaw config path", async () => {
const result = await toolDetector.detectTool("openclaw");
assert.ok(result !== null, "detectTool('openclaw') must not return null");
assert.strictEqual(result!.id, "openclaw");
assert.strictEqual(result!.name, "OpenClaw");
assert.strictEqual(result!.installed, true);
assert.strictEqual(result!.version, "0.3.1");
assert.ok(
result!.configPath.includes(".openclaw/openclaw.json"),
`expected configPath to include '.openclaw/openclaw.json', got: ${result!.configPath}`
);
assert.strictEqual(typeof result!.configured, "boolean");
});
it("normalizes the legacy kilocode id to the canonical kilo target", async () => {
const result = await toolDetector.detectTool("kilocode");
assert.ok(result !== null);
assert.strictEqual(result!.id, "kilo");
assert.strictEqual(result!.name, "Kilo Code");
assert.ok(result!.configPath.includes(".local/share/kilo/auth.json"));
});
});
describe("detectAllTools", () => {
it("returns array (may be empty if tools not installed)", async () => {
const tools = await toolDetector.detectAllTools();
assert.ok(Array.isArray(tools));
// All items must pass shape check
for (const t of tools) {
assert.ok(t.id);
assert.ok(t.name);
assert.strictEqual(typeof t.installed, "boolean");
assert.ok("configPath" in t);
assert.ok("configured" in t);
}
});
// Regression test for #2833 — openclaw must appear in detectAllTools()
it("includes openclaw in the detected tools list", async () => {
const tools = await toolDetector.detectAllTools();
const openclaw = tools.find((t) => t.id === "openclaw");
assert.ok(
openclaw !== undefined,
"detectAllTools() must include an entry with id='openclaw'"
);
assert.strictEqual(openclaw!.name, "OpenClaw");
assert.ok(
openclaw!.configPath.includes(".openclaw/openclaw.json"),
`expected configPath to include '.openclaw/openclaw.json', got: ${openclaw!.configPath}`
);
});
});
});