summaryrefslogtreecommitdiff
path: root/internal/api/server_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/api/server_test.go')
-rw-r--r--internal/api/server_test.go67
1 files changed, 67 insertions, 0 deletions
diff --git a/internal/api/server_test.go b/internal/api/server_test.go
index 27fc645..4b45f25 100644
--- a/internal/api/server_test.go
+++ b/internal/api/server_test.go
@@ -2014,3 +2014,70 @@ func TestProjects_CRUD(t *testing.T) {
t.Error("expected at least one project in list")
}
}
+
+func TestHandleRunTask_CascadesRetryToFailedDeps(t *testing.T) {
+ srv, store := testServer(t)
+
+ now := time.Now().UTC()
+
+ // Task A: the dependency, in FAILED state.
+ taskA := &task.Task{
+ ID: "cascade-dep-a",
+ Name: "Dep A",
+ State: task.StateFailed,
+ DependsOn: []string{},
+ Priority: task.PriorityNormal,
+ Tags: []string{},
+ Agent: task.AgentConfig{Type: "claude", Instructions: "do A"},
+ Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "exponential"},
+ CreatedAt: now,
+ UpdatedAt: now,
+ }
+ if err := store.CreateTask(taskA); err != nil {
+ t.Fatalf("CreateTask A: %v", err)
+ }
+
+ // Task B: depends on A, in CANCELLED state (was cancelled because A failed).
+ taskB := &task.Task{
+ ID: "cascade-task-b",
+ Name: "Task B",
+ State: task.StateCancelled,
+ DependsOn: []string{taskA.ID},
+ Priority: task.PriorityNormal,
+ Tags: []string{},
+ Agent: task.AgentConfig{Type: "claude", Instructions: "do B"},
+ Retry: task.RetryConfig{MaxAttempts: 1, Backoff: "exponential"},
+ CreatedAt: now,
+ UpdatedAt: now,
+ }
+ if err := store.CreateTask(taskB); err != nil {
+ t.Fatalf("CreateTask B: %v", err)
+ }
+
+ // Run task B — should cascade-retry dep A.
+ req := httptest.NewRequest("POST", "/api/tasks/cascade-task-b/run", nil)
+ w := httptest.NewRecorder()
+ srv.mux.ServeHTTP(w, req)
+
+ if w.Code != http.StatusAccepted {
+ t.Fatalf("expected 202, got %d: %s", w.Code, w.Body.String())
+ }
+
+ // Dep A should now be QUEUED.
+ a, err := store.GetTask(taskA.ID)
+ if err != nil {
+ t.Fatalf("GetTask A: %v", err)
+ }
+ if a.State != task.StateQueued {
+ t.Errorf("dep A: want QUEUED after cascade, got %s", a.State)
+ }
+
+ // Task B itself should be QUEUED.
+ b, err := store.GetTask(taskB.ID)
+ if err != nil {
+ t.Fatalf("GetTask B: %v", err)
+ }
+ if b.State != task.StateQueued {
+ t.Errorf("task B: want QUEUED, got %s", b.State)
+ }
+}