1package modes
2
3import (
4 "context"
5 "strings"
6 "testing"
7
8 "github.com/bnema/zut/packages/core"
9 "github.com/bnema/zut/packages/provider"
10 "github.com/bnema/zut/packages/tui"
11)
12
13func TestQueuedMessageSummaryKeepsImageIndicatorWhenTextIsTruncated(t *testing.T) {
14 message := core.QueuedMessage{
15 Text: strings.Repeat("x", 100),
16 Images: []provider.ImageBlock{{MimeType: "image/png", Data: []byte("png-1")}},
17 }
18
19 summary := queuedMessageSummary(message, 24)
20
21 if !strings.HasSuffix(summary, " [image]") {
22 t.Fatalf("summary = %q, want visible image indicator", summary)
23 }
24 if len([]rune(summary)) > 24 {
25 t.Fatalf("summary width = %d, want at most 24", len([]rune(summary)))
26 }
27}
28
29func TestBusySubmitQueuesClipboardImagePrompt(t *testing.T) {
30 agent := core.NewAgent(nil, "test-model", "", nil)
31 i := NewInteractive(InteractiveConfig{Agent: agent})
32 i.mu.Lock()
33 i.busy = true
34 i.mu.Unlock()
35 i.ed.SetValue("inspect [clipboard image #1]")
36 i.clipboardImages = []clipboardImageAttachment{testClipboardImage("[clipboard image #1]", "png-1")}
37
38 i.handleKey(context.Background(), tui.Key{Kind: tui.KeyEnter})
39
40 queued := agent.PendingQueuedMessages()
41 if len(queued) != 1 || queued[0].Text != "inspect" {
42 t.Fatalf("queued messages = %#v, want one inspect prompt", queued)
43 }
44 if len(queued[0].Images) != 1 || string(queued[0].Images[0].Data) != "png-1" {
45 t.Fatalf("queued images = %#v, want png-1", queued[0].Images)
46 }
47 if !i.ed.IsEmpty() || len(i.clipboardImages) != 0 {
48 t.Fatal("submitted image prompt remained in the editor")
49 }
50}
51
52func TestSlideBackRestoresQueuedImagesToEditor(t *testing.T) {
53 agent := core.NewAgent(nil, "test-model", "", nil)
54 image := testClipboardImage("unused", "png-1").Image
55 agent.QueueMessage("inspect", []provider.ImageBlock{image})
56 i := NewInteractive(InteractiveConfig{Agent: agent})
57 i.mu.Lock()
58 i.busy = true
59 i.mu.Unlock()
60
61 i.handleKey(context.Background(), tui.Key{Kind: tui.KeyUp, Alt: true})
62
63 if got := i.ed.Value(); got != "inspect [clipboard image #1]" {
64 t.Fatalf("editor = %q, want restored image marker", got)
65 }
66 if len(i.clipboardImages) != 1 || string(i.clipboardImages[0].Image.Data) != "png-1" {
67 t.Fatalf("clipboard images = %#v, want png-1", i.clipboardImages)
68 }
69}
70
71func TestEscapeRestoresMostRecentQueuedMessageToEditor(t *testing.T) {
72 agent := core.NewAgent(nil, "test-model", "", nil)
73 agent.QueueMessage("older follow-up", nil)
74 agent.QueueMessage("recover this draft", nil)
75
76 i := NewInteractive(InteractiveConfig{Agent: agent})
77 i.ed.SetValue("existing draft")
78 cancelled := 0
79 i.mu.Lock()
80 i.busy = true
81 i.cancelTurn = func() { cancelled++ }
82 i.mu.Unlock()
83
84 if done := i.handleKey(context.Background(), tui.Key{Kind: tui.KeyEsc}); done {
85 t.Fatal("Escape exited")
86 }
87 if cancelled != 1 {
88 t.Fatalf("cancelled = %d, want 1", cancelled)
89 }
90 if got, want := i.ed.Value(), "recover this draft"; got != want {
91 t.Errorf("editor = %q, want %q", got, want)
92 }
93 if got := agent.PendingQueuedMessages(); len(got) != 1 || got[0].Text != "older follow-up" {
94 t.Errorf("remaining queued messages = %v, want [older follow-up]", got)
95 }
96}
97
98func TestEscapeRestoresHostQueuedMessageToEditor(t *testing.T) {
99 i := NewInteractive(InteractiveConfig{})
100 cancelled := 0
101 i.mu.Lock()
102 i.busy = true
103 i.cancelTurn = func() { cancelled++ }
104 i.queued = []core.QueuedMessage{{Text: "recover this draft"}}
105 i.mu.Unlock()
106
107 i.handleKey(context.Background(), tui.Key{Kind: tui.KeyEsc})
108
109 if cancelled != 1 {
110 t.Fatalf("cancelled = %d, want 1", cancelled)
111 }
112 if got, want := i.ed.Value(), "recover this draft"; got != want {
113 t.Errorf("editor = %q, want %q", got, want)
114 }
115}