feat: Minstrel no longer talks to Lidarr: its connection, Delete via Lidarr and the unmonitor options are gone, and the track reports are plain quarantine (M502 #5525)
release / govulncheck (push) Successful in 15s
release / web (push) Failing after 17s
release / go (push) Successful in 1m13s
release / integration (push) Successful in 4m56s
release / android (push) Successful in 5m9s
release / Build signed APK (releases and dev) (push) Successful in 5m19s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 15s
release / web (push) Failing after 17s
release / go (push) Successful in 1m13s
release / integration (push) Successful in 4m56s
release / android (push) Successful in 5m9s
release / Build signed APK (releases and dev) (push) Successful in 5m19s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Skipped
release / Verify release artifacts (tag releases only) (push) Skipped
Migration 0091 drops lidarr_config and renames the quarantine tables, types, indexes and the history column off the Lidarr name. The Integrations Lidarr card, the Reports "Delete via Lidarr" action, the merge and remove-track unmonitor options, the Lidarr audit actions and error codes go, on the server, web and Android. The README describes acquisition as it is now. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,7 +4,6 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
@@ -15,41 +14,12 @@ import (
|
||||
|
||||
"git.fabledsword.com/bvandeusen/minstrel/internal/auth"
|
||||
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
||||
"git.fabledsword.com/bvandeusen/minstrel/internal/tracks"
|
||||
)
|
||||
|
||||
// fakeLidarrUnmonitorer captures UnmonitorTrack calls for assertion and lets
|
||||
// tests dictate the return error. Used to drive both the Lidarr-success and
|
||||
// Lidarr-failure branches without spinning up a stub HTTP server.
|
||||
type fakeLidarrUnmonitorer struct {
|
||||
calls []fakeUnmonitorCall
|
||||
err error
|
||||
}
|
||||
|
||||
type fakeUnmonitorCall struct {
|
||||
trackMbid string
|
||||
albumMbid string
|
||||
}
|
||||
|
||||
func (f *fakeLidarrUnmonitorer) UnmonitorTrack(_ context.Context, trackMbid, albumMbid string) error {
|
||||
f.calls = append(f.calls, fakeUnmonitorCall{trackMbid: trackMbid, albumMbid: albumMbid})
|
||||
return f.err
|
||||
}
|
||||
|
||||
// installTracksLidarrStub replaces h.tracks with a fresh tracks.Service whose
|
||||
// LidarrUnmonitorer is the supplied fake. Returns the fake so tests can
|
||||
// assert call counts and inject error returns.
|
||||
func installTracksLidarrStub(t *testing.T, h *handlers) *fakeLidarrUnmonitorer {
|
||||
t.Helper()
|
||||
fake := &fakeLidarrUnmonitorer{}
|
||||
h.tracks = tracks.NewService(h.pool, h.logger, fake, h.dataDir)
|
||||
return fake
|
||||
}
|
||||
|
||||
// newAdminTracksRouter builds a test chi router with the admin-tracks
|
||||
// endpoint. RequireAdmin middleware is applied; tests inject the user into
|
||||
// context manually (RequireUser is bypassed). Mirrors the pattern from
|
||||
// admin_lidarr_test.go and admin_quarantine_test.go.
|
||||
// admin_quarantine_test.go.
|
||||
func newAdminTracksRouter(h *handlers) chi.Router {
|
||||
r := chi.NewRouter()
|
||||
r.Route("/api/admin", func(admin chi.Router) {
|
||||
@@ -90,8 +60,8 @@ func doAdminTracksReqNoUser(t *testing.T, h *handlers, method, path, idParam str
|
||||
}
|
||||
|
||||
// seedTrackForRemoveTest creates a fresh artist+album+track on disk and
|
||||
// returns the track row. The mbid string is set on both track and album
|
||||
// when non-empty so the unmonitor branch has the data it needs.
|
||||
// returns the track row. The mbids are set on the track and album when
|
||||
// non-empty.
|
||||
func seedTrackForRemoveTest(t *testing.T, h *handlers, unique, trackMbid, albumMbid string) (dbq.Track, string) {
|
||||
t.Helper()
|
||||
artist := seedArtist(t, h.pool, "RemoveTest Artist "+unique)
|
||||
@@ -206,11 +176,9 @@ func TestHandleRemoveTrack_UnknownID404(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleRemoveTrack_HappyPath_NoUnmonitor verifies the success envelope
|
||||
// when the operator does NOT request unmonitor: 200 with deleted_track_id
|
||||
// (and the optional cascade fields when they fired) but NO
|
||||
// lidarr_unmonitor_failed key in the body.
|
||||
func TestHandleRemoveTrack_HappyPath_NoUnmonitor(t *testing.T) {
|
||||
// TestHandleRemoveTrack_HappyPath verifies the success envelope: 200 with
|
||||
// deleted_track_id and the optional cascade fields when they fired.
|
||||
func TestHandleRemoveTrack_HappyPath(t *testing.T) {
|
||||
h, pool := testHandlers(t)
|
||||
truncateLibrary(t, pool)
|
||||
admin := seedUser(t, pool, "admin", "pw", true)
|
||||
@@ -237,21 +205,6 @@ func TestHandleRemoveTrack_HappyPath_NoUnmonitor(t *testing.T) {
|
||||
if got.DeletedArtistID == nil {
|
||||
t.Error("deleted_artist_id missing; expected sole-track cascade")
|
||||
}
|
||||
if got.LidarrUnmonitorFailed != nil {
|
||||
t.Errorf("lidarr_unmonitor_failed should be omitted when unmonitor not requested; got %v",
|
||||
*got.LidarrUnmonitorFailed)
|
||||
}
|
||||
|
||||
// Verify the raw JSON has no `lidarr_unmonitor_failed` key — omitempty
|
||||
// on a *bool drops it, but we double-check the wire to catch
|
||||
// regressions.
|
||||
var raw map[string]any
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &raw); err != nil {
|
||||
t.Fatalf("decode raw: %v", err)
|
||||
}
|
||||
if _, ok := raw["lidarr_unmonitor_failed"]; ok {
|
||||
t.Error("wire body contained lidarr_unmonitor_failed when unmonitor was not requested")
|
||||
}
|
||||
|
||||
// File is gone, DB row is gone.
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
@@ -261,50 +214,3 @@ func TestHandleRemoveTrack_HappyPath_NoUnmonitor(t *testing.T) {
|
||||
t.Error("track row still exists")
|
||||
}
|
||||
}
|
||||
|
||||
// TestHandleRemoveTrack_UnmonitorFailureFlag verifies that when the
|
||||
// operator sends ?unmonitor=true AND the Lidarr call fails, the response
|
||||
// is still 200 (file + DB delete completed) but carries
|
||||
// lidarr_unmonitor_failed: true.
|
||||
func TestHandleRemoveTrack_UnmonitorFailureFlag(t *testing.T) {
|
||||
h, pool := testHandlers(t)
|
||||
truncateLibrary(t, pool)
|
||||
admin := seedUser(t, pool, "admin", "pw", true)
|
||||
|
||||
fake := installTracksLidarrStub(t, h)
|
||||
fake.err = errors.New("lidarr exploded")
|
||||
|
||||
tr, path := seedTrackForRemoveTest(t, h, "umon-fail", "track-mbid-x", "album-mbid-x")
|
||||
w := doAdminTracksReq(t, h, http.MethodDelete,
|
||||
"/api/admin/tracks/"+uuidToString(tr.ID)+"?unmonitor=true", admin)
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("status = %d, want 200; body = %s", w.Code, w.Body.String())
|
||||
}
|
||||
|
||||
var got removeTrackResponse
|
||||
if err := json.Unmarshal(w.Body.Bytes(), &got); err != nil {
|
||||
t.Fatalf("decode: %v; body = %s", err, w.Body.String())
|
||||
}
|
||||
if got.DeletedTrackID != uuidToString(tr.ID) {
|
||||
t.Errorf("deleted_track_id = %q, want %q", got.DeletedTrackID, uuidToString(tr.ID))
|
||||
}
|
||||
if got.LidarrUnmonitorFailed == nil || !*got.LidarrUnmonitorFailed {
|
||||
t.Errorf("lidarr_unmonitor_failed = %v, want true", got.LidarrUnmonitorFailed)
|
||||
}
|
||||
|
||||
// Lidarr was actually called with the right mbids.
|
||||
if len(fake.calls) != 1 {
|
||||
t.Fatalf("UnmonitorTrack call count = %d, want 1", len(fake.calls))
|
||||
}
|
||||
if fake.calls[0].trackMbid != "track-mbid-x" || fake.calls[0].albumMbid != "album-mbid-x" {
|
||||
t.Errorf("unmonitor call = %+v, want {track-mbid-x album-mbid-x}", fake.calls[0])
|
||||
}
|
||||
|
||||
// Destructive part still happened.
|
||||
if _, err := os.Stat(path); !os.IsNotExist(err) {
|
||||
t.Errorf("file still exists: %v", err)
|
||||
}
|
||||
if _, err := dbq.New(pool).GetTrackByID(context.Background(), tr.ID); err == nil {
|
||||
t.Error("track row still exists")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user