import * as fs from "node:fs"; import * as path from "node:path"; import * as os from "node:os"; import { ArtifactManager, ProjectManifest } from "../core/artifacts.js"; describe("ArtifactManager", () => { let tempDir: string; let manager: ArtifactManager; beforeEach(() => { tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "ciagent-artifact-test-")); manager = new ArtifactManager(tempDir); }); afterEach(() => { fs.rmSync(tempDir, { recursive: true, force: true }); }); describe("ensureStructure", () => { it("creates .ciagent directory structure", () => { manager.ensureStructure(); expect(fs.existsSync(path.join(tempDir, ".ciagent"))).toBe(true); expect(fs.existsSync(path.join(tempDir, ".ciagent", "audit"))).toBe(true); }); it("is idempotent", () => { manager.ensureStructure(); manager.ensureStructure(); expect(fs.existsSync(path.join(tempDir, ".ciagent"))).toBe(true); }); }); describe("isInitialized", () => { it("returns false before project is written", () => { manager.ensureStructure(); expect(manager.isInitialized()).toBe(false); }); it("returns true after project is written", () => { manager.ensureStructure(); manager.writeProject({ name: "Test Project", objective: "Build it", created_at: new Date().toISOString(), phases: [{ id: 1, name: "Phase 1", status: "pending" }], current_phase: 1, status: "initializing", }); expect(manager.isInitialized()).toBe(true); }); }); describe("writeProject / readState / writePhaseArtifact", () => { it("writes and reads project artifacts", () => { manager.ensureStructure(); const manifest: ProjectManifest = { name: "Test Project", objective: "Build a REST API", created_at: new Date().toISOString(), phases: [ { id: 1, name: "Research", status: "pending" }, { id: 2, name: "Plan & Execute", status: "pending" }, ], current_phase: 1, status: "initializing", }; manager.writeProject(manifest); const projectPath = path.join(tempDir, ".ciagent", "PROJECT.md"); expect(fs.existsSync(projectPath)).toBe(true); const content = fs.readFileSync(projectPath, "utf-8"); expect(content).toContain("Test Project"); expect(content).toContain("Build a REST API"); expect(content).toContain("Phase 1: Research"); }); it("writes phase artifacts", () => { manager.ensureStructure(); manager.writePhaseArtifact(1, "PLAN.md", "# My Plan\n\nThis is the plan."); const artifact = manager.readPhaseArtifact(1, "PLAN.md"); expect(artifact).not.toBeNull(); expect(artifact).toContain("My Plan"); }); it("returns null for non-existent artifact", () => { manager.ensureStructure(); const artifact = manager.readPhaseArtifact(99, "NONEXISTENT.md"); expect(artifact).toBeNull(); }); it("writes and reads state", () => { manager.ensureStructure(); manager.writeState({ current_phase: 1, current_stage: "execute", last_agent: "executor", last_action: "Implemented feature X", updated_at: new Date().toISOString(), pipeline_progress: { specify: true, clarify: true, research: true, plan: true, execute: false, verify: false, complete: false }, }); const state = manager.readState(); expect(state).not.toBeNull(); expect(state!.current_phase).toBe(1); expect(state!.current_stage).toBe("execute"); expect(state!.pipeline_progress.specify).toBe(true); }); it("returns null for state when not written", () => { manager.ensureStructure(); const state = manager.readState(); expect(state).toBeNull(); }); }); describe("writeDecisions", () => { it("writes decisions to DECISIONS.md", () => { manager.ensureStructure(); manager.writeDecisions({ decisions: [ { id: "D-001", decision: "Use PostgreSQL", rationale: "ACID compliance", confidence: 0.92, category: "technology_choice", timestamp: new Date().toISOString(), }, ], }); const decisionsPath = path.join(tempDir, ".ciagent", "DECISIONS.md"); expect(fs.existsSync(decisionsPath)).toBe(true); const content = fs.readFileSync(decisionsPath, "utf-8"); expect(content).toContain("D-001"); expect(content).toContain("Use PostgreSQL"); expect(content).toContain("92%"); }); }); });