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: "custom scheme spotify unchanged",
81 input: "spotify://track/123",
82 shortcuts: testShortcuts,
83 defaultSearch: testDefaultSearch,
84 want: "spotify://track/123",
85 },
86 {
87 name: "mailto scheme unchanged",
88 input: "mailto:foo@example.com",
89 shortcuts: testShortcuts,
90 defaultSearch: testDefaultSearch,
91 want: "mailto:foo@example.com",
92 },
93 {
94 name: "plain search query uses default",
95 input: "how to parse json",
96 shortcuts: testShortcuts,
97 defaultSearch: testDefaultSearch,
98 want: "https://duckduckgo.com/?q=how to parse json",
99 },
100 {
101 name: "empty input returns empty",
102 input: "",
103 shortcuts: testShortcuts,
104 defaultSearch: testDefaultSearch,
105 want: "",
106 },
107 {
108 name: "no shortcuts uses default for bang",
109 input: "!g test",
110 shortcuts: nil,
111 defaultSearch: testDefaultSearch,
112 want: "https://duckduckgo.com/?q=!g test",
113 },
114 {
115 name: "no default search returns input for plain query",
116 input: "plain query",
117 shortcuts: testShortcuts,
118 defaultSearch: "",
119 want: "plain query",
120 },
121 {
122 name: "bang without query treated as search",
123 input: "!g",
124 shortcuts: testShortcuts,
125 defaultSearch: testDefaultSearch,
126 want: "https://duckduckgo.com/?q=!g",
127 },
128 }
129
130 for _, tt := range tests {
131 t.Run(tt.name, func(t *testing.T) {
132 got := BuildSearchURL(tt.input, tt.shortcuts, tt.defaultSearch)
133 if got != tt.want {
134 t.Errorf("BuildSearchURL() = %q, want %q", got, tt.want)
135 }
136 })
137 }
138}
139
140func TestParseBangShortcut(t *testing.T) {
141 tests := []struct {
142 name string
143 input string
144 wantShortcut string
145 wantQuery string
146 wantFound bool
147 }{
148 {
149 name: "simple google search",
150 input: "!g golang",
151 wantShortcut: "g",
152 wantQuery: "golang",
153 wantFound: true,
154 },
155 {
156 name: "duckduckgo multi-word query",
157 input: "!ddg rust async await",
158 wantShortcut: "ddg",
159 wantQuery: "rust async await",
160 wantFound: true,
161 },
162 {
163 name: "github search",
164 input: "!gh opencode cli",
165 wantShortcut: "gh",
166 wantQuery: "opencode cli",
167 wantFound: true,
168 },
169 {
170 name: "google images",
171 input: "!gi cats",
172 wantShortcut: "gi",
173 wantQuery: "cats",
174 wantFound: true,
175 },
176 {
177 name: "youtube search",
178 input: "!yt music video",
179 wantShortcut: "yt",
180 wantQuery: "music video",
181 wantFound: true,
182 },
183 {
184 name: "no query after shortcut",
185 input: "!g",
186 wantShortcut: "",
187 wantQuery: "",
188 wantFound: false,
189 },
190 {
191 name: "empty query after space",
192 input: "!g ",
193 wantShortcut: "",
194 wantQuery: "",
195 wantFound: false,
196 },
197 {
198 name: "multiple spaces only",
199 input: "!g ",
200 wantShortcut: "",
201 wantQuery: "",
202 wantFound: false,
203 },
204 {
205 name: "empty shortcut",
206 input: "! query",
207 wantShortcut: "",
208 wantQuery: "",
209 wantFound: false,
210 },
211 {
212 name: "plain text no bang",
213 input: "plain text",
214 wantShortcut: "",
215 wantQuery: "",
216 wantFound: false,
217 },
218 {
219 name: "bang at end not supported",
220 input: "query !g",
221 wantShortcut: "",
222 wantQuery: "",
223 wantFound: false,
224 },
225 {
226 name: "url-like input",
227 input: "example.com",
228 wantShortcut: "",
229 wantQuery: "",
230 wantFound: false,
231 },
232 {
233 name: "empty input",
234 input: "",
235 wantShortcut: "",
236 wantQuery: "",
237 wantFound: false,
238 },
239 {
240 name: "just exclamation mark",
241 input: "!",
242 wantShortcut: "",
243 wantQuery: "",
244 wantFound: false,
245 },
246 {
247 name: "query with extra spaces trimmed",
248 input: "!g lots of spaces ",
249 wantShortcut: "g",
250 wantQuery: "lots of spaces",
251 wantFound: true,
252 },
253 {
254 name: "long shortcut key",
255 input: "!stackoverflow how to parse json",
256 wantShortcut: "stackoverflow",
257 wantQuery: "how to parse json",
258 wantFound: true,
259 },
260 }
261
262 for _, tt := range tests {
263 t.Run(tt.name, func(t *testing.T) {
264 gotShortcut, gotQuery, gotFound := ParseBangShortcut(tt.input)
265 if gotShortcut != tt.wantShortcut {
266 t.Errorf("ParseBangShortcut() shortcut = %q, want %q", gotShortcut, tt.wantShortcut)
267 }
268 if gotQuery != tt.wantQuery {
269 t.Errorf("ParseBangShortcut() query = %q, want %q", gotQuery, tt.wantQuery)
270 }
271 if gotFound != tt.wantFound {
272 t.Errorf("ParseBangShortcut() found = %v, want %v", gotFound, tt.wantFound)
273 }
274 })
275 }
276}