Files
minstrel/internal/api/admin_covers_test.go
T
bvandeusen 62222d8438 fix(server,web/m7-cover-sources): forward-fix CI round 2
Three issues from the previous push:

1. internal/api/admin_covers_test.go used t.Context() (Go 1.24+) but
   go.mod declares go 1.23.0. Replace with context.Background() in
   the testHandlersWithCovers helper; add "context" import.

2. internal/server/server_test.go's New() call missed the
   *coverart.SettingsService argument added in T11. The new param
   sits between the cover-art-backfill cap (int) and the *library.Scanner
   pointer in the function signature; the test was using 12 args
   instead of 13. Insert nil at the right position in all six call sites.

3. integrations.test.ts had 11 failing tests after T13 added the
   cover-art-providers section. Both panels render "Save changes",
   "Test connection", and "API key" controls, so the existing
   Lidarr tests' role/text queries matched multiple elements. Add
   helper functions (lidarrSection, coverProvidersSection,
   providerCard) and scope every relevant query with within() so
   each panel's tests interact only with their own controls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-06 13:55:35 -04:00

135 lines
4.6 KiB
Go

package api
import (
"context"
"encoding/json"
"log/slog"
"net/http"
"net/http/httptest"
"os"
"testing"
"github.com/go-chi/chi/v5"
"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
"git.fabledsword.com/bvandeusen/minstrel/internal/coverart"
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
)
// newAdminCoversRouter builds a test chi router with the two cover admin
// endpoints. RequireAdmin middleware is applied; tests inject the user into
// context manually (RequireUser is bypassed).
func newAdminCoversRouter(h *handlers) chi.Router {
r := chi.NewRouter()
r.Route("/api/admin", func(admin chi.Router) {
admin.Use(auth.RequireAdmin())
admin.Post("/albums/{id}/cover/refetch", h.handleAdminAlbumRefetchCover)
admin.Post("/covers/refetch-missing", h.handleAdminBulkRefetchCovers)
})
return r
}
// doAdminCoversReq fires an HTTP request against the admin covers router
// with the given user in context.
func doAdminCoversReq(t *testing.T, h *handlers, method, path string, user dbq.User) *httptest.ResponseRecorder {
t.Helper()
req := httptest.NewRequest(method, path, nil)
req = withUser(req, user)
w := httptest.NewRecorder()
newAdminCoversRouter(h).ServeHTTP(w, req)
return w
}
// testHandlersWithCovers returns a handlers instance with a stub coverart
// Enricher wired in. No external providers are registered, so the
// sidecar-only path is exercised and cover_art_source lands as 'none' for
// albums without real track files.
func testHandlersWithCovers(t *testing.T) (*handlers, *coverart.Enricher) {
t.Helper()
h, pool := testHandlers(t)
settings, err := coverart.NewSettingsService(context.Background(), pool, slog.Default())
if err != nil {
t.Fatalf("NewSettingsService: %v", err)
}
enricher := coverart.NewEnricher(pool, slog.Default(), settings)
h.coverart = enricher
h.coverArtBackfillCap = 100
return h, enricher
}
func TestAdminAlbumRefetchCover_NonAdminReturns403(t *testing.T) {
if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
}
h, _ := testHandlersWithCovers(t)
user := seedUser(t, h.pool, "alice", "pw", false)
artist := seedArtist(t, h.pool, "TestArtist")
album := seedAlbum(t, h.pool, artist.ID, "TestAlbum", 0)
w := doAdminCoversReq(t, h, http.MethodPost,
"/api/admin/albums/"+uuidToString(album.ID)+"/cover/refetch", user)
if w.Code != http.StatusForbidden {
t.Errorf("status = %d, want 403; body=%s", w.Code, w.Body.String())
}
}
func TestAdminAlbumRefetchCover_AdminOK(t *testing.T) {
if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
}
h, _ := testHandlersWithCovers(t)
admin := seedUser(t, h.pool, "rootuser", "pw", true)
artist := seedArtist(t, h.pool, "TestArtist")
album := seedAlbum(t, h.pool, artist.ID, "TestAlbum", 0)
w := doAdminCoversReq(t, h, http.MethodPost,
"/api/admin/albums/"+uuidToString(album.ID)+"/cover/refetch", admin)
if w.Code != http.StatusOK {
t.Errorf("status = %d, want 200; body=%s", w.Code, w.Body.String())
}
var resp adminCoverRefetchResp
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode: %v", err)
}
// Album has no MBID and no sidecar, so source should be 'none'.
if resp.CoverArtSource == nil || *resp.CoverArtSource != "none" {
t.Errorf("source = %v, want 'none'", resp.CoverArtSource)
}
}
func TestAdminBulkRefetchCovers_AdminReturnsQueuedCount(t *testing.T) {
if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
}
h, _ := testHandlersWithCovers(t)
admin := seedUser(t, h.pool, "rootuser2", "pw", true)
artist := seedArtist(t, h.pool, "TestArtist")
_ = seedAlbum(t, h.pool, artist.ID, "Album1", 0)
_ = seedAlbum(t, h.pool, artist.ID, "Album2", 0)
w := doAdminCoversReq(t, h, http.MethodPost, "/api/admin/covers/refetch-missing", admin)
if w.Code != http.StatusOK {
t.Errorf("status = %d, want 200; body=%s", w.Code, w.Body.String())
}
var resp adminBulkRefetchResp
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
t.Fatalf("decode: %v", err)
}
if resp.Queued != h.coverArtBackfillCap {
t.Errorf("queued = %d, want %d (cap)", resp.Queued, h.coverArtBackfillCap)
}
}
func TestAdminBulkRefetchCovers_NonAdminReturns403(t *testing.T) {
if os.Getenv("MINSTREL_TEST_DATABASE_URL") == "" {
t.Skip("MINSTREL_TEST_DATABASE_URL not set")
}
h, _ := testHandlersWithCovers(t)
user := seedUser(t, h.pool, "alicebulk", "pw", false)
w := doAdminCoversReq(t, h, http.MethodPost, "/api/admin/covers/refetch-missing", user)
if w.Code != http.StatusForbidden {
t.Errorf("status = %d, want 403; body=%s", w.Code, w.Body.String())
}
}