1package subagents
2
3import (
4 "context"
5 "errors"
6 "strings"
7 "testing"
8)
9
10func TestResidentCompletionProjection(t *testing.T) {
11 for _, tc := range []struct {
12 err error
13 status string
14 }{
15 {nil, "completed"},
16 {errors.New("failure"), "failed"},
17 {context.Canceled, "interrupted"},
18 {errors.Join(ErrBudgetExceeded, errors.New("capture failed")), "budget_exhausted"},
19 {errors.Join(context.Canceled, ErrBudgetExceeded), "interrupted"},
20 } {
21 t.Run(tc.status, func(t *testing.T) {
22 got := (ResidentCompletion{ChildID: "child", TurnID: "turn", Task: "task", Summary: "saved progress", Err: tc.err}).Completion()
23 if got.Status != tc.status || got.AgentID != "child" || got.TurnID != "turn" || got.Task != "task" || got.Summary != "saved progress" {
24 t.Fatalf("completion = %#v", got)
25 }
26 if tc.err != nil && got.Error != tc.err.Error() {
27 t.Fatalf("error = %q, want %q", got.Error, tc.err.Error())
28 }
29 text := FormatCompletionUpdate([]Completion{got}, "")
30 label := "partial: "
31 if tc.err == nil {
32 label = "final: "
33 }
34 if !strings.Contains(text, label+"saved progress") {
35 t.Fatalf("update = %q", text)
36 }
37 })
38 }
39}
40
41func TestFormatCompletionUpdateIncludesFinalSummary(t *testing.T) {
42 got := FormatCompletionUpdate([]Completion{{AgentID: "child", Status: "completed", Task: "review", Summary: "found the regression"}}, "")
43 if !strings.Contains(got, "final: found the regression") {
44 t.Fatalf("completion update = %q", got)
45 }
46}
47
48func TestCompletionTrackerDropsReportsAfterCancellationReset(t *testing.T) {
49 tracker := NewCompletionTracker()
50 if !tracker.TrackResident("child", "turn-1") {
51 t.Fatal("TrackResident returned false")
52 }
53 tracker.Reset()
54 if tracker.Report(Completion{AgentID: "child", TurnID: "turn-1"}) {
55 t.Fatal("Report returned true after reset")
56 }
57 got, err := tracker.WaitIdle(context.Background())
58 if err != nil {
59 t.Fatal(err)
60 }
61 if len(got) != 0 {
62 t.Fatalf("stale completions = %#v", got)
63 }
64}
65
66func TestCompletionTrackerMatchesTerminalReportsToAcceptedTurns(t *testing.T) {
67 tracker := NewCompletionTracker()
68 if !tracker.TrackResident("child", "turn-1") || !tracker.TrackResident("child", "turn-2") {
69 t.Fatal("TrackResident returned false")
70 }
71 if tracker.Report(Completion{AgentID: "child", TurnID: "other"}) {
72 t.Fatal("Report accepted an unknown turn")
73 }
74 if !tracker.Report(Completion{AgentID: "child", TurnID: "turn-1"}) {
75 t.Fatal("Report rejected an accepted turn")
76 }
77 if tracker.Report(Completion{AgentID: "child", TurnID: "turn-1"}) {
78 t.Fatal("Report accepted a duplicate terminal result")
79 }
80 if got := tracker.Pending(); got != 1 {
81 t.Fatalf("Pending = %d, want 1", got)
82 }
83 if !tracker.Report(Completion{AgentID: "child", TurnID: "turn-2"}) {
84 t.Fatal("Report rejected second accepted turn")
85 }
86 got, err := tracker.WaitIdle(context.Background())
87 if err != nil {
88 t.Fatal(err)
89 }
90 if len(got) != 2 {
91 t.Fatalf("completions = %#v, want 2", got)
92 }
93}