import { CommitBuilder } from "../core/commit-builder.js"; import { extractCIAgentBlock, parseCIAgentBlock } from "../core/commit-parser.js"; import { CIAgentMetadata } from "../types/commit-meta.js"; describe("CommitBuilder", () => { describe("buildCiBlock", () => { it("builds minimal ci block", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "execute" }; const block = CommitBuilder.buildCiBlock(ci); expect(block).toContain("phase: 1"); expect(block).toContain("milestone: v1.0"); expect(block).toContain("status: execute"); }); it("builds ci block with project", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "execute", project: "task-api" }; const block = CommitBuilder.buildCiBlock(ci); expect(block).toContain("project: task-api"); }); it("builds ci block without project when not set", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "execute" }; const block = CommitBuilder.buildCiBlock(ci); expect(block).not.toContain("project:"); }); it("builds ci block with decisions", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "execute", decisions: [ { id: "D-001", decision: "Use PostgreSQL", rationale: "ACID compliance", confidence: 0.9, alternatives: ["MongoDB", "SQLite"], }, ], }; const block = CommitBuilder.buildCiBlock(ci); expect(block).toContain("decisions:"); expect(block).toContain("id: D-001"); expect(block).toContain("decision: Use PostgreSQL"); expect(block).toContain("alternatives: [MongoDB, SQLite]"); }); it("builds ci block with lessons", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "complete", lessons: ["Always use async bcrypt", "Check JWT expiry first"], }; const block = CommitBuilder.buildCiBlock(ci); expect(block).toContain("lessons:"); expect(block).toContain(" - Always use async bcrypt"); expect(block).toContain(" - Check JWT expiry first"); }); it("builds ci block with compound", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "complete", compound: { category: "auth", problem: "Token replay", solution: "Refresh rotation", }, }; const block = CommitBuilder.buildCiBlock(ci); expect(block).toContain("compound:"); expect(block).toContain("category: auth"); expect(block).toContain("problem: Token replay"); expect(block).toContain("solution: Refresh rotation"); }); it("builds ci block with escalations", () => { const ci: CIAgentMetadata = { phase: 3, milestone: "v1.0", status: "execute", escalations: [ { id: "E-001", type: "irreversible_action", description: "Deploy to staging", resolution: "pending", }, ], }; const block = CommitBuilder.buildCiBlock(ci); expect(block).toContain("escalations:"); expect(block).toContain("id: E-001"); expect(block).toContain("type: irreversible_action"); }); it("builds ci block with requirements", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "complete", requirements: { covered: ["AUTH-01", "AUTH-02"], partial: ["AUTH-03"], }, }; const block = CommitBuilder.buildCiBlock(ci); expect(block).toContain("requirements:"); expect(block).toContain("covered: [AUTH-01, AUTH-02]"); expect(block).toContain("partial: [AUTH-03]"); }); }); describe("round-trip: build then parse", () => { it("round-trips a simple ci block", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "execute" }; const block = CommitBuilder.buildCiBlock(ci); const fullMessage = `feat(P01): test\n\n---ci---\n${block}\n---/ci---\n\nBody text`; const extracted = extractCIAgentBlock(fullMessage)!; const parsed = parseCIAgentBlock(extracted)!; expect(parsed.phase).toBe(1); expect(parsed.milestone).toBe("v1.0"); expect(parsed.status).toBe("execute"); }); it("round-trips decisions", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "execute", decisions: [ { id: "D-001", decision: "Use PostgreSQL", rationale: "ACID compliance", confidence: 0.9, alternatives: ["MongoDB"], }, ], }; const block = CommitBuilder.buildCiBlock(ci); const fullMessage = `feat(P01): test\n\n---ci---\n${block}\n---/ci---`; const extracted = extractCIAgentBlock(fullMessage)!; const parsed = parseCIAgentBlock(extracted)!; expect(parsed.decisions).toHaveLength(1); expect(parsed.decisions![0].id).toBe("D-001"); expect(parsed.decisions![0].decision).toBe("Use PostgreSQL"); expect(parsed.decisions![0].confidence).toBe(0.9); expect(parsed.decisions![0].alternatives).toEqual(["MongoDB"]); }); it("round-trips compound with lessons", () => { const ci: CIAgentMetadata = { phase: 2, milestone: "v1.0", status: "complete", compound: { category: "auth", problem: "Token replay attacks", solution: "Refresh rotation with family IDs", }, lessons: ["Token rotation is not optional"], }; const block = CommitBuilder.buildCiBlock(ci); const fullMessage = `compound(P02): test\n\n---ci---\n${block}\n---/ci---`; const extracted = extractCIAgentBlock(fullMessage)!; const parsed = parseCIAgentBlock(extracted)!; expect(parsed.compound!.category).toBe("auth"); expect(parsed.compound!.problem).toBe("Token replay attacks"); expect(parsed.lessons).toHaveLength(1); }); it("round-trips project field", () => { const ci: CIAgentMetadata = { phase: 1, milestone: "v1.0", status: "execute", project: "task-api" }; const block = CommitBuilder.buildCiBlock(ci); const fullMessage = `feat(task-api/P01): test\n\n---ci---\n${block}\n---/ci---`; const extracted = extractCIAgentBlock(fullMessage)!; const parsed = parseCIAgentBlock(extracted)!; expect(parsed.project).toBe("task-api"); }); }); describe("buildInitCommit", () => { it("builds an init commit message", () => { const msg = CommitBuilder.buildInitCommit({ projectName: "task-api", phaseCount: 4, milestone: "v1.0", specification: "Build a REST API for task management", requirements: ["AUTH-01", "TASK-01"], constraints: ["Node.js"], outOfScope: ["Admin dashboard"], }); expect(msg).toContain("docs(init):"); expect(msg).toContain("---ci---"); expect(msg).toContain("phase: 0"); expect(msg).toContain("milestone: v1.0"); expect(msg).toContain("Build a REST API for task management"); expect(msg).toContain("AUTH-01"); }); it("builds an init commit message with project", () => { const msg = CommitBuilder.buildInitCommit({ projectName: "task-api", phaseCount: 4, milestone: "v1.0", project: "task-api", specification: "Build a REST API", requirements: ["AUTH-01"], }); expect(msg).toContain("project: task-api"); }); }); describe("buildTaskCommit", () => { it("builds a task commit message", () => { const msg = CommitBuilder.buildTaskCommit({ type: "feat", phase: 1, milestone: "v1.0", plan: "01-01", task: "01-01-02", subject: "create user registration endpoint", status: "execute", decisions: [ { id: "D-003", decision: "Use bcrypt with 12 rounds", rationale: "Industry standard", confidence: 0.88, alternatives: ["argon2"], }, ], requirements: { covered: ["AUTH-01"], partial: [] }, }); expect(msg).toContain("feat(P01-01-02):"); expect(msg).toContain("plan: 01-01"); expect(msg).toContain("task: 01-01-02"); expect(msg).toContain("D-003"); expect(msg).toContain("AUTH-01"); }); it("builds a task commit with project prefix", () => { const msg = CommitBuilder.buildTaskCommit({ type: "feat", phase: 1, milestone: "v1.0", project: "task-api", plan: "01-01", task: "01-01-02", subject: "registration endpoint", status: "execute", }); expect(msg).toContain("feat(task-api/P01-01-02):"); expect(msg).toContain("project: task-api"); }); }); describe("buildPhaseCompletionCommit", () => { it("builds a phase completion commit", () => { const msg = CommitBuilder.buildPhaseCompletionCommit({ phase: 1, milestone: "v1.0", phaseName: "authentication", tasksCompleted: 4, tasksTotal: 4, taskNames: ["scaffold", "registration", "login", "reset"], requirements: { covered: ["AUTH-01", "AUTH-02", "AUTH-03", "AUTH-04"], partial: [] }, lessons: ["Always use async bcrypt"], }); expect(msg).toContain("docs(P01): complete authentication phase"); expect(msg).toContain("status: complete"); expect(msg).toContain("Tasks completed: 4/4"); expect(msg).toContain("Always use async bcrypt"); }); }); describe("buildCompoundCommit", () => { it("builds a compound commit", () => { const msg = CommitBuilder.buildCompoundCommit({ phase: 1, milestone: "v1.0", category: "auth", problem: "Token replay allows persistent access", solution: "Refresh token rotation with family IDs", lessons: ["Rotation is not optional"], }); expect(msg).toContain("compound(P01):"); expect(msg).toContain("category: auth"); expect(msg).toContain("problem: Token replay"); expect(msg).toContain("solution: Refresh token rotation"); }); }); describe("buildDecisionCommit", () => { it("builds a decision-only commit", () => { const msg = CommitBuilder.buildDecisionCommit({ phase: 1, milestone: "v1.0", subject: "use PostgreSQL over MongoDB", decisions: [ { id: "D-001", decision: "PostgreSQL", rationale: "ACID", confidence: 0.92, alternatives: ["MongoDB"], }, ], }); expect(msg).toContain("decision(P01): use PostgreSQL over MongoDB"); expect(msg).toContain("D-001"); }); }); describe("buildEscalationCommit", () => { it("builds an escalation commit", () => { const msg = CommitBuilder.buildEscalationCommit({ phase: 3, milestone: "v1.0", subject: "deploy to staging requires approval", escalations: [ { id: "E-001", type: "irreversible_action", description: "Deploy to staging", resolution: "pending", }, ], }); expect(msg).toContain("escalation(P03): deploy to staging requires approval"); expect(msg).toContain("E-001"); }); }); describe("buildVerifyCommit", () => { it("builds a verify commit", () => { const msg = CommitBuilder.buildVerifyCommit({ phase: 1, milestone: "v1.0", subject: "all must-haves pass automated tests", requirements: { covered: ["AUTH-01", "AUTH-02"], partial: [] }, }); expect(msg).toContain("verify(P01): all must-haves pass automated tests"); expect(msg).toContain("AUTH-01"); }); }); });