1package url
2
3import "testing"
4
5// Test shortcuts for BuildSearchURL tests
6var testShortcuts = map[string]string{
7 "g": "https://google.com/search?q=%s",
8 "ddg": "https://duckduckgo.com/?q=%s",
9 "gh": "https://github.com/search?q=%s",
10 "gi": "https://google.com/search?tbm=isch&q=%s",
11}
12
13const testDefaultSearch = "https://duckduckgo.com/?q=%s"
14
15func TestBuildSearchURL(t *testing.T) {
16 tests := []struct {
17 name string
18 input string
19 shortcuts map[string]string
20 defaultSearch string
21 want string
22 }{
23 {
24 name: "bang shortcut google",
25 input: "!g golang",
26 shortcuts: testShortcuts,
27 defaultSearch: testDefaultSearch,
28 want: "https://google.com/search?q=golang",
29 },
30 {
31 name: "bang shortcut duckduckgo multi-word",
32 input: "!ddg rust async await",
33 shortcuts: testShortcuts,
34 defaultSearch: testDefaultSearch,
35 want: "https://duckduckgo.com/?q=rust async await",
36 },
37 {
38 name: "bang shortcut google images",
39 input: "!gi cats",
40 shortcuts: testShortcuts,
41 defaultSearch: testDefaultSearch,
42 want: "https://google.com/search?tbm=isch&q=cats",
43 },
44 {
45 name: "unknown bang falls back to default search",
46 input: "!unknown test query",
47 shortcuts: testShortcuts,
48 defaultSearch: testDefaultSearch,
49 want: "https://duckduckgo.com/?q=!unknown test query",
50 },
51 {
52 name: "url-like input gets normalized",
53 input: "example.com",
54 shortcuts: testShortcuts,
55 defaultSearch: testDefaultSearch,
56 want: "https://example.com",
57 },
58 {
59 name: "url with path",
60 input: "github.com/user/repo",
61 shortcuts: testShortcuts,
62 defaultSearch: testDefaultSearch,
63 want: "https://github.com/user/repo",
64 },
65 {
66 name: "url with scheme unchanged",
67 input: "https://example.com",
68 shortcuts: testShortcuts,
69 defaultSearch: testDefaultSearch,
70 want: "https://example.com",
71 },
72 {
73 name: "custom scheme dumb unchanged",
74 input: "dumb://settings",
75 shortcuts: testShortcuts,
76 defaultSearch: testDefaultSearch,
77 want: "dumb://settings",
78 },
79 {
80 name: "plain search query uses default",
81 input: "how to parse json",
82 shortcuts: testShortcuts,
83 defaultSearch: testDefaultSearch,
84 want: "https://duckduckgo.com/?q=how to parse json",
85 },
86 {
87 name: "empty input returns empty",
88 input: "",
89 shortcuts: testShortcuts,
90 defaultSearch: testDefaultSearch,
91 want: "",
92 },
93 {
94 name: "no shortcuts uses default for bang",
95 input: "!g test",
96 shortcuts: nil,
97 defaultSearch: testDefaultSearch,
98 want: "https://duckduckgo.com/?q=!g test",
99 },
100 {
101 name: "no default search returns input for plain query",
102 input: "plain query",
103 shortcuts: testShortcuts,
104 defaultSearch: "",
105 want: "plain query",
106 },
107 {
108 name: "bang without query treated as search",
109 input: "!g",
110 shortcuts: testShortcuts,
111 defaultSearch: testDefaultSearch,
112 want: "https://duckduckgo.com/?q=!g",
113 },
114 }
115
116 for _, tt := range tests {
117 t.Run(tt.name, func(t *testing.T) {
118 got := BuildSearchURL(tt.input, tt.shortcuts, tt.defaultSearch)
119 if got != tt.want {
120 t.Errorf("BuildSearchURL() = %q, want %q", got, tt.want)
121 }
122 })
123 }
124}
125
126func TestParseBangShortcut(t *testing.T) {
127 tests := []struct {
128 name string
129 input string
130 wantShortcut string
131 wantQuery string
132 wantFound bool
133 }{
134 {
135 name: "simple google search",
136 input: "!g golang",
137 wantShortcut: "g",
138 wantQuery: "golang",
139 wantFound: true,
140 },
141 {
142 name: "duckduckgo multi-word query",
143 input: "!ddg rust async await",
144 wantShortcut: "ddg",
145 wantQuery: "rust async await",
146 wantFound: true,
147 },
148 {
149 name: "github search",
150 input: "!gh opencode cli",
151 wantShortcut: "gh",
152 wantQuery: "opencode cli",
153 wantFound: true,
154 },
155 {
156 name: "google images",
157 input: "!gi cats",
158 wantShortcut: "gi",
159 wantQuery: "cats",
160 wantFound: true,
161 },
162 {
163 name: "youtube search",
164 input: "!yt music video",
165 wantShortcut: "yt",
166 wantQuery: "music video",
167 wantFound: true,
168 },
169 {
170 name: "no query after shortcut",
171 input: "!g",
172 wantShortcut: "",
173 wantQuery: "",
174 wantFound: false,
175 },
176 {
177 name: "empty query after space",
178 input: "!g ",
179 wantShortcut: "",
180 wantQuery: "",
181 wantFound: false,
182 },
183 {
184 name: "multiple spaces only",
185 input: "!g ",
186 wantShortcut: "",
187 wantQuery: "",
188 wantFound: false,
189 },
190 {
191 name: "empty shortcut",
192 input: "! query",
193 wantShortcut: "",
194 wantQuery: "",
195 wantFound: false,
196 },
197 {
198 name: "plain text no bang",
199 input: "plain text",
200 wantShortcut: "",
201 wantQuery: "",
202 wantFound: false,
203 },
204 {
205 name: "bang at end not supported",
206 input: "query !g",
207 wantShortcut: "",
208 wantQuery: "",
209 wantFound: false,
210 },
211 {
212 name: "url-like input",
213 input: "example.com",
214 wantShortcut: "",
215 wantQuery: "",
216 wantFound: false,
217 },
218 {
219 name: "empty input",
220 input: "",
221 wantShortcut: "",
222 wantQuery: "",
223 wantFound: false,
224 },
225 {
226 name: "just exclamation mark",
227 input: "!",
228 wantShortcut: "",
229 wantQuery: "",
230 wantFound: false,
231 },
232 {
233 name: "query with extra spaces trimmed",
234 input: "!g lots of spaces ",
235 wantShortcut: "g",
236 wantQuery: "lots of spaces",
237 wantFound: true,
238 },
239 {
240 name: "long shortcut key",
241 input: "!stackoverflow how to parse json",
242 wantShortcut: "stackoverflow",
243 wantQuery: "how to parse json",
244 wantFound: true,
245 },
246 }
247
248 for _, tt := range tests {
249 t.Run(tt.name, func(t *testing.T) {
250 gotShortcut, gotQuery, gotFound := ParseBangShortcut(tt.input)
251 if gotShortcut != tt.wantShortcut {
252 t.Errorf("ParseBangShortcut() shortcut = %q, want %q", gotShortcut, tt.wantShortcut)
253 }
254 if gotQuery != tt.wantQuery {
255 t.Errorf("ParseBangShortcut() query = %q, want %q", gotQuery, tt.wantQuery)
256 }
257 if gotFound != tt.wantFound {
258 t.Errorf("ParseBangShortcut() found = %v, want %v", gotFound, tt.wantFound)
259 }
260 })
261 }
262}