package main import ( "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestUnifiedSearch(t *testing.T) { setup := setupDeviceTest(t) defer setup.Server.Close() libraryID := setup.CreateLibrary(t, "Test Search Library", "ebooks") _ = setup.CreateDevice(t, "Test Search Device", "koreader", "search-test-123") t.Run("Fuzzy author filter", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&author_filter=asimov", nil) req.Header.Set("Authorization", "Bearer "+setup.UserToken) rec := httptest.NewRecorder() setup.Server.Config.Handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code, "Should fuzzy match author") }) t.Run("Fuzzy genre filter", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&genre_filter=scifi", nil) req.Header.Set("Authorization", "Bearer "+setup.UserToken) rec := httptest.NewRecorder() setup.Server.Config.Handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code, "Should fuzzy match genre") }) t.Run("Exact match with quotes", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&q=%22Foundation%20and%20Empire%22", nil) req.Header.Set("Authorization", "Bearer "+setup.UserToken) rec := httptest.NewRecorder() setup.Server.Config.Handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code, "Should exact match quoted query") }) t.Run("Combined search + filters", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&q=foundation&author_filter=asimov", nil) req.Header.Set("Authorization", "Bearer "+setup.UserToken) rec := httptest.NewRecorder() setup.Server.Config.Handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code, "Should combine search and filters") }) t.Run("Field-specific search for dropdown - authors", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&authors=asimov", nil) req.Header.Set("Authorization", "Bearer "+setup.UserToken) rec := httptest.NewRecorder() setup.Server.Config.Handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code, "Should return author values") var response struct { Results []struct { Value string `json:"value"` Count int64 `json:"count"` Score float64 `json:"score"` } `json:"results"` Total int `json:"total"` } err := json.Unmarshal(rec.Body.Bytes(), &response) require.NoError(t, err, "Should unmarshal field values response") assert.Greater(t, len(response.Results), 0, "Should have results") }) t.Run("Year range filter (exact)", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&year_min=2000&year_max=2020", nil) req.Header.Set("Authorization", "Bearer "+setup.UserToken) rec := httptest.NewRecorder() setup.Server.Config.Handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code, "Should filter by year range") }) t.Run("Boolean filter (exact)", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&has_cover=true", nil) req.Header.Set("Authorization", "Bearer "+setup.UserToken) rec := httptest.NewRecorder() setup.Server.Config.Handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusOK, rec.Code, "Should filter by has_cover") }) t.Run("Missing library_id", func(t *testing.T) { req := httptest.NewRequest("GET", "/api/media-items/search?q=test", nil) req.Header.Set("Authorization", "Bearer "+setup.UserToken) rec := httptest.NewRecorder() setup.Server.Config.Handler.ServeHTTP(rec, req) assert.Equal(t, http.StatusBadRequest, rec.Code, "Should require library_id") }) }