File size: 3,921 Bytes
cd8bd0a
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
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}`,
      );
    });
  });
});