Files
musicseerr/backend/tests/test_phase9_observability.py
T
Harvey a69a26852e Cut down unnecessary logging (#48)
* Cut down unnecessary logging

* fix format etc

* fix checks

* fix tests
2026-04-14 00:02:38 +01:00

169 lines
5.6 KiB
Python

"""Phase 9 observability contract tests.
Verifies SSE / cache-stats wiring propagates AudioDB data end-to-end.
"""
from unittest.mock import AsyncMock, MagicMock, patch
import msgspec
import pytest
from repositories.audiodb_models import (
AudioDBArtistImages,
AudioDBArtistResponse,
AudioDBAlbumImages,
AudioDBAlbumResponse,
)
from services.audiodb_image_service import AudioDBImageService
TEST_MBID = "cc197bad-dc9c-440d-a5b5-d52ba2e14234"
TEST_ALBUM_MBID = "1dc4c347-a1db-32aa-b14f-bc9cc507b843"
SAMPLE_ARTIST_RESP = AudioDBArtistResponse(
idArtist="111239",
strArtist="Coldplay",
strMusicBrainzID=TEST_MBID,
strArtistThumb="https://example.com/thumb.jpg",
strArtistFanart="https://example.com/fanart.jpg",
)
SAMPLE_ALBUM_RESP = AudioDBAlbumResponse(
idAlbum="2115888",
strAlbum="Parachutes",
strMusicBrainzID=TEST_ALBUM_MBID,
strAlbumThumb="https://example.com/album_thumb.jpg",
strAlbumBack="https://example.com/album_back.jpg",
)
def _make_settings(enabled=True, name_search_fallback=False):
s = MagicMock()
s.audiodb_enabled = enabled
s.audiodb_name_search_fallback = name_search_fallback
s.cache_ttl_audiodb_found = 604800
s.cache_ttl_audiodb_not_found = 86400
s.cache_ttl_audiodb_library = 1209600
s.audiodb_prewarm_concurrency = 4
s.audiodb_prewarm_delay = 0.0
return s
def _make_image_service(settings=None, disk_cache=None, repo=None):
if settings is None:
settings = _make_settings()
prefs = MagicMock()
prefs.get_advanced_settings.return_value = settings
if disk_cache is None:
disk_cache = AsyncMock()
disk_cache.get_audiodb_artist = AsyncMock(return_value=None)
disk_cache.get_audiodb_album = AsyncMock(return_value=None)
disk_cache.set_audiodb_artist = AsyncMock()
disk_cache.set_audiodb_album = AsyncMock()
if repo is None:
repo = AsyncMock()
return AudioDBImageService(
audiodb_repo=repo,
disk_cache=disk_cache,
preferences_service=prefs,
)
class TestPrewarmLogContract:
def _make_status_service(self):
status = MagicMock()
status.update_phase = AsyncMock()
status.update_progress = AsyncMock()
status.persist_progress = AsyncMock()
status.is_cancelled.return_value = False
return status
def _make_precache_service(self, audiodb_svc=None, prefs=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:
settings = MagicMock()
settings.audiodb_enabled = True
settings.audiodb_name_search_fallback = False
settings.audiodb_prewarm_concurrency = 4
settings.audiodb_prewarm_delay = 0.0
prefs = MagicMock()
prefs.get_advanced_settings.return_value = settings
return LibraryPrecacheService(
lidarr_repo=AsyncMock(),
cover_repo=AsyncMock(),
preferences_service=prefs,
sync_state_store=AsyncMock(),
genre_index=AsyncMock(),
library_db=AsyncMock(),
audiodb_image_service=audiodb_svc,
)
@pytest.mark.asyncio
async def test_prewarm_calls_update_phase_audiodb(self):
svc = AsyncMock()
svc.get_cached_artist_images = AsyncMock(return_value=None)
svc.get_cached_album_images = AsyncMock(return_value=None)
svc.fetch_and_cache_artist_images = AsyncMock(return_value=None)
precache = self._make_precache_service(audiodb_svc=svc)
status = self._make_status_service()
artists = [{"mbid": TEST_MBID, "name": "Coldplay"}]
await precache._precache_audiodb_data(artists, [], status)
phase_calls = [
c for c in status.update_phase.call_args_list
if c.args[0] == "audiodb_prewarm"
]
assert len(phase_calls) >= 1, "Expected update_phase('audiodb_prewarm', ...) call"
class TestCacheStatsAudioDBWiring:
@pytest.mark.asyncio
async def test_get_stats_includes_audiodb_counts(self):
from services.cache_service import CacheService
disk_cache = MagicMock()
disk_cache.get_stats.return_value = {
"total_count": 100,
"album_count": 60,
"artist_count": 40,
"audiodb_artist_count": 15,
"audiodb_album_count": 25,
}
mem_cache = MagicMock()
mem_cache.size.return_value = 10
mem_cache.estimate_memory_bytes.return_value = 2048
library_db = AsyncMock()
library_db.get_stats = AsyncMock(return_value={
"artist_count": 5,
"album_count": 8,
"db_size_bytes": 4096,
})
svc = CacheService(
cache=mem_cache,
library_db=library_db,
disk_cache=disk_cache,
)
svc._stats_cache_ttl = 0
with patch("services.cache_service.get_covers_cache_dir") as mock_get_dir:
mock_dir = MagicMock()
mock_dir.exists.return_value = False
mock_get_dir.return_value = mock_dir
stats = await svc.get_stats()
assert stats.disk_audiodb_artist_count == 15
assert stats.disk_audiodb_album_count == 25