Files
musicseerr/backend/tests/services/test_audiodb_prewarm.py
T
2026-04-03 15:53:00 +01:00

186 lines
6.6 KiB
Python

import asyncio
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from repositories.audiodb_models import AudioDBArtistImages, AudioDBAlbumImages
TEST_MBID = "cc197bad-dc9c-440d-a5b5-d52ba2e14234"
TEST_ALBUM_MBID = "1dc4c347-a1db-32aa-b14f-bc9cc507b843"
def _make_settings(audiodb_enabled: bool = True, name_search_fallback: bool = False):
s = MagicMock()
s.audiodb_enabled = audiodb_enabled
s.audiodb_name_search_fallback = name_search_fallback
return s
def _make_prefs(settings=None):
if settings is None:
settings = _make_settings()
prefs = MagicMock()
prefs.get_advanced_settings.return_value = settings
return prefs
def _make_status_service():
status = MagicMock()
status.update_phase = AsyncMock()
status.update_progress = AsyncMock()
status.persist_progress = AsyncMock()
status.skip_phase = AsyncMock()
status.is_cancelled.return_value = False
return status
def _make_precache_service(audiodb_svc=None, prefs=None, cover_repo=None):
from services.library_precache_service import LibraryPrecacheService
if audiodb_svc is None:
audiodb_svc = AsyncMock()
audiodb_svc.get_cached_artist_images = AsyncMock(return_value=None)
audiodb_svc.get_cached_album_images = AsyncMock(return_value=None)
audiodb_svc.fetch_and_cache_artist_images = AsyncMock(return_value=None)
audiodb_svc.fetch_and_cache_album_images = AsyncMock(return_value=None)
if prefs is None:
prefs = _make_prefs()
if cover_repo is None:
cover_repo = AsyncMock()
return LibraryPrecacheService(
lidarr_repo=AsyncMock(),
cover_repo=cover_repo,
preferences_service=prefs,
sync_state_store=AsyncMock(),
genre_index=AsyncMock(),
library_db=AsyncMock(),
audiodb_image_service=audiodb_svc,
)
class TestCheckAudioDBCacheNeeds:
@pytest.mark.asyncio
async def test_skips_cached_artists(self):
svc = AsyncMock()
images = AudioDBArtistImages(thumb_url="https://x.com/t.jpg", is_negative=False)
svc.get_cached_artist_images = AsyncMock(return_value=images)
svc.get_cached_album_images = AsyncMock(return_value=None)
precache = _make_precache_service(audiodb_svc=svc)
artists = [{"mbid": TEST_MBID, "name": "Coldplay"}]
needed_artists, needed_albums = await precache._check_audiodb_cache_needs(artists, [])
assert len(needed_artists) == 0
@pytest.mark.asyncio
async def test_includes_uncached_artists(self):
svc = AsyncMock()
svc.get_cached_artist_images = AsyncMock(return_value=None)
svc.get_cached_album_images = AsyncMock(return_value=None)
precache = _make_precache_service(audiodb_svc=svc)
artists = [{"mbid": TEST_MBID, "name": "Coldplay"}]
needed_artists, _ = await precache._check_audiodb_cache_needs(artists, [])
assert len(needed_artists) == 1
assert needed_artists[0]["mbid"] == TEST_MBID
@pytest.mark.asyncio
async def test_skips_unknown_mbid(self):
svc = AsyncMock()
svc.get_cached_artist_images = AsyncMock(return_value=None)
precache = _make_precache_service(audiodb_svc=svc)
artists = [{"mbid": "unknown_abc123", "name": "Unknown"}]
needed_artists, _ = await precache._check_audiodb_cache_needs(artists, [])
assert len(needed_artists) == 0
class TestPrecacheAudioDBData:
@pytest.mark.asyncio
async def test_skips_when_disabled(self):
settings = _make_settings(audiodb_enabled=False)
prefs = _make_prefs(settings)
svc = AsyncMock()
precache = _make_precache_service(audiodb_svc=svc, prefs=prefs)
status = _make_status_service()
await precache._precache_audiodb_data(
[{"mbid": TEST_MBID, "name": "Coldplay"}], [], status,
)
svc.fetch_and_cache_artist_images.assert_not_called()
@pytest.mark.asyncio
async def test_skips_when_all_cached(self):
svc = AsyncMock()
images = AudioDBArtistImages(thumb_url="https://x.com/t.jpg", is_negative=False)
svc.get_cached_artist_images = AsyncMock(return_value=images)
svc.get_cached_album_images = AsyncMock(return_value=images)
precache = _make_precache_service(audiodb_svc=svc)
status = _make_status_service()
await precache._precache_audiodb_data(
[{"mbid": TEST_MBID, "name": "Coldplay"}], [], status,
)
svc.fetch_and_cache_artist_images.assert_not_called()
@pytest.mark.asyncio
async def test_processes_uncached_artists(self):
svc = AsyncMock()
svc.get_cached_artist_images = AsyncMock(return_value=None)
svc.get_cached_album_images = AsyncMock(return_value=None)
result = AudioDBArtistImages(thumb_url="https://x.com/t.jpg", is_negative=False)
svc.fetch_and_cache_artist_images = AsyncMock(return_value=result)
precache = _make_precache_service(audiodb_svc=svc)
status = _make_status_service()
with patch.object(precache._audiodb_phase, 'download_bytes', new_callable=AsyncMock, return_value=True):
await precache._precache_audiodb_data(
[{"mbid": TEST_MBID, "name": "Coldplay"}], [], status,
)
svc.fetch_and_cache_artist_images.assert_called_once()
@pytest.mark.asyncio
async def test_respects_cancellation(self):
svc = AsyncMock()
svc.get_cached_artist_images = AsyncMock(return_value=None)
svc.get_cached_album_images = AsyncMock(return_value=None)
precache = _make_precache_service(audiodb_svc=svc)
status = _make_status_service()
status.is_cancelled.return_value = True
await precache._precache_audiodb_data(
[{"mbid": TEST_MBID, "name": "Coldplay"}], [], status,
)
svc.fetch_and_cache_artist_images.assert_not_called()
class TestSortByCoverPriority:
def test_coverless_first(self, tmp_path):
from repositories.coverart_disk_cache import get_cache_filename
fake_cache_dir = tmp_path / "covers"
fake_cache_dir.mkdir()
cover_repo = MagicMock()
cover_repo.cache_dir = fake_cache_dir
precache = _make_precache_service(cover_repo=cover_repo)
identifier_a = "artist_a_500"
file_name = f"{get_cache_filename(identifier_a, 'img')}.bin"
(fake_cache_dir / file_name).write_bytes(b"fake")
artists = [
{"mbid": "a", "name": "A"},
{"mbid": "b", "name": "B"},
]
sorted_list = precache._sort_by_cover_priority(artists, "artist")
assert sorted_list[0]["mbid"] == "b"