1package tools
2
3import (
4 "context"
5 "encoding/json"
6 "strings"
7 "testing"
8
9 "github.com/bnema/zut/packages/core"
10)
11
12func TestUpdateGoalToolDescriptionRequiresGoalsForSubstantialWork(t *testing.T) {
13 description := (&UpdateGoalTool{}).Description()
14 for _, clause := range []string{
15 "requires substantial multi-step work",
16 "before substantial execution",
17 "Do not start a mission for brief questions, casual conversation",
18 "requests completed immediately",
19 "Incidental messages and interrupted turns do not end an active mission",
20 } {
21 if !strings.Contains(description, clause) {
22 t.Fatalf("update_goal description missing %q: %q", clause, description)
23 }
24 }
25}
26
27func TestUpdateGoalToolReturnsPersistableStatus(t *testing.T) {
28 tool := &UpdateGoalTool{}
29 raw, err := json.Marshal(map[string]string{"status": "complete"})
30 if err != nil {
31 t.Fatal(err)
32 }
33 result, err := tool.Execute(context.Background(), raw, nil)
34 if err != nil {
35 t.Fatal(err)
36 }
37 if result.IsError {
38 t.Fatalf("result = %#v, want success", result)
39 }
40 update, ok := GoalUpdateFromResult(result)
41 if !ok {
42 t.Fatalf("details = %#v, want goal update", result.Details)
43 }
44 if update.Status != core.GoalDone || update.Reason != "" {
45 t.Fatalf("update = %#v", update)
46 }
47}
48
49func TestUpdateGoalToolReturnsPersistableManagerGoal(t *testing.T) {
50 tool := &UpdateGoalTool{}
51 result, err := tool.Execute(context.Background(), json.RawMessage(`{"status":"active","objective":"reproduce the reported failure"}`), nil)
52 if err != nil {
53 t.Fatal(err)
54 }
55 update, ok := GoalUpdateFromResult(result)
56 if !ok {
57 t.Fatalf("details = %#v, want goal update", result.Details)
58 }
59 if update.Status != core.GoalActive || update.Objective != "reproduce the reported failure" {
60 t.Fatalf("update = %#v", update)
61 }
62}
63
64func TestUpdateGoalToolReturnsPersistableSupersedingGoal(t *testing.T) {
65 tool := &UpdateGoalTool{}
66 result, err := tool.Execute(context.Background(), json.RawMessage(`{"status":"superseded","objective":"use the supported API","goal_id":"goal-1","mission_id":"mission-1"}`), nil)
67 if err != nil {
68 t.Fatal(err)
69 }
70 update, ok := GoalUpdateFromResult(result)
71 if !ok {
72 t.Fatalf("details = %#v, want superseding goal update", result.Details)
73 }
74 if update.Status != core.GoalSuperseded || update.Objective != "use the supported API" || update.GoalID != "goal-1" || update.MissionID != "mission-1" {
75 t.Fatalf("update = %#v", update)
76 }
77}
78
79func TestUpdateGoalToolRejectsUnknownFields(t *testing.T) {
80 tool := &UpdateGoalTool{}
81 result, err := tool.Execute(context.Background(), json.RawMessage(`{"status":"complete","unexpected":true}`), nil)
82 if err != nil {
83 t.Fatal(err)
84 }
85 if !result.IsError {
86 t.Fatalf("result = %#v, want tool error", result)
87 }
88}
89
90func TestGoalUpdateFromResultRejectsBlockedWithoutReason(t *testing.T) {
91 result := core.ToolResult{Details: GoalUpdate{Status: core.GoalBlocked}}
92 if _, ok := GoalUpdateFromResult(result); ok {
93 t.Fatal("blank-reason blocked update was accepted")
94 }
95
96 result.Details = GoalUpdate{Status: core.GoalBlocked, Reason: "needs user input"}
97 if _, ok := GoalUpdateFromResult(result); !ok {
98 t.Fatal("blocked update with reason was rejected")
99 }
100
101 result.Details = GoalUpdate{Status: core.GoalDone}
102 if _, ok := GoalUpdateFromResult(result); !ok {
103 t.Fatal("completed update without reason was rejected")
104 }
105}
106
107func TestUpdateGoalToolRejectsSupersededWithoutReplacement(t *testing.T) {
108 tool := &UpdateGoalTool{}
109 result, err := tool.Execute(context.Background(), json.RawMessage(`{"status":"superseded"}`), nil)
110 if err != nil {
111 t.Fatal(err)
112 }
113 if !result.IsError {
114 t.Fatalf("result = %#v, want tool error", result)
115 }
116}
117
118func TestUpdateGoalToolRejectsUnsupportedStatus(t *testing.T) {
119 tool := &UpdateGoalTool{}
120 raw := json.RawMessage(`{"status":"active"}`)
121 result, err := tool.Execute(context.Background(), raw, nil)
122 if err != nil {
123 t.Fatal(err)
124 }
125 if !result.IsError {
126 t.Fatalf("result = %#v, want tool error", result)
127 }
128 if _, ok := GoalUpdateFromResult(result); ok {
129 t.Fatal("invalid result exposed a goal update")
130 }
131}