1package tui
2
3import (
4 "encoding/json"
5 "strings"
6 "testing"
7
8 "github.com/patriceckhart/zot/packages/provider"
9)
10
11func TestLiveToolOverlayRemainsAfterAssistantToolUse(t *testing.T) {
12 args := json.RawMessage(`{"command":"sleep 1"}`)
13 v := View{
14 Theme: Dark,
15 Messages: []provider.Message{
16 {
17 Role: provider.RoleAssistant,
18 Content: []provider.Content{
19 provider.ToolCallBlock{ID: "toolu_1", Name: "bash", Arguments: args},
20 },
21 },
22 },
23 ToolCalls: []ToolCallView{
24 {ID: "toolu_1", Name: "bash", Args: ShortArgs("bash", args), Done: false},
25 },
26 }
27
28 plain := stripANSI(strings.Join(v.Build(80), "\n"))
29 if !strings.Contains(plain, "bash sleep 1") {
30 t.Fatalf("live tool overlay disappeared after assistant tool_use was appended:\n%s", plain)
31 }
32}
33
34func TestLiveToolOverlayShowsFullBashCommandBeforeResult(t *testing.T) {
35 args := json.RawMessage(`{"command":"printf 'start' && sleep 60 && printf 'done with a command long enough to exceed the header truncation limit'"}`)
36 v := View{
37 Theme: Dark,
38 ToolCalls: []ToolCallView{
39 {
40 ID: "toolu_1",
41 Name: "bash",
42 Args: ShortArgs("bash", args),
43 RawJSONBuf: string(args),
44 },
45 },
46 }
47
48 plain := stripANSI(strings.Join(v.BuildLive(100), "\n"))
49 if !strings.Contains(plain, "$ printf 'start' && sleep 60") || !strings.Contains(plain, "header truncation limit") {
50 t.Fatalf("bash command was not visible before result arrived:\n%s", plain)
51 }
52}
53
54func TestLiveToolOverlayKeepsWritePreviewAfterArgsEnd(t *testing.T) {
55 args := json.RawMessage(`{"path":"/tmp/sample.ts","content":"export const n = 1\n"}`)
56 v := View{
57 Theme: Dark,
58 Messages: []provider.Message{
59 {
60 Role: provider.RoleAssistant,
61 Content: []provider.Content{
62 provider.ToolCallBlock{ID: "toolu_1", Name: "write", Arguments: args},
63 },
64 },
65 },
66 ToolCalls: []ToolCallView{
67 {
68 ID: "toolu_1",
69 Name: "write",
70 Args: ShortArgs("write", args),
71 Streaming: false,
72 RawJSONBuf: string(args),
73 LivePath: "/tmp/sample.ts",
74 },
75 },
76 }
77
78 plain := stripANSI(strings.Join(v.Build(80), "\n"))
79 if !strings.Contains(plain, "export const n = 1") {
80 t.Fatalf("write preview collapsed after tool args ended but before tool_result arrived:\n%s", plain)
81 }
82}
83
84func TestLiveToolOverlayHidesAfterToolResult(t *testing.T) {
85 args := json.RawMessage(`{"command":"sleep 1"}`)
86 v := View{
87 Theme: Dark,
88 Messages: []provider.Message{
89 {
90 Role: provider.RoleAssistant,
91 Content: []provider.Content{
92 provider.ToolCallBlock{ID: "toolu_1", Name: "bash", Arguments: args},
93 },
94 },
95 {
96 Role: provider.RoleTool,
97 Content: []provider.Content{
98 provider.ToolResultBlock{
99 CallID: "toolu_1",
100 Content: []provider.Content{provider.TextBlock{Text: "done"}},
101 },
102 },
103 },
104 },
105 ToolCalls: []ToolCallView{
106 {ID: "toolu_1", Name: "bash", Args: ShortArgs("bash", args), Result: "done", Done: true},
107 },
108 }
109
110 plain := stripANSI(strings.Join(v.BuildLive(80), "\n"))
111 if strings.Contains(plain, "bash sleep 1") {
112 t.Fatalf("live tool overlay still rendered after tool_result was appended:\n%s", plain)
113 }
114}