Files
silo-server/internal/api/handlers/libraries_match_test.go
T

176 lines
5.0 KiB
Go

package handlers
import (
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"github.com/go-chi/chi/v5"
)
// TestLibraryMatchUnmatchedItems_NilPool verifies that the unmatched-items
// endpoint returns a clear error when the database pool is not configured.
func TestLibraryMatchUnmatchedItems_NilPool(t *testing.T) {
h := &LibraryHandler{}
r := chi.NewRouter()
r.Get("/libraries/unmatched-items", h.HandleListUnmatchedItems)
req := httptest.NewRequest(http.MethodGet, "/libraries/unmatched-items", nil)
rec := httptest.NewRecorder()
r.ServeHTTP(rec, req)
if rec.Code != http.StatusInternalServerError {
t.Fatalf("expected 500 for nil pool, got %d: %s", rec.Code, rec.Body.String())
}
var resp errorResponse
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
t.Fatalf("decoding error response: %v", err)
}
if resp.Error != "internal_error" {
t.Errorf("expected error code 'internal_error', got %q", resp.Error)
}
}
// TestLibraryMatchUnmatchedItems_ResponseShape verifies that the
// unmatchedItemResponse type has the expected JSON field tags for the
// admin maintenance page. This is a compile-time structure test.
func TestLibraryMatchUnmatchedItems_ResponseShape(t *testing.T) {
item := unmatchedItemResponse{
ContentID: "abc-123",
Title: "Test Movie",
Year: 2024,
ContentType: "movie",
LibraryID: 1,
LibraryName: "Movies",
Status: "unmatched",
}
data, err := json.Marshal(item)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var m map[string]any
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("unmarshal: %v", err)
}
expectedKeys := []string{
"content_id", "title", "year", "content_type",
"library_id", "library_name", "status",
}
for _, key := range expectedKeys {
if _, ok := m[key]; !ok {
t.Errorf("expected JSON key %q in response, got keys: %v", key, keys(m))
}
}
}
// TestLibraryMatchStaleIDs_NilRepo verifies that the stale-IDs endpoint
// returns an empty array when the repository is nil, confirming backward
// compatibility after the new match endpoints are introduced.
func TestLibraryMatchStaleIDs_NilRepo(t *testing.T) {
h := &LibraryHandler{} // StaleIDRepo is nil
r := chi.NewRouter()
r.Get("/libraries/stale-ids", h.HandleListStaleIDs)
req := httptest.NewRequest(http.MethodGet, "/libraries/stale-ids", nil)
rec := httptest.NewRecorder()
r.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200 for nil StaleIDRepo, got %d: %s", rec.Code, rec.Body.String())
}
var resp []staleMediaIDResponse
if err := json.NewDecoder(rec.Body).Decode(&resp); err != nil {
t.Fatalf("decoding response: %v", err)
}
if len(resp) != 0 {
t.Errorf("expected empty stale IDs list, got %d entries", len(resp))
}
}
// TestLibraryMatchStaleIDs_ResponseShape verifies the stale media ID response
// JSON structure still contains the expected fields.
func TestLibraryMatchStaleIDs_ResponseShape(t *testing.T) {
item := staleMediaIDResponse{
ContentID: "content-1",
LibraryID: 2,
LibraryName: "TV Shows",
Title: "Test Show",
Year: 2023,
ContentType: "series",
Provider: "tvdb",
ProviderID: "12345",
FirstSeenAt: "2023-01-01T00:00:00Z",
LastSeenAt: "2023-06-01T00:00:00Z",
}
data, err := json.Marshal(item)
if err != nil {
t.Fatalf("marshal: %v", err)
}
var m map[string]any
if err := json.Unmarshal(data, &m); err != nil {
t.Fatalf("unmarshal: %v", err)
}
expectedKeys := []string{
"content_id", "library_id", "library_name", "title",
"year", "content_type", "provider", "provider_id",
"first_seen_at", "last_seen_at",
}
for _, key := range expectedKeys {
if _, ok := m[key]; !ok {
t.Errorf("expected JSON key %q in response, got keys: %v", key, keys(m))
}
}
}
// TestLibraryMatchRematch_DeprecatedStillRoutes verifies that the deprecated
// HandleRematchStaleID handler method still exists and can be registered as a
// route alongside the new match endpoints. This is a compile-time routing test.
func TestLibraryMatchRematch_DeprecatedStillRoutes(t *testing.T) {
h := &LibraryHandler{}
r := chi.NewRouter()
// Register both old and new endpoints to prove they coexist.
r.Post("/libraries/stale-ids/{contentID}/rematch", h.HandleRematchStaleID)
r.Get("/libraries/unmatched-items", h.HandleListUnmatchedItems)
r.Get("/libraries/stale-ids", h.HandleListStaleIDs)
// Verify the routes are registered by checking that a walk finds them.
found := map[string]bool{}
chi.Walk(r, func(method, route string, _ http.Handler, _ ...func(http.Handler) http.Handler) error {
found[method+" "+route] = true
return nil
})
expectedRoutes := []string{
"POST /libraries/stale-ids/{contentID}/rematch",
"GET /libraries/unmatched-items",
"GET /libraries/stale-ids",
}
for _, route := range expectedRoutes {
if !found[route] {
t.Errorf("expected route %q to be registered, registered routes: %v", route, found)
}
}
}
func keys(m map[string]any) []string {
out := make([]string, 0, len(m))
for k := range m {
out = append(out, k)
}
return out
}