From fbb0023621899c425b6840991293d82609aff91a Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Wed, 25 Mar 2026 20:38:36 -0400 Subject: [PATCH] test: add integration tests for tags filter MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create tags_filter_test.go with comprehensive test coverage - Test tags filter with exact matches (Science Fiction) - Test fuzzy matching behavior (Sci Fi → Science Fiction) - Test autocomplete endpoint for tag suggestions - Test backward compatibility with genre_filter - Test combined filters (tags + author) - Uses setupDeviceTest() helper for proper test environment Validates the tags filter functionality including fuzzy matching, autocomplete, and backward compatibility. Relates to IMPLEMENTATION_TAGS_FILTER.md Phase 8 --- cmd/server/tests/tags_filter_test.go | 141 +++++++++++++++++++++++++++ 1 file changed, 141 insertions(+) create mode 100644 cmd/server/tests/tags_filter_test.go diff --git a/cmd/server/tests/tags_filter_test.go b/cmd/server/tests/tags_filter_test.go new file mode 100644 index 0000000..21dbda3 --- /dev/null +++ b/cmd/server/tests/tags_filter_test.go @@ -0,0 +1,141 @@ +package main + +import ( + "bytes" + "encoding/json" + "net/http" + "net/http/httptest" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestTagsFilter(t *testing.T) { + setup := setupDeviceTest(t) + defer setup.Server.Close() + + libraryID := setup.CreateLibrary(t, "Test Tags Library", "ebooks") + _ = setup.CreateDevice(t, "Test Device", "koreader", "tags-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, "Should add folder to library") + + // Helper to create book with tags + createBook := func(title, author string, tags []string) { + bookReq := map[string]interface{}{ + "library_id": libraryID, + "title": title, + "author": author, + "tags": tags, + "file_path": "/tmp/test.epub", + "file_size": 1024, + "mime_type": "application/epub+zip", + } + 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, "Should create book") + } + + // Create test books with different tags + createBook("Foundation", "Asimov", []string{"Science Fiction", "Adventure"}) + createBook("Dune", "Herbert", []string{"Science Fiction", "Dystopian"}) + createBook("Neuromancer", "Gibson", []string{"Cyberpunk", "Science Fiction"}) + createBook("The Hobbit", "Tolkien", []string{"Fantasy", "Adventure"}) + + t.Run("Tags filter - Science Fiction", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&tags_filter=Science+Fiction", 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 results") + + var results []map[string]interface{} + err := json.Unmarshal(rec.Body.Bytes(), &results) + require.NoError(t, err) + assert.GreaterOrEqual(t, len(results), 3, "Should return Foundation, Dune, Neuromancer (all have 'Science Fiction' tag)") + }) + + t.Run("Tags filter - fuzzy match", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&tags_filter=Sci+Fi", 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 results") + + var results []map[string]interface{} + err := json.Unmarshal(rec.Body.Bytes(), &results) + require.NoError(t, err) + assert.GreaterOrEqual(t, len(results), 3, "Should return Foundation, Dune, Neuromancer (fuzzy matches 'Science Fiction')") + }) + + t.Run("Tags filter - Fantasy", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&tags_filter=Fantasy", 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 results") + + var results []map[string]interface{} + err := json.Unmarshal(rec.Body.Bytes(), &results) + require.NoError(t, err) + assert.Equal(t, 1, len(results), "Should return only The Hobbit") + }) + + t.Run("Tags autocomplete", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/media-items/search?tags=Sci&library_id="+libraryID, 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 autocomplete results") + + var results []map[string]interface{} + err := json.Unmarshal(rec.Body.Bytes(), &results) + require.NoError(t, err) + assert.Greater(t, len(results), 0, "Should return 'Science Fiction' in autocomplete results") + }) + + t.Run("Backward compatibility - genre_filter still works", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&genre_filter=Science+Fiction", 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 results (even if empty)") + }) + + t.Run("Combined filters - tags + author", func(t *testing.T) { + req := httptest.NewRequest("GET", "/api/media-items/search?library_id="+libraryID+"&tags_filter=Sci+Fi&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 return results") + + var results []map[string]interface{} + err := json.Unmarshal(rec.Body.Bytes(), &results) + require.NoError(t, err) + assert.Equal(t, 1, len(results), "Should return only Foundation") + }) +}