SPB Git

spb/khaelor Public

KHAELOR — a terminal-native autonomous engineering agent powered by Anthropic.

TypeScript 82.9% HTML 14.9% CSS 1.1% JavaScript 0.7%
4.1 KB · 122 lines typescript
Raw Blame History
1/**2 * KHAELOR3 * File: tests/tools/registry.test.ts4 * Description: Unit tests for the ToolRegistry — registration, schema export, and input validation.5 *6 * Author: Simon-Pierre Boucher7 * Contact: contact@spboucher.ai8 */910import { describe, expect, it } from "vitest";11import {12  ToolRegistry,13  createDefaultToolRegistry,14  createReadTool,15  parseToolInput,16} from "../../src/tools/index.js";1718describe("ToolRegistry", () => {19  it("registers, lists, and gets tools", () => {20    const registry = new ToolRegistry();21    registry.register(createReadTool());22    expect(registry.list().map((t) => t.name)).toEqual(["read"]);23    expect(registry.get("read")?.name).toBe("read");24    expect(registry.get("missing")).toBeUndefined();25  });2627  it("rejects duplicate registration", () => {28    const registry = new ToolRegistry();29    registry.register(createReadTool());30    expect(() => registry.register(createReadTool())).toThrowError(/already registered/);31  });3233  it("registers the seven V1 tools plus the four v2 tools", () => {34    const registry = createDefaultToolRegistry();35    expect(registry.list().map((t) => t.name)).toEqual([36      "read",37      "write",38      "edit",39      "grep",40      "glob",41      "bash",42      "process",43      "design",44      "remember",45      "symbols",46      "refs",47    ]);48  });4950  it("exports valid Anthropic tool params with at most 5 parameters each", () => {51    const params = createDefaultToolRegistry().toAnthropicTools();52    expect(params).toHaveLength(11);53    for (const param of params) {54      expect(typeof param.name).toBe("string");55      expect(param.description.length).toBeGreaterThan(50);56      expect(param.input_schema.type).toBe("object");57      const propNames = Object.keys(param.input_schema.properties);58      expect(propNames.length).toBeGreaterThan(0);59      expect(propNames.length).toBeLessThanOrEqual(5);60      for (const required of param.input_schema.required) {61        expect(propNames).toContain(required);62      }63      for (const prop of Object.values(param.input_schema.properties)) {64        expect(["string", "integer", "boolean"]).toContain(prop.type);65        expect(prop.description.length).toBeGreaterThan(0);66      }67      // Round-trips as plain JSON (what the Anthropic SDK serializes).68      expect(JSON.parse(JSON.stringify(param))).toEqual(param);69    }70  });7172  it("snake_case parameter naming throughout", () => {73    for (const param of createDefaultToolRegistry().toAnthropicTools()) {74      for (const name of Object.keys(param.input_schema.properties)) {75        expect(name).toMatch(/^[a-z][a-z0-9_]*$/);76      }77    }78  });79});8081describe("parseToolInput", () => {82  const read = createReadTool();8384  it("accepts valid input", () => {85    const parsed = parseToolInput(read, { file_path: "a.ts", offset: 2 });86    expect(parsed.ok).toBe(true);87  });8889  it("produces repair prose for a missing required parameter", () => {90    const parsed = parseToolInput(read, { offset: 2 });91    expect(parsed.ok).toBe(false);92    if (!parsed.ok) {93      expect(parsed.error).toBe(94        'Invalid input for tool "read": parameter "file_path" is required. Please rewrite the input so it satisfies the expected schema.',95      );96    }97  });9899  it("rejects wrong types with repair prose", () => {100    const parsed = parseToolInput(read, { file_path: "a.ts", offset: "two" });101    expect(parsed.ok).toBe(false);102    if (!parsed.ok) expect(parsed.error).toContain('parameter "offset" must be an integer');103  });104105  it("rejects non-object input", () => {106    const parsed = parseToolInput(read, "just a string");107    expect(parsed.ok).toBe(false);108    if (!parsed.ok) expect(parsed.error).toContain("must be a JSON object");109  });110111  it("enforces enums (process action)", () => {112    const registry = createDefaultToolRegistry();113    const processTool = registry.get("process");114    expect(processTool).toBeDefined();115    const parsed = parseToolInput(processTool!, { action: "restart" });116    expect(parsed.ok).toBe(false);117    if (!parsed.ok) {118      expect(parsed.error).toContain('parameter "action" must be one of "start", "list", "read", "write", "stop"');119    }120  });121});122