1package usecase
2
3import (
4 "context"
5 "testing"
6)
7
8func TestFilterBangs(t *testing.T) {
9 shortcuts := map[string]SearchShortcut{
10 "ddg": {URL: "https://duckduckgo.com/?q=%s", Description: "DuckDuckGo search"},
11 "g": {URL: "https://google.com/search?q=%s", Description: "Google search"},
12 "gh": {URL: "https://github.com/search?q=%s", Description: "GitHub search"},
13 "n": {URL: "https://news.ycombinator.com/", Description: ""},
14 }
15 uc := NewSearchShortcutsUseCase(shortcuts)
16 ctx := context.Background()
17
18 cases := []struct {
19 name string
20 query string
21 wantKeys []string
22 wantDescr map[string]string
23 }{
24 {
25 name: "bang only returns all sorted",
26 query: "!",
27 wantKeys: []string{"ddg", "g", "gh", "n"},
28 },
29 {
30 name: "filters by prefix",
31 query: "!g",
32 wantKeys: []string{"g", "gh"},
33 },
34 {
35 name: "filters by prefix case-insensitive",
36 query: "!DD",
37 wantKeys: []string{"ddg"},
38 },
39 {
40 name: "stops prefix at space",
41 query: "!g query",
42 wantKeys: []string{"g", "gh"},
43 },
44 {
45 name: "falls back to url when description empty",
46 query: "!n",
47 wantKeys: []string{"n"},
48 wantDescr: map[string]string{
49 "n": "https://news.ycombinator.com/",
50 },
51 },
52 }
53
54 for _, tc := range cases {
55 t.Run(tc.name, func(t *testing.T) {
56 output := uc.FilterBangs(ctx, FilterBangsInput{Query: tc.query})
57 got := output.Suggestions
58 if len(got) != len(tc.wantKeys) {
59 t.Fatalf("len=%d want=%d", len(got), len(tc.wantKeys))
60 }
61 for i, wantKey := range tc.wantKeys {
62 if got[i].Key != wantKey {
63 t.Fatalf("idx %d key=%q want=%q", i, got[i].Key, wantKey)
64 }
65 if tc.wantDescr != nil {
66 if wantD, ok := tc.wantDescr[wantKey]; ok {
67 if got[i].Description != wantD {
68 t.Fatalf("key %q description=%q want=%q", wantKey, got[i].Description, wantD)
69 }
70 }
71 }
72 }
73 })
74 }
75}
76
77func TestDetectBangKey(t *testing.T) {
78 shortcuts := map[string]SearchShortcut{
79 "gh": {URL: "https://github.com/search?q=%s", Description: "GitHub search"},
80 "ddg": {URL: "https://duckduckgo.com/?q=%s", Description: ""},
81 }
82 uc := NewSearchShortcutsUseCase(shortcuts)
83 ctx := context.Background()
84
85 cases := []struct {
86 name string
87 query string
88 wantKey string
89 wantDescr string
90 }{
91 {name: "no bang prefix", query: "gh test", wantKey: "", wantDescr: ""},
92 {name: "bang only has no space", query: "!gh", wantKey: "", wantDescr: ""},
93 {name: "space at position 1", query: "! test", wantKey: "", wantDescr: ""},
94 {name: "unknown bang", query: "!nope test", wantKey: "", wantDescr: ""},
95 {name: "case-insensitive match", query: "!GH test", wantKey: "gh", wantDescr: "GitHub search"},
96 {name: "valid bang key", query: "!ddg test", wantKey: "ddg", wantDescr: "https://duckduckgo.com/?q=%s"},
97 {name: "description fallback to URL", query: "!DDG query", wantKey: "ddg", wantDescr: "https://duckduckgo.com/?q=%s"},
98 }
99
100 for _, tc := range cases {
101 t.Run(tc.name, func(t *testing.T) {
102 output := uc.DetectBangKey(ctx, DetectBangKeyInput{Query: tc.query})
103 if output.Key != tc.wantKey {
104 t.Fatalf("DetectBangKey(%q).Key=%q want=%q", tc.query, output.Key, tc.wantKey)
105 }
106 if output.Description != tc.wantDescr {
107 t.Fatalf("DetectBangKey(%q).Description=%q want=%q", tc.query, output.Description, tc.wantDescr)
108 }
109 })
110 }
111}
112
113func TestBuildNavigationText(t *testing.T) {
114 shortcuts := map[string]SearchShortcut{
115 "gh": {URL: "https://github.com/search?q=%s"},
116 "ddg": {URL: "https://duckduckgo.com/?q=%s"},
117 }
118 uc := NewSearchShortcutsUseCase(shortcuts)
119 ctx := context.Background()
120
121 cases := []struct {
122 name string
123 entryText string
124 want string
125 wantValid bool
126 }{
127 {name: "not a bang shortcut", entryText: "example.com", want: "", wantValid: false},
128 {name: "bang key without query", entryText: "!gh ", want: "", wantValid: false},
129 {name: "unknown bang key", entryText: "!nope test", want: "", wantValid: false},
130 {name: "normalizes key case", entryText: "!GH dumber", want: "!gh dumber", wantValid: true},
131 {name: "keeps query unchanged", entryText: "!ddg some query", want: "!ddg some query", wantValid: true},
132 }
133
134 for _, tc := range cases {
135 t.Run(tc.name, func(t *testing.T) {
136 output := uc.BuildNavigationText(ctx, BuildNavigationTextInput{EntryText: tc.entryText})
137 if output.Text != tc.want {
138 t.Fatalf("BuildNavigationText(%q).Text=%q want=%q", tc.entryText, output.Text, tc.want)
139 }
140 if output.Valid != tc.wantValid {
141 t.Fatalf("BuildNavigationText(%q).Valid=%v want=%v", tc.entryText, output.Valid, tc.wantValid)
142 }
143 })
144 }
145}
146
147func TestGetShortcut(t *testing.T) {
148 shortcuts := map[string]SearchShortcut{
149 "gh": {URL: "https://github.com/search?q=%s", Description: "GitHub"},
150 }
151 uc := NewSearchShortcutsUseCase(shortcuts)
152
153 cases := []struct {
154 name string
155 key string
156 wantOK bool
157 wantURL string
158 }{
159 {name: "exact match", key: "gh", wantOK: true, wantURL: "https://github.com/search?q=%s"},
160 {name: "case insensitive", key: "GH", wantOK: true, wantURL: "https://github.com/search?q=%s"},
161 {name: "not found", key: "unknown", wantOK: false, wantURL: ""},
162 }
163
164 for _, tc := range cases {
165 t.Run(tc.name, func(t *testing.T) {
166 shortcut, ok := uc.GetShortcut(tc.key)
167 if ok != tc.wantOK {
168 t.Fatalf("GetShortcut(%q) ok=%v want=%v", tc.key, ok, tc.wantOK)
169 }
170 if shortcut.URL != tc.wantURL {
171 t.Fatalf("GetShortcut(%q).URL=%q want=%q", tc.key, shortcut.URL, tc.wantURL)
172 }
173 })
174 }
175}