v0.2.0: Git-native architecture (#1)
This commit was merged in pull request #1.
This commit is contained in:
@@ -0,0 +1,322 @@
|
||||
import { CommitBuilder } from "../core/commit-builder.js";
|
||||
import { extractCiBlock, parseCiBlock } from "../core/commit-parser.js";
|
||||
import { CiMetadata } from "../types/commit-meta.js";
|
||||
|
||||
describe("CommitBuilder", () => {
|
||||
describe("buildCiBlock", () => {
|
||||
it("builds minimal ci block", () => {
|
||||
const ci: CiMetadata = { 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 decisions", () => {
|
||||
const ci: CiMetadata = {
|
||||
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: CiMetadata = {
|
||||
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: CiMetadata = {
|
||||
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: CiMetadata = {
|
||||
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: CiMetadata = {
|
||||
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: CiMetadata = { 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 = extractCiBlock(fullMessage)!;
|
||||
const parsed = parseCiBlock(extracted)!;
|
||||
|
||||
expect(parsed.phase).toBe(1);
|
||||
expect(parsed.milestone).toBe("v1.0");
|
||||
expect(parsed.status).toBe("execute");
|
||||
});
|
||||
|
||||
it("round-trips decisions", () => {
|
||||
const ci: CiMetadata = {
|
||||
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 = extractCiBlock(fullMessage)!;
|
||||
const parsed = parseCiBlock(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: CiMetadata = {
|
||||
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 = extractCiBlock(fullMessage)!;
|
||||
const parsed = parseCiBlock(extracted)!;
|
||||
|
||||
expect(parsed.compound!.category).toBe("auth");
|
||||
expect(parsed.compound!.problem).toBe("Token replay attacks");
|
||||
expect(parsed.lessons).toHaveLength(1);
|
||||
});
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
});
|
||||
|
||||
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");
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user