import pytest from server import actions, mf def make_yt_hit(): return mf.Hit(source="youtube", kind="track", title="Together", artist="Avril Lavigne", album="Under My Skin", year="2004", payload={"videoId": "abc"}) def make_lidarr_album_hit(): return mf.Hit(source="lidarr", kind="album", title="Under My Skin", artist="Avril Lavigne", album="Under My Skin", year="2004", payload={"album": {"id": 5, "title": "Under My Skin"}}) def test_started_message_mentions_source_and_title(): msg = actions.started_message(make_yt_hit()) assert "Together" in msg assert "Avril Lavigne" in msg assert "YouTube" in msg def test_done_message_mentions_title(): msg = actions.done_message(make_yt_hit()) assert "Together" in msg assert "Avril Lavigne" in msg def test_messages_use_only_first_artist(): hit = mf.Hit(source="youtube", kind="track", title="Under My Skin", artist="SLVMLORD, James John, BobbyGee", album="X", year="", payload={"videoId": "abc"}) for msg in (actions.started_message(hit), actions.done_message(hit), actions.failed_message(hit)): assert "SLVMLORD" in msg assert "James John" not in msg assert "BobbyGee" not in msg def test_perform_youtube_calls_act_youtube(monkeypatch): calls = {} monkeypatch.setattr(mf, "act_youtube", lambda hit, root, quality, dry_run: calls.update(hit=hit, root=root, quality=quality)) hit = make_yt_hit() result = actions.perform_fetch(hit, [hit], quality="best", root="/media/music") assert calls["quality"] == "best" assert result["path"] == "/media/music/Avril Lavigne/youtube" assert result["lidarr_album_id"] is None def _release(guid="g1"): return {"guid": guid, "indexerId": 2, "title": "Avril-Under_My_Skin", "size": 312 * 1024 * 1024, "seeders": 24, "indexer": "Redacted", "quality": {"quality": {"name": "FLAC"}}} @pytest.fixture def in_library(monkeypatch): """Lidarr holds the artist and the album.""" monkeypatch.setattr(mf, "ensure_album", lambda album, root, search_all, dry_run: { "album": {"id": 5}, "album_id": 5, "artist_name": "Avril Lavigne", "album_title": "Under My Skin", "artist_state": "existing", "album_state": "existing"}) def test_perform_lidarr_album_offers_choices_by_default(monkeypatch, in_library): monkeypatch.setattr(mf, "list_releases", lambda album_id, **kw: [_release("a"), _release("b")]) monkeypatch.setattr(mf, "grab_release", lambda *a, **kw: (_ for _ in ()).throw( AssertionError("must not grab before the user picks"))) hit = make_lidarr_album_hit() result = actions.perform_fetch(hit, [hit], quality="best", root="/media/music") assert result["outcome"] == "choices" assert [c["n"] for c in result["choices"]] == [1, 2] assert result["pending"] == [_release("a"), _release("b")] assert result["lidarr_album_id"] == 5 def test_perform_lidarr_album_auto_grabs_the_best_release(monkeypatch, in_library): grabbed = {} monkeypatch.setattr(mf, "list_releases", lambda album_id, **kw: [_release("best"), _release("b")]) monkeypatch.setattr(mf, "grab_release", lambda rel, dry_run=False: grabbed.update(guid=rel["guid"]) or True) hit = make_lidarr_album_hit() result = actions.perform_fetch(hit, [hit], quality="best", root="/media/music", auto=True) assert result["outcome"] == "album_grabbed" assert grabbed["guid"] == "best" assert not result.get("choices") def test_perform_lidarr_album_fallsthrough_to_youtube(monkeypatch, in_library): monkeypatch.setattr(mf, "list_releases", lambda album_id, **kw: []) yt_calls = {} monkeypatch.setattr(mf, "act_youtube", lambda hit, root, quality, dry_run: yt_calls.update(hit=hit)) lidarr_hit = make_lidarr_album_hit() yt_hit = make_yt_hit() result = actions.perform_fetch(lidarr_hit, [lidarr_hit, yt_hit], quality="best", root="/media/music") assert yt_calls["hit"] is yt_hit assert result["outcome"] == "youtube_fallback" assert result["path"] == "/media/music/Avril Lavigne/youtube" def test_perform_lidarr_album_reports_no_release_without_a_fallback(monkeypatch, in_library): monkeypatch.setattr(mf, "list_releases", lambda album_id, **kw: []) hit = make_lidarr_album_hit() result = actions.perform_fetch(hit, [hit], quality="best", root="/media/music") assert result["outcome"] == "no_release" def test_perform_lidarr_album_skips_youtube_when_not_allowed(monkeypatch, in_library): monkeypatch.setattr(mf, "list_releases", lambda album_id, **kw: []) monkeypatch.setattr(mf, "act_youtube", lambda *a, **kw: (_ for _ in ()).throw( AssertionError("source=lidarr must not use YouTube"))) lidarr_hit = make_lidarr_album_hit() result = actions.perform_fetch(lidarr_hit, [lidarr_hit, make_yt_hit()], quality="best", root="/media/music", allow_youtube=False) assert result["outcome"] == "no_release" def test_perform_lidarr_album_raises_when_the_album_is_not_in_lidarr(monkeypatch): monkeypatch.setattr(mf, "ensure_album", lambda album, root, search_all, dry_run: { "album": None, "album_id": None, "artist_name": "Avril Lavigne", "album_title": "Under My Skin", "artist_state": "existing", "album_state": "missing"}) hit = make_lidarr_album_hit() with pytest.raises(RuntimeError, match="no album called"): actions.perform_fetch(hit, [hit], quality="best", root="/media/music") def test_failed_message_mentions_title_and_artist(): msg = actions.failed_message(make_yt_hit()) assert "Together" in msg assert "Avril Lavigne" in msg