1package usecase_test
2
3import (
4 "testing"
5
6 "github.com/bnema/dumber/internal/application/usecase"
7 "github.com/bnema/dumber/internal/domain/autocomplete"
8 "github.com/bnema/dumber/internal/domain/entity"
9 repomocks "github.com/bnema/dumber/internal/domain/repository/mocks"
10 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/mock"
12 "github.com/stretchr/testify/require"
13)
14
15func TestAutocompleteUseCase_GetSuggestion_EmptyInput(t *testing.T) {
16 ctx := testContext()
17 uc := usecase.NewAutocompleteUseCase(nil, nil, nil)
18
19 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: ""})
20
21 require.NotNil(t, output)
22 assert.False(t, output.Found)
23 assert.Nil(t, output.Suggestion)
24}
25
26func TestAutocompleteUseCase_GetSuggestion_NilDependencies(t *testing.T) {
27 ctx := testContext()
28 uc := usecase.NewAutocompleteUseCase(nil, nil, nil)
29
30 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: "github"})
31
32 require.NotNil(t, output)
33 assert.False(t, output.Found)
34}
35
36func TestAutocompleteUseCase_GetSuggestion_FavoritesPriority(t *testing.T) {
37 ctx := testContext()
38
39 favoriteRepo := repomocks.NewMockFavoriteRepository(t)
40 folderRepo := repomocks.NewMockFolderRepository(t)
41 tagRepo := repomocks.NewMockTagRepository(t)
42
43 favorites := []*entity.Favorite{
44 {ID: 1, URL: "https://github.com/user/repo", Title: "My Repo"},
45 {ID: 2, URL: "https://google.com", Title: "Google"},
46 }
47 favoriteRepo.EXPECT().GetAll(mock.Anything).Return(favorites, nil)
48
49 favoritesUC := usecase.NewManageFavoritesUseCase(favoriteRepo, folderRepo, tagRepo)
50 uc := usecase.NewAutocompleteUseCase(nil, favoritesUC, nil)
51
52 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: "github"})
53
54 require.NotNil(t, output)
55 assert.True(t, output.Found)
56 assert.NotNil(t, output.Suggestion)
57 assert.Equal(t, autocomplete.SourceFavorite, output.Suggestion.Source)
58 assert.Equal(t, "github.com/user/repo", output.Suggestion.FullText)
59 assert.Equal(t, ".com/user/repo", output.Suggestion.Suffix)
60 assert.Equal(t, "My Repo", output.Suggestion.Title)
61}
62
63func TestAutocompleteUseCase_GetSuggestion_HistoryFallback(t *testing.T) {
64 ctx := testContext()
65
66 // Favorites returns empty
67 favoriteRepo := repomocks.NewMockFavoriteRepository(t)
68 folderRepo := repomocks.NewMockFolderRepository(t)
69 tagRepo := repomocks.NewMockTagRepository(t)
70 favoriteRepo.EXPECT().GetAll(mock.Anything).Return([]*entity.Favorite{}, nil)
71
72 // History returns a match
73 historyRepo := repomocks.NewMockHistoryRepository(t)
74 historyMatches := []entity.HistoryMatch{
75 {Entry: &entity.HistoryEntry{URL: "https://news.ycombinator.com", Title: "Hacker News"}},
76 }
77 historyRepo.EXPECT().Search(mock.Anything, "news", 10).Return(historyMatches, nil)
78
79 favoritesUC := usecase.NewManageFavoritesUseCase(favoriteRepo, folderRepo, tagRepo)
80 historyUC := usecase.NewSearchHistoryUseCase(historyRepo)
81 uc := usecase.NewAutocompleteUseCase(historyUC, favoritesUC, nil)
82
83 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: "news"})
84
85 require.NotNil(t, output)
86 assert.True(t, output.Found)
87 assert.NotNil(t, output.Suggestion)
88 assert.Equal(t, autocomplete.SourceHistory, output.Suggestion.Source)
89 assert.Equal(t, "news.ycombinator.com", output.Suggestion.FullText)
90 assert.Equal(t, ".ycombinator.com", output.Suggestion.Suffix)
91}
92
93func TestAutocompleteUseCase_GetSuggestion_BangShortcut(t *testing.T) {
94 ctx := testContext()
95
96 shortcuts := map[string]usecase.SearchShortcut{
97 "gh": {URL: "https://github.com/search?q=%s", Description: "GitHub search"},
98 "ddg": {URL: "https://duckduckgo.com/?q=%s", Description: "DuckDuckGo"},
99 }
100 shortcutsUC := usecase.NewSearchShortcutsUseCase(shortcuts)
101 uc := usecase.NewAutocompleteUseCase(nil, nil, shortcutsUC)
102
103 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: "!g"})
104
105 require.NotNil(t, output)
106 assert.True(t, output.Found)
107 assert.NotNil(t, output.Suggestion)
108 assert.Equal(t, autocomplete.SourceBangShortcut, output.Suggestion.Source)
109 assert.Equal(t, "!gh", output.Suggestion.FullText)
110 assert.Equal(t, "h", output.Suggestion.Suffix)
111 assert.Equal(t, "GitHub search", output.Suggestion.Title)
112}
113
114func TestAutocompleteUseCase_GetSuggestion_BangWithoutPrefix(t *testing.T) {
115 ctx := testContext()
116
117 shortcuts := map[string]usecase.SearchShortcut{
118 "gh": {URL: "https://github.com/search?q=%s", Description: "GitHub search"},
119 }
120 shortcutsUC := usecase.NewSearchShortcutsUseCase(shortcuts)
121 uc := usecase.NewAutocompleteUseCase(nil, nil, shortcutsUC)
122
123 // Input without ! should not match bang shortcuts
124 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: "gh"})
125
126 require.NotNil(t, output)
127 assert.False(t, output.Found)
128}
129
130func TestAutocompleteUseCase_GetSuggestion_NoMatch(t *testing.T) {
131 ctx := testContext()
132
133 favoriteRepo := repomocks.NewMockFavoriteRepository(t)
134 folderRepo := repomocks.NewMockFolderRepository(t)
135 tagRepo := repomocks.NewMockTagRepository(t)
136 favoriteRepo.EXPECT().GetAll(mock.Anything).Return([]*entity.Favorite{}, nil)
137
138 historyRepo := repomocks.NewMockHistoryRepository(t)
139 historyRepo.EXPECT().Search(mock.Anything, "xyz123", 10).Return([]entity.HistoryMatch{}, nil)
140
141 favoritesUC := usecase.NewManageFavoritesUseCase(favoriteRepo, folderRepo, tagRepo)
142 historyUC := usecase.NewSearchHistoryUseCase(historyRepo)
143 uc := usecase.NewAutocompleteUseCase(historyUC, favoritesUC, nil)
144
145 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: "xyz123"})
146
147 require.NotNil(t, output)
148 assert.False(t, output.Found)
149}
150
151func TestAutocompleteUseCase_GetSuggestionForURL_EmptyInput(t *testing.T) {
152 ctx := testContext()
153 uc := usecase.NewAutocompleteUseCase(nil, nil, nil)
154
155 tests := []struct {
156 name string
157 input string
158 targetURL string
159 }{
160 {name: "empty input", input: "", targetURL: "https://example.com"},
161 {name: "empty URL", input: "exa", targetURL: ""},
162 {name: "both empty", input: "", targetURL: ""},
163 }
164
165 for _, tt := range tests {
166 t.Run(tt.name, func(t *testing.T) {
167 output := uc.GetSuggestionForURL(ctx, tt.input, tt.targetURL)
168 require.NotNil(t, output)
169 assert.False(t, output.Found)
170 })
171 }
172}
173
174func TestAutocompleteUseCase_GetSuggestionForURL_Match(t *testing.T) {
175 ctx := testContext()
176 uc := usecase.NewAutocompleteUseCase(nil, nil, nil)
177
178 tests := []struct {
179 name string
180 input string
181 targetURL string
182 wantSuffix string
183 wantMatchedURL string
184 }{
185 {
186 name: "prefix match without protocol",
187 input: "github",
188 targetURL: "https://github.com/user/repo",
189 wantSuffix: ".com/user/repo",
190 wantMatchedURL: "github.com/user/repo",
191 },
192 {
193 name: "prefix match with protocol",
194 input: "https://github",
195 targetURL: "https://github.com/user/repo",
196 wantSuffix: ".com/user/repo",
197 wantMatchedURL: "https://github.com/user/repo",
198 },
199 {
200 name: "case insensitive match",
201 input: "GITHUB",
202 targetURL: "https://github.com",
203 wantSuffix: ".com",
204 wantMatchedURL: "github.com",
205 },
206 }
207
208 for _, tt := range tests {
209 t.Run(tt.name, func(t *testing.T) {
210 output := uc.GetSuggestionForURL(ctx, tt.input, tt.targetURL)
211 require.NotNil(t, output)
212 assert.True(t, output.Found)
213 require.NotNil(t, output.Suggestion)
214 assert.Equal(t, tt.wantSuffix, output.Suggestion.Suffix)
215 assert.Equal(t, tt.wantMatchedURL, output.Suggestion.FullText)
216 })
217 }
218}
219
220func TestAutocompleteUseCase_GetSuggestionForURL_NoMatch(t *testing.T) {
221 ctx := testContext()
222 uc := usecase.NewAutocompleteUseCase(nil, nil, nil)
223
224 output := uc.GetSuggestionForURL(ctx, "xyz", "https://github.com")
225
226 require.NotNil(t, output)
227 assert.False(t, output.Found)
228}
229
230func TestAutocompleteUseCase_GetSuggestion_FavoritesOverHistory(t *testing.T) {
231 ctx := testContext()
232
233 // Both favorites and history have matches - favorites should win
234 favoriteRepo := repomocks.NewMockFavoriteRepository(t)
235 folderRepo := repomocks.NewMockFolderRepository(t)
236 tagRepo := repomocks.NewMockTagRepository(t)
237 favorites := []*entity.Favorite{
238 {ID: 1, URL: "https://github.com/favorite", Title: "Favorite Repo"},
239 }
240 favoriteRepo.EXPECT().GetAll(mock.Anything).Return(favorites, nil)
241
242 // History would also match, but should not be called because favorites matched
243 historyRepo := repomocks.NewMockHistoryRepository(t)
244 // Note: no EXPECT on historyRepo - it shouldn't be called
245
246 favoritesUC := usecase.NewManageFavoritesUseCase(favoriteRepo, folderRepo, tagRepo)
247 historyUC := usecase.NewSearchHistoryUseCase(historyRepo)
248 uc := usecase.NewAutocompleteUseCase(historyUC, favoritesUC, nil)
249
250 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: "github"})
251
252 require.NotNil(t, output)
253 assert.True(t, output.Found)
254 assert.Equal(t, autocomplete.SourceFavorite, output.Suggestion.Source)
255 assert.Equal(t, "Favorite Repo", output.Suggestion.Title)
256}
257
258func TestAutocompleteUseCase_GetSuggestion_HistoryNotPrefixMatch(t *testing.T) {
259 ctx := testContext()
260
261 // Favorites returns empty
262 favoriteRepo := repomocks.NewMockFavoriteRepository(t)
263 folderRepo := repomocks.NewMockFolderRepository(t)
264 tagRepo := repomocks.NewMockTagRepository(t)
265 favoriteRepo.EXPECT().GetAll(mock.Anything).Return([]*entity.Favorite{}, nil)
266
267 // History returns results but none are prefix matches
268 historyRepo := repomocks.NewMockHistoryRepository(t)
269 historyMatches := []entity.HistoryMatch{
270 // URL doesn't start with "git", so no prefix match
271 {Entry: &entity.HistoryEntry{URL: "https://example.com/git", Title: "Example"}},
272 }
273 historyRepo.EXPECT().Search(mock.Anything, "git", 10).Return(historyMatches, nil)
274
275 favoritesUC := usecase.NewManageFavoritesUseCase(favoriteRepo, folderRepo, tagRepo)
276 historyUC := usecase.NewSearchHistoryUseCase(historyRepo)
277 uc := usecase.NewAutocompleteUseCase(historyUC, favoritesUC, nil)
278
279 output := uc.GetSuggestion(ctx, usecase.GetSuggestionInput{Input: "git"})
280
281 require.NotNil(t, output)
282 assert.False(t, output.Found)
283}