Files
Lyra/worker/tests/test_monitor_enqueue.py
T
Jonathan e8483ee22b fix(worker): match library ownership by rgMbid + retire stale failed attempts
Two related monitor bugs surfaced by owned albums that never leave the press:

1. Ownership was matched on (artist, album) strings, but MusicBrainz canonicalizes
   the artist on import ("Kanye West"->"Ye", "Ne-Yo"->"Ne‐Yo", "The Goo Goo Dolls"
   ->"Goo Goo Dolls"). The strings no longer matched, so reconcile/enqueue/backfill
   never recognized the album as owned — it stayed "wanted" and re-pressed every
   retry interval. Match on the release-group MBID when the library row has one
   (falling back to artist+album for scanned rows without an MBID) across
   _in_library_at_cutoff, fulfill_owned_releases, and reconcile.

2. Each failed attempt left a needs_attention Request behind, so a hard-to-match
   album piled up phantom "needs attention" cards on the press even after a later
   attempt imported it (e.g. Maroon 5 "V": 3 stale cards + 1 success). Retire prior
   failed attempts when a release is fulfilled or re-attempted (_retire_failed_attempts,
   monitor-owned requests only — manual requests have no monitoredReleaseId).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 17:04:31 +02:00

139 lines
6.1 KiB
Python

from lyra_worker.monitor import MonitorConfig, enqueue_due
from tests.conftest import insert_monitored_release
CFG = MonitorConfig(enabled=True, retry_interval_hours=6, quality_cutoff=2, upgrade_window_days=14)
def _job_count_for(conn, rel_id):
with conn.cursor() as cur:
cur.execute(
'SELECT count(*) FROM "Request" r JOIN "Job" j ON j."requestId" = r.id '
'WHERE r."monitoredReleaseId" = %s',
(rel_id,),
)
return cur.fetchone()[0]
def _state(conn, rel_id):
with conn.cursor() as cur:
cur.execute('SELECT state FROM "MonitoredRelease" WHERE id = %s', (rel_id,))
return cur.fetchone()[0]
def test_enqueues_a_wanted_release(conn):
rel_id = insert_monitored_release(conn, artist_name="John Mayer", album="Continuum",
rg_mbid="rg-1", monitored=True, state="wanted")
assert enqueue_due(conn, CFG) == 1
assert _job_count_for(conn, rel_id) == 1
with conn.cursor() as cur:
cur.execute(
'SELECT artist, album FROM "Request" WHERE "monitoredReleaseId" = %s', (rel_id,)
)
assert cur.fetchone() == ("John Mayer", "Continuum")
def test_skips_unmonitored_and_fulfilled(conn):
insert_monitored_release(conn, rg_mbid="rg-a", monitored=False, state="wanted")
insert_monitored_release(conn, rg_mbid="rg-b", monitored=True, state="fulfilled")
assert enqueue_due(conn, CFG) == 0
def test_skips_ignored_release(conn):
rel_id = insert_monitored_release(conn, rg_mbid="rg-ig", monitored=True, state="wanted")
with conn.cursor() as cur:
cur.execute('UPDATE "MonitoredRelease" SET ignored = true WHERE id = %s', (rel_id,))
conn.commit()
assert enqueue_due(conn, CFG) == 0 # ignored → never enqueued even though monitored+wanted
assert _job_count_for(conn, rel_id) == 0
def test_skips_when_recently_searched(conn):
insert_monitored_release(conn, rg_mbid="rg-1", monitored=True, state="wanted",
last_searched_sql="now() - interval '1 hour'") # < 6h retry
assert enqueue_due(conn, CFG) == 0
def test_skips_release_with_active_job(conn):
rel_id = insert_monitored_release(conn, rg_mbid="rg-1", monitored=True, state="wanted")
assert enqueue_due(conn, CFG) == 1 # first sweep enqueues
assert enqueue_due(conn, CFG) == 0 # active (requested) job blocks a second
def test_grabbed_within_window_below_cutoff_re_searches(conn):
rel_id = insert_monitored_release(conn, rg_mbid="rg-1", monitored=True, state="grabbed",
current_quality_class=1, first_grabbed_sql="now() - interval '1 day'")
assert enqueue_due(conn, CFG) == 1
def test_grabbed_past_window_is_fulfilled_not_enqueued(conn):
rel_id = insert_monitored_release(conn, rg_mbid="rg-1", monitored=True, state="grabbed",
current_quality_class=1, first_grabbed_sql="now() - interval '30 days'")
assert enqueue_due(conn, CFG) == 0
assert _state(conn, rel_id) == "fulfilled"
def test_owned_by_rgmbid_despite_name_mismatch_is_fulfilled(conn):
# The monitor followed "Kanye West" but the library stores MB's canonical "Ye"; matching on
# rgMbid recognises ownership so the release is fulfilled, not re-pressed.
rel_id = insert_monitored_release(conn, artist_name="Kanye West", album="Graduation",
rg_mbid="rg-grad", monitored=True, state="wanted")
with conn.cursor() as cur:
cur.execute(
'INSERT INTO "LibraryItem" (id, artist, album, path, source, format, '
'"qualityClass", "rgMbid", "importedAt") '
"VALUES (gen_random_uuid()::text, 'Ye', 'Graduation', '/m', 'soulseek', 'FLAC', 2, "
"'rg-grad', now())"
)
conn.commit()
assert enqueue_due(conn, CFG) == 0
assert _state(conn, rel_id) == "fulfilled"
def test_re_enqueue_retires_prior_failed_attempt(conn):
# A hard-to-match release re-pressed each retry interval must not pile up needs_attention
# Request rows; the prior failed attempt is retired when the next one is enqueued.
rel_id = insert_monitored_release(conn, artist_name="X", album="Y", rg_mbid="rg-1",
monitored=True, state="wanted")
with conn.cursor() as cur:
cur.execute(
'INSERT INTO "Request" (id, artist, album, status, "monitoredReleaseId", "createdAt") '
"VALUES (gen_random_uuid()::text, 'X', 'Y', 'needs_attention', %s, now()) RETURNING id",
(rel_id,),
)
req = cur.fetchone()[0]
cur.execute(
'INSERT INTO "Job" (id, "requestId", state, "currentStage", attempts, "createdAt", "updatedAt") '
"VALUES (gen_random_uuid()::text, %s, 'needs_attention', 'rank', 1, now(), now())",
(req,),
)
conn.commit()
assert enqueue_due(conn, CFG) == 1
assert _job_count_for(conn, rel_id) == 1 # old failed attempt retired, one fresh attempt
with conn.cursor() as cur:
cur.execute(
'SELECT j.state FROM "Job" j JOIN "Request" r ON r.id = j."requestId" '
'WHERE r."monitoredReleaseId" = %s',
(rel_id,),
)
assert cur.fetchone()[0] == "requested"
def test_already_in_library_at_cutoff_is_fulfilled(conn):
rel_id = insert_monitored_release(conn, artist_name="A", album="B", rg_mbid="rg-1",
monitored=True, state="wanted")
with conn.cursor() as cur:
cur.execute(
'INSERT INTO "Request" (id, artist, album, status, "createdAt") '
"VALUES (gen_random_uuid()::text, 'A', 'B', 'completed', now()) RETURNING id"
)
req = cur.fetchone()[0]
cur.execute(
'INSERT INTO "LibraryItem" (id, "requestId", artist, album, path, source, format, '
'"qualityClass", "importedAt") '
"VALUES (gen_random_uuid()::text, %s, 'A', 'B', '/m/A/B', 'qobuz', 'FLAC', 2, now())",
(req,),
)
conn.commit()
assert enqueue_due(conn, CFG) == 0
assert _state(conn, rel_id) == "fulfilled"