/** * KHAELOR * File: tests/cli/args.test.ts * Description: CLI argv parsing — flags, values, errors, and the missing-API-key message quality. * * Author: Simon-Pierre Boucher * Contact: contact@spboucher.ai */ import { describe, expect, it } from "vitest"; import { KHAELOR_VERSION, helpText, missingApiKeyMessage, parseArgs, } from "../../src/cli/args.js"; describe("parseArgs", () => { it("defaults: interactive mode, no flags", () => { const parsed = parseArgs([]); expect(parsed.help).toBe(false); expect(parsed.version).toBe(false); expect(parsed.debug).toBe(false); expect(parsed.print).toBeNull(); expect(parsed.resume).toBeNull(); expect(parsed.model).toBeNull(); expect(parsed.errors).toEqual([]); }); it("parses --model, --debug, --version, --help", () => { const parsed = parseArgs(["--model", "claude-sonnet-5", "--debug", "--version", "--help"]); expect(parsed.model).toBe("claude-sonnet-5"); expect(parsed.debug).toBe(true); expect(parsed.version).toBe(true); expect(parsed.help).toBe(true); expect(parsed.errors).toEqual([]); }); it("parses short aliases -h, -v, -p", () => { expect(parseArgs(["-h"]).help).toBe(true); expect(parseArgs(["-v"]).version).toBe(true); expect(parseArgs(["-p", "hello"]).print).toBe("hello"); }); it("parses --print with the prompt value", () => { const parsed = parseArgs(["--print", "read the file and report"]); expect(parsed.print).toBe("read the file and report"); expect(parsed.errors).toEqual([]); }); it("parses --resume, --aux-model, --thinking, --max-output-tokens", () => { const parsed = parseArgs([ "--resume", "01ABC", "--aux-model", "claude-haiku-4-5", "--thinking", "off", "--max-output-tokens", "2048", ]); expect(parsed.resume).toBe("01ABC"); expect(parsed.auxModel).toBe("claude-haiku-4-5"); expect(parsed.thinking).toBe("off"); expect(parsed.maxOutputTokens).toBe(2048); }); it("rejects a flag missing its value", () => { expect(parseArgs(["--model"]).errors).toContain("--model requires a value"); expect(parseArgs(["--print", "--debug"]).errors).toContain("--print requires a value"); }); it("rejects non-integer --max-output-tokens", () => { expect(parseArgs(["--max-output-tokens", "lots"]).errors.length).toBe(1); expect(parseArgs(["--max-output-tokens", "-5"]).errors.length).toBe(1); }); it("rejects unknown options by name", () => { const parsed = parseArgs(["--frobnicate"]); expect(parsed.errors).toEqual(["unknown option: --frobnicate"]); }); it("help text documents every user-facing flag", () => { const help = helpText(); for (const flag of ["--model", "--print", "--resume", "--debug", "--version", "--help"]) { expect(help).toContain(flag); } expect(help).toContain("ANTHROPIC_API_KEY"); }); it("version constant matches package shape", () => { expect(KHAELOR_VERSION).toMatch(/^\d+\.\d+\.\d+$/); }); }); describe("missing API key message", () => { it("is actionable: names the env var and where keys come from", () => { const message = missingApiKeyMessage(); expect(message).toContain("ANTHROPIC_API_KEY"); expect(message).toContain("export ANTHROPIC_API_KEY="); expect(message).toContain("console.anthropic.com"); }); it("never contains secret-shaped material", () => { expect(missingApiKeyMessage()).not.toMatch(/sk-ant-/); }); });