feat: the library's albums go on the wanted list as held, once; after that the list is the truth (M502 #5567)
release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m24s
release / go (push) Successful in 1m44s
release / integration (push) Successful in 5m19s
release / android (push) Successful in 5m31s
release / Build signed APK (releases and dev) (push) Successful in 5m35s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m24s
release / go (push) Successful in 1m44s
release / integration (push) Successful in 5m19s
release / android (push) Successful in 5m31s
release / Build signed APK (releases and dev) (push) Successful in 5m35s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 25s
release / Verify release artifacts (tag releases only) (push) Skipped
A one-time job in the wanted worker lists every album in the library as a held want, one per release group, with a record that it happened (acquisition_settings.library_listed_at). Albums whose tags name only a release are named on MusicBrainz first, 120 a pass; what nothing names is counted and shown on the Held tab. Re-acquisition now wants back only albums on the list. Taking a held album off the list leaves its files, and it is never fetched back; albums put in the library folder by hand afterwards are not added. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -455,3 +455,135 @@ func seedUser(t *testing.T, pool *pgxpool.Pool) pgtype.UUID {
|
||||
}
|
||||
return u.ID
|
||||
}
|
||||
|
||||
// startListing clears the reset's "already listed", as a fresh install has it.
|
||||
func startListing(t *testing.T, pool *pgxpool.Pool) {
|
||||
t.Helper()
|
||||
if _, err := pool.Exec(context.Background(), `
|
||||
UPDATE acquisition_settings SET library_listing_started_at = NULL, library_listed_at = NULL, library_unlisted = 0`); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
// The library's albums go on the list as held, once (#5567). An album the
|
||||
// tags name by release only is named through MusicBrainz first; one nothing
|
||||
// names is counted, not listed. Taken off the list, an album stays off, and
|
||||
// one added to the library afterwards is never listed.
|
||||
func TestListLibrary_OnceAndThenTheListIsTheTruth_Integration(t *testing.T) {
|
||||
pool := newPool(t)
|
||||
ctx := context.Background()
|
||||
q := dbq.New(pool)
|
||||
s := newService(t, pool, newFakeMB(t))
|
||||
s.releaseGroup = func(_ context.Context, release string) (string, error) {
|
||||
if release == "rel-parklife" {
|
||||
return "rg-parklife", nil
|
||||
}
|
||||
return "", tags.ErrNotFound
|
||||
}
|
||||
blurAlbum := seedHeld(t, q, "Blur", "rel-blur", "rg-blur", 3)
|
||||
parklife := seedHeld(t, q, "Parklife", "rel-parklife", "", 2)
|
||||
_ = seedHeld(t, q, "Bootleg", "rel-bootleg", "", 1)
|
||||
// An album with a track gone: held from before the loss, so
|
||||
// re-acquisition still fetches it back.
|
||||
lost := seedHeld(t, q, "13", "rel-13", "rg-13", 2)
|
||||
if _, err := pool.Exec(ctx, `UPDATE tracks SET missing_since = now() - interval '1 day'
|
||||
WHERE album_id = $1 AND track_number = 1`, lost.ID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
startListing(t, pool)
|
||||
|
||||
if err := s.listLibrary(ctx, &PassResult{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for group, album := range map[string]pgtype.UUID{"rg-blur": blurAlbum.ID, "rg-parklife": parklife.ID, "rg-13": lost.ID} {
|
||||
w, err := q.GetWantByReleaseGroup(ctx, group)
|
||||
if err != nil {
|
||||
t.Fatalf("%s not listed: %v", group, err)
|
||||
}
|
||||
if w.Reason != ReasonLibrary || !w.FulfilledAt.Valid || w.AlbumID != album {
|
||||
t.Errorf("%s = %+v: a held library want about the album", group, w)
|
||||
}
|
||||
}
|
||||
if w, _ := q.GetWantByReleaseGroup(ctx, "rg-13"); !w.FulfilledAt.Time.Before(time.Now().Add(-23 * time.Hour)) {
|
||||
t.Errorf("an album missing a track is held from %v, not from before the loss", w.FulfilledAt.Time)
|
||||
}
|
||||
state, err := q.GetLibraryListing(ctx)
|
||||
if err != nil || !state.LibraryListedAt.Valid || state.LibraryUnlisted != 1 {
|
||||
t.Fatalf("listing = %+v %v: done, with the bootleg unlisted", state, err)
|
||||
}
|
||||
|
||||
// Off the list stays off; an album added later is never listed.
|
||||
w, _ := q.GetWantByReleaseGroup(ctx, "rg-blur")
|
||||
if _, err := s.RemoveWant(ctx, w.ID); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = seedHeld(t, q, "Think Tank", "rel-think", "rg-think", 2)
|
||||
if _, err := s.Pass(ctx); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for _, group := range []string{"rg-blur", "rg-think"} {
|
||||
if _, err := q.GetWantByReleaseGroup(ctx, group); err == nil {
|
||||
t.Errorf("%s is on the list", group)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// While MusicBrainz is switched off nothing can be named, so the listing
|
||||
// finishes with what it has rather than waiting for ever.
|
||||
func TestListLibrary_MusicBrainzOffFinishesWithWhatIsNamed_Integration(t *testing.T) {
|
||||
pool := newPool(t)
|
||||
ctx := context.Background()
|
||||
q := dbq.New(pool)
|
||||
s := newService(t, pool, newFakeMB(t))
|
||||
s.releaseGroup = func(context.Context, string) (string, error) { return "", tags.ErrSwitchedOff }
|
||||
_ = seedHeld(t, q, "Blur", "rel-blur", "rg-blur", 1)
|
||||
_ = seedHeld(t, q, "Parklife", "rel-parklife", "", 1)
|
||||
startListing(t, pool)
|
||||
if err := s.listLibrary(ctx, &PassResult{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
state, _ := q.GetLibraryListing(ctx)
|
||||
if !state.LibraryListedAt.Valid || state.LibraryUnlisted != 1 {
|
||||
t.Errorf("listing = %+v", state)
|
||||
}
|
||||
var named *string
|
||||
if err := pool.QueryRow(ctx, `SELECT release_group_mbid FROM albums WHERE title = 'Parklife'`).Scan(&named); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if named != nil {
|
||||
t.Errorf("an album was marked unnameable while MusicBrainz was off: %q", *named)
|
||||
}
|
||||
}
|
||||
|
||||
// A MusicBrainz that does not answer pauses the naming; the next pass goes on.
|
||||
func TestListLibrary_UnreachablePausesTheNaming_Integration(t *testing.T) {
|
||||
pool := newPool(t)
|
||||
ctx := context.Background()
|
||||
q := dbq.New(pool)
|
||||
s := newService(t, pool, newFakeMB(t))
|
||||
down := true
|
||||
s.releaseGroup = func(context.Context, string) (string, error) {
|
||||
if down {
|
||||
return "", tags.ErrTransient
|
||||
}
|
||||
return "rg-parklife", nil
|
||||
}
|
||||
_ = seedHeld(t, q, "Parklife", "rel-parklife", "", 1)
|
||||
startListing(t, pool)
|
||||
if err := s.listLibrary(ctx, &PassResult{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if state, _ := q.GetLibraryListing(ctx); state.LibraryListedAt.Valid {
|
||||
t.Fatal("finished while an album was still to be named")
|
||||
}
|
||||
down = false
|
||||
if err := s.listLibrary(ctx, &PassResult{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err := q.GetWantByReleaseGroup(ctx, "rg-parklife"); err != nil {
|
||||
t.Errorf("not listed once named: %v", err)
|
||||
}
|
||||
if state, _ := q.GetLibraryListing(ctx); !state.LibraryListedAt.Valid {
|
||||
t.Error("not finished")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user