c0487ecddb
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
167 lines
5.3 KiB
Go
167 lines
5.3 KiB
Go
package api
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/go-chi/chi/v5"
|
|
)
|
|
|
|
// newLibraryRouter builds a test-only chi router with the library handlers
|
|
// mounted at their path-style routes. Tests hit this router rather than
|
|
// calling handler methods directly so chi URL params populate correctly.
|
|
// RequireUser is NOT applied — library handlers don't read user context.
|
|
func newLibraryRouter(h *handlers) chi.Router {
|
|
r := chi.NewRouter()
|
|
r.Get("/api/artists", h.handleListArtists)
|
|
r.Get("/api/artists/{id}", h.handleGetArtist)
|
|
r.Get("/api/albums/{id}", h.handleGetAlbum)
|
|
r.Get("/api/tracks/{id}", h.handleGetTrack)
|
|
r.Get("/api/search", h.handleSearch)
|
|
return r
|
|
}
|
|
|
|
func TestHandleGetTrack_HappyPath(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
truncateLibrary(t, pool)
|
|
artist := seedArtist(t, pool, "Beatles")
|
|
album := seedAlbum(t, pool, artist.ID, "Abbey Road", 1969)
|
|
track := seedTrack(t, pool, album.ID, artist.ID, "Something", 3, 183_000)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/tracks/"+uuidToString(track.ID), 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 TrackRef
|
|
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
|
t.Fatalf("decode: %v body=%s", err, w.Body.String())
|
|
}
|
|
if got.Title != "Something" || got.AlbumTitle != "Abbey Road" || got.ArtistName != "Beatles" {
|
|
t.Errorf("ref = %+v", got)
|
|
}
|
|
if got.TrackNumber != 3 || got.DurationSec != 183 {
|
|
t.Errorf("positions = %+v", got)
|
|
}
|
|
want := "/api/tracks/" + uuidToString(track.ID) + "/stream"
|
|
if got.StreamURL != want {
|
|
t.Errorf("stream_url = %q, want %q", got.StreamURL, want)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetTrack_BadUUID(t *testing.T) {
|
|
h, _ := testHandlers(t)
|
|
req := httptest.NewRequest(http.MethodGet, "/api/tracks/not-a-uuid", nil)
|
|
w := httptest.NewRecorder()
|
|
newLibraryRouter(h).ServeHTTP(w, req)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("status = %d, want 400", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetTrack_NotFound(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
truncateLibrary(t, pool)
|
|
req := httptest.NewRequest(http.MethodGet,
|
|
"/api/tracks/00000000-0000-0000-0000-000000000001", nil)
|
|
w := httptest.NewRecorder()
|
|
newLibraryRouter(h).ServeHTTP(w, req)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("status = %d, want 404", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetAlbum_HappyPath(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
truncateLibrary(t, pool)
|
|
artist := seedArtist(t, pool, "Beatles")
|
|
album := seedAlbum(t, pool, artist.ID, "Abbey Road", 1969)
|
|
seedTrack(t, pool, album.ID, artist.ID, "Come Together", 1, 259_000)
|
|
seedTrack(t, pool, album.ID, artist.ID, "Something", 2, 183_000)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/albums/"+uuidToString(album.ID), 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 AlbumDetail
|
|
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
|
t.Fatalf("decode: %v body=%s", err, w.Body.String())
|
|
}
|
|
if got.Title != "Abbey Road" || got.ArtistName != "Beatles" || got.Year != 1969 {
|
|
t.Errorf("album = %+v", got)
|
|
}
|
|
if len(got.Tracks) != 2 {
|
|
t.Fatalf("tracks len = %d, want 2", len(got.Tracks))
|
|
}
|
|
if got.Tracks[0].Title != "Come Together" || got.Tracks[1].Title != "Something" {
|
|
t.Errorf("tracks = %+v", got.Tracks)
|
|
}
|
|
if got.TrackCount != 2 || got.DurationSec != 442 { // 259 + 183
|
|
t.Errorf("counts = track=%d duration=%d", got.TrackCount, got.DurationSec)
|
|
}
|
|
want := "/api/albums/" + uuidToString(album.ID) + "/cover"
|
|
if got.CoverURL != want {
|
|
t.Errorf("cover_url = %q", got.CoverURL)
|
|
}
|
|
// StreamURL on nested tracks must be set
|
|
if got.Tracks[0].StreamURL == "" {
|
|
t.Error("nested track missing stream_url")
|
|
}
|
|
}
|
|
|
|
func TestHandleGetAlbum_NoTracks(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
truncateLibrary(t, pool)
|
|
artist := seedArtist(t, pool, "Beatles")
|
|
album := seedAlbum(t, pool, artist.ID, "Empty", 0)
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/api/albums/"+uuidToString(album.ID), 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 AlbumDetail
|
|
_ = json.Unmarshal(w.Body.Bytes(), &got)
|
|
if got.Tracks == nil {
|
|
t.Error("tracks = nil, want [] for JSON encoding")
|
|
}
|
|
if len(got.Tracks) != 0 {
|
|
t.Errorf("tracks len = %d, want 0", len(got.Tracks))
|
|
}
|
|
// Year omitempty: releaseYear=0 => Year=0 => field should be absent from JSON
|
|
if got.Year != 0 {
|
|
t.Errorf("year = %d, want 0", got.Year)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetAlbum_BadUUID(t *testing.T) {
|
|
h, _ := testHandlers(t)
|
|
req := httptest.NewRequest(http.MethodGet, "/api/albums/not-a-uuid", nil)
|
|
w := httptest.NewRecorder()
|
|
newLibraryRouter(h).ServeHTTP(w, req)
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("status = %d, want 400", w.Code)
|
|
}
|
|
}
|
|
|
|
func TestHandleGetAlbum_NotFound(t *testing.T) {
|
|
h, pool := testHandlers(t)
|
|
truncateLibrary(t, pool)
|
|
req := httptest.NewRequest(http.MethodGet,
|
|
"/api/albums/00000000-0000-0000-0000-000000000001", nil)
|
|
w := httptest.NewRecorder()
|
|
newLibraryRouter(h).ServeHTTP(w, req)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("status = %d, want 404", w.Code)
|
|
}
|
|
}
|