2025-12-28 21:18:33 +00:00
|
|
|
package handlers
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
|
|
"veza-backend-api/internal/services"
|
|
|
|
|
|
|
|
|
|
"github.com/gin-gonic/gin"
|
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
|
|
|
"github.com/stretchr/testify/mock"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// MockSearchService implements SearchService interface for testing
|
|
|
|
|
type MockSearchService struct {
|
|
|
|
|
mock.Mock
|
|
|
|
|
}
|
|
|
|
|
|
feat(search): faceted filters (genre/key/BPM/year) + FacetSidebar UI (W4 Day 18)
Backend
- services/search_service.go : new SearchFilters struct (Genre,
MusicalKey, BPMMin, BPMMax, YearFrom, YearTo) + appendTrackFacets
helper that composes additional AND clauses onto the existing FTS
WHERE condition. Filters apply ONLY to the track query — users +
playlists ignore them silently (no relevant columns).
- handlers/search_handlers.go : new parseSearchFilters reads + bounds-
checks query params (BPM in [1,999], year in [1900,2100], min<=max).
Search() now passes filters into the service ; OTel span attribute
search.filtered surfaces whether facets were applied.
- elasticsearch/search_service.go : signature updated to match the
interface ; ES path doesn't translate facets yet (different filter
DSL needed) — logs a warning when facets arrive on this path.
- handlers/search_handlers_test.go : MockSearchService.Search updated
+ 4 mock.On call sites pass mock.Anything for the new filters arg.
Frontend
- services/api/search.ts : new SearchFacets shape ; searchApi.search
accepts an opts.facets bag. When non-empty, bypasses orval's typed
getSearch (its GetSearchParams pre-dates the new query params) and
uses apiClient.get directly with snake_case keys matching the
backend's parseSearchFilters().
- features/search/components/FacetSidebar.tsx (new) : sidebar with
genre + musical_key inputs (datalist suggestions), BPM min/max
pair, year from/to pair. Stateless ; SearchPage owns state.
data-testids on every control for E2E.
- features/search/components/search-page/useSearchPage.ts : facets
state stored in URL (genre, musical_key, bpm_min, bpm_max,
year_from, year_to) so deep links reproduce the result set.
300 ms debounce on facet changes.
- features/search/components/search-page/SearchPage.tsx : layout
switches to a 2-column grid (sidebar + results) when query is
non-empty ; discovery view keeps the full width when empty.
Collateral cleanup
- internal/api/routes_users.go : removed unused strconv + time
imports that were blocking the build (pre-existing dead imports
surfaced by the SearchServiceInterface signature change).
E2E
- tests/e2e/32-faceted-search.spec.ts : 4 tests. (36) backend rejects
bpm_min > bpm_max with 400. (37) out-of-range BPM rejected. (38)
valid range returns 200 with a tracks array. (39) UI — typing in
the sidebar updates URL query params within the 300 ms debounce.
Acceptance (Day 18) : promtool not relevant ; backend test suite
green for handlers + services + api ; TS strict pass ; E2E spec
covers the gates the roadmap acceptance asked for. The 'rock + BPM
120-130 = restricted results' assertion needs seed data with measurable
BPM (none today) — flagged in the spec as a follow-up to un-skip
once seed BPM data lands.
W4 progress : Day 16 done · Day 17 done · Day 18 done · Day 19
(HAProxy sticky WS) pending · Day 20 (k6 nightly) pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:33:35 +00:00
|
|
|
func (m *MockSearchService) Search(query string, types []string, filters *services.SearchFilters) (*services.SearchResult, error) {
|
|
|
|
|
args := m.Called(query, types, filters)
|
2025-12-28 21:18:33 +00:00
|
|
|
if args.Get(0) == nil {
|
|
|
|
|
return nil, args.Error(1)
|
|
|
|
|
}
|
|
|
|
|
return args.Get(0).(*services.SearchResult), args.Error(1)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-20 15:54:17 +00:00
|
|
|
func (m *MockSearchService) Suggestions(query string, limit int) (*services.SearchResult, error) {
|
|
|
|
|
args := m.Called(query, limit)
|
|
|
|
|
if args.Get(0) == nil {
|
|
|
|
|
return nil, args.Error(1)
|
|
|
|
|
}
|
|
|
|
|
return args.Get(0).(*services.SearchResult), args.Error(1)
|
|
|
|
|
}
|
|
|
|
|
|
2025-12-28 21:18:33 +00:00
|
|
|
func setupTestSearchRouter(mockService *MockSearchService) *gin.Engine {
|
|
|
|
|
gin.SetMode(gin.TestMode)
|
|
|
|
|
router := gin.New()
|
|
|
|
|
|
|
|
|
|
handler := NewSearchHandlersWithInterface(mockService)
|
|
|
|
|
|
|
|
|
|
api := router.Group("/api/v1")
|
|
|
|
|
{
|
|
|
|
|
api.GET("/search", handler.Search)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return router
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSearchHandlers_Search_Success(t *testing.T) {
|
|
|
|
|
// Setup
|
|
|
|
|
mockService := new(MockSearchService)
|
|
|
|
|
router := setupTestSearchRouter(mockService)
|
|
|
|
|
|
|
|
|
|
expectedResult := &services.SearchResult{
|
|
|
|
|
Tracks: []services.TrackResult{
|
|
|
|
|
{ID: "track-1", Title: "Test Track", Artist: "Test Artist", URL: "http://example.com/track-1"},
|
|
|
|
|
},
|
|
|
|
|
Users: []services.UserResult{
|
chore: consolidate CI, E2E, backend and frontend updates
- CI: workflows updates (cd, ci), remove playwright.yml
- E2E: global-setup, auth/playlists/profile specs
- Remove playwright-report and test-results artifacts from tracking
- Backend: auth, handlers, services, workers, migrations
- Frontend: components, features, vite config
- Add e2e-results.json to gitignore
- Docs: REMEDIATION_PROGRESS, audit archive
- Rust: chat-server, stream-server updates
2026-02-17 15:43:21 +00:00
|
|
|
{ID: "user-1", Username: "testuser", AvatarURL: "http://example.com/avatar.jpg"},
|
2025-12-28 21:18:33 +00:00
|
|
|
},
|
|
|
|
|
Playlists: []services.PlaylistResult{
|
|
|
|
|
{ID: "playlist-1", Name: "Test Playlist", Cover: "http://example.com/cover.jpg"},
|
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
|
feat(search): faceted filters (genre/key/BPM/year) + FacetSidebar UI (W4 Day 18)
Backend
- services/search_service.go : new SearchFilters struct (Genre,
MusicalKey, BPMMin, BPMMax, YearFrom, YearTo) + appendTrackFacets
helper that composes additional AND clauses onto the existing FTS
WHERE condition. Filters apply ONLY to the track query — users +
playlists ignore them silently (no relevant columns).
- handlers/search_handlers.go : new parseSearchFilters reads + bounds-
checks query params (BPM in [1,999], year in [1900,2100], min<=max).
Search() now passes filters into the service ; OTel span attribute
search.filtered surfaces whether facets were applied.
- elasticsearch/search_service.go : signature updated to match the
interface ; ES path doesn't translate facets yet (different filter
DSL needed) — logs a warning when facets arrive on this path.
- handlers/search_handlers_test.go : MockSearchService.Search updated
+ 4 mock.On call sites pass mock.Anything for the new filters arg.
Frontend
- services/api/search.ts : new SearchFacets shape ; searchApi.search
accepts an opts.facets bag. When non-empty, bypasses orval's typed
getSearch (its GetSearchParams pre-dates the new query params) and
uses apiClient.get directly with snake_case keys matching the
backend's parseSearchFilters().
- features/search/components/FacetSidebar.tsx (new) : sidebar with
genre + musical_key inputs (datalist suggestions), BPM min/max
pair, year from/to pair. Stateless ; SearchPage owns state.
data-testids on every control for E2E.
- features/search/components/search-page/useSearchPage.ts : facets
state stored in URL (genre, musical_key, bpm_min, bpm_max,
year_from, year_to) so deep links reproduce the result set.
300 ms debounce on facet changes.
- features/search/components/search-page/SearchPage.tsx : layout
switches to a 2-column grid (sidebar + results) when query is
non-empty ; discovery view keeps the full width when empty.
Collateral cleanup
- internal/api/routes_users.go : removed unused strconv + time
imports that were blocking the build (pre-existing dead imports
surfaced by the SearchServiceInterface signature change).
E2E
- tests/e2e/32-faceted-search.spec.ts : 4 tests. (36) backend rejects
bpm_min > bpm_max with 400. (37) out-of-range BPM rejected. (38)
valid range returns 200 with a tracks array. (39) UI — typing in
the sidebar updates URL query params within the 300 ms debounce.
Acceptance (Day 18) : promtool not relevant ; backend test suite
green for handlers + services + api ; TS strict pass ; E2E spec
covers the gates the roadmap acceptance asked for. The 'rock + BPM
120-130 = restricted results' assertion needs seed data with measurable
BPM (none today) — flagged in the spec as a follow-up to un-skip
once seed BPM data lands.
W4 progress : Day 16 done · Day 17 done · Day 18 done · Day 19
(HAProxy sticky WS) pending · Day 20 (k6 nightly) pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:33:35 +00:00
|
|
|
mockService.On("Search", "test", []string(nil), mock.Anything).Return(expectedResult, nil)
|
2025-12-28 21:18:33 +00:00
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
|
req, _ := http.NewRequest("GET", "/api/v1/search?q=test", nil)
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
|
mockService.AssertExpectations(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSearchHandlers_Search_WithTypes(t *testing.T) {
|
|
|
|
|
// Setup
|
|
|
|
|
mockService := new(MockSearchService)
|
|
|
|
|
router := setupTestSearchRouter(mockService)
|
|
|
|
|
|
|
|
|
|
expectedResult := &services.SearchResult{
|
|
|
|
|
Tracks: []services.TrackResult{
|
|
|
|
|
{ID: "track-1", Title: "Test Track", Artist: "Test Artist", URL: "http://example.com/track-1"},
|
|
|
|
|
},
|
|
|
|
|
Users: []services.UserResult{},
|
|
|
|
|
Playlists: []services.PlaylistResult{},
|
|
|
|
|
}
|
|
|
|
|
|
feat(search): faceted filters (genre/key/BPM/year) + FacetSidebar UI (W4 Day 18)
Backend
- services/search_service.go : new SearchFilters struct (Genre,
MusicalKey, BPMMin, BPMMax, YearFrom, YearTo) + appendTrackFacets
helper that composes additional AND clauses onto the existing FTS
WHERE condition. Filters apply ONLY to the track query — users +
playlists ignore them silently (no relevant columns).
- handlers/search_handlers.go : new parseSearchFilters reads + bounds-
checks query params (BPM in [1,999], year in [1900,2100], min<=max).
Search() now passes filters into the service ; OTel span attribute
search.filtered surfaces whether facets were applied.
- elasticsearch/search_service.go : signature updated to match the
interface ; ES path doesn't translate facets yet (different filter
DSL needed) — logs a warning when facets arrive on this path.
- handlers/search_handlers_test.go : MockSearchService.Search updated
+ 4 mock.On call sites pass mock.Anything for the new filters arg.
Frontend
- services/api/search.ts : new SearchFacets shape ; searchApi.search
accepts an opts.facets bag. When non-empty, bypasses orval's typed
getSearch (its GetSearchParams pre-dates the new query params) and
uses apiClient.get directly with snake_case keys matching the
backend's parseSearchFilters().
- features/search/components/FacetSidebar.tsx (new) : sidebar with
genre + musical_key inputs (datalist suggestions), BPM min/max
pair, year from/to pair. Stateless ; SearchPage owns state.
data-testids on every control for E2E.
- features/search/components/search-page/useSearchPage.ts : facets
state stored in URL (genre, musical_key, bpm_min, bpm_max,
year_from, year_to) so deep links reproduce the result set.
300 ms debounce on facet changes.
- features/search/components/search-page/SearchPage.tsx : layout
switches to a 2-column grid (sidebar + results) when query is
non-empty ; discovery view keeps the full width when empty.
Collateral cleanup
- internal/api/routes_users.go : removed unused strconv + time
imports that were blocking the build (pre-existing dead imports
surfaced by the SearchServiceInterface signature change).
E2E
- tests/e2e/32-faceted-search.spec.ts : 4 tests. (36) backend rejects
bpm_min > bpm_max with 400. (37) out-of-range BPM rejected. (38)
valid range returns 200 with a tracks array. (39) UI — typing in
the sidebar updates URL query params within the 300 ms debounce.
Acceptance (Day 18) : promtool not relevant ; backend test suite
green for handlers + services + api ; TS strict pass ; E2E spec
covers the gates the roadmap acceptance asked for. The 'rock + BPM
120-130 = restricted results' assertion needs seed data with measurable
BPM (none today) — flagged in the spec as a follow-up to un-skip
once seed BPM data lands.
W4 progress : Day 16 done · Day 17 done · Day 18 done · Day 19
(HAProxy sticky WS) pending · Day 20 (k6 nightly) pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:33:35 +00:00
|
|
|
mockService.On("Search", "test", []string{"track"}, mock.Anything).Return(expectedResult, nil)
|
2025-12-28 21:18:33 +00:00
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
|
req, _ := http.NewRequest("GET", "/api/v1/search?q=test&type=track", nil)
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
|
mockService.AssertExpectations(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSearchHandlers_Search_MissingQuery(t *testing.T) {
|
|
|
|
|
// Setup
|
|
|
|
|
mockService := new(MockSearchService)
|
|
|
|
|
router := setupTestSearchRouter(mockService)
|
|
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
|
req, _ := http.NewRequest("GET", "/api/v1/search", nil)
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
|
mockService.AssertNotCalled(t, "Search")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSearchHandlers_Search_EmptyQuery(t *testing.T) {
|
|
|
|
|
// Setup
|
|
|
|
|
mockService := new(MockSearchService)
|
|
|
|
|
router := setupTestSearchRouter(mockService)
|
|
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
|
req, _ := http.NewRequest("GET", "/api/v1/search?q=", nil)
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
assert.Equal(t, http.StatusBadRequest, w.Code)
|
|
|
|
|
mockService.AssertNotCalled(t, "Search")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSearchHandlers_Search_ServiceError(t *testing.T) {
|
|
|
|
|
// Setup
|
|
|
|
|
mockService := new(MockSearchService)
|
|
|
|
|
router := setupTestSearchRouter(mockService)
|
|
|
|
|
|
feat(search): faceted filters (genre/key/BPM/year) + FacetSidebar UI (W4 Day 18)
Backend
- services/search_service.go : new SearchFilters struct (Genre,
MusicalKey, BPMMin, BPMMax, YearFrom, YearTo) + appendTrackFacets
helper that composes additional AND clauses onto the existing FTS
WHERE condition. Filters apply ONLY to the track query — users +
playlists ignore them silently (no relevant columns).
- handlers/search_handlers.go : new parseSearchFilters reads + bounds-
checks query params (BPM in [1,999], year in [1900,2100], min<=max).
Search() now passes filters into the service ; OTel span attribute
search.filtered surfaces whether facets were applied.
- elasticsearch/search_service.go : signature updated to match the
interface ; ES path doesn't translate facets yet (different filter
DSL needed) — logs a warning when facets arrive on this path.
- handlers/search_handlers_test.go : MockSearchService.Search updated
+ 4 mock.On call sites pass mock.Anything for the new filters arg.
Frontend
- services/api/search.ts : new SearchFacets shape ; searchApi.search
accepts an opts.facets bag. When non-empty, bypasses orval's typed
getSearch (its GetSearchParams pre-dates the new query params) and
uses apiClient.get directly with snake_case keys matching the
backend's parseSearchFilters().
- features/search/components/FacetSidebar.tsx (new) : sidebar with
genre + musical_key inputs (datalist suggestions), BPM min/max
pair, year from/to pair. Stateless ; SearchPage owns state.
data-testids on every control for E2E.
- features/search/components/search-page/useSearchPage.ts : facets
state stored in URL (genre, musical_key, bpm_min, bpm_max,
year_from, year_to) so deep links reproduce the result set.
300 ms debounce on facet changes.
- features/search/components/search-page/SearchPage.tsx : layout
switches to a 2-column grid (sidebar + results) when query is
non-empty ; discovery view keeps the full width when empty.
Collateral cleanup
- internal/api/routes_users.go : removed unused strconv + time
imports that were blocking the build (pre-existing dead imports
surfaced by the SearchServiceInterface signature change).
E2E
- tests/e2e/32-faceted-search.spec.ts : 4 tests. (36) backend rejects
bpm_min > bpm_max with 400. (37) out-of-range BPM rejected. (38)
valid range returns 200 with a tracks array. (39) UI — typing in
the sidebar updates URL query params within the 300 ms debounce.
Acceptance (Day 18) : promtool not relevant ; backend test suite
green for handlers + services + api ; TS strict pass ; E2E spec
covers the gates the roadmap acceptance asked for. The 'rock + BPM
120-130 = restricted results' assertion needs seed data with measurable
BPM (none today) — flagged in the spec as a follow-up to un-skip
once seed BPM data lands.
W4 progress : Day 16 done · Day 17 done · Day 18 done · Day 19
(HAProxy sticky WS) pending · Day 20 (k6 nightly) pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:33:35 +00:00
|
|
|
mockService.On("Search", "test", []string(nil), mock.Anything).Return(nil, assert.AnError)
|
2025-12-28 21:18:33 +00:00
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
|
req, _ := http.NewRequest("GET", "/api/v1/search?q=test", nil)
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
|
|
|
|
mockService.AssertExpectations(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestSearchHandlers_Search_MultipleTypes(t *testing.T) {
|
|
|
|
|
// Setup
|
|
|
|
|
mockService := new(MockSearchService)
|
|
|
|
|
router := setupTestSearchRouter(mockService)
|
|
|
|
|
|
|
|
|
|
expectedResult := &services.SearchResult{
|
|
|
|
|
Tracks: []services.TrackResult{
|
|
|
|
|
{ID: "track-1", Title: "Test Track", Artist: "Test Artist", URL: "http://example.com/track-1"},
|
|
|
|
|
},
|
|
|
|
|
Users: []services.UserResult{
|
chore: consolidate CI, E2E, backend and frontend updates
- CI: workflows updates (cd, ci), remove playwright.yml
- E2E: global-setup, auth/playlists/profile specs
- Remove playwright-report and test-results artifacts from tracking
- Backend: auth, handlers, services, workers, migrations
- Frontend: components, features, vite config
- Add e2e-results.json to gitignore
- Docs: REMEDIATION_PROGRESS, audit archive
- Rust: chat-server, stream-server updates
2026-02-17 15:43:21 +00:00
|
|
|
{ID: "user-1", Username: "testuser", AvatarURL: "http://example.com/avatar.jpg"},
|
2025-12-28 21:18:33 +00:00
|
|
|
},
|
|
|
|
|
Playlists: []services.PlaylistResult{},
|
|
|
|
|
}
|
|
|
|
|
|
feat(search): faceted filters (genre/key/BPM/year) + FacetSidebar UI (W4 Day 18)
Backend
- services/search_service.go : new SearchFilters struct (Genre,
MusicalKey, BPMMin, BPMMax, YearFrom, YearTo) + appendTrackFacets
helper that composes additional AND clauses onto the existing FTS
WHERE condition. Filters apply ONLY to the track query — users +
playlists ignore them silently (no relevant columns).
- handlers/search_handlers.go : new parseSearchFilters reads + bounds-
checks query params (BPM in [1,999], year in [1900,2100], min<=max).
Search() now passes filters into the service ; OTel span attribute
search.filtered surfaces whether facets were applied.
- elasticsearch/search_service.go : signature updated to match the
interface ; ES path doesn't translate facets yet (different filter
DSL needed) — logs a warning when facets arrive on this path.
- handlers/search_handlers_test.go : MockSearchService.Search updated
+ 4 mock.On call sites pass mock.Anything for the new filters arg.
Frontend
- services/api/search.ts : new SearchFacets shape ; searchApi.search
accepts an opts.facets bag. When non-empty, bypasses orval's typed
getSearch (its GetSearchParams pre-dates the new query params) and
uses apiClient.get directly with snake_case keys matching the
backend's parseSearchFilters().
- features/search/components/FacetSidebar.tsx (new) : sidebar with
genre + musical_key inputs (datalist suggestions), BPM min/max
pair, year from/to pair. Stateless ; SearchPage owns state.
data-testids on every control for E2E.
- features/search/components/search-page/useSearchPage.ts : facets
state stored in URL (genre, musical_key, bpm_min, bpm_max,
year_from, year_to) so deep links reproduce the result set.
300 ms debounce on facet changes.
- features/search/components/search-page/SearchPage.tsx : layout
switches to a 2-column grid (sidebar + results) when query is
non-empty ; discovery view keeps the full width when empty.
Collateral cleanup
- internal/api/routes_users.go : removed unused strconv + time
imports that were blocking the build (pre-existing dead imports
surfaced by the SearchServiceInterface signature change).
E2E
- tests/e2e/32-faceted-search.spec.ts : 4 tests. (36) backend rejects
bpm_min > bpm_max with 400. (37) out-of-range BPM rejected. (38)
valid range returns 200 with a tracks array. (39) UI — typing in
the sidebar updates URL query params within the 300 ms debounce.
Acceptance (Day 18) : promtool not relevant ; backend test suite
green for handlers + services + api ; TS strict pass ; E2E spec
covers the gates the roadmap acceptance asked for. The 'rock + BPM
120-130 = restricted results' assertion needs seed data with measurable
BPM (none today) — flagged in the spec as a follow-up to un-skip
once seed BPM data lands.
W4 progress : Day 16 done · Day 17 done · Day 18 done · Day 19
(HAProxy sticky WS) pending · Day 20 (k6 nightly) pending.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 08:33:35 +00:00
|
|
|
mockService.On("Search", "test", []string{"track", "user"}, mock.Anything).Return(expectedResult, nil)
|
2025-12-28 21:18:33 +00:00
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
|
req, _ := http.NewRequest("GET", "/api/v1/search?q=test&type=track&type=user", nil)
|
|
|
|
|
w := httptest.NewRecorder()
|
|
|
|
|
router.ServeHTTP(w, req)
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
assert.Equal(t, http.StatusOK, w.Code)
|
|
|
|
|
mockService.AssertExpectations(t)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func TestNewSearchHandlers(t *testing.T) {
|
|
|
|
|
// Setup
|
|
|
|
|
mockService := &services.SearchService{}
|
|
|
|
|
|
|
|
|
|
// Execute
|
|
|
|
|
NewSearchHandlers(mockService)
|
|
|
|
|
|
|
|
|
|
// Assert
|
|
|
|
|
assert.NotNil(t, SearchHandlersInstance)
|
|
|
|
|
assert.NotNil(t, SearchHandlersInstance.searchService)
|
|
|
|
|
}
|