summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--internal/executor/executor_test.go55
1 files changed, 55 insertions, 0 deletions
diff --git a/internal/executor/executor_test.go b/internal/executor/executor_test.go
index a8fd9da..67b2764 100644
--- a/internal/executor/executor_test.go
+++ b/internal/executor/executor_test.go
@@ -1960,6 +1960,61 @@ func TestPool_ValidationTask_Pass_SetsReviewReady(t *testing.T) {
}
}
+// TestPool_DependsOn_NoDeadlock verifies that a task waiting for a dependency
+// does NOT hold the per-agent slot, allowing the dependency to run first.
+func TestPool_DependsOn_NoDeadlock(t *testing.T) {
+ store := testStore(t)
+ runner := &mockRunner{} // succeeds immediately
+ pool := NewPool(2, map[string]Runner{"claude": runner}, store,
+ slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError})))
+ pool.requeueDelay = 10 * time.Millisecond
+
+ // Task A has no deps; Task B depends on A.
+ taskA := makeTask("dep-a")
+ taskA.State = task.StateQueued
+ taskB := makeTask("dep-b")
+ taskB.DependsOn = []string{"dep-a"}
+ taskB.State = task.StateQueued
+
+ store.CreateTask(taskA)
+ store.CreateTask(taskB)
+
+ ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
+ defer cancel()
+
+ // Submit B first — it should not deadlock by holding the slot while waiting for A.
+ pool.Submit(ctx, taskB)
+ pool.Submit(ctx, taskA)
+
+ var gotA, gotB bool
+ for i := 0; i < 2; i++ {
+ select {
+ case res := <-pool.Results():
+ if res.TaskID == "dep-a" {
+ gotA = true
+ }
+ if res.TaskID == "dep-b" {
+ gotB = true
+ }
+ case <-ctx.Done():
+ t.Fatal("timeout: likely deadlock — dep-b held the slot while waiting for dep-a")
+ }
+ }
+ if !gotA || !gotB {
+ t.Errorf("expected both tasks to complete: gotA=%v gotB=%v", gotA, gotB)
+ }
+
+ // B must complete after A.
+ ta, _ := store.GetTask("dep-a")
+ tb, _ := store.GetTask("dep-b")
+ if ta.State != task.StateReady && ta.State != task.StateCompleted {
+ t.Errorf("dep-a should be READY/COMPLETED, got %s", ta.State)
+ }
+ if tb.State != task.StateReady && tb.State != task.StateCompleted {
+ t.Errorf("dep-b should be READY/COMPLETED, got %s", tb.State)
+ }
+}
+
func TestPool_ValidationTask_Fail_SetsNeedsFix(t *testing.T) {
store := testStore(t)
logger := slog.New(slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: slog.LevelError}))