返回 DeepSeek-Reasonix
select_test.go
根目录 / internal / cli / select_test.go
1 package cli
2
3 import (
4 "testing"
5 )
6
7 func TestFrameLines(t *testing.T) {
8 tests := []struct {
9 name string
10 filtered int
11 termRows int
12 searching bool
13 wantLines int
14 }{
15 {
16 name: "small list no search",
17 filtered: 5,
18 termRows: 24,
19 searching: false,
20 wantLines: 4 + 5, // fixed(4) + items(5)
21 },
22 {
23 name: "small list with search",
24 filtered: 5,
25 termRows: 24,
26 searching: true,
27 wantLines: 5 + 5, // fixed(5 search) + items(5)
28 },
29 {
30 name: "list exceeds terminal height",
31 filtered: 100,
32 termRows: 24,
33 searching: false,
34 wantLines: 4 + 20, // fixed(4) + viewport(24-4=20)
35 },
36 {
37 name: "list exceeds terminal with search",
38 filtered: 100,
39 termRows: 24,
40 searching: true,
41 wantLines: 5 + 19, // fixed(5) + viewport(24-5=19)
42 },
43 {
44 name: "empty list",
45 filtered: 0,
46 termRows: 24,
47 searching: false,
48 wantLines: 4 + 0, // fixed(4) + viewport(0 items)
49 },
50 {
51 name: "tiny terminal",
52 filtered: 10,
53 termRows: 8,
54 searching: false,
55 wantLines: 4 + 4, // fixed(4) + viewport(8-4=4)
56 },
57 {
58 name: "tiny terminal with search",
59 filtered: 10,
60 termRows: 8,
61 searching: true,
62 wantLines: 5 + 3, // fixed(5) + viewport(8-5=3)
63 },
64 }
65 for _, tt := range tests {
66 t.Run(tt.name, func(t *testing.T) {
67 got := FrameLines(tt.filtered, tt.termRows, tt.searching)
68 if got != tt.wantLines {
69 t.Errorf("FrameLines(%d, %d, %v) = %d, want %d",
70 tt.filtered, tt.termRows, tt.searching, got, tt.wantLines)
71 }
72 })
73 }
74 }
75
76 func TestFrameLinesNeverExceedsTerminal(t *testing.T) {
77 // The frame must never print more lines than the terminal has rows.
78 // Otherwise the terminal scrolls and cursor repositioning drifts.
79 termRows := 24
80 for _, searching := range []bool{false, true} {
81 for n := 0; n <= 200; n++ {
82 lines := FrameLines(n, termRows, searching)
83 if lines > termRows {
84 t.Errorf("FrameLines(%d, %d, searching=%v) = %d, exceeds terminal",
85 n, termRows, searching, lines)
86 }
87 }
88 }
89 }
90
91 func TestMaxViewportBounds(t *testing.T) {
92 // Viewport must be at least 1 even on impossibly small terminals.
93 vp := maxViewport(10, 3, false)
94 if vp < 1 {
95 t.Errorf("maxViewport(10, 3, false) = %d, want >= 1", vp)
96 }
97 // When items < available rows, viewport equals items.
98 vp = maxViewport(3, 24, false)
99 if vp != 3 {
100 t.Errorf("maxViewport(3, 24, false) = %d, want 3", vp)
101 }
102 }
103
104 func TestFilterMenuItems(t *testing.T) {
105 items := []menuItem{
106 {name: "deepseek-v4", desc: "DeepSeek V4"},
107 {name: "gpt-4o", desc: "OpenAI GPT-4o"},
108 {name: "mimo-pro", desc: "MiMo Pro"},
109 }
110 // Empty query returns all.
111 if got := filterMenuItems(items, ""); len(got) != 3 {
112 t.Errorf("empty query: got %d, want 3", len(got))
113 }
114 // Case-insensitive match on name.
115 if got := filterMenuItems(items, "GPT"); len(got) != 1 || got[0].name != "gpt-4o" {
116 t.Errorf("GPT: got %v", got)
117 }
118 // Case-insensitive match on desc.
119 if got := filterMenuItems(items, "mimo"); len(got) != 1 || got[0].name != "mimo-pro" {
120 t.Errorf("mimo: got %v", got)
121 }
122 // No match.
123 if got := filterMenuItems(items, "claude"); len(got) != 0 {
124 t.Errorf("claude: got %d, want 0", len(got))
125 }
126 }
127
127 lines GO