release / govulncheck (push) Successful in 18s
release / web (push) Successful in 1m26s
release / go (push) Successful in 1m45s
release / integration (push) Successful in 5m6s
release / android (push) Successful in 5m58s
release / Build signed APK (releases and dev) (push) Successful in 6m17s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 26s
release / Verify release artifacts (tag releases only) (push) Skipped
The operator chose option 2: an album holding a video rip that is the only copy of its song is searched in Lidarr (AlbumSearch), at most once a week and ten albums a pass. Lidarr grabs only when the quality profile allows an upgrade, so nothing is removed and the rip stays held back either way. - lidarr.Client.SearchAlbums posts the AlbumSearch command. - ListAlbumsWithSoleCopyRips finds the albums: a held-back rip with no present clean copy sharing its song or its pending group, and not searched in the last week (audit action lidarr_rip_search). - Suspect sources shows when Lidarr last searched the track's album. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
139 lines
4.9 KiB
Go
139 lines
4.9 KiB
Go
package lidarr
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"io"
|
|
"net/http"
|
|
"testing"
|
|
)
|
|
|
|
func TestListUnmappedTrackFiles(t *testing.T) {
|
|
c, srv := newTestClient(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/api/v1/trackfile" || r.URL.Query().Get("unmapped") != "true" {
|
|
t.Errorf("request = %s?%s, want /api/v1/trackfile?unmapped=true", r.URL.Path, r.URL.RawQuery)
|
|
}
|
|
_, _ = w.Write([]byte(`[{"id":7,"path":"/music/A/B (2017)/A - B - 02 - X.mp3","albumId":0}]`))
|
|
})
|
|
defer srv.Close()
|
|
|
|
got, err := c.ListUnmappedTrackFiles(context.Background())
|
|
if err != nil {
|
|
t.Fatalf("ListUnmappedTrackFiles: %v", err)
|
|
}
|
|
if len(got) != 1 || got[0].ID != 7 || got[0].Path != "/music/A/B (2017)/A - B - 02 - X.mp3" {
|
|
t.Errorf("got %+v", got)
|
|
}
|
|
}
|
|
|
|
func TestReleaseReads(t *testing.T) {
|
|
c, srv := newTestClient(func(w http.ResponseWriter, r *http.Request) {
|
|
switch {
|
|
case r.URL.Path == "/api/v1/album/4723":
|
|
_, _ = w.Write([]byte(`{"id":4723,"releases":[
|
|
{"id":10148,"title":"Humanz","format":"14x12\" Vinyl, Digital Media","trackCount":68,"mediumCount":15,"monitored":true},
|
|
{"id":10149,"title":"Humanz","format":"Digital Media","trackCount":40,"mediumCount":1,"monitored":false}]}`))
|
|
case r.URL.Path == "/api/v1/track" && r.URL.Query().Get("albumId") == "4723":
|
|
_, _ = w.Write([]byte(`[{"id":1,"title":"Ascension","mediumNumber":1,"trackFileId":9}]`))
|
|
case r.URL.Path == "/api/v1/track" && r.URL.Query().Get("albumReleaseId") == "10149":
|
|
_, _ = w.Write([]byte(`[{"id":2,"title":"Ascension","mediumNumber":1,"trackFileId":0}]`))
|
|
default:
|
|
t.Errorf("unexpected request %s?%s", r.URL.Path, r.URL.RawQuery)
|
|
w.WriteHeader(http.StatusNotFound)
|
|
}
|
|
})
|
|
defer srv.Close()
|
|
ctx := context.Background()
|
|
|
|
rels, err := c.GetAlbumReleases(ctx, 4723)
|
|
if err != nil {
|
|
t.Fatalf("GetAlbumReleases: %v", err)
|
|
}
|
|
if len(rels) != 2 || !rels[0].Monitored || rels[0].MediumCount != 15 || rels[1].TrackCount != 40 {
|
|
t.Errorf("releases = %+v", rels)
|
|
}
|
|
tr, err := c.ListAlbumTracks(ctx, 4723)
|
|
if err != nil || len(tr) != 1 || tr[0].TrackFileID != 9 {
|
|
t.Errorf("ListAlbumTracks = %+v, %v", tr, err)
|
|
}
|
|
tr, err = c.ListReleaseTracks(ctx, 10149)
|
|
if err != nil || len(tr) != 1 || tr[0].Title != "Ascension" {
|
|
t.Errorf("ListReleaseTracks = %+v, %v", tr, err)
|
|
}
|
|
}
|
|
|
|
// The PUT carries the whole resource back, with only the releases' monitored
|
|
// flags changed: exactly the chosen release is monitored, and a field Minstrel
|
|
// does not model survives the round trip.
|
|
func TestSetMonitoredRelease_RoundTripsTheResource(t *testing.T) {
|
|
var put map[string]any
|
|
c, srv := newTestClient(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.URL.Path != "/api/v1/album/4723" {
|
|
t.Errorf("path = %q", r.URL.Path)
|
|
}
|
|
switch r.Method {
|
|
case http.MethodGet:
|
|
_, _ = w.Write([]byte(`{"id":4723,"anyReleaseOk":false,"secondaryTypes":["Studio"],"releases":[
|
|
{"id":10148,"monitored":true},{"id":10149,"monitored":false}]}`))
|
|
case http.MethodPut:
|
|
b, _ := io.ReadAll(r.Body)
|
|
if err := json.Unmarshal(b, &put); err != nil {
|
|
t.Errorf("PUT body: %v", err)
|
|
}
|
|
w.WriteHeader(http.StatusAccepted)
|
|
}
|
|
})
|
|
defer srv.Close()
|
|
|
|
if err := c.SetMonitoredRelease(context.Background(), 4723, 10149); err != nil {
|
|
t.Fatalf("SetMonitoredRelease: %v", err)
|
|
}
|
|
rels := put["releases"].([]any)
|
|
if m := rels[0].(map[string]any)["monitored"]; m != false {
|
|
t.Errorf("old release monitored = %v, want false", m)
|
|
}
|
|
if m := rels[1].(map[string]any)["monitored"]; m != true {
|
|
t.Errorf("new release monitored = %v, want true", m)
|
|
}
|
|
if st, _ := put["secondaryTypes"].([]any); len(st) != 1 || st[0] != "Studio" {
|
|
t.Errorf("secondaryTypes = %v, want it carried through unchanged", put["secondaryTypes"])
|
|
}
|
|
}
|
|
|
|
func TestSetMonitoredRelease_UnknownReleaseSendsNothing(t *testing.T) {
|
|
c, srv := newTestClient(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method == http.MethodPut {
|
|
t.Error("PUT sent for a release the album does not have")
|
|
}
|
|
_, _ = w.Write([]byte(`{"id":4723,"releases":[{"id":10148,"monitored":true}]}`))
|
|
})
|
|
defer srv.Close()
|
|
|
|
if err := c.SetMonitoredRelease(context.Background(), 4723, 999); !errors.Is(err, ErrNotFound) {
|
|
t.Errorf("err = %v, want ErrNotFound", err)
|
|
}
|
|
}
|
|
|
|
func TestSearchAlbums_PostsTheCommand(t *testing.T) {
|
|
var got map[string]any
|
|
c, srv := newTestClient(func(w http.ResponseWriter, r *http.Request) {
|
|
if r.Method != http.MethodPost || r.URL.Path != "/api/v1/command" {
|
|
t.Errorf("request = %s %s, want POST /api/v1/command", r.Method, r.URL.Path)
|
|
}
|
|
b, _ := io.ReadAll(r.Body)
|
|
_ = json.Unmarshal(b, &got)
|
|
w.WriteHeader(http.StatusCreated)
|
|
_, _ = w.Write([]byte(`{"id":1,"name":"AlbumSearch"}`))
|
|
})
|
|
defer srv.Close()
|
|
|
|
if err := c.SearchAlbums(context.Background(), []int{4723, 12}); err != nil {
|
|
t.Fatalf("SearchAlbums: %v", err)
|
|
}
|
|
ids, _ := got["albumIds"].([]any)
|
|
if got["name"] != "AlbumSearch" || len(ids) != 2 || ids[0] != float64(4723) || ids[1] != float64(12) {
|
|
t.Errorf("command = %v", got)
|
|
}
|
|
}
|