refactor: migrate role handling from tiers to roles module
- Removed the deprecated tiers.ts file and migrated all related functionality to roles/index.js. - Updated tests and tools to reflect the new role structure, replacing references to "dev", "qa", and "architect" with "developer", "tester", and "architect". - Adjusted workflow configurations and state management to accommodate the new role naming conventions. - Enhanced project registration and health check tools to support dynamic role handling. - Updated task creation, update, and completion processes to align with the new role definitions. - Improved documentation and comments to clarify role responsibilities and usage.
This commit is contained in:
254
lib/projects.test.ts
Normal file
254
lib/projects.test.ts
Normal file
@@ -0,0 +1,254 @@
|
||||
/**
|
||||
* Tests for projects.ts — worker state, migration, and accessors.
|
||||
* Run with: npx tsx --test lib/projects.test.ts
|
||||
*/
|
||||
import { describe, it } from "node:test";
|
||||
import assert from "node:assert";
|
||||
import fs from "node:fs/promises";
|
||||
import path from "node:path";
|
||||
import os from "node:os";
|
||||
import { readProjects, getWorker, emptyWorkerState, writeProjects, type ProjectsData } from "./projects.js";
|
||||
|
||||
describe("readProjects migration", () => {
|
||||
it("should migrate old format (dev/qa/architect fields) to workers map", async () => {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "devclaw-proj-"));
|
||||
const projDir = path.join(tmpDir, "projects");
|
||||
await fs.mkdir(projDir, { recursive: true });
|
||||
|
||||
// Old format: hardcoded dev/qa/architect fields
|
||||
const oldFormat = {
|
||||
projects: {
|
||||
"group-1": {
|
||||
name: "test-project",
|
||||
repo: "~/git/test",
|
||||
groupName: "Test",
|
||||
deployUrl: "",
|
||||
baseBranch: "main",
|
||||
deployBranch: "main",
|
||||
dev: { active: true, issueId: "42", startTime: null, level: "mid", sessions: { mid: "key-1" } },
|
||||
qa: { active: false, issueId: null, startTime: null, level: null, sessions: {} },
|
||||
architect: { active: false, issueId: null, startTime: null, level: null, sessions: {} },
|
||||
},
|
||||
},
|
||||
};
|
||||
await fs.writeFile(path.join(projDir, "projects.json"), JSON.stringify(oldFormat), "utf-8");
|
||||
|
||||
const data = await readProjects(tmpDir);
|
||||
const project = data.projects["group-1"];
|
||||
|
||||
// Should have workers map with migrated role keys
|
||||
assert.ok(project.workers, "should have workers map");
|
||||
assert.ok(project.workers.developer, "should have developer worker (migrated from dev)");
|
||||
assert.ok(project.workers.tester, "should have tester worker (migrated from qa)");
|
||||
assert.ok(project.workers.architect, "should have architect worker");
|
||||
|
||||
// Developer worker should be active with migrated level
|
||||
assert.strictEqual(project.workers.developer.active, true);
|
||||
assert.strictEqual(project.workers.developer.issueId, "42");
|
||||
assert.strictEqual(project.workers.developer.level, "medior");
|
||||
|
||||
// Old fields should not exist on the object
|
||||
assert.strictEqual((project as any).dev, undefined);
|
||||
assert.strictEqual((project as any).qa, undefined);
|
||||
assert.strictEqual((project as any).architect, undefined);
|
||||
|
||||
await fs.rm(tmpDir, { recursive: true });
|
||||
});
|
||||
|
||||
it("should migrate old level names in old format", async () => {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "devclaw-proj-"));
|
||||
const projDir = path.join(tmpDir, "projects");
|
||||
await fs.mkdir(projDir, { recursive: true });
|
||||
|
||||
const oldFormat = {
|
||||
projects: {
|
||||
"group-1": {
|
||||
name: "legacy",
|
||||
repo: "~/git/legacy",
|
||||
groupName: "Legacy",
|
||||
deployUrl: "",
|
||||
baseBranch: "main",
|
||||
deployBranch: "main",
|
||||
dev: { active: false, issueId: null, startTime: null, level: "medior", sessions: { medior: "key-1" } },
|
||||
qa: { active: false, issueId: null, startTime: null, level: "reviewer", sessions: { reviewer: "key-2" } },
|
||||
architect: { active: false, issueId: null, startTime: null, level: "opus", sessions: { opus: "key-3" } },
|
||||
},
|
||||
},
|
||||
};
|
||||
await fs.writeFile(path.join(projDir, "projects.json"), JSON.stringify(oldFormat), "utf-8");
|
||||
|
||||
const data = await readProjects(tmpDir);
|
||||
const project = data.projects["group-1"];
|
||||
|
||||
// Level names should be migrated (dev→developer, qa→tester, medior→medior, reviewer→medior)
|
||||
assert.strictEqual(project.workers.developer.level, "medior");
|
||||
assert.strictEqual(project.workers.tester.level, "medior");
|
||||
assert.strictEqual(project.workers.architect.level, "senior");
|
||||
|
||||
// Session keys should be migrated
|
||||
assert.strictEqual(project.workers.developer.sessions.medior, "key-1");
|
||||
assert.strictEqual(project.workers.tester.sessions.medior, "key-2");
|
||||
assert.strictEqual(project.workers.architect.sessions.senior, "key-3");
|
||||
|
||||
await fs.rm(tmpDir, { recursive: true });
|
||||
});
|
||||
|
||||
it("should read new format (workers map) correctly", async () => {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "devclaw-proj-"));
|
||||
const projDir = path.join(tmpDir, "projects");
|
||||
await fs.mkdir(projDir, { recursive: true });
|
||||
|
||||
const newFormat = {
|
||||
projects: {
|
||||
"group-1": {
|
||||
name: "modern",
|
||||
repo: "~/git/modern",
|
||||
groupName: "Modern",
|
||||
deployUrl: "",
|
||||
baseBranch: "main",
|
||||
deployBranch: "main",
|
||||
workers: {
|
||||
developer: { active: true, issueId: "10", startTime: null, level: "senior", sessions: { senior: "key-s" } },
|
||||
tester: { active: false, issueId: null, startTime: null, level: null, sessions: {} },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
await fs.writeFile(path.join(projDir, "projects.json"), JSON.stringify(newFormat), "utf-8");
|
||||
|
||||
const data = await readProjects(tmpDir);
|
||||
const project = data.projects["group-1"];
|
||||
|
||||
assert.ok(project.workers.developer);
|
||||
assert.strictEqual(project.workers.developer.active, true);
|
||||
assert.strictEqual(project.workers.developer.level, "senior");
|
||||
assert.ok(project.workers.tester);
|
||||
|
||||
await fs.rm(tmpDir, { recursive: true });
|
||||
});
|
||||
|
||||
it("should migrate old worker keys in new format", async () => {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "devclaw-proj-"));
|
||||
const projDir = path.join(tmpDir, "projects");
|
||||
await fs.mkdir(projDir, { recursive: true });
|
||||
|
||||
// Workers map but with old role keys
|
||||
const mixedFormat = {
|
||||
projects: {
|
||||
"group-1": {
|
||||
name: "mixed",
|
||||
repo: "~/git/mixed",
|
||||
groupName: "Mixed",
|
||||
deployUrl: "",
|
||||
baseBranch: "main",
|
||||
deployBranch: "main",
|
||||
workers: {
|
||||
dev: { active: true, issueId: "10", startTime: null, level: "mid", sessions: { mid: "key-m" } },
|
||||
qa: { active: false, issueId: null, startTime: null, level: null, sessions: {} },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
await fs.writeFile(path.join(projDir, "projects.json"), JSON.stringify(mixedFormat), "utf-8");
|
||||
|
||||
const data = await readProjects(tmpDir);
|
||||
const project = data.projects["group-1"];
|
||||
|
||||
// Old keys should be migrated
|
||||
assert.ok(project.workers.developer, "dev should be migrated to developer");
|
||||
assert.ok(project.workers.tester, "qa should be migrated to tester");
|
||||
assert.strictEqual(project.workers.developer.level, "medior");
|
||||
assert.strictEqual(project.workers.developer.sessions.medior, "key-m");
|
||||
|
||||
await fs.rm(tmpDir, { recursive: true });
|
||||
});
|
||||
});
|
||||
|
||||
describe("getWorker", () => {
|
||||
it("should return worker from workers map", async () => {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "devclaw-proj-"));
|
||||
const projDir = path.join(tmpDir, "projects");
|
||||
await fs.mkdir(projDir, { recursive: true });
|
||||
|
||||
const data: ProjectsData = {
|
||||
projects: {
|
||||
"g1": {
|
||||
name: "test",
|
||||
repo: "~/git/test",
|
||||
groupName: "Test",
|
||||
deployUrl: "",
|
||||
baseBranch: "main",
|
||||
deployBranch: "main",
|
||||
workers: {
|
||||
developer: { active: true, issueId: "5", startTime: null, level: "medior", sessions: {} },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const worker = getWorker(data.projects["g1"], "developer");
|
||||
assert.strictEqual(worker.active, true);
|
||||
assert.strictEqual(worker.issueId, "5");
|
||||
|
||||
await fs.rm(tmpDir, { recursive: true });
|
||||
});
|
||||
|
||||
it("should return empty worker for unknown role", async () => {
|
||||
const data: ProjectsData = {
|
||||
projects: {
|
||||
"g1": {
|
||||
name: "test",
|
||||
repo: "~/git/test",
|
||||
groupName: "Test",
|
||||
deployUrl: "",
|
||||
baseBranch: "main",
|
||||
deployBranch: "main",
|
||||
workers: {},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const worker = getWorker(data.projects["g1"], "nonexistent");
|
||||
assert.strictEqual(worker.active, false);
|
||||
assert.strictEqual(worker.issueId, null);
|
||||
});
|
||||
});
|
||||
|
||||
describe("writeProjects round-trip", () => {
|
||||
it("should preserve workers map through write/read cycle", async () => {
|
||||
const tmpDir = await fs.mkdtemp(path.join(os.tmpdir(), "devclaw-proj-"));
|
||||
const projDir = path.join(tmpDir, "projects");
|
||||
await fs.mkdir(projDir, { recursive: true });
|
||||
|
||||
const data: ProjectsData = {
|
||||
projects: {
|
||||
"g1": {
|
||||
name: "roundtrip",
|
||||
repo: "~/git/rt",
|
||||
groupName: "RT",
|
||||
deployUrl: "",
|
||||
baseBranch: "main",
|
||||
deployBranch: "main",
|
||||
workers: {
|
||||
developer: emptyWorkerState(["junior", "medior", "senior"]),
|
||||
tester: emptyWorkerState(["junior", "medior", "senior"]),
|
||||
architect: emptyWorkerState(["junior", "senior"]),
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
await writeProjects(tmpDir, data);
|
||||
const loaded = await readProjects(tmpDir);
|
||||
const project = loaded.projects["g1"];
|
||||
|
||||
assert.ok(project.workers.developer);
|
||||
assert.ok(project.workers.tester);
|
||||
assert.ok(project.workers.architect);
|
||||
assert.strictEqual(project.workers.developer.sessions.junior, null);
|
||||
assert.strictEqual(project.workers.developer.sessions.medior, null);
|
||||
assert.strictEqual(project.workers.developer.sessions.senior, null);
|
||||
|
||||
await fs.rm(tmpDir, { recursive: true });
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user