summaryrefslogtreecommitdiff
path: root/internal/task/currentattempt_test.go
diff options
context:
space:
mode:
authorClaudomator Agent <agent@claudomator.local>2026-07-09 04:39:05 +0000
committerClaudomator Agent <agent@claudomator.local>2026-07-09 04:39:05 +0000
commit4ba335c8eacb3f9e20bccce1643b2e5320d5239d (patch)
tree397433d1db8564b3818f6b73744567662c15c9d0 /internal/task/currentattempt_test.go
parent884da0ba74f622dc5c41cde25710718762321ae8 (diff)
feat(task,executor): add CurrentAttempt resolution, wire maybeUnblockParent to resolve through it
Diffstat (limited to 'internal/task/currentattempt_test.go')
-rw-r--r--internal/task/currentattempt_test.go130
1 files changed, 130 insertions, 0 deletions
diff --git a/internal/task/currentattempt_test.go b/internal/task/currentattempt_test.go
new file mode 100644
index 0000000..76661d8
--- /dev/null
+++ b/internal/task/currentattempt_test.go
@@ -0,0 +1,130 @@
+package task
+
+import (
+ "errors"
+ "testing"
+)
+
+type fakeLookup struct {
+ tasks map[string]*Task
+}
+
+func newFakeLookup() *fakeLookup {
+ return &fakeLookup{tasks: make(map[string]*Task)}
+}
+
+func (f *fakeLookup) GetTask(id string) (*Task, error) {
+ t, ok := f.tasks[id]
+ if !ok {
+ return nil, errors.New("not found")
+ }
+ return t, nil
+}
+
+func (f *fakeLookup) ListDependents(taskID string) ([]*Task, error) {
+ var out []*Task
+ for _, t := range f.tasks {
+ for _, d := range t.DependsOn {
+ if d == taskID {
+ out = append(out, t)
+ break
+ }
+ }
+ }
+ return out, nil
+}
+
+func TestCurrentAttempt_NoFixAttempt_ReturnsAnchorItself(t *testing.T) {
+ lookup := newFakeLookup()
+ lookup.tasks["a"] = &Task{ID: "a"}
+
+ got, err := CurrentAttempt(lookup, "a")
+ if err != nil {
+ t.Fatalf("CurrentAttempt: %v", err)
+ }
+ if got.ID != "a" {
+ t.Errorf("got %q, want a", got.ID)
+ }
+}
+
+func TestCurrentAttempt_OneFixAttempt_ResolvesToIt(t *testing.T) {
+ lookup := newFakeLookup()
+ lookup.tasks["a"] = &Task{ID: "a", ParentTaskID: "parent-1"}
+ lookup.tasks["a-fix1"] = &Task{ID: "a-fix1", ParentTaskID: "parent-1", DependsOn: []string{"a"}}
+
+ got, err := CurrentAttempt(lookup, "a")
+ if err != nil {
+ t.Fatalf("CurrentAttempt: %v", err)
+ }
+ if got.ID != "a-fix1" {
+ t.Errorf("got %q, want a-fix1", got.ID)
+ }
+}
+
+func TestCurrentAttempt_ChainOfFixAttempts_ResolvesToTip(t *testing.T) {
+ lookup := newFakeLookup()
+ lookup.tasks["a"] = &Task{ID: "a", ParentTaskID: "parent-1"}
+ lookup.tasks["a-fix1"] = &Task{ID: "a-fix1", ParentTaskID: "parent-1", DependsOn: []string{"a"}}
+ lookup.tasks["a-fix2"] = &Task{ID: "a-fix2", ParentTaskID: "parent-1", DependsOn: []string{"a-fix1"}}
+
+ got, err := CurrentAttempt(lookup, "a")
+ if err != nil {
+ t.Fatalf("CurrentAttempt: %v", err)
+ }
+ if got.ID != "a-fix2" {
+ t.Errorf("got %q, want a-fix2 (tip of the chain)", got.ID)
+ }
+
+ // Resolving from an intermediate link in the chain also lands on the tip.
+ got, err = CurrentAttempt(lookup, "a-fix1")
+ if err != nil {
+ t.Fatalf("CurrentAttempt: %v", err)
+ }
+ if got.ID != "a-fix2" {
+ t.Errorf("resolving from a-fix1: got %q, want a-fix2", got.ID)
+ }
+}
+
+// TestCurrentAttempt_DependentWithDifferentParent_NotTreatedAsFixAttempt
+// proves a task that depends on "a" but has a DIFFERENT ParentTaskID (e.g.
+// an unrelated Evaluator task depending on a Builder for scheduling reasons)
+// must not be mistaken for a's fix attempt.
+func TestCurrentAttempt_DependentWithDifferentParent_NotTreatedAsFixAttempt(t *testing.T) {
+ lookup := newFakeLookup()
+ lookup.tasks["a"] = &Task{ID: "a", ParentTaskID: "parent-1"}
+ lookup.tasks["unrelated"] = &Task{ID: "unrelated", ParentTaskID: "", DependsOn: []string{"a"}}
+
+ got, err := CurrentAttempt(lookup, "a")
+ if err != nil {
+ t.Fatalf("CurrentAttempt: %v", err)
+ }
+ if got.ID != "a" {
+ t.Errorf("got %q, want a (unrelated dependent must not be treated as a fix attempt)", got.ID)
+ }
+}
+
+// TestCurrentAttempt_DependentWithMultipleDependsOn_NotTreatedAsFixAttempt
+// proves a fix attempt has exactly one DependsOn entry (the task it
+// supersedes); a task depending on "a" among several dependencies (e.g. an
+// Arbitration task depending on all 4 Evaluators) must not be mistaken for
+// a's fix attempt.
+func TestCurrentAttempt_DependentWithMultipleDependsOn_NotTreatedAsFixAttempt(t *testing.T) {
+ lookup := newFakeLookup()
+ lookup.tasks["a"] = &Task{ID: "a", ParentTaskID: "parent-1"}
+ lookup.tasks["multi-dep"] = &Task{ID: "multi-dep", ParentTaskID: "parent-1", DependsOn: []string{"a", "b"}}
+
+ got, err := CurrentAttempt(lookup, "a")
+ if err != nil {
+ t.Fatalf("CurrentAttempt: %v", err)
+ }
+ if got.ID != "a" {
+ t.Errorf("got %q, want a", got.ID)
+ }
+}
+
+func TestCurrentAttempt_UnknownAnchor_ReturnsError(t *testing.T) {
+ lookup := newFakeLookup()
+ if _, err := CurrentAttempt(lookup, "does-not-exist"); err == nil {
+ t.Error("expected an error for an unknown anchor ID")
+ }
+}