package main import ( "bytes" "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") client := &http.Client{} // Add folder to library (required before adding media items) folderReq := map[string]interface{}{ "folder_path": "/app/uploads", } folderBody, _ := json.Marshal(folderReq) folderHTTP, _ := http.NewRequest("POST", setup.Server.URL+"/api/libraries/"+libraryID+"/folders", bytes.NewBuffer(folderBody)) folderHTTP.Header.Set("Content-Type", "application/json") folderHTTP.Header.Set("Authorization", "Bearer "+setup.UserToken) folderResp, err := client.Do(folderHTTP) require.NoError(t, err) folderResp.Body.Close() require.Equal(t, http.StatusCreated, folderResp.StatusCode) // Helper to create book with fields createBook := func(title, author, genre string, year int, hasCover bool) { bookReq := map[string]interface{}{ "library_id": libraryID, "title": title, "author": author, "genre": genre, "copyright_year": year, "file_path": "/tmp/test.epub", "file_size": 1024, "mime_type": "application/epub+zip", } if hasCover { bookReq["cover_image_path"] = "/tmp/cover.jpg" } body, _ := json.Marshal(bookReq) req, _ := http.NewRequest("POST", setup.Server.URL+"/api/media-items", bytes.NewBuffer(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+setup.UserToken) resp, err := client.Do(req) require.NoError(t, err) defer resp.Body.Close() require.Equal(t, http.StatusCreated, resp.StatusCode) } // Create test books with various fields for filtering createBook("Foundation and Empire", "asimov", "scifi", 1951, true) createBook("The Martian", "weir", "scifi", 2010, true) createBook("I, Robot", "asimov", "fiction", 1950, false) t.Run("Fuzzy author filter", 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("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) // library_id is now optional - searches all libraries when omitted // Returns 404 when no results match the search query assert.Equal(t, http.StatusNotFound, rec.Code, "Should return 404 when no results found") }) }