package builtin import ( "context" "encoding/json" "testing" ) func TestTodoTool_ReplacesListAndRendersStatus(t *testing.T) { tt := &TodoTool{} env := testEnv(t) in, _ := json.Marshal(map[string]any{ "items": []map[string]string{ {"content": "read file", "status": "completed"}, {"content": "write fix", "status": "in_progress"}, {"content": "run tests", "status": "pending"}, }, }) res, err := tt.Run(context.Background(), in, env) if err != nil { t.Fatal(err) } if res.IsError { t.Fatalf("unexpected error: %s", res.ForModel) } if !contains(res.ForModel, "[x] read file") { t.Errorf("expected completed marker, got: %q", res.ForModel) } if !contains(res.ForModel, "[~] write fix") { t.Errorf("expected in_progress marker, got: %q", res.ForModel) } if !contains(res.ForModel, "[ ] run tests") { t.Errorf("expected pending marker, got: %q", res.ForModel) } ui, ok := res.ForUI.(TodoResult) if !ok || len(ui.Items) != 3 { t.Errorf("ForUI = %+v, want TodoResult with 3 items", res.ForUI) } } func TestTodoTool_RejectsInvalidStatus(t *testing.T) { tt := &TodoTool{} env := testEnv(t) in, _ := json.Marshal(map[string]any{ "items": []map[string]string{{"content": "x", "status": "bogus"}}, }) res, err := tt.Run(context.Background(), in, env) if err != nil { t.Fatal(err) } if !res.IsError { t.Fatal("expected error for invalid status") } } func TestTodoTool_SecondCallReplacesFirst(t *testing.T) { tt := &TodoTool{} env := testEnv(t) first, _ := json.Marshal(map[string]any{ "items": []map[string]string{{"content": "a", "status": "pending"}, {"content": "b", "status": "pending"}}, }) if _, err := tt.Run(context.Background(), first, env); err != nil { t.Fatal(err) } second, _ := json.Marshal(map[string]any{ "items": []map[string]string{{"content": "c", "status": "pending"}}, }) res, err := tt.Run(context.Background(), second, env) if err != nil { t.Fatal(err) } ui := res.ForUI.(TodoResult) if len(ui.Items) != 1 || ui.Items[0].Content != "c" { t.Errorf("expected list replaced with single item 'c', got: %+v", ui.Items) } }