@ronkovic/aad 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +312 -0
- package/bin/aad.js +2 -0
- package/package.json +78 -0
- package/src/__tests__/e2e/pipeline-e2e.test.ts +279 -0
- package/src/__tests__/e2e/resume-e2e.test.ts +200 -0
- package/src/__tests__/integration/cli-smoke.test.ts +175 -0
- package/src/__tests__/integration/pipeline.test.ts +346 -0
- package/src/bun-imports.d.ts +14 -0
- package/src/main.ts +52 -0
- package/src/modules/claude-provider/__tests__/claude-cli.adapter.test.ts +277 -0
- package/src/modules/claude-provider/__tests__/claude-sdk-real-env.test.ts +127 -0
- package/src/modules/claude-provider/__tests__/claude-sdk.adapter.test.ts +347 -0
- package/src/modules/claude-provider/__tests__/effort-strategy.test.ts +212 -0
- package/src/modules/claude-provider/__tests__/provider-registry.test.ts +251 -0
- package/src/modules/claude-provider/__tests__/retry.test.ts +201 -0
- package/src/modules/claude-provider/claude-cli.adapter.ts +156 -0
- package/src/modules/claude-provider/claude-provider.port.ts +35 -0
- package/src/modules/claude-provider/claude-sdk.adapter.ts +217 -0
- package/src/modules/claude-provider/effort-strategy.ts +94 -0
- package/src/modules/claude-provider/index.ts +32 -0
- package/src/modules/claude-provider/provider-registry.ts +92 -0
- package/src/modules/claude-provider/retry.ts +81 -0
- package/src/modules/cli/__tests__/app.test.ts +160 -0
- package/src/modules/cli/__tests__/cleanup.test.ts +111 -0
- package/src/modules/cli/__tests__/commands.test.ts +186 -0
- package/src/modules/cli/__tests__/output.test.ts +329 -0
- package/src/modules/cli/__tests__/resume.test.ts +324 -0
- package/src/modules/cli/__tests__/run.test.ts +168 -0
- package/src/modules/cli/__tests__/shutdown.test.ts +168 -0
- package/src/modules/cli/__tests__/status.test.ts +144 -0
- package/src/modules/cli/app.ts +241 -0
- package/src/modules/cli/commands/cleanup.ts +120 -0
- package/src/modules/cli/commands/resume.ts +156 -0
- package/src/modules/cli/commands/run.ts +322 -0
- package/src/modules/cli/commands/status.ts +101 -0
- package/src/modules/cli/index.ts +29 -0
- package/src/modules/cli/output.ts +256 -0
- package/src/modules/cli/shutdown.ts +122 -0
- package/src/modules/dashboard/__tests__/api-routes.test.ts +204 -0
- package/src/modules/dashboard/__tests__/file-watcher.test.ts +34 -0
- package/src/modules/dashboard/__tests__/server.test.ts +120 -0
- package/src/modules/dashboard/__tests__/sse-broadcaster.test.ts +163 -0
- package/src/modules/dashboard/__tests__/sse-routes.test.ts +58 -0
- package/src/modules/dashboard/__tests__/state-aggregator.test.ts +330 -0
- package/src/modules/dashboard/index.ts +8 -0
- package/src/modules/dashboard/routes/api.ts +84 -0
- package/src/modules/dashboard/routes/sse.ts +37 -0
- package/src/modules/dashboard/server.ts +111 -0
- package/src/modules/dashboard/services/file-watcher.ts +36 -0
- package/src/modules/dashboard/services/sse-broadcaster.ts +81 -0
- package/src/modules/dashboard/services/state-aggregator.ts +132 -0
- package/src/modules/dashboard/ui/dashboard.html +405 -0
- package/src/modules/git-workspace/__tests__/branch-manager.test.ts +335 -0
- package/src/modules/git-workspace/__tests__/git-exec.test.ts +91 -0
- package/src/modules/git-workspace/__tests__/memory-sync.test.ts +273 -0
- package/src/modules/git-workspace/__tests__/merge-service.test.ts +286 -0
- package/src/modules/git-workspace/__tests__/settings-merge.test.ts +163 -0
- package/src/modules/git-workspace/__tests__/worktree-manager.test.ts +247 -0
- package/src/modules/git-workspace/branch-manager.ts +191 -0
- package/src/modules/git-workspace/git-exec.ts +124 -0
- package/src/modules/git-workspace/index.ts +17 -0
- package/src/modules/git-workspace/memory-sync.ts +89 -0
- package/src/modules/git-workspace/merge-service.ts +156 -0
- package/src/modules/git-workspace/settings-merge.ts +95 -0
- package/src/modules/git-workspace/worktree-manager.ts +199 -0
- package/src/modules/logging/__tests__/log-store.test.ts +242 -0
- package/src/modules/logging/__tests__/logger.test.ts +81 -0
- package/src/modules/logging/__tests__/sse-transport.test.ts +93 -0
- package/src/modules/logging/index.ts +7 -0
- package/src/modules/logging/log-store.ts +80 -0
- package/src/modules/logging/logger.ts +55 -0
- package/src/modules/logging/transports/sse-transport.ts +28 -0
- package/src/modules/multi-repo/__tests__/multi-repo-planner.test.ts +93 -0
- package/src/modules/multi-repo/__tests__/repo-context.test.ts +79 -0
- package/src/modules/multi-repo/index.ts +12 -0
- package/src/modules/multi-repo/multi-repo-planner.ts +112 -0
- package/src/modules/multi-repo/repo-context.ts +71 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-81991/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-82469/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85301/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-85759/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-86184/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-88026/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89475/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/progress.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/completed/task-getall-2.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-1.json +13 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-getall-1.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/queue/pending/task-status-change.json +10 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/workers/worker-1.json +5 -0
- package/src/modules/persistence/__tests__/.tmp-stores-test-89924/workers/worker-2.json +5 -0
- package/src/modules/persistence/__tests__/file-lock.test.ts +141 -0
- package/src/modules/persistence/__tests__/index.test.ts +38 -0
- package/src/modules/persistence/__tests__/stores.test.ts +594 -0
- package/src/modules/persistence/file-lock.ts +158 -0
- package/src/modules/persistence/fs-run-store.ts +73 -0
- package/src/modules/persistence/fs-task-store.ts +152 -0
- package/src/modules/persistence/fs-worker-store.ts +116 -0
- package/src/modules/persistence/in-memory-stores.ts +98 -0
- package/src/modules/persistence/index.ts +60 -0
- package/src/modules/persistence/stores.port.ts +60 -0
- package/src/modules/planning/__tests__/file-conflict-validator.test.ts +256 -0
- package/src/modules/planning/__tests__/planning-service.test.ts +366 -0
- package/src/modules/planning/__tests__/project-detection.test.ts +707 -0
- package/src/modules/planning/file-conflict-validator.ts +135 -0
- package/src/modules/planning/index.ts +40 -0
- package/src/modules/planning/planning.service.ts +262 -0
- package/src/modules/planning/project-detection.ts +525 -0
- package/src/modules/plugin/__tests__/plugin-loader.test.ts +83 -0
- package/src/modules/plugin/__tests__/plugin-manager.test.ts +187 -0
- package/src/modules/plugin/index.ts +3 -0
- package/src/modules/plugin/plugin-loader.ts +46 -0
- package/src/modules/plugin/plugin-manager.ts +90 -0
- package/src/modules/plugin/plugin.types.ts +37 -0
- package/src/modules/process-manager/__tests__/process-manager.test.ts +210 -0
- package/src/modules/process-manager/__tests__/worker.test.ts +89 -0
- package/src/modules/process-manager/index.ts +5 -0
- package/src/modules/process-manager/process-manager.ts +193 -0
- package/src/modules/process-manager/worker.ts +106 -0
- package/src/modules/task-execution/__tests__/default-spawner.test.ts +154 -0
- package/src/modules/task-execution/__tests__/executor.test.ts +760 -0
- package/src/modules/task-execution/__tests__/implementer-green.test.ts +286 -0
- package/src/modules/task-execution/__tests__/merge-phase.test.ts +368 -0
- package/src/modules/task-execution/__tests__/reviewer.test.ts +302 -0
- package/src/modules/task-execution/__tests__/tester-red.test.ts +281 -0
- package/src/modules/task-execution/__tests__/tester-verify.test.ts +313 -0
- package/src/modules/task-execution/executor.ts +303 -0
- package/src/modules/task-execution/index.ts +45 -0
- package/src/modules/task-execution/phases/default-spawner.ts +49 -0
- package/src/modules/task-execution/phases/implementer-green.ts +100 -0
- package/src/modules/task-execution/phases/merge.ts +122 -0
- package/src/modules/task-execution/phases/reviewer.ts +160 -0
- package/src/modules/task-execution/phases/tester-red.ts +100 -0
- package/src/modules/task-execution/phases/tester-verify.ts +120 -0
- package/src/modules/task-queue/__tests__/dependency-resolver.test.ts +456 -0
- package/src/modules/task-queue/__tests__/dispatcher.test.ts +824 -0
- package/src/modules/task-queue/__tests__/task-plan.test.ts +122 -0
- package/src/modules/task-queue/__tests__/task.test.ts +130 -0
- package/src/modules/task-queue/dependency-resolver.ts +171 -0
- package/src/modules/task-queue/dispatcher.ts +372 -0
- package/src/modules/task-queue/index.ts +16 -0
- package/src/modules/task-queue/task-plan.ts +40 -0
- package/src/modules/task-queue/task.ts +67 -0
- package/src/shared/__tests__/config.test.ts +204 -0
- package/src/shared/__tests__/errors.test.ts +285 -0
- package/src/shared/__tests__/events.test.ts +496 -0
- package/src/shared/__tests__/types.test.ts +360 -0
- package/src/shared/config.ts +133 -0
- package/src/shared/errors.ts +128 -0
- package/src/shared/events.ts +171 -0
- package/src/shared/types.ts +143 -0
- package/tsconfig.json +30 -0
|
@@ -0,0 +1,760 @@
|
|
|
1
|
+
import { describe, test, expect } from "bun:test";
|
|
2
|
+
import { executeTddPipeline } from "../executor";
|
|
3
|
+
import type { Task, WorkspaceInfo } from "@aad/shared/types";
|
|
4
|
+
import type { ClaudeProvider, ClaudeResponse } from "@aad/claude-provider";
|
|
5
|
+
import type { MergeService, MergeResult } from "@aad/git-workspace";
|
|
6
|
+
import type { Config } from "@aad/shared/config";
|
|
7
|
+
import type { EventBus } from "@aad/shared/events";
|
|
8
|
+
import { createTaskId, createRunId } from "@aad/shared/types";
|
|
9
|
+
|
|
10
|
+
describe("executeTddPipeline", () => {
|
|
11
|
+
test("executes full TDD pipeline successfully", async () => {
|
|
12
|
+
const task: Task = {
|
|
13
|
+
taskId: createTaskId("task-001"),
|
|
14
|
+
title: "Add feature",
|
|
15
|
+
description: "New feature",
|
|
16
|
+
filesToModify: ["src/feature.ts"],
|
|
17
|
+
dependsOn: [],
|
|
18
|
+
priority: 1,
|
|
19
|
+
status: "running",
|
|
20
|
+
retryCount: 0,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const workspace: WorkspaceInfo = {
|
|
24
|
+
path: "/workspace",
|
|
25
|
+
language: "typescript",
|
|
26
|
+
packageManager: "bun",
|
|
27
|
+
framework: "hono",
|
|
28
|
+
testFramework: "bun-test",
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const taskBranch = "task/001-add-feature";
|
|
32
|
+
const parentBranch = "main";
|
|
33
|
+
const parentWorktree = "/parent";
|
|
34
|
+
const runId = createRunId("run-001");
|
|
35
|
+
|
|
36
|
+
const config: Config = {
|
|
37
|
+
workers: { num: 2, max: 8 },
|
|
38
|
+
models: {},
|
|
39
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
40
|
+
retry: { maxRetries: 2 },
|
|
41
|
+
debug: false,
|
|
42
|
+
adaptiveEffort: false,
|
|
43
|
+
teams: { splitter: false, reviewer: false },
|
|
44
|
+
memorySync: true,
|
|
45
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const mockProvider: ClaudeProvider = {
|
|
49
|
+
async call(): Promise<ClaudeResponse> {
|
|
50
|
+
return {
|
|
51
|
+
result: "OK",
|
|
52
|
+
exitCode: 0,
|
|
53
|
+
model: "claude-sonnet-4-5",
|
|
54
|
+
effortLevel: "medium",
|
|
55
|
+
duration: 1000,
|
|
56
|
+
};
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
const mockMergeService = {
|
|
61
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
message: "Merged successfully",
|
|
65
|
+
};
|
|
66
|
+
},
|
|
67
|
+
} as unknown as MergeService;
|
|
68
|
+
|
|
69
|
+
const events: string[] = [];
|
|
70
|
+
const mockEventBus = {
|
|
71
|
+
on() {},
|
|
72
|
+
off() {},
|
|
73
|
+
emit(event: any) {
|
|
74
|
+
events.push(event.type);
|
|
75
|
+
},
|
|
76
|
+
} as unknown as EventBus;
|
|
77
|
+
|
|
78
|
+
const mockSpawner = {
|
|
79
|
+
async spawn() {
|
|
80
|
+
return { exitCode: 0, stdout: "All tests passed", stderr: "" };
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const result = await executeTddPipeline(
|
|
85
|
+
task,
|
|
86
|
+
workspace,
|
|
87
|
+
taskBranch,
|
|
88
|
+
parentBranch,
|
|
89
|
+
parentWorktree,
|
|
90
|
+
runId,
|
|
91
|
+
config,
|
|
92
|
+
mockProvider,
|
|
93
|
+
mockMergeService,
|
|
94
|
+
mockEventBus,
|
|
95
|
+
mockSpawner
|
|
96
|
+
);
|
|
97
|
+
|
|
98
|
+
expect(result.status).toBe("completed");
|
|
99
|
+
expect(result.taskId).toBe(task.taskId);
|
|
100
|
+
expect(result.duration).toBeGreaterThanOrEqual(0);
|
|
101
|
+
|
|
102
|
+
// Verify all phases executed
|
|
103
|
+
expect(events).toContain("execution:phase:started");
|
|
104
|
+
expect(events).toContain("execution:phase:completed");
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
test("handles tester-red phase failure", async () => {
|
|
108
|
+
const task: Task = {
|
|
109
|
+
taskId: createTaskId("task-002"),
|
|
110
|
+
title: "Failing task",
|
|
111
|
+
description: "Will fail in Red phase",
|
|
112
|
+
filesToModify: [],
|
|
113
|
+
dependsOn: [],
|
|
114
|
+
priority: 1,
|
|
115
|
+
status: "running",
|
|
116
|
+
retryCount: 0,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const workspace: WorkspaceInfo = {
|
|
120
|
+
path: "/workspace",
|
|
121
|
+
language: "typescript",
|
|
122
|
+
packageManager: "bun",
|
|
123
|
+
framework: "hono",
|
|
124
|
+
testFramework: "bun-test",
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const taskBranch = "task/002-failing";
|
|
128
|
+
const parentBranch = "main";
|
|
129
|
+
const parentWorktree = "/parent";
|
|
130
|
+
const runId = createRunId("run-002");
|
|
131
|
+
|
|
132
|
+
const config: Config = {
|
|
133
|
+
workers: { num: 2, max: 8 },
|
|
134
|
+
models: {},
|
|
135
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
136
|
+
retry: { maxRetries: 2 },
|
|
137
|
+
debug: false,
|
|
138
|
+
adaptiveEffort: false,
|
|
139
|
+
teams: { splitter: false, reviewer: false },
|
|
140
|
+
memorySync: true,
|
|
141
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
let callCount = 0;
|
|
145
|
+
const mockProvider: ClaudeProvider = {
|
|
146
|
+
async call(): Promise<ClaudeResponse> {
|
|
147
|
+
callCount++;
|
|
148
|
+
// Fail on first call (Red phase)
|
|
149
|
+
return {
|
|
150
|
+
result: "Red phase failed",
|
|
151
|
+
exitCode: 1,
|
|
152
|
+
model: "claude-sonnet-4-5",
|
|
153
|
+
effortLevel: "medium",
|
|
154
|
+
duration: 500,
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
const mockMergeService = {
|
|
160
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
161
|
+
throw new Error("Should not reach merge phase");
|
|
162
|
+
},
|
|
163
|
+
} as unknown as MergeService;
|
|
164
|
+
|
|
165
|
+
const events: string[] = [];
|
|
166
|
+
const mockEventBus = {
|
|
167
|
+
on() {},
|
|
168
|
+
off() {},
|
|
169
|
+
emit(event: any) {
|
|
170
|
+
events.push(event.type);
|
|
171
|
+
},
|
|
172
|
+
} as unknown as EventBus;
|
|
173
|
+
|
|
174
|
+
const result = await executeTddPipeline(
|
|
175
|
+
task,
|
|
176
|
+
workspace,
|
|
177
|
+
taskBranch,
|
|
178
|
+
parentBranch,
|
|
179
|
+
parentWorktree,
|
|
180
|
+
runId,
|
|
181
|
+
config,
|
|
182
|
+
mockProvider,
|
|
183
|
+
mockMergeService,
|
|
184
|
+
mockEventBus
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
expect(result.status).toBe("failed");
|
|
188
|
+
expect(result.error).toContain("Red phase failed");
|
|
189
|
+
expect(callCount).toBe(1); // Only Red phase called
|
|
190
|
+
expect(events).toContain("execution:phase:failed");
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("handles test verification failure", async () => {
|
|
194
|
+
const task: Task = {
|
|
195
|
+
taskId: createTaskId("task-003"),
|
|
196
|
+
title: "Test verification fails",
|
|
197
|
+
description: "Tests fail after implementation",
|
|
198
|
+
filesToModify: [],
|
|
199
|
+
dependsOn: [],
|
|
200
|
+
priority: 1,
|
|
201
|
+
status: "running",
|
|
202
|
+
retryCount: 0,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
const workspace: WorkspaceInfo = {
|
|
206
|
+
path: "/workspace",
|
|
207
|
+
language: "typescript",
|
|
208
|
+
packageManager: "bun",
|
|
209
|
+
framework: "hono",
|
|
210
|
+
testFramework: "bun-test",
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const taskBranch = "task/003-test-fail";
|
|
214
|
+
const parentBranch = "main";
|
|
215
|
+
const parentWorktree = "/parent";
|
|
216
|
+
const runId = createRunId("run-003");
|
|
217
|
+
|
|
218
|
+
const config: Config = {
|
|
219
|
+
workers: { num: 2, max: 8 },
|
|
220
|
+
models: {},
|
|
221
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
222
|
+
retry: { maxRetries: 2 },
|
|
223
|
+
debug: false,
|
|
224
|
+
adaptiveEffort: false,
|
|
225
|
+
teams: { splitter: false, reviewer: false },
|
|
226
|
+
memorySync: true,
|
|
227
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
const mockProvider: ClaudeProvider = {
|
|
231
|
+
async call(): Promise<ClaudeResponse> {
|
|
232
|
+
return {
|
|
233
|
+
result: "OK",
|
|
234
|
+
exitCode: 0,
|
|
235
|
+
model: "claude-sonnet-4-5",
|
|
236
|
+
effortLevel: "medium",
|
|
237
|
+
duration: 1000,
|
|
238
|
+
};
|
|
239
|
+
},
|
|
240
|
+
};
|
|
241
|
+
|
|
242
|
+
const mockMergeService = {
|
|
243
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
244
|
+
throw new Error("Should not reach merge phase");
|
|
245
|
+
},
|
|
246
|
+
} as unknown as MergeService;
|
|
247
|
+
|
|
248
|
+
const mockEventBus = {
|
|
249
|
+
on() {},
|
|
250
|
+
off() {},
|
|
251
|
+
emit() {},
|
|
252
|
+
} as unknown as EventBus;
|
|
253
|
+
|
|
254
|
+
const mockSpawner = {
|
|
255
|
+
async spawn() {
|
|
256
|
+
// Simulate test failure in verify phase
|
|
257
|
+
return {
|
|
258
|
+
exitCode: 1,
|
|
259
|
+
stdout: "Tests failed",
|
|
260
|
+
stderr: "AssertionError",
|
|
261
|
+
};
|
|
262
|
+
},
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
const result = await executeTddPipeline(
|
|
266
|
+
task,
|
|
267
|
+
workspace,
|
|
268
|
+
taskBranch,
|
|
269
|
+
parentBranch,
|
|
270
|
+
parentWorktree,
|
|
271
|
+
runId,
|
|
272
|
+
config,
|
|
273
|
+
mockProvider,
|
|
274
|
+
mockMergeService,
|
|
275
|
+
mockEventBus,
|
|
276
|
+
mockSpawner
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
expect(result.status).toBe("failed");
|
|
280
|
+
expect(result.error).toContain("Verify phase failed");
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("continues execution when review phase fails (non-fatal)", async () => {
|
|
284
|
+
const task: Task = {
|
|
285
|
+
taskId: createTaskId("task-004"),
|
|
286
|
+
title: "Review fails",
|
|
287
|
+
description: "Review will fail but execution continues",
|
|
288
|
+
filesToModify: [],
|
|
289
|
+
dependsOn: [],
|
|
290
|
+
priority: 1,
|
|
291
|
+
status: "running",
|
|
292
|
+
retryCount: 0,
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
const workspace: WorkspaceInfo = {
|
|
296
|
+
path: "/workspace",
|
|
297
|
+
language: "typescript",
|
|
298
|
+
packageManager: "bun",
|
|
299
|
+
framework: "hono",
|
|
300
|
+
testFramework: "bun-test",
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const taskBranch = "task/004-review-fail";
|
|
304
|
+
const parentBranch = "main";
|
|
305
|
+
const parentWorktree = "/parent";
|
|
306
|
+
const runId = createRunId("run-004");
|
|
307
|
+
|
|
308
|
+
const config: Config = {
|
|
309
|
+
workers: { num: 2, max: 8 },
|
|
310
|
+
models: {},
|
|
311
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
312
|
+
retry: { maxRetries: 2 },
|
|
313
|
+
debug: false,
|
|
314
|
+
adaptiveEffort: false,
|
|
315
|
+
teams: { splitter: false, reviewer: false },
|
|
316
|
+
memorySync: true,
|
|
317
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
let phaseCount = 0;
|
|
321
|
+
const mockProvider: ClaudeProvider = {
|
|
322
|
+
async call(): Promise<ClaudeResponse> {
|
|
323
|
+
phaseCount++;
|
|
324
|
+
// Fail only on Review phase (4th call)
|
|
325
|
+
if (phaseCount === 3) {
|
|
326
|
+
return {
|
|
327
|
+
result: "Review failed",
|
|
328
|
+
exitCode: 1,
|
|
329
|
+
model: "claude-sonnet-4-5",
|
|
330
|
+
effortLevel: "medium",
|
|
331
|
+
duration: 500,
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
return {
|
|
335
|
+
result: "OK",
|
|
336
|
+
exitCode: 0,
|
|
337
|
+
model: "claude-sonnet-4-5",
|
|
338
|
+
effortLevel: "medium",
|
|
339
|
+
duration: 1000,
|
|
340
|
+
};
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
const mockMergeService = {
|
|
345
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
346
|
+
return {
|
|
347
|
+
success: true,
|
|
348
|
+
message: "Merged successfully",
|
|
349
|
+
};
|
|
350
|
+
},
|
|
351
|
+
} as unknown as MergeService;
|
|
352
|
+
|
|
353
|
+
const mockEventBus: EventBus = {
|
|
354
|
+
on() {},
|
|
355
|
+
off() {},
|
|
356
|
+
emit() {},
|
|
357
|
+
} as unknown as EventBus;
|
|
358
|
+
|
|
359
|
+
const mockSpawner = {
|
|
360
|
+
async spawn() {
|
|
361
|
+
return { exitCode: 0, stdout: "OK", stderr: "" };
|
|
362
|
+
},
|
|
363
|
+
};
|
|
364
|
+
|
|
365
|
+
const result = await executeTddPipeline(
|
|
366
|
+
task,
|
|
367
|
+
workspace,
|
|
368
|
+
taskBranch,
|
|
369
|
+
parentBranch,
|
|
370
|
+
parentWorktree,
|
|
371
|
+
runId,
|
|
372
|
+
config,
|
|
373
|
+
mockProvider,
|
|
374
|
+
mockMergeService,
|
|
375
|
+
mockEventBus,
|
|
376
|
+
mockSpawner
|
|
377
|
+
);
|
|
378
|
+
|
|
379
|
+
// Should still complete despite review failure
|
|
380
|
+
expect(result.status).toBe("completed");
|
|
381
|
+
expect(phaseCount).toBeGreaterThanOrEqual(3); // Red, Green, Review phases called
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
test("emits execution events", async () => {
|
|
385
|
+
const task: Task = {
|
|
386
|
+
taskId: createTaskId("task-005"),
|
|
387
|
+
title: "Event test",
|
|
388
|
+
description: "Test events",
|
|
389
|
+
filesToModify: [],
|
|
390
|
+
dependsOn: [],
|
|
391
|
+
priority: 1,
|
|
392
|
+
status: "running",
|
|
393
|
+
retryCount: 0,
|
|
394
|
+
};
|
|
395
|
+
|
|
396
|
+
const workspace: WorkspaceInfo = {
|
|
397
|
+
path: "/workspace",
|
|
398
|
+
language: "typescript",
|
|
399
|
+
packageManager: "bun",
|
|
400
|
+
framework: "hono",
|
|
401
|
+
testFramework: "bun-test",
|
|
402
|
+
};
|
|
403
|
+
|
|
404
|
+
const taskBranch = "task/005-events";
|
|
405
|
+
const parentBranch = "main";
|
|
406
|
+
const parentWorktree = "/parent";
|
|
407
|
+
const runId = createRunId("run-005");
|
|
408
|
+
|
|
409
|
+
const config: Config = {
|
|
410
|
+
workers: { num: 2, max: 8 },
|
|
411
|
+
models: {},
|
|
412
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
413
|
+
retry: { maxRetries: 2 },
|
|
414
|
+
debug: false,
|
|
415
|
+
adaptiveEffort: false,
|
|
416
|
+
teams: { splitter: false, reviewer: false },
|
|
417
|
+
memorySync: true,
|
|
418
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const mockProvider: ClaudeProvider = {
|
|
422
|
+
async call(): Promise<ClaudeResponse> {
|
|
423
|
+
return {
|
|
424
|
+
result: "OK",
|
|
425
|
+
exitCode: 0,
|
|
426
|
+
model: "claude-sonnet-4-5",
|
|
427
|
+
effortLevel: "medium",
|
|
428
|
+
duration: 1000,
|
|
429
|
+
};
|
|
430
|
+
},
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
const mockMergeService = {
|
|
434
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
435
|
+
return {
|
|
436
|
+
success: true,
|
|
437
|
+
};
|
|
438
|
+
},
|
|
439
|
+
} as unknown as MergeService;
|
|
440
|
+
|
|
441
|
+
const events: Array<{ type: string; phase?: string }> = [];
|
|
442
|
+
const mockEventBus = {
|
|
443
|
+
on() {},
|
|
444
|
+
off() {},
|
|
445
|
+
emit(event: any) {
|
|
446
|
+
events.push({ type: event.type, phase: event.phase });
|
|
447
|
+
},
|
|
448
|
+
} as unknown as EventBus;
|
|
449
|
+
|
|
450
|
+
const mockSpawner = {
|
|
451
|
+
async spawn() {
|
|
452
|
+
return { exitCode: 0, stdout: "OK", stderr: "" };
|
|
453
|
+
},
|
|
454
|
+
};
|
|
455
|
+
|
|
456
|
+
await executeTddPipeline(
|
|
457
|
+
task,
|
|
458
|
+
workspace,
|
|
459
|
+
taskBranch,
|
|
460
|
+
parentBranch,
|
|
461
|
+
parentWorktree,
|
|
462
|
+
runId,
|
|
463
|
+
config,
|
|
464
|
+
mockProvider,
|
|
465
|
+
mockMergeService,
|
|
466
|
+
mockEventBus,
|
|
467
|
+
mockSpawner
|
|
468
|
+
);
|
|
469
|
+
|
|
470
|
+
const phaseStartedEvents = events.filter((e) => e.type === "execution:phase:started");
|
|
471
|
+
const phaseCompletedEvents = events.filter((e) => e.type === "execution:phase:completed");
|
|
472
|
+
|
|
473
|
+
expect(phaseStartedEvents.length).toBeGreaterThan(0);
|
|
474
|
+
expect(phaseCompletedEvents.length).toBeGreaterThan(0);
|
|
475
|
+
|
|
476
|
+
// Check that all expected phases are present
|
|
477
|
+
const phases = phaseStartedEvents.map((e) => e.phase);
|
|
478
|
+
expect(phases).toContain("red");
|
|
479
|
+
expect(phases).toContain("green");
|
|
480
|
+
expect(phases).toContain("verify");
|
|
481
|
+
expect(phases).toContain("review");
|
|
482
|
+
expect(phases).toContain("merge");
|
|
483
|
+
});
|
|
484
|
+
|
|
485
|
+
test("handles implementer-green phase failure", async () => {
|
|
486
|
+
const task: Task = {
|
|
487
|
+
taskId: createTaskId("task-green-fail"),
|
|
488
|
+
title: "Green fails",
|
|
489
|
+
description: "Will fail in Green phase",
|
|
490
|
+
filesToModify: [],
|
|
491
|
+
dependsOn: [],
|
|
492
|
+
priority: 1,
|
|
493
|
+
status: "running",
|
|
494
|
+
retryCount: 0,
|
|
495
|
+
};
|
|
496
|
+
|
|
497
|
+
const workspace: WorkspaceInfo = {
|
|
498
|
+
path: "/workspace",
|
|
499
|
+
language: "typescript",
|
|
500
|
+
packageManager: "bun",
|
|
501
|
+
framework: "hono",
|
|
502
|
+
testFramework: "bun-test",
|
|
503
|
+
};
|
|
504
|
+
|
|
505
|
+
const config: Config = {
|
|
506
|
+
workers: { num: 2, max: 8 },
|
|
507
|
+
models: {},
|
|
508
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
509
|
+
retry: { maxRetries: 2 },
|
|
510
|
+
debug: false,
|
|
511
|
+
adaptiveEffort: false,
|
|
512
|
+
teams: { splitter: false, reviewer: false },
|
|
513
|
+
memorySync: true,
|
|
514
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
515
|
+
};
|
|
516
|
+
|
|
517
|
+
let callCount = 0;
|
|
518
|
+
const mockProvider: ClaudeProvider = {
|
|
519
|
+
async call(): Promise<ClaudeResponse> {
|
|
520
|
+
callCount++;
|
|
521
|
+
if (callCount === 1) {
|
|
522
|
+
// Red phase succeeds
|
|
523
|
+
return { result: "OK", exitCode: 0, model: "claude-sonnet-4-5", effortLevel: "medium", duration: 1000 };
|
|
524
|
+
}
|
|
525
|
+
// Green phase fails
|
|
526
|
+
return { result: "Green failed", exitCode: 1, model: "claude-sonnet-4-5", effortLevel: "medium", duration: 500 };
|
|
527
|
+
},
|
|
528
|
+
};
|
|
529
|
+
|
|
530
|
+
const mockMergeService = { async mergeToParent() { throw new Error("Should not reach"); } } as unknown as MergeService;
|
|
531
|
+
const events: string[] = [];
|
|
532
|
+
const mockEventBus = { on() {}, off() {}, emit(event: any) { events.push(event.type); } } as unknown as EventBus;
|
|
533
|
+
|
|
534
|
+
const result = await executeTddPipeline(
|
|
535
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-gf"), config,
|
|
536
|
+
mockProvider, mockMergeService, mockEventBus
|
|
537
|
+
);
|
|
538
|
+
|
|
539
|
+
expect(result.status).toBe("failed");
|
|
540
|
+
expect(result.error).toContain("Green phase failed");
|
|
541
|
+
expect(callCount).toBe(2);
|
|
542
|
+
expect(events).toContain("execution:phase:failed");
|
|
543
|
+
});
|
|
544
|
+
|
|
545
|
+
test("handles merge phase failure", async () => {
|
|
546
|
+
const task: Task = {
|
|
547
|
+
taskId: createTaskId("task-merge-fail"),
|
|
548
|
+
title: "Merge fails",
|
|
549
|
+
description: "Will fail in Merge phase",
|
|
550
|
+
filesToModify: [],
|
|
551
|
+
dependsOn: [],
|
|
552
|
+
priority: 1,
|
|
553
|
+
status: "running",
|
|
554
|
+
retryCount: 0,
|
|
555
|
+
};
|
|
556
|
+
|
|
557
|
+
const workspace: WorkspaceInfo = {
|
|
558
|
+
path: "/workspace",
|
|
559
|
+
language: "typescript",
|
|
560
|
+
packageManager: "bun",
|
|
561
|
+
framework: "hono",
|
|
562
|
+
testFramework: "bun-test",
|
|
563
|
+
};
|
|
564
|
+
|
|
565
|
+
const config: Config = {
|
|
566
|
+
workers: { num: 2, max: 8 },
|
|
567
|
+
models: {},
|
|
568
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
569
|
+
retry: { maxRetries: 2 },
|
|
570
|
+
debug: false,
|
|
571
|
+
adaptiveEffort: false,
|
|
572
|
+
teams: { splitter: false, reviewer: false },
|
|
573
|
+
memorySync: true,
|
|
574
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
575
|
+
};
|
|
576
|
+
|
|
577
|
+
const mockProvider: ClaudeProvider = {
|
|
578
|
+
async call(): Promise<ClaudeResponse> {
|
|
579
|
+
return { result: "OK", exitCode: 0, model: "claude-sonnet-4-5", effortLevel: "medium", duration: 1000 };
|
|
580
|
+
},
|
|
581
|
+
};
|
|
582
|
+
|
|
583
|
+
const mockMergeService = {
|
|
584
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
585
|
+
return { success: false, message: "Merge failed" };
|
|
586
|
+
},
|
|
587
|
+
} as unknown as MergeService;
|
|
588
|
+
|
|
589
|
+
const events: string[] = [];
|
|
590
|
+
const mockEventBus = { on() {}, off() {}, emit(event: any) { events.push(event.type); } } as unknown as EventBus;
|
|
591
|
+
const mockSpawner = { async spawn() { return { exitCode: 0, stdout: "OK", stderr: "" }; } };
|
|
592
|
+
|
|
593
|
+
const result = await executeTddPipeline(
|
|
594
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-mf"), config,
|
|
595
|
+
mockProvider, mockMergeService, mockEventBus, mockSpawner
|
|
596
|
+
);
|
|
597
|
+
|
|
598
|
+
expect(result.status).toBe("failed");
|
|
599
|
+
expect(result.error).toContain("Merge phase failed");
|
|
600
|
+
});
|
|
601
|
+
|
|
602
|
+
test("handles merge with conflict and emits conflict event", async () => {
|
|
603
|
+
const task: Task = {
|
|
604
|
+
taskId: createTaskId("task-merge-conflict"),
|
|
605
|
+
title: "Merge with conflict",
|
|
606
|
+
description: "Merge has conflicts but resolves",
|
|
607
|
+
filesToModify: [],
|
|
608
|
+
dependsOn: [],
|
|
609
|
+
priority: 1,
|
|
610
|
+
status: "running",
|
|
611
|
+
retryCount: 0,
|
|
612
|
+
};
|
|
613
|
+
|
|
614
|
+
const workspace: WorkspaceInfo = {
|
|
615
|
+
path: "/workspace",
|
|
616
|
+
language: "typescript",
|
|
617
|
+
packageManager: "bun",
|
|
618
|
+
framework: "hono",
|
|
619
|
+
testFramework: "bun-test",
|
|
620
|
+
};
|
|
621
|
+
|
|
622
|
+
const config: Config = {
|
|
623
|
+
workers: { num: 2, max: 8 },
|
|
624
|
+
models: {},
|
|
625
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
626
|
+
retry: { maxRetries: 2 },
|
|
627
|
+
debug: false,
|
|
628
|
+
adaptiveEffort: false,
|
|
629
|
+
teams: { splitter: false, reviewer: false },
|
|
630
|
+
memorySync: true,
|
|
631
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
632
|
+
};
|
|
633
|
+
|
|
634
|
+
const mockProvider: ClaudeProvider = {
|
|
635
|
+
async call(): Promise<ClaudeResponse> {
|
|
636
|
+
return { result: "OK", exitCode: 0, model: "claude-sonnet-4-5", effortLevel: "medium", duration: 1000 };
|
|
637
|
+
},
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
// MergeService returns conflict but merge phase ultimately succeeds (conflicts resolved by Claude)
|
|
641
|
+
const mockMergeService = {
|
|
642
|
+
async mergeToParent(): Promise<MergeResult> {
|
|
643
|
+
return { success: false, conflicts: ["file.ts"], message: "Conflict" };
|
|
644
|
+
},
|
|
645
|
+
} as unknown as MergeService;
|
|
646
|
+
|
|
647
|
+
const events: string[] = [];
|
|
648
|
+
const mockEventBus = { on() {}, off() {}, emit(event: any) { events.push(event.type); } } as unknown as EventBus;
|
|
649
|
+
const mockSpawner = { async spawn() { return { exitCode: 0, stdout: "OK", stderr: "" }; } };
|
|
650
|
+
|
|
651
|
+
const result = await executeTddPipeline(
|
|
652
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-mc"), config,
|
|
653
|
+
mockProvider, mockMergeService, mockEventBus, mockSpawner
|
|
654
|
+
);
|
|
655
|
+
|
|
656
|
+
expect(result.status).toBe("completed");
|
|
657
|
+
expect(events).toContain("execution:merge:conflict");
|
|
658
|
+
});
|
|
659
|
+
|
|
660
|
+
test("handles non-PhaseError exceptions", async () => {
|
|
661
|
+
const task: Task = {
|
|
662
|
+
taskId: createTaskId("task-unknown-err"),
|
|
663
|
+
title: "Unknown error",
|
|
664
|
+
description: "Will throw non-PhaseError",
|
|
665
|
+
filesToModify: [],
|
|
666
|
+
dependsOn: [],
|
|
667
|
+
priority: 1,
|
|
668
|
+
status: "running",
|
|
669
|
+
retryCount: 0,
|
|
670
|
+
};
|
|
671
|
+
|
|
672
|
+
const workspace: WorkspaceInfo = {
|
|
673
|
+
path: "/workspace",
|
|
674
|
+
language: "typescript",
|
|
675
|
+
packageManager: "bun",
|
|
676
|
+
framework: "hono",
|
|
677
|
+
testFramework: "bun-test",
|
|
678
|
+
};
|
|
679
|
+
|
|
680
|
+
const config: Config = {
|
|
681
|
+
workers: { num: 2, max: 8 },
|
|
682
|
+
models: {},
|
|
683
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
684
|
+
retry: { maxRetries: 2 },
|
|
685
|
+
debug: false,
|
|
686
|
+
adaptiveEffort: false,
|
|
687
|
+
teams: { splitter: false, reviewer: false },
|
|
688
|
+
memorySync: true,
|
|
689
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
690
|
+
};
|
|
691
|
+
|
|
692
|
+
const mockProvider: ClaudeProvider = {
|
|
693
|
+
async call(): Promise<ClaudeResponse> {
|
|
694
|
+
throw new Error("Network connection failed");
|
|
695
|
+
},
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
const mockMergeService = { async mergeToParent() { throw new Error("no"); } } as unknown as MergeService;
|
|
699
|
+
const mockEventBus = { on() {}, off() {}, emit() {} } as unknown as EventBus;
|
|
700
|
+
|
|
701
|
+
const result = await executeTddPipeline(
|
|
702
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-ue"), config,
|
|
703
|
+
mockProvider, mockMergeService, mockEventBus
|
|
704
|
+
);
|
|
705
|
+
|
|
706
|
+
expect(result.status).toBe("failed");
|
|
707
|
+
expect(result.error).toBe("Network connection failed");
|
|
708
|
+
expect(result.output).toBeUndefined();
|
|
709
|
+
});
|
|
710
|
+
|
|
711
|
+
test("handles non-Error thrown exceptions", async () => {
|
|
712
|
+
const task: Task = {
|
|
713
|
+
taskId: createTaskId("task-string-err"),
|
|
714
|
+
title: "String error",
|
|
715
|
+
description: "Will throw a string",
|
|
716
|
+
filesToModify: [],
|
|
717
|
+
dependsOn: [],
|
|
718
|
+
priority: 1,
|
|
719
|
+
status: "running",
|
|
720
|
+
retryCount: 0,
|
|
721
|
+
};
|
|
722
|
+
|
|
723
|
+
const workspace: WorkspaceInfo = {
|
|
724
|
+
path: "/workspace",
|
|
725
|
+
language: "typescript",
|
|
726
|
+
packageManager: "bun",
|
|
727
|
+
framework: "hono",
|
|
728
|
+
testFramework: "bun-test",
|
|
729
|
+
};
|
|
730
|
+
|
|
731
|
+
const config: Config = {
|
|
732
|
+
workers: { num: 2, max: 8 },
|
|
733
|
+
models: {},
|
|
734
|
+
timeouts: { claude: 1200, test: 600, staleTask: 5400 },
|
|
735
|
+
retry: { maxRetries: 2 },
|
|
736
|
+
debug: false,
|
|
737
|
+
adaptiveEffort: false,
|
|
738
|
+
teams: { splitter: false, reviewer: false },
|
|
739
|
+
memorySync: true,
|
|
740
|
+
dashboard: { enabled: true, port: 7333, host: "localhost" },
|
|
741
|
+
};
|
|
742
|
+
|
|
743
|
+
const mockProvider: ClaudeProvider = {
|
|
744
|
+
async call(): Promise<ClaudeResponse> {
|
|
745
|
+
throw "string error";
|
|
746
|
+
},
|
|
747
|
+
};
|
|
748
|
+
|
|
749
|
+
const mockMergeService = { async mergeToParent() { throw new Error("no"); } } as unknown as MergeService;
|
|
750
|
+
const mockEventBus = { on() {}, off() {}, emit() {} } as unknown as EventBus;
|
|
751
|
+
|
|
752
|
+
const result = await executeTddPipeline(
|
|
753
|
+
task, workspace, "branch", "main", "/parent", createRunId("run-se"), config,
|
|
754
|
+
mockProvider, mockMergeService, mockEventBus
|
|
755
|
+
);
|
|
756
|
+
|
|
757
|
+
expect(result.status).toBe("failed");
|
|
758
|
+
expect(result.error).toBe("Unknown error");
|
|
759
|
+
});
|
|
760
|
+
});
|