4a58aa1657
- Type renames: CIConfig → CIAgentConfig, DEFAULT_CI_CONFIG → DEFAULT_CIAGENT_CONFIG - Type renames: CiMetadata → CIAgentMetadata, ParsedCiCommit → ParsedCIAgentCommit - Function renames: initCI → initCIAgent, isCIInitialized → isCIAgentInitialized - Function renames: extractCiBlock → extractCIAgentBlock, parseCiBlock → parseCIAgentBlock - Class renames: CiFiles → CIAgentFiles - Import paths: ci-files.js → ciagent-files.js - Directory paths: .ci/ → .ciagent/ across all source and test files - Check names: ".ci directory exists" → ".ciagent directory exists" - Check names: "CI config valid" → "CIAgent config valid" - Temp dir names: ci-*-test- → ciagent-*-test- - CLI examples: "ci init" → "ciagent init" - Fix deepMerge infinite recursion bug in config.ts - ---ci---/---/ci--- block markers preserved unchanged - All 31 test suites, 370 tests passing ---ci--- phase: 1 milestone: v0.5 plan: 07 task: 07-01-01 status: execute ---/ci---
142 lines
4.6 KiB
TypeScript
142 lines
4.6 KiB
TypeScript
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%");
|
|
});
|
|
});
|
|
}); |