release / govulncheck (push) Successful in 16s
release / web (push) Successful in 1m23s
release / go (push) Successful in 1m30s
release / integration (push) Successful in 5m0s
release / android (push) Successful in 5m1s
release / Build signed APK (releases and dev) (push) Successful in 5m9s
release / Attach APK to the Release (tag releases only) (push) Skipped
release / Build + push container image (push) Successful in 29s
release / Verify release artifacts (tag releases only) (push) Skipped
The scanner found an album by artist and title alone, so Weezer's six self-titled albums (1994–2026) were one 118-track row and their merges, wants and upgrades counted them as one. A same-named row whose release group is known and differs from the file's is now another album; tagReadVersion 7 re-reads the library so existing tracks move to their own rows. WantHoldings compared discs with IS NOT DISTINCT FROM, so a missing track 6 with no disc was never covered by a present disc 1 track 6. Air's Love 2 stayed wanted and was downloaded again for nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
175 lines
5.8 KiB
Go
175 lines
5.8 KiB
Go
package wanted
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/jackc/pgx/v5/pgtype"
|
|
"github.com/jackc/pgx/v5/pgxpool"
|
|
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/db/dbq"
|
|
"git.fabledsword.com/bvandeusen/minstrel/internal/release"
|
|
)
|
|
|
|
func TestUpgradeFor(t *testing.T) {
|
|
low := release.Held{Tier: release.TierLossyLow}
|
|
flac := release.Held{Tier: release.TierLossless}
|
|
high := release.Held{Tier: release.TierLossyHigh}
|
|
cases := []struct {
|
|
name string
|
|
held release.Held
|
|
judged bool
|
|
rips int
|
|
until release.Tier
|
|
want string
|
|
}{
|
|
{"below the cutoff", low, true, 0, release.TierLossyHigh, UpgradeQuality},
|
|
{"at the cutoff", high, true, 0, release.TierLossyHigh, ""},
|
|
{"above the cutoff", flac, true, 0, release.TierLossyHigh, ""},
|
|
{"upgrades off", low, true, 0, "", ""},
|
|
{"a rip, upgrades off", flac, true, 1, "", UpgradeRip},
|
|
{"nothing to judge", release.Held{}, false, 0, release.TierLossless, ""},
|
|
}
|
|
for _, c := range cases {
|
|
if got := UpgradeFor(c.held, c.judged, c.rips, c.until); got != c.want {
|
|
t.Errorf("%s: %q, want %q", c.name, got, c.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// seedMP3s puts Blur's The Ballad of Darren in the library as MP3s at about
|
|
// kbps, two tracks, the first held back as a video rip when rip is set.
|
|
func seedMP3s(t *testing.T, pool *pgxpool.Pool, kbps int64, rip bool) dbq.Album {
|
|
t.Helper()
|
|
ctx := context.Background()
|
|
q := dbq.New(pool)
|
|
mbid, group, rel := blur, theBallad, "r-ballad-mp3"
|
|
artist, err := q.UpsertArtist(ctx, dbq.UpsertArtistParams{Name: "Blur", SortName: "Blur", Mbid: &mbid})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
album, err := q.UpsertAlbum(ctx, dbq.UpsertAlbumParams{Title: "The Ballad of Darren", SortTitle: "Ballad of Darren",
|
|
ArtistID: artist.ID, Mbid: &rel, ReleaseGroupMbid: &group})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for i := range 2 {
|
|
num := int32(i + 1)
|
|
tr, err := q.UpsertTrack(ctx, dbq.UpsertTrackParams{
|
|
Title: fmt.Sprintf("Track %d", i+1), AlbumID: album.ID, ArtistID: artist.ID, TrackNumber: &num,
|
|
DurationMs: 200000, FilePath: fmt.Sprintf("/music/ballad/%02d.mp3", i+1),
|
|
FileSize: kbps * 200000 / 8, FileFormat: "mp3",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if rip && i == 0 {
|
|
if _, err := pool.Exec(ctx, `UPDATE tracks SET source_verdict = 'suspect' WHERE id = $1`, tr.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
}
|
|
return album
|
|
}
|
|
|
|
func TestCheckUpgrades_AHeldAlbumBelowTheCutoffIsWantedAgain_Integration(t *testing.T) {
|
|
pool := newPool(t)
|
|
q := dbq.New(pool)
|
|
ctx := context.Background()
|
|
s := newService(t, pool, newFakeMB(t))
|
|
|
|
if _, err := WantWith(ctx, q, WantParams{
|
|
GroupMBID: theBallad, ArtistMBID: blur, ArtistName: "Blur", Title: "The Ballad of Darren", Reason: ReasonRequest,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
seedMP3s(t, pool, 192, false)
|
|
res, err := s.Pass(ctx)
|
|
if err != nil || res.Held != 1 || res.Upgrades != 1 {
|
|
t.Fatalf("pass: %+v, %v", res, err)
|
|
}
|
|
w, _ := q.GetWantByReleaseGroup(ctx, theBallad)
|
|
if w.Upgrade != UpgradeQuality || w.HeldTier != string(release.TierLossyLow) || w.HeldQuality != "MP3, about 192 kbps" {
|
|
t.Fatalf("want = upgrade %q, tier %q, %q", w.Upgrade, w.HeldTier, w.HeldQuality)
|
|
}
|
|
due, err := q.ListWantsDueForSearch(ctx, dbq.ListWantsDueForSearchParams{Hours: 24, MaxRows: 10})
|
|
if err != nil || len(due) != 1 || due[0].Upgrade != UpgradeQuality {
|
|
t.Fatalf("due for search: %+v, %v", due, err)
|
|
}
|
|
|
|
// Upgrades switched off: no longer wanted again.
|
|
if _, err := pool.Exec(ctx, `UPDATE acquisition_settings SET upgrade_until = ''`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
s.upgradesChecked = time.Time{}
|
|
if _, err := s.Pass(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w, _ = q.GetWantByReleaseGroup(ctx, theBallad)
|
|
if w.Upgrade != "" {
|
|
t.Errorf("upgrades off: upgrade %q", w.Upgrade)
|
|
}
|
|
}
|
|
|
|
func TestCheckUpgrades_ARipIsReplacedWhateverTheSetting_Integration(t *testing.T) {
|
|
pool := newPool(t)
|
|
q := dbq.New(pool)
|
|
ctx := context.Background()
|
|
s := newService(t, pool, newFakeMB(t))
|
|
|
|
if _, err := WantWith(ctx, q, WantParams{
|
|
GroupMBID: theBallad, ArtistMBID: blur, ArtistName: "Blur", Title: "The Ballad of Darren", Reason: ReasonRequest,
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
seedMP3s(t, pool, 320, true)
|
|
if _, err := pool.Exec(ctx, `UPDATE acquisition_settings SET upgrade_until = ''`); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.Pass(ctx); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
w, _ := q.GetWantByReleaseGroup(ctx, theBallad)
|
|
if w.Upgrade != UpgradeRip {
|
|
t.Errorf("a rip: upgrade %q", w.Upgrade)
|
|
}
|
|
}
|
|
|
|
// A missing track with no disc number is on disc 1, so a present 1/2 stands in
|
|
// for a missing "2". Air's Love 2 stayed wanted over one such row and was
|
|
// downloaded again for nothing (2026-10-20). A missing track no present track
|
|
// covers still counts.
|
|
func TestWantHoldings_AMissingTrackWithNoDiscIsOnDiscOne_Integration(t *testing.T) {
|
|
pool := newPool(t)
|
|
ctx := context.Background()
|
|
album := seedMP3s(t, pool, 320, false)
|
|
if _, err := pool.Exec(ctx, `UPDATE tracks SET disc_number = 1 WHERE album_id = $1`, album.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
q := dbq.New(pool)
|
|
for _, n := range []int32{2, 3} {
|
|
num := n
|
|
tr, err := q.UpsertTrack(ctx, dbq.UpsertTrackParams{
|
|
Title: fmt.Sprintf("Stale %d", n), AlbumID: album.ID, ArtistID: album.ArtistID, TrackNumber: &num,
|
|
DurationMs: 200000, FilePath: fmt.Sprintf("/music/ballad/stale-%d.mp3", n), FileSize: 1, FileFormat: "mp3",
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := pool.Exec(ctx, `UPDATE tracks SET missing_since = now() - interval '2 days' WHERE id = $1`, tr.ID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
rows, err := q.WantHoldings(ctx, dbq.WantHoldingsParams{
|
|
Since: pgtype.Timestamptz{Time: time.Now(), Valid: true}, GroupMbid: theBallad, AlbumID: album.ID,
|
|
})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(rows) != 1 || rows[0].UncoveredMissing != 1 {
|
|
t.Fatalf("holdings = %+v, want one album with 1 uncovered missing track (track 3 only)", rows)
|
|
}
|
|
}
|