import { test, expect } from "vitest"; import type { KernelCommandDefinition } from "@warpgogol/werkstatt-shared/kernel"; import { executeKernelCommand, parseKernelArgv, resolveCommandFlags } from "../index.ts"; import { createDefaultIO } from "@warpgogol/werkstatt-shared/kernel/workspace-io"; /* Unit tests for flags covering its documented public contract and regression-sensitive edge cases. Do not cover the kernel.flags.lint static scan here — see site-kernel-checks tests. Fixtures stay inline and minimal so each case states its own setup. RFC-0260: initial test suite, written before the rfc.* migration lands. */ // @ai-invariant fixtures must not couple to internals beyond the public surface under test function fixtureCommand(flags: KernelCommandDefinition["flags"]): KernelCommandDefinition { return { name: "fixture.command", modulePath: "test", description: "fixture", scope: "workspace", flags, execute() { return { exitCode: 0, summary: "fixture.command: ok" }; }, }; } test("resolveCommandFlags: declared boolean flag never consumes the next token", () => { const command = fixtureCommand({ mini: { kind: "boolean", description: "fixture boolean" }, }); const resolved = resolveCommandFlags(["--mini", "positional"], command); expect(resolved.diagnostics.length).toBe(1); expect(resolved.diagnostics[0]?.ruleId).toBe("KERNEL-ARG-01"); expect(resolved.flags.mini).toBe(true); }); test("resolveCommandFlags: unknown flag -> KERNEL-FLAG-01 listing valid flags", () => { const command = fixtureCommand({ title: { kind: "string", description: "fixture string" }, }); const resolved = resolveCommandFlags(["--bogus-flag"], command); expect(resolved.diagnostics.length).toBe(1); expect(resolved.diagnostics[0]?.ruleId).toBe("KERNEL-FLAG-01"); expect(resolved.diagnostics[0]?.severity).toBe("error"); expect(resolved.diagnostics[0]?.message ?? "").toMatch(/Unknown flag "--bogus-flag"/); expect(resolved.diagnostics[0]?.message ?? "").toMatch(/Valid flags: /); expect(resolved.diagnostics[0]?.message ?? "").toMatch(/title/); }); test("resolveCommandFlags: string flag at argv end -> KERNEL-FLAG-02", () => { const command = fixtureCommand({ title: { kind: "string", description: "fixture string" }, }); const resolved = resolveCommandFlags(["--title"], command); expect(resolved.diagnostics.length).toBe(1); expect(resolved.diagnostics[0]?.ruleId).toBe("KERNEL-FLAG-02"); }); test("resolveCommandFlags: required flag absent -> KERNEL-FLAG-03", () => { const command = fixtureCommand({ title: { kind: "string", required: true, description: "fixture required string" }, }); const resolved = resolveCommandFlags([], command); expect(resolved.diagnostics.length).toBe(1); expect(resolved.diagnostics[0]?.ruleId).toBe("KERNEL-FLAG-03"); expect(resolved.diagnostics[0]?.message ?? "").toMatch(/Required flag "--title"/); }); test("resolveCommandFlags: --flag=value inline form", () => { const command = fixtureCommand({ title: { kind: "string", description: "fixture string" }, }); const resolved = resolveCommandFlags(["--title=Hello World"], command); expect(resolved.diagnostics.length).toBe(0); expect(resolved.flags.title).toBe("Hello World"); }); test("resolveCommandFlags: tokens after -- stay positional", () => { const command = fixtureCommand({ title: { kind: "string", description: "fixture string" }, }); const resolved = resolveCommandFlags(["--", "--title", "not-a-flag"], command); expect(resolved.diagnostics.length).toBe(2); expect(resolved.diagnostics[0]?.ruleId).toBe("KERNEL-ARG-01"); expect(resolved.diagnostics[1]?.ruleId).toBe("KERNEL-ARG-01"); expect(resolved.flags.title).toBe(undefined); }); test("resolveCommandFlags: universal flags are merged into every schema-carrying command", () => { const command = fixtureCommand({}); const resolved = resolveCommandFlags(["--json", "--site", "demo"], command); expect(resolved.diagnostics.length).toBe(0); expect(resolved.flags.json).toBe(true); expect(resolved.flags.site).toBe("demo"); }); test("resolveCommandFlags: missing flag with a default is filled in", () => { const command = fixtureCommand({ kind: { kind: "string", default: "architecture", description: "fixture string with default" }, }); const resolved = resolveCommandFlags([], command); expect(resolved.diagnostics.length).toBe(0); expect(resolved.flags.kind).toBe("architecture"); }); test("parseKernelArgv: schema-less commands parse exactly as before (golden fixture)", () => { const parsed = parseKernelArgv([ "--root", "src", "--dry-run", "alpha", "--tag=beta", "--", "tail", ]); expect(parsed.diagnostics.length).toBe(2); expect(parsed.diagnostics[0]?.ruleId).toBe("KERNEL-ARG-01"); expect(parsed.diagnostics[1]?.ruleId).toBe("KERNEL-ARG-01"); expect(parsed.flags.root).toBe("src"); expect(parsed.flags["dry-run"]).toBe(true); expect(parsed.flags.tag).toBe("beta"); }); test("executeKernelCommand: unknown option key fails with an explicit error naming argv (regression: commit 8b3e62ab)", async () => { try { await executeKernelCommand({ io: createDefaultIO().io, workspaceRoot: process.cwd(), commandName: "noop", args: ["--phase=05-audit"], } as never); expect.fail("should have thrown"); } catch (error) { expect(error).toBeInstanceOf(Error); expect((error as Error).message).toMatch(/unknown key/i); expect((error as Error).message).toMatch(/"args"/); expect((error as Error).message).toMatch(/did you mean "argv"/i); } });