Spaces:
Runtime error
Runtime error
| import { describe, it, before } from "node:test"; | |
| import assert from "node:assert"; | |
| import * as toolDetector from "../../../src/lib/cli-helper/tool-detector.ts"; | |
| 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"); | |
| }); | |
| }); | |
| 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}`, | |
| ); | |
| }); | |
| }); | |
| }); | |