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