test: add integration tests for tags filter

- 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
This commit is contained in:
2026-03-25 20:38:36 -04:00
parent ab86eec32f
commit fbb0023621
+141
View File
@@ -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")
})
}