0119eacf14
Sibling to /api/home that returns the same five sections (recently added, rediscover albums, rediscover artists, most played, last played) but as flat slices of entity ID strings instead of denormalized objects. The Flutter client uses this to drive its per-item rendering pass — small discovery response then per-tile hydration via the existing /api/albums/:id, /api/artists/:id, /api/tracks/:id endpoints. Reuses recommendation.HomeData so the DB cost is identical to /api/home. JSON payload shrinks roughly an order of magnitude on populated libraries (no embedded title / artist / cover URL fields). Old /api/home stays untouched so the web client and older Flutter builds keep working — no min-client-version bump needed until both clients have migrated.
144 lines
4.3 KiB
Go
144 lines
4.3 KiB
Go
package api
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
|
)
|
|
|
|
func newHomeRouter(h *handlers) chi.Router {
|
|
r := chi.NewRouter()
|
|
r.Get("/api/home", h.handleGetHome)
|
|
return r
|
|
}
|
|
|
|
func doGetHome(h *handlers, user dbq.User) *httptest.ResponseRecorder {
|
|
req := httptest.NewRequest(http.MethodGet, "/api/home", nil)
|
|
req = withUser(req, user)
|
|
w := httptest.NewRecorder()
|
|
newHomeRouter(h).ServeHTTP(w, req)
|
|
return w
|
|
}
|
|
|
|
func TestHome_EmptyForNewUser(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
user := seedUser(t, pool, "alice", "pw", false)
|
|
|
|
w := doGetHome(h, user)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, want 200; body = %s", w.Code, w.Body.String())
|
|
}
|
|
var got HomePayload
|
|
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
|
t.Fatalf("decode: %v", err)
|
|
}
|
|
// All slices must be non-nil so the SPA branches consistently on
|
|
// length rather than dealing with null.
|
|
if got.RecentlyAddedAlbums == nil || got.RediscoverAlbums == nil ||
|
|
got.RediscoverArtists == nil || got.MostPlayedTracks == nil ||
|
|
got.LastPlayedArtists == nil {
|
|
t.Errorf("payload has nil slice: %+v", got)
|
|
}
|
|
if len(got.RecentlyAddedAlbums) != 0 {
|
|
t.Errorf("RecentlyAddedAlbums len = %d, want 0", len(got.RecentlyAddedAlbums))
|
|
}
|
|
}
|
|
|
|
func TestHome_RecentlyAddedSurfacesAlbums(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
user := seedUser(t, pool, "alice", "pw", false)
|
|
q := dbq.New(pool)
|
|
artist, _ := q.UpsertArtist(context.Background(), dbq.UpsertArtistParams{
|
|
Name: "ArtistOne", SortName: "ArtistOne",
|
|
})
|
|
_, _ = q.UpsertAlbum(context.Background(), dbq.UpsertAlbumParams{
|
|
Title: "Newest", SortTitle: "Newest", ArtistID: artist.ID,
|
|
})
|
|
|
|
w := doGetHome(h, user)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, body = %s", w.Code, w.Body.String())
|
|
}
|
|
var got HomePayload
|
|
_ = json.Unmarshal(w.Body.Bytes(), &got)
|
|
if len(got.RecentlyAddedAlbums) != 1 {
|
|
t.Fatalf("len = %d, want 1", len(got.RecentlyAddedAlbums))
|
|
}
|
|
if got.RecentlyAddedAlbums[0].Title != "Newest" {
|
|
t.Errorf("title = %q, want Newest", got.RecentlyAddedAlbums[0].Title)
|
|
}
|
|
if got.RecentlyAddedAlbums[0].ArtistName != "ArtistOne" {
|
|
t.Errorf("artist_name = %q, want ArtistOne", got.RecentlyAddedAlbums[0].ArtistName)
|
|
}
|
|
}
|
|
|
|
func newHomeIndexRouter(h *handlers) chi.Router {
|
|
r := chi.NewRouter()
|
|
r.Get("/api/home/index", h.handleGetHomeIndex)
|
|
return r
|
|
}
|
|
|
|
func doGetHomeIndex(h *handlers, user dbq.User) *httptest.ResponseRecorder {
|
|
req := httptest.NewRequest(http.MethodGet, "/api/home/index", nil)
|
|
req = withUser(req, user)
|
|
w := httptest.NewRecorder()
|
|
newHomeIndexRouter(h).ServeHTTP(w, req)
|
|
return w
|
|
}
|
|
|
|
func TestHomeIndex_EmptyForNewUser(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
user := seedUser(t, pool, "alice", "pw", false)
|
|
|
|
w := doGetHomeIndex(h, user)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, want 200; body = %s", w.Code, w.Body.String())
|
|
}
|
|
var got HomeIndexPayload
|
|
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
|
t.Fatalf("decode: %v", err)
|
|
}
|
|
// Same non-nil slice contract as /api/home so client parsers can
|
|
// trust `length == 0` instead of branching on null.
|
|
if got.RecentlyAddedAlbums == nil || got.RediscoverAlbums == nil ||
|
|
got.RediscoverArtists == nil || got.MostPlayedTracks == nil ||
|
|
got.LastPlayedArtists == nil {
|
|
t.Errorf("payload has nil slice: %+v", got)
|
|
}
|
|
if len(got.RecentlyAddedAlbums) != 0 {
|
|
t.Errorf("RecentlyAddedAlbums len = %d, want 0", len(got.RecentlyAddedAlbums))
|
|
}
|
|
}
|
|
|
|
func TestHomeIndex_RecentlyAddedReturnsIDs(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
user := seedUser(t, pool, "alice", "pw", false)
|
|
q := dbq.New(pool)
|
|
artist, _ := q.UpsertArtist(context.Background(), dbq.UpsertArtistParams{
|
|
Name: "ArtistOne", SortName: "ArtistOne",
|
|
})
|
|
album, _ := q.UpsertAlbum(context.Background(), dbq.UpsertAlbumParams{
|
|
Title: "Newest", SortTitle: "Newest", ArtistID: artist.ID,
|
|
})
|
|
|
|
w := doGetHomeIndex(h, user)
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("status = %d, body = %s", w.Code, w.Body.String())
|
|
}
|
|
var got HomeIndexPayload
|
|
_ = json.Unmarshal(w.Body.Bytes(), &got)
|
|
if len(got.RecentlyAddedAlbums) != 1 {
|
|
t.Fatalf("len = %d, want 1", len(got.RecentlyAddedAlbums))
|
|
}
|
|
want := uuidToString(album.ID)
|
|
if got.RecentlyAddedAlbums[0] != want {
|
|
t.Errorf("id = %q, want %q", got.RecentlyAddedAlbums[0], want)
|
|
}
|
|
}
|