fix(P01): rename ci-files.test.ts → ciagent-files.test.ts
---ci--- project: ci phase: 1 milestone: v0.7 status: execute requirements: covered: [RENAME-01, RENAME-02, RENAME-03, RENAME-04, RENAME-05, RENAME-06, RENAME-07, RENAME-08, RENAME-09, RENAME-10, RENAME-11, RENAME-12] ---/ci--- All 12 RENAME requirements covered. 31 test suites, 370 tests passing.
This commit is contained in:
@@ -0,0 +1,560 @@
|
||||
import * as os from "node:os";
|
||||
import * as path from "node:path";
|
||||
import * as fs from "node:fs";
|
||||
import { CIAgentFiles, ProjectMd, RoadmapMd, RequirementsMd, ArchitectureMd } from "../core/ciagent-files.js";
|
||||
|
||||
function createTempDir(): string {
|
||||
return fs.mkdtempSync(path.join(os.tmpdir(), "ciagent-files-test-"));
|
||||
}
|
||||
|
||||
function cleanup(dir: string): void {
|
||||
fs.rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
describe("CIAgentFiles", () => {
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = createTempDir();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(dir);
|
||||
});
|
||||
|
||||
describe("ensureCIAgentDir", () => {
|
||||
it("creates .ciagent directory", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
expect(fs.existsSync(path.join(dir, ".ciagent"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("isInitialized", () => {
|
||||
it("returns false when no config.json exists", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
expect(ciFiles.isInitialized()).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true when config.json exists", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), "{}");
|
||||
expect(ciFiles.isInitialized()).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("projectSlug", () => {
|
||||
it("defaults to empty string", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
expect(ciFiles.getProjectSlug()).toBe("");
|
||||
});
|
||||
|
||||
it("uses provided project slug", () => {
|
||||
const ciFiles = new CIAgentFiles(dir, "task-api");
|
||||
expect(ciFiles.getProjectSlug()).toBe("task-api");
|
||||
});
|
||||
|
||||
it("setProjectSlug updates slug", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.setProjectSlug("auth-svc");
|
||||
expect(ciFiles.getProjectSlug()).toBe("auth-svc");
|
||||
});
|
||||
});
|
||||
|
||||
describe("multi-project support", () => {
|
||||
it("isMultiProject returns false when not initialized", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
expect(ciFiles.isMultiProject()).toBe(false);
|
||||
});
|
||||
|
||||
it("isMultiProject returns false for single-project config", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "default", name: "Default" }],
|
||||
active_project: "default",
|
||||
}));
|
||||
expect(ciFiles.isMultiProject()).toBe(true);
|
||||
});
|
||||
|
||||
it("isMultiProject returns false for config without projects array", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({}));
|
||||
expect(ciFiles.isMultiProject()).toBe(false);
|
||||
});
|
||||
|
||||
it("addProject adds a project to config", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [],
|
||||
active_project: "",
|
||||
}));
|
||||
|
||||
ciFiles.addProject("task-api", "Task API", true);
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(path.join(dir, ".ciagent", "config.json"), "utf-8"));
|
||||
expect(config.projects).toHaveLength(1);
|
||||
expect(config.projects[0].slug).toBe("task-api");
|
||||
expect(config.active_project).toBe("task-api");
|
||||
});
|
||||
|
||||
it("addProject does not duplicate existing project", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "task-api", name: "Task API" }],
|
||||
active_project: "task-api",
|
||||
}));
|
||||
|
||||
ciFiles.addProject("task-api", "Task API V2");
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(path.join(dir, ".ciagent", "config.json"), "utf-8"));
|
||||
expect(config.projects).toHaveLength(1);
|
||||
});
|
||||
|
||||
it("addProject creates project subdirectory", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [],
|
||||
active_project: "",
|
||||
}));
|
||||
|
||||
ciFiles.addProject("task-api", "Task API", true);
|
||||
|
||||
expect(fs.existsSync(path.join(dir, ".ciagent", "task-api"))).toBe(true);
|
||||
});
|
||||
|
||||
it("getActiveProject returns from config", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "task-api", name: "Task API", default: true }],
|
||||
active_project: "task-api",
|
||||
}));
|
||||
|
||||
expect(ciFiles.getActiveProject()).toBe("task-api");
|
||||
});
|
||||
|
||||
it("setActiveProject updates config", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [
|
||||
{ slug: "task-api", name: "Task API" },
|
||||
{ slug: "auth-svc", name: "Auth Service" },
|
||||
],
|
||||
active_project: "task-api",
|
||||
}));
|
||||
|
||||
ciFiles.setActiveProject("auth-svc");
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(path.join(dir, ".ciagent", "config.json"), "utf-8"));
|
||||
expect(config.active_project).toBe("auth-svc");
|
||||
});
|
||||
|
||||
it("listProjects returns projects from config", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [
|
||||
{ slug: "task-api", name: "Task API", default: true },
|
||||
{ slug: "auth-svc", name: "Auth Service" },
|
||||
],
|
||||
active_project: "task-api",
|
||||
}));
|
||||
|
||||
const projects = ciFiles.listProjects();
|
||||
expect(projects).toHaveLength(2);
|
||||
expect(projects[0].slug).toBe("task-api");
|
||||
expect(projects[1].slug).toBe("auth-svc");
|
||||
});
|
||||
});
|
||||
|
||||
describe("needsMigration", () => {
|
||||
it("returns false when not initialized", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
expect(ciFiles.needsMigration()).toBe(false);
|
||||
});
|
||||
|
||||
it("returns false when already multi-project", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "default", name: "Default" }],
|
||||
}));
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "PROJECT.md"), "# Test");
|
||||
expect(ciFiles.needsMigration()).toBe(false);
|
||||
});
|
||||
|
||||
it("returns true when flat files exist without subdirs or multi-project config", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({}));
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "PROJECT.md"), "# Test");
|
||||
expect(ciFiles.needsMigration()).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false when flat files exist but subdirs also exist", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({}));
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "PROJECT.md"), "# Test");
|
||||
fs.mkdirSync(path.join(dir, ".ciagent", "task-api"));
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "task-api", "PROJECT.md"), "# Task API");
|
||||
expect(ciFiles.needsMigration()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("migrateFlatToProject", () => {
|
||||
it("moves flat files to project subdirectory", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({}));
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "PROJECT.md"), "# Test Project");
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "ARCHITECTURE.md"), "# Architecture");
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "ROADMAP.md"), "# Roadmap");
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "REQUIREMENTS.md"), "# Requirements");
|
||||
|
||||
ciFiles.migrateFlatToProject("my-app");
|
||||
|
||||
expect(fs.existsSync(path.join(dir, ".ciagent", "my-app", "PROJECT.md"))).toBe(true);
|
||||
expect(fs.existsSync(path.join(dir, ".ciagent", "my-app", "ARCHITECTURE.md"))).toBe(true);
|
||||
expect(fs.existsSync(path.join(dir, ".ciagent", "my-app", "ROADMAP.md"))).toBe(true);
|
||||
expect(fs.existsSync(path.join(dir, ".ciagent", "my-app", "REQUIREMENTS.md"))).toBe(true);
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(path.join(dir, ".ciagent", "config.json"), "utf-8"));
|
||||
expect(config.projects).toHaveLength(1);
|
||||
expect(config.active_project).toBe("my-app");
|
||||
});
|
||||
|
||||
it("does not migrate when not needed", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "existing", name: "Existing" }],
|
||||
}));
|
||||
|
||||
ciFiles.migrateFlatToProject("new-proj");
|
||||
|
||||
const config = JSON.parse(fs.readFileSync(path.join(dir, ".ciagent", "config.json"), "utf-8"));
|
||||
expect(config.projects).toHaveLength(1);
|
||||
expect(config.projects[0].slug).toBe("existing");
|
||||
});
|
||||
});
|
||||
|
||||
describe("isNfrMilestone", () => {
|
||||
it("returns true when no roadmap exists", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
expect(ciFiles.isNfrMilestone()).toBe(true);
|
||||
});
|
||||
|
||||
it("returns true when phases are all NFR types", () => {
|
||||
const ciFiles = new CIAgentFiles(dir, "nfr-proj");
|
||||
ciFiles.ensureProjectDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "nfr-proj", name: "NFR Project", default: true }],
|
||||
active_project: "nfr-proj",
|
||||
}));
|
||||
const roadmap: RoadmapMd = {
|
||||
overview: "NFR-only",
|
||||
phases: [
|
||||
{ number: 1, name: "test-coverage", description: "Add tests", status: "in_progress", dependsOn: [], requirements: [], successCriteria: [] },
|
||||
{ number: 2, name: "perf-tune", description: "Tune perf", status: "not_started", dependsOn: [], requirements: [], successCriteria: [] },
|
||||
],
|
||||
};
|
||||
ciFiles.writeRoadmapMd(roadmap);
|
||||
expect(ciFiles.isNfrMilestone()).toBe(true);
|
||||
});
|
||||
|
||||
it("returns false when phases include feature work", () => {
|
||||
const ciFiles = new CIAgentFiles(dir, "feat-proj");
|
||||
ciFiles.ensureProjectDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "feat-proj", name: "Feature Project", default: true }],
|
||||
active_project: "feat-proj",
|
||||
}));
|
||||
const roadmap: RoadmapMd = {
|
||||
overview: "mixed",
|
||||
phases: [
|
||||
{ number: 1, name: "authentication", description: "Auth feature", status: "in_progress", dependsOn: [], requirements: [], successCriteria: [] },
|
||||
{ number: 2, name: "test-coverage", description: "Add tests", status: "not_started", dependsOn: [], requirements: [], successCriteria: [] },
|
||||
],
|
||||
};
|
||||
ciFiles.writeRoadmapMd(roadmap);
|
||||
expect(ciFiles.isNfrMilestone()).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("getMilestoneType", () => {
|
||||
it("returns nfr when no roadmap exists", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
expect(ciFiles.getMilestoneType()).toBe("nfr");
|
||||
});
|
||||
|
||||
it("returns nfr when phases are all NFR types", () => {
|
||||
const ciFiles = new CIAgentFiles(dir, "nfr-proj2");
|
||||
ciFiles.ensureProjectDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "nfr-proj2", name: "NFR Project 2", default: true }],
|
||||
active_project: "nfr-proj2",
|
||||
}));
|
||||
const roadmap: RoadmapMd = {
|
||||
overview: "NFR-only",
|
||||
phases: [
|
||||
{ number: 1, name: "fix-bug", description: "Fix bug", status: "in_progress", dependsOn: [], requirements: [], successCriteria: [] },
|
||||
],
|
||||
};
|
||||
ciFiles.writeRoadmapMd(roadmap);
|
||||
expect(ciFiles.getMilestoneType()).toBe("nfr");
|
||||
});
|
||||
|
||||
it("returns feature when phases include feat work", () => {
|
||||
const ciFiles = new CIAgentFiles(dir, "feat-proj2");
|
||||
ciFiles.ensureProjectDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "feat-proj2", name: "Feature Project 2", default: true }],
|
||||
active_project: "feat-proj2",
|
||||
}));
|
||||
const roadmap: RoadmapMd = {
|
||||
overview: "feature",
|
||||
phases: [
|
||||
{ number: 1, name: "auth-flow", description: "Auth feature", status: "in_progress", dependsOn: [], requirements: [], successCriteria: [] },
|
||||
],
|
||||
};
|
||||
ciFiles.writeRoadmapMd(roadmap);
|
||||
expect(ciFiles.getMilestoneType()).toBe("feature");
|
||||
});
|
||||
|
||||
it("returns schema-breaking when phases include refactor/rewrite/migrate", () => {
|
||||
const ciFiles = new CIAgentFiles(dir, "schema-proj");
|
||||
ciFiles.ensureProjectDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "schema-proj", name: "Schema Project", default: true }],
|
||||
active_project: "schema-proj",
|
||||
}));
|
||||
const roadmap: RoadmapMd = {
|
||||
overview: "schema-breaking",
|
||||
phases: [
|
||||
{ number: 1, name: "refactor-core", description: "Refactor core", status: "in_progress", dependsOn: [], requirements: [], successCriteria: [] },
|
||||
],
|
||||
};
|
||||
ciFiles.writeRoadmapMd(roadmap);
|
||||
expect(ciFiles.getMilestoneType()).toBe("schema-breaking");
|
||||
});
|
||||
});
|
||||
|
||||
describe("multi-project file paths", () => {
|
||||
it("writes PROJECT.md to project subdirectory when slug is set", () => {
|
||||
const ciFiles = new CIAgentFiles(dir, "my-app");
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({
|
||||
projects: [{ slug: "my-app", name: "My App", default: true }],
|
||||
active_project: "my-app",
|
||||
}));
|
||||
|
||||
const project: ProjectMd = {
|
||||
name: "My App",
|
||||
coreValue: "Build something cool",
|
||||
requirements: { validated: [], active: [], outOfScope: [] },
|
||||
constraints: [],
|
||||
context: "Test context",
|
||||
keyDecisions: [],
|
||||
};
|
||||
|
||||
ciFiles.writeProjectMd(project, "initial");
|
||||
|
||||
expect(fs.existsSync(path.join(dir, ".ciagent", "my-app", "PROJECT.md"))).toBe(true);
|
||||
});
|
||||
|
||||
it("writes PROJECT.md to .ci root when no slug is set", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.ensureCIDir();
|
||||
fs.writeFileSync(path.join(dir, ".ciagent", "config.json"), JSON.stringify({}));
|
||||
|
||||
const project: ProjectMd = {
|
||||
name: "Default App",
|
||||
coreValue: "Build something",
|
||||
requirements: { validated: [], active: [], outOfScope: [] },
|
||||
constraints: [],
|
||||
context: "Test context",
|
||||
keyDecisions: [],
|
||||
};
|
||||
|
||||
ciFiles.writeProjectMd(project, "initial");
|
||||
|
||||
expect(fs.existsSync(path.join(dir, ".ciagent", "PROJECT.md"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("PROJECT.md", () => {
|
||||
const project: ProjectMd = {
|
||||
name: "Task API",
|
||||
coreValue: "Build a REST API for task management",
|
||||
requirements: {
|
||||
validated: ["User auth works"],
|
||||
active: ["Real-time notifications", "CRUD operations"],
|
||||
outOfScope: ["Admin dashboard"],
|
||||
},
|
||||
constraints: ["Must use Node.js", "Production-ready"],
|
||||
context: "This is a task management API",
|
||||
keyDecisions: [
|
||||
{ decision: "Use PostgreSQL", rationale: "ACID compliance", outcome: "✓ Good" },
|
||||
],
|
||||
};
|
||||
|
||||
it("writes and reads PROJECT.md", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.writeProjectMd(project, "initial creation");
|
||||
|
||||
const read = ciFiles.readProjectMd();
|
||||
expect(read).not.toBeNull();
|
||||
expect(read!.name).toBe("Task API");
|
||||
expect(read!.requirements.active).toContain("Real-time notifications");
|
||||
expect(read!.constraints).toContain("Must use Node.js");
|
||||
});
|
||||
|
||||
it("overwrites PROJECT.md on update", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.writeProjectMd(project, "initial");
|
||||
|
||||
const updated = { ...project, coreValue: "Updated description" };
|
||||
ciFiles.writeProjectMd(updated, "phase 1 complete");
|
||||
|
||||
const read = ciFiles.readProjectMd();
|
||||
expect(read!.coreValue).toBe("Updated description");
|
||||
});
|
||||
});
|
||||
|
||||
describe("ROADMAP.md", () => {
|
||||
const roadmap: RoadmapMd = {
|
||||
overview: "4-phase delivery",
|
||||
phases: [
|
||||
{
|
||||
number: 1,
|
||||
name: "auth",
|
||||
description: "Auth system",
|
||||
status: "in_progress",
|
||||
dependsOn: [],
|
||||
requirements: ["AUTH-01"],
|
||||
successCriteria: ["Users can sign up"],
|
||||
},
|
||||
{
|
||||
number: 2,
|
||||
name: "tasks",
|
||||
description: "Task CRUD",
|
||||
status: "not_started",
|
||||
dependsOn: [1],
|
||||
requirements: ["TASK-01"],
|
||||
successCriteria: ["Users can create tasks"],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
it("writes and reads ROADMAP.md", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.writeRoadmapMd(roadmap);
|
||||
|
||||
const read = ciFiles.readRoadmapMd();
|
||||
expect(read).not.toBeNull();
|
||||
expect(read!.overview).toBe("4-phase delivery");
|
||||
});
|
||||
});
|
||||
|
||||
describe("REQUIREMENTS.md", () => {
|
||||
const requirements: RequirementsMd = {
|
||||
v1: [
|
||||
{
|
||||
category: "Auth",
|
||||
items: [
|
||||
{ id: "AUTH-01", description: "User can sign up" },
|
||||
{ id: "AUTH-02", description: "User can log in" },
|
||||
],
|
||||
},
|
||||
],
|
||||
v2: [
|
||||
{
|
||||
category: "Notifications",
|
||||
items: [{ id: "NOTIF-01", description: "Push notifications" }],
|
||||
},
|
||||
],
|
||||
outOfScope: [{ feature: "Admin dashboard", reason: "Not core value" }],
|
||||
traceability: [
|
||||
{ requirement: "AUTH-01", phase: 1, status: "pending" },
|
||||
{ requirement: "AUTH-02", phase: 1, status: "pending" },
|
||||
],
|
||||
};
|
||||
|
||||
it("writes and reads REQUIREMENTS.md", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.writeRequirementsMd(requirements);
|
||||
|
||||
const read = ciFiles.readRequirementsMd();
|
||||
expect(read).not.toBeNull();
|
||||
});
|
||||
|
||||
it("updates requirement status", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.writeRequirementsMd(requirements);
|
||||
|
||||
ciFiles.updateRequirementStatus("AUTH-01", "complete");
|
||||
|
||||
const read = ciFiles.readRequirementsMd();
|
||||
expect(read).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("ARCHITECTURE.md", () => {
|
||||
const arch: ArchitectureMd = {
|
||||
overview: "Monolith with modules",
|
||||
components: [
|
||||
{
|
||||
name: "API",
|
||||
description: "REST API server",
|
||||
boundaries: "HTTP layer only",
|
||||
dependsOn: ["Auth", "Tasks"],
|
||||
},
|
||||
],
|
||||
dataFlow: "Client -> API -> DB",
|
||||
buildOrder: ["Auth", "Tasks", "API"],
|
||||
};
|
||||
|
||||
it("writes and reads ARCHITECTURE.md", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
ciFiles.writeArchitectureMd(arch);
|
||||
|
||||
const read = ciFiles.readArchitectureMd();
|
||||
expect(read).not.toBeNull();
|
||||
expect(read!.overview).toBe("Monolith with modules");
|
||||
});
|
||||
});
|
||||
|
||||
describe("updatePhaseStatus", () => {
|
||||
it("updates phase status in roadmap", () => {
|
||||
const ciFiles = new CIAgentFiles(dir);
|
||||
const roadmap: RoadmapMd = {
|
||||
overview: "test",
|
||||
phases: [
|
||||
{
|
||||
number: 1,
|
||||
name: "auth",
|
||||
description: "Auth",
|
||||
status: "not_started",
|
||||
dependsOn: [],
|
||||
requirements: [],
|
||||
successCriteria: [],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
ciFiles.writeRoadmapMd(roadmap);
|
||||
ciFiles.updatePhaseStatus(1, "complete");
|
||||
|
||||
const read = ciFiles.readRoadmapMd();
|
||||
expect(read).not.toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user