diff --git a/internal/api/playlists_discover_refresh_test.go b/internal/api/playlists_discover_refresh_test.go deleted file mode 100644 index 0134c622..00000000 --- a/internal/api/playlists_discover_refresh_test.go +++ /dev/null @@ -1,64 +0,0 @@ -package api - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/go-chi/chi/v5" - - "git.fabledsword.com/bvandeusen/minstrel/internal/auth" -) - -func newDiscoverRefreshRouter(h *handlers) chi.Router { - r := chi.NewRouter() - r.Route("/api", func(api chi.Router) { - api.Use(auth.RequireUser(h.pool)) - api.Post("/playlists/system/discover/refresh", h.handleDiscoverRefresh) - }) - return r -} - -func TestDiscoverRefresh_AuthenticatedUser_Returns200(t *testing.T) { - if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" { - t.Skip("MINSTREL_TEST_DATABASE_URL not set") - } - h, pool := testHandlers(t) - user := seedUser(t, pool, "discrefresh", "pw", false) - - req := httptest.NewRequest(http.MethodPost, "/api/playlists/system/discover/refresh", nil) - req = withUser(req, user) - rec := httptest.NewRecorder() - newDiscoverRefreshRouter(h).ServeHTTP(rec, req) - - if rec.Code != http.StatusOK { - t.Fatalf("status = %d, want 200; body=%s", rec.Code, rec.Body.String()) - } - var resp discoverRefreshResp - if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { - t.Fatalf("decode: %v", err) - } - // On a fresh user with no library, the build runs and emits a - // Discover playlist (or no row if library is truly empty). - // Either case is OK; we only assert the response shape decoded. - if resp.TrackCount < 0 { - t.Errorf("track_count = %d, want >= 0", resp.TrackCount) - } -} - -func TestDiscoverRefresh_NoAuth_Returns401(t *testing.T) { - if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" { - t.Skip("MINSTREL_TEST_DATABASE_URL not set") - } - h, _ := testHandlers(t) - - req := httptest.NewRequest(http.MethodPost, "/api/playlists/system/discover/refresh", nil) - rec := httptest.NewRecorder() - newDiscoverRefreshRouter(h).ServeHTTP(rec, req) - - if rec.Code != http.StatusUnauthorized { - t.Errorf("status = %d, want 401", rec.Code) - } -} diff --git a/internal/api/playlists_foryou_refresh_test.go b/internal/api/playlists_foryou_refresh_test.go deleted file mode 100644 index 76b6122a..00000000 --- a/internal/api/playlists_foryou_refresh_test.go +++ /dev/null @@ -1,67 +0,0 @@ -package api - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "os" - "testing" - - "github.com/go-chi/chi/v5" - - "git.fabledsword.com/bvandeusen/minstrel/internal/auth" -) - -func newForYouRefreshRouter(h *handlers) chi.Router { - r := chi.NewRouter() - r.Route("/api", func(api chi.Router) { - api.Use(auth.RequireUser(h.pool)) - api.Post("/playlists/system/for-you/refresh", h.handleForYouRefresh) - }) - return r -} - -func TestForYouRefresh_AuthenticatedUser_Returns200(t *testing.T) { - if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" { - t.Skip("MINSTREL_TEST_DATABASE_URL not set") - } - h, pool := testHandlers(t) - user := seedUser(t, pool, "foryourefresh", "pw", false) - - req := httptest.NewRequest(http.MethodPost, "/api/playlists/system/for-you/refresh", nil) - req = withUser(req, user) - rec := httptest.NewRecorder() - newForYouRefreshRouter(h).ServeHTTP(rec, req) - - if rec.Code != http.StatusOK { - t.Fatalf("status = %d, want 200; body=%s", rec.Code, rec.Body.String()) - } - var resp foryouRefreshResp - if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { - t.Fatalf("decode: %v", err) - } - if resp.TrackIDs == nil { - t.Errorf("track_ids = nil, want []string (empty array, not null)") - } - if int(resp.TrackCount) != len(resp.TrackIDs) { - // Acceptable mismatch only when some tracks were removed from - // the library between build and response. For a fresh test - // user the counts should match. - t.Errorf("track_count=%d, len(track_ids)=%d — mismatch", resp.TrackCount, len(resp.TrackIDs)) - } -} - -func TestForYouRefresh_NoAuth_Returns401(t *testing.T) { - if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" { - t.Skip("MINSTREL_TEST_DATABASE_URL not set") - } - h, _ := testHandlers(t) - - req := httptest.NewRequest(http.MethodPost, "/api/playlists/system/for-you/refresh", nil) - rec := httptest.NewRecorder() - newForYouRefreshRouter(h).ServeHTTP(rec, req) - - if rec.Code != http.StatusUnauthorized { - t.Errorf("status = %d, want 401", rec.Code) - } -} diff --git a/internal/api/playlists_system_test.go b/internal/api/playlists_system_test.go new file mode 100644 index 00000000..1a1570e1 --- /dev/null +++ b/internal/api/playlists_system_test.go @@ -0,0 +1,111 @@ +package api + +import ( + "encoding/json" + "net/http" + "net/http/httptest" + "os" + "testing" + + "github.com/go-chi/chi/v5" + + "git.fabledsword.com/bvandeusen/minstrel/internal/auth" +) + +// Generic registry-driven system-playlist endpoints (#411 R2). +// Replaces the former per-kind refresh handler tests. +func newSystemPlaylistRouter(h *handlers) chi.Router { + r := chi.NewRouter() + r.Route("/api", func(api chi.Router) { + api.Use(auth.RequireUser(h.pool)) + api.Post("/playlists/system/{kind}/refresh", h.handleSystemPlaylistRefresh) + api.Get("/playlists/system/{kind}/shuffle", h.handleSystemPlaylistShuffle) + }) + return r +} + +func TestSystemRefresh_ForYou_Returns200(t *testing.T) { + if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" { + t.Skip("MINSTREL_TEST_DATABASE_URL not set") + } + h, pool := testHandlers(t) + user := seedUser(t, pool, "sysforyou", "pw", false) + + req := httptest.NewRequest(http.MethodPost, "/api/playlists/system/for_you/refresh", nil) + req = withUser(req, user) + rec := httptest.NewRecorder() + newSystemPlaylistRouter(h).ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want 200; body=%s", rec.Code, rec.Body.String()) + } + var resp systemRefreshResp + if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { + t.Fatalf("decode: %v", err) + } + if resp.TrackIDs == nil { + t.Errorf("track_ids = nil, want [] (empty array, not null)") + } + if int(resp.TrackCount) != len(resp.TrackIDs) { + t.Errorf("track_count=%d, len(track_ids)=%d — mismatch", resp.TrackCount, len(resp.TrackIDs)) + } +} + +func TestSystemRefresh_Discover_Returns200(t *testing.T) { + if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" { + t.Skip("MINSTREL_TEST_DATABASE_URL not set") + } + h, pool := testHandlers(t) + user := seedUser(t, pool, "sysdiscover", "pw", false) + + req := httptest.NewRequest(http.MethodPost, "/api/playlists/system/discover/refresh", nil) + req = withUser(req, user) + rec := httptest.NewRecorder() + newSystemPlaylistRouter(h).ServeHTTP(rec, req) + + if rec.Code != http.StatusOK { + t.Fatalf("status = %d, want 200; body=%s", rec.Code, rec.Body.String()) + } + var resp systemRefreshResp + if err := json.Unmarshal(rec.Body.Bytes(), &resp); err != nil { + t.Fatalf("decode: %v", err) + } + if resp.TrackCount < 0 { + t.Errorf("track_count = %d, want >= 0", resp.TrackCount) + } +} + +// A non-singleton (songs_like_artist) or unknown kind isn't +// addressable by the generic endpoints → 404, not a build. +func TestSystemRefresh_NonSingletonKind_Returns404(t *testing.T) { + if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" { + t.Skip("MINSTREL_TEST_DATABASE_URL not set") + } + h, pool := testHandlers(t) + user := seedUser(t, pool, "sysbadkind", "pw", false) + + for _, kind := range []string{"songs_like_artist", "bogus"} { + req := httptest.NewRequest(http.MethodPost, "/api/playlists/system/"+kind+"/refresh", nil) + req = withUser(req, user) + rec := httptest.NewRecorder() + newSystemPlaylistRouter(h).ServeHTTP(rec, req) + if rec.Code != http.StatusNotFound { + t.Errorf("kind=%s: status = %d, want 404", kind, rec.Code) + } + } +} + +func TestSystemRefresh_NoAuth_Returns401(t *testing.T) { + if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" { + t.Skip("MINSTREL_TEST_DATABASE_URL not set") + } + h, _ := testHandlers(t) + + req := httptest.NewRequest(http.MethodPost, "/api/playlists/system/for_you/refresh", nil) + rec := httptest.NewRecorder() + newSystemPlaylistRouter(h).ServeHTTP(rec, req) + + if rec.Code != http.StatusUnauthorized { + t.Errorf("status = %d, want 401", rec.Code) + } +}