62222d8438
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>
135 lines
4.6 KiB
Go
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())
|
|
}
|
|
}
|