soulsync/tests/metadata/test_metadata_engine.py
Antti Kettunen 896b1b1012
Add typed metadata engine baseline
- introduce the in-process metadata engine and fresh provider adapters
- normalize external identity through source_id while keeping id as a compatibility alias
- route metadata callers through the new seam and add regression coverage
2026-05-09 11:44:47 +03:00

141 lines
4.7 KiB
Python

import sys
import types
from types import SimpleNamespace
if "spotipy" not in sys.modules:
spotipy = types.ModuleType("spotipy")
oauth2 = types.ModuleType("spotipy.oauth2")
class _DummySpotify:
def __init__(self, *args, **kwargs):
pass
class _DummyOAuth:
def __init__(self, *args, **kwargs):
pass
spotipy.Spotify = _DummySpotify
oauth2.SpotifyOAuth = _DummyOAuth
oauth2.SpotifyClientCredentials = _DummyOAuth
spotipy.oauth2 = oauth2
sys.modules["spotipy"] = spotipy
sys.modules["spotipy.oauth2"] = oauth2
if "config.settings" not in sys.modules:
config_pkg = types.ModuleType("config")
settings_mod = types.ModuleType("config.settings")
class _DummyConfigManager:
def get(self, key, default=None):
return default
def get_active_media_server(self):
return "primary"
settings_mod.config_manager = _DummyConfigManager()
config_pkg.settings = settings_mod
sys.modules["config"] = config_pkg
sys.modules["config.settings"] = settings_mod
from core.metadata.engine import MetadataEngine, MetadataSourceFacade
from core.metadata.models import MetadataArtist, MetadataRecord
from core.metadata.registry import get_client_for_source
from core.metadata.service import MetadataService
def test_get_client_for_source_returns_source_facade():
client = get_client_for_source("itunes")
assert isinstance(client, MetadataSourceFacade)
assert client.source == "itunes"
assert client.is_connected() is True
assert hasattr(client, "_get_artist_image_from_albums")
def test_metadata_service_searches_via_engine(monkeypatch):
calls = []
class _FakeOutcome:
def __init__(self, items):
self.items = items
class _FakeEngine:
def search_artists(self, query, **kwargs):
calls.append(("artists", query, dict(kwargs)))
return _FakeOutcome([SimpleNamespace(id="artist-1", name="Artist One")])
def search_tracks(self, query, **kwargs):
calls.append(("tracks", query, dict(kwargs)))
return _FakeOutcome([])
def search_albums(self, query, **kwargs):
calls.append(("albums", query, dict(kwargs)))
return _FakeOutcome([])
monkeypatch.setattr("core.metadata.service.get_spotify_client", lambda *args, **kwargs: None)
monkeypatch.setattr("core.metadata.service.get_primary_source", lambda *args, **kwargs: "deezer")
monkeypatch.setattr("core.metadata.service.get_client_for_source", lambda source: SimpleNamespace(source=source))
monkeypatch.setattr("core.metadata.service.get_metadata_engine", lambda: _FakeEngine())
service = MetadataService()
results = service.search_artists("Artist One", limit=5)
assert [result.name for result in results] == ["Artist One"]
assert calls[0][0] == "artists"
assert calls[0][1] == "Artist One"
assert calls[0][2]["limit"] == 5
assert calls[0][2]["source_override"] == "deezer"
def test_metadata_record_mirrors_source_id_and_id():
record = MetadataRecord({"id": "provider-1"})
assert record["source_id"] == "provider-1"
assert record["id"] == "provider-1"
record = MetadataRecord({"source_id": "provider-2"})
assert record["source_id"] == "provider-2"
assert record["id"] == "provider-2"
record["id"] = "different-id"
assert record["source_id"] == "provider-2"
assert record["id"] == "provider-2"
def test_metadata_artist_to_dict_prefers_source_id():
artist = MetadataArtist(
id="legacy-id",
name="Artist One",
popularity=0,
genres=[],
followers=0,
source_id="provider-id",
)
payload = artist.to_dict()
assert payload["source_id"] == "provider-id"
assert payload["id"] == "provider-id"
def test_metadata_engine_caches_search_results_by_source_id(monkeypatch):
stored_rows = []
class _FakeAdapter:
def search_artists_raw(self, query, limit=20):
return [{"id": "provider-1", "name": "Artist One"}]
class _FakeCache:
def store_entities_bulk(self, source, entity_kind, rows, skip_if_exists=True):
stored_rows.append((source, entity_kind, rows, skip_if_exists))
engine = MetadataEngine()
monkeypatch.setattr(engine, "_get_adapter", lambda source: _FakeAdapter())
monkeypatch.setattr("core.metadata.engine.get_metadata_cache", lambda: _FakeCache())
outcome = engine.search_artists("Artist One", source_override="deezer", allow_fallback=False)
assert outcome.items[0]["source_id"] == "provider-1"
assert stored_rows[0][0] == "deezer"
assert stored_rows[0][1] == "artist"
assert stored_rows[0][2][0][0] == "provider-1"