119 lines
4.0 KiB
TypeScript
119 lines
4.0 KiB
TypeScript
import { describe, it, expect, beforeEach, afterEach } from "bun:test";
|
||
import { createRunner } from "./agent-mock.ts";
|
||
import {
|
||
setupTempDir,
|
||
cleanupTempDir,
|
||
getTempDir,
|
||
createFreshProject,
|
||
changeFileExists,
|
||
} from "./fixtures.ts";
|
||
import { assertAllTasksDone } from "./validators.ts";
|
||
import { scanChanges, scanArchives } from "../../src/core/scanner.ts";
|
||
import { assembleDiscussPrompt } from "../../src/core/assembler.ts";
|
||
import { validateChangeName } from "../../src/cli.ts";
|
||
|
||
describe("e2e: 全流程", () => {
|
||
let runner: ReturnType<typeof createRunner>;
|
||
|
||
beforeEach(async () => {
|
||
await setupTempDir();
|
||
runner = createRunner();
|
||
});
|
||
|
||
afterEach(async () => {
|
||
await cleanupTempDir();
|
||
});
|
||
|
||
it("完整四阶段流程(discuss → plan → build → archive)", async () => {
|
||
const config = await createFreshProject();
|
||
|
||
const discussPrompt = assembleDiscussPrompt(config);
|
||
expect(typeof discussPrompt).toBe("string");
|
||
expect(discussPrompt.length).toBeGreaterThan(0);
|
||
|
||
await runner.runPlan(getTempDir(), "full-flow", "design", config);
|
||
await runner.runPlan(getTempDir(), "full-flow", "task", config);
|
||
|
||
let changes = await scanChanges(getTempDir(), config);
|
||
expect(changes).toHaveLength(1);
|
||
expect(changes[0]!.name).toBe("full-flow");
|
||
expect(changes[0]!.planCompleted).toBe(true);
|
||
expect(changes[0]!.buildUnlocked).toBe(true);
|
||
|
||
const buildResult = await runner.runBuild(getTempDir(), "full-flow", config);
|
||
expect(buildResult.files.length).toBeGreaterThan(0);
|
||
|
||
changes = await scanChanges(getTempDir(), config);
|
||
expect(changes).toHaveLength(1);
|
||
assertAllTasksDone(changes[0]!);
|
||
|
||
await runner.runArchive(getTempDir(), "full-flow", config);
|
||
|
||
expect(changeFileExists("full-flow", "task.md")).toBe(false);
|
||
|
||
const archives = await scanArchives(getTempDir());
|
||
expect(archives.length).toBeGreaterThanOrEqual(1);
|
||
|
||
changes = await scanChanges(getTempDir(), config);
|
||
expect(changes.find((c) => c.name === "full-flow")).toBeUndefined();
|
||
});
|
||
|
||
it("多变更并行互不干扰", async () => {
|
||
const config = await createFreshProject();
|
||
|
||
await runner.runPlan(getTempDir(), "变更A", "design", config);
|
||
await runner.runPlan(getTempDir(), "变更A", "task", config);
|
||
|
||
await runner.runPlan(getTempDir(), "变更B", "design", config);
|
||
await runner.runPlan(getTempDir(), "变更B", "task", config);
|
||
|
||
let changes = await scanChanges(getTempDir(), config);
|
||
expect(changes).toHaveLength(2);
|
||
|
||
const changeA = changes.find((c) => c.name === "变更A");
|
||
const changeB = changes.find((c) => c.name === "变更B");
|
||
expect(changeA).toBeDefined();
|
||
expect(changeB).toBeDefined();
|
||
expect(changeA!.planCompleted).toBe(true);
|
||
expect(changeB!.planCompleted).toBe(true);
|
||
|
||
await runner.runBuild(getTempDir(), "变更A", config);
|
||
await runner.runBuild(getTempDir(), "变更B", config);
|
||
|
||
changes = await scanChanges(getTempDir(), config);
|
||
expect(changes).toHaveLength(2);
|
||
|
||
assertAllTasksDone(changes.find((c) => c.name === "变更A")!);
|
||
assertAllTasksDone(changes.find((c) => c.name === "变更B")!);
|
||
|
||
expect(changes[0]!.taskProgress?.completed).toBe(changes[0]!.taskProgress?.total);
|
||
expect(changes[1]!.taskProgress?.completed).toBe(changes[1]!.taskProgress?.total);
|
||
});
|
||
|
||
describe("变更名非法字符拒绝", () => {
|
||
it("空字符串抛出错误", () => {
|
||
expect(() => validateChangeName("")).toThrow();
|
||
});
|
||
|
||
it("包含 / 抛出错误", () => {
|
||
expect(() => validateChangeName("变更/A")).toThrow();
|
||
});
|
||
|
||
it("包含 . 抛出错误", () => {
|
||
expect(() => validateChangeName("变更.A")).toThrow();
|
||
});
|
||
|
||
it("合法中文名称不抛出错误", () => {
|
||
expect(() => validateChangeName("变更名")).not.toThrow();
|
||
});
|
||
|
||
it("合法英文名称不抛出错误", () => {
|
||
expect(() => validateChangeName("my-change")).not.toThrow();
|
||
});
|
||
|
||
it("合法短横线名称不抛出错误", () => {
|
||
expect(() => validateChangeName("abc-def-xyz")).not.toThrow();
|
||
});
|
||
});
|
||
});
|