import { describe, it, expect } from "vitest"; import { buildDiscussPrompt, buildVerifyPrompt, canAdvancePhase, canGoBack, getPhaseLabel, generateCriterionId, type WorkflowState, type VerifyCriterion, } from "./workflow"; // ─── buildDiscussPrompt ─────────────────────────────────────────────────────── describe("buildDiscussPrompt", () => { it("includes the description in the output", () => { const prompt = buildDiscussPrompt("Build a user authentication system"); expect(prompt).toContain("Build a user authentication system"); }); it("references CONTEXT.md", () => { const prompt = buildDiscussPrompt("some project"); expect(prompt).toContain("CONTEXT.md"); }); it("mentions acceptance criteria section", () => { const prompt = buildDiscussPrompt("some project"); expect(prompt.toLowerCase()).toContain("acceptance criteria"); }); it("returns a non-empty string for any description", () => { expect(buildDiscussPrompt("a").length).toBeGreaterThan(0); expect(buildDiscussPrompt("very long description ".repeat(20)).length).toBeGreaterThan(0); }); }); // ─── buildVerifyPrompt ──────────────────────────────────────────────────────── describe("buildVerifyPrompt", () => { it("references VERIFY.md", () => { const prompt = buildVerifyPrompt([]); expect(prompt).toContain("VERIFY.md"); }); it("handles empty criteria list gracefully", () => { const prompt = buildVerifyPrompt([]); expect(prompt.length).toBeGreaterThan(0); expect(prompt).not.toContain("undefined"); }); it("includes all criteria text in the output", () => { const criteria: VerifyCriterion[] = [ { id: "c1", text: "Login must work", status: "pending" }, { id: "c2", text: "Tests must pass", status: "pending" }, ]; const prompt = buildVerifyPrompt(criteria); expect(prompt).toContain("Login must work"); expect(prompt).toContain("Tests must pass"); }); it("numbers criteria starting from 1", () => { const criteria: VerifyCriterion[] = [{ id: "c1", text: "First criterion", status: "pending" }]; const prompt = buildVerifyPrompt(criteria); expect(prompt).toContain("1. First criterion"); }); it("includes all criteria when multiple are provided", () => { const criteria: VerifyCriterion[] = [ { id: "c1", text: "Alpha", status: "pending" }, { id: "c2", text: "Beta", status: "pending" }, { id: "c3", text: "Gamma", status: "pending" }, ]; const prompt = buildVerifyPrompt(criteria); expect(prompt).toContain("1. Alpha"); expect(prompt).toContain("2. Beta"); expect(prompt).toContain("3. Gamma"); }); }); // ─── canAdvancePhase ────────────────────────────────────────────────────────── function makeState(overrides: Partial = {}): WorkflowState { return { version: 1, currentPhase: 1, quickMode: false, discuss: { description: "", contextCaptured: false }, plan: { tasksApproved: false }, execute: { completed: false }, verify: { criteria: [], verificationComplete: false, report: "" }, ...overrides, }; } describe("canAdvancePhase", () => { describe("phase 1 (Discuss)", () => { it("returns false when context not captured and not quick mode", () => { const state = makeState({ currentPhase: 1 }); expect(canAdvancePhase(state)).toBe(false); }); it("returns true when context is captured", () => { const state = makeState({ currentPhase: 1, discuss: { description: "test", contextCaptured: true }, }); expect(canAdvancePhase(state)).toBe(true); }); it("returns true in quick mode even without context captured", () => { const state = makeState({ currentPhase: 1, quickMode: true }); expect(canAdvancePhase(state)).toBe(true); }); }); describe("phase 2 (Plan)", () => { it("returns false when plan not approved", () => { const state = makeState({ currentPhase: 2 }); expect(canAdvancePhase(state)).toBe(false); }); it("returns true when plan is approved", () => { const state = makeState({ currentPhase: 2, plan: { tasksApproved: true } }); expect(canAdvancePhase(state)).toBe(true); }); }); describe("phase 3 (Execute)", () => { it("returns false when execution not completed", () => { const state = makeState({ currentPhase: 3 }); expect(canAdvancePhase(state)).toBe(false); }); it("returns true when execution is completed", () => { const state = makeState({ currentPhase: 3, execute: { completed: true } }); expect(canAdvancePhase(state)).toBe(true); }); }); describe("phase 4 (Verify)", () => { it("returns false when verification not complete", () => { const state = makeState({ currentPhase: 4 }); expect(canAdvancePhase(state)).toBe(false); }); it("returns true when verification is complete", () => { const state = makeState({ currentPhase: 4, verify: { criteria: [], verificationComplete: true, report: "All good" }, }); expect(canAdvancePhase(state)).toBe(true); }); }); }); // ─── canGoBack ──────────────────────────────────────────────────────────────── describe("canGoBack", () => { it("returns false for phase 1", () => { expect(canGoBack(1)).toBe(false); }); it("returns true for phase 2", () => { expect(canGoBack(2)).toBe(true); }); it("returns true for phase 3", () => { expect(canGoBack(3)).toBe(true); }); it("returns true for phase 4", () => { expect(canGoBack(4)).toBe(true); }); }); // ─── getPhaseLabel ──────────────────────────────────────────────────────────── describe("getPhaseLabel", () => { it("returns Discuss for phase 1", () => { expect(getPhaseLabel(1)).toBe("Discuss"); }); it("returns Plan for phase 2", () => { expect(getPhaseLabel(2)).toBe("Plan"); }); it("returns Execute for phase 3", () => { expect(getPhaseLabel(3)).toBe("Execute"); }); it("returns Verify for phase 4", () => { expect(getPhaseLabel(4)).toBe("Verify"); }); }); // ─── generateCriterionId ───────────────────────────────────────────────────── describe("generateCriterionId", () => { it("returns a non-empty string", () => { expect(generateCriterionId().length).toBeGreaterThan(0); }); it("returns unique IDs on successive calls", () => { const ids = new Set(Array.from({ length: 20 }, () => generateCriterionId())); expect(ids.size).toBe(20); }); it("starts with the expected prefix", () => { expect(generateCriterionId()).toMatch(/^criterion-/); }); });