package api import ( "encoding/json" "net/http" "net/http/httptest" "testing" ) func TestHandleSearch_ThreeFacets(t *testing.T) { h, pool := testHandlers(t) truncateLibrary(t, pool) artist := seedArtist(t, pool, "Beatles") album := seedAlbum(t, pool, artist.ID, "Beatles Greatest", 1982) seedTrack(t, pool, album.ID, artist.ID, "Beatles Jam", 1, 60_000) req := httptest.NewRequest(http.MethodGet, "/api/search?q=beatles", nil) w := httptest.NewRecorder() newLibraryRouter(h).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d body = %s", w.Code, w.Body.String()) } var got SearchResponse if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil { t.Fatalf("decode: %v", err) } if got.Artists.Total != 1 || len(got.Artists.Items) != 1 { t.Errorf("artists = %+v", got.Artists) } if got.Albums.Total != 1 || len(got.Albums.Items) != 1 { t.Errorf("albums = %+v", got.Albums) } if got.Tracks.Total != 1 || len(got.Tracks.Items) != 1 { t.Errorf("tracks = %+v", got.Tracks) } if got.Artists.Items[0].Name != "Beatles" { t.Errorf("artist name = %q", got.Artists.Items[0].Name) } if got.Albums.Items[0].ArtistName != "Beatles" { t.Errorf("album.artist_name = %q", got.Albums.Items[0].ArtistName) } if got.Tracks.Items[0].AlbumTitle != "Beatles Greatest" { t.Errorf("track.album_title = %q", got.Tracks.Items[0].AlbumTitle) } } func TestHandleSearch_NoMatches(t *testing.T) { h, pool := testHandlers(t) truncateLibrary(t, pool) seedArtist(t, pool, "Beatles") req := httptest.NewRequest(http.MethodGet, "/api/search?q=nothinghere", nil) w := httptest.NewRecorder() newLibraryRouter(h).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d", w.Code) } var got SearchResponse _ = json.Unmarshal(w.Body.Bytes(), &got) if got.Artists.Total != 0 || got.Albums.Total != 0 || got.Tracks.Total != 0 { t.Errorf("totals = %+v", got) } // All three item slices must JSON-encode as [] not null. if got.Artists.Items == nil || got.Albums.Items == nil || got.Tracks.Items == nil { t.Errorf("nil item slice: %+v", got) } } func TestHandleSearch_EmptyQuery(t *testing.T) { h, _ := testHandlers(t) req := httptest.NewRequest(http.MethodGet, "/api/search", nil) w := httptest.NewRecorder() newLibraryRouter(h).ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("status = %d, want 400", w.Code) } } func TestHandleSearch_BlankQuery(t *testing.T) { h, _ := testHandlers(t) req := httptest.NewRequest(http.MethodGet, "/api/search?q=%20%20", nil) w := httptest.NewRecorder() newLibraryRouter(h).ServeHTTP(w, req) if w.Code != http.StatusBadRequest { t.Errorf("status = %d, want 400", w.Code) } } func TestHandleSearch_PagingAppliedPerFacet(t *testing.T) { h, pool := testHandlers(t) truncateLibrary(t, pool) // Create three matching artists so paging shows. a1 := seedArtist(t, pool, "Match One") a2 := seedArtist(t, pool, "Match Two") a3 := seedArtist(t, pool, "Match Three") _ = a1 _ = a2 _ = a3 req := httptest.NewRequest(http.MethodGet, "/api/search?q=Match&limit=2&offset=1", nil) w := httptest.NewRecorder() newLibraryRouter(h).ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d", w.Code) } var got SearchResponse _ = json.Unmarshal(w.Body.Bytes(), &got) if got.Artists.Total != 3 { t.Errorf("total = %d, want 3", got.Artists.Total) } if got.Artists.Limit != 2 || got.Artists.Offset != 1 { t.Errorf("page = limit=%d offset=%d", got.Artists.Limit, got.Artists.Offset) } if len(got.Artists.Items) != 2 { t.Errorf("items len = %d, want 2", len(got.Artists.Items)) } }