`core/media_server/` package with the Protocol contract that every media server client (Plex, Jellyfin, Navidrome, SoulSync standalone) satisfies, plus the registry that holds them. Required methods conservatively limited to the four every server truly implements today: is_connected, ensure_connection, get_all_artists, get_all_album_ids. Other generic methods (search_tracks, trigger_library_scan, get_recently_added_albums, etc.) are listed as OPTIONAL — present on most servers but not all (SoulSync has no library-scan API since it walks the filesystem directly; Jellyfin uses a different search shape). Phase B's engine adapters route around the gaps with per-server fallback instead of forcing every client to declare a no-op stub. Same registry shape as the download plugin registry — single source of truth for which servers exist + name resolution. Adding a 5th server (Subsonic, Emby, etc.) becomes one register call plus the new client class. 5 conformance tests pin every server class implements every required method. Plan doc at docs/media-server-engine-refactor-plan.md. Pure additive — no consumer routes through the contract or registry yet. Suite still green (1921 passed).
54 lines
1.9 KiB
Python
54 lines
1.9 KiB
Python
"""Pin the structural conformance of every media server client to
|
|
``MediaServerClient``. Mirrors the download plugin conformance test
|
|
shape — class-level checks (not instance-level) so importing the
|
|
test doesn't drag every server's heavy auth init into the test
|
|
collection phase.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import pytest
|
|
|
|
from core.media_server.contract import REQUIRED_METHODS
|
|
|
|
|
|
def _import_server_classes():
|
|
"""Import every server client class lazily inside tests so
|
|
auth-init-heavy modules (Plex, Jellyfin) aren't imported at test
|
|
collection."""
|
|
from core.jellyfin_client import JellyfinClient
|
|
from core.navidrome_client import NavidromeClient
|
|
from core.plex_client import PlexClient
|
|
from core.soulsync_client import SoulSyncClient
|
|
|
|
return {
|
|
'plex': PlexClient,
|
|
'jellyfin': JellyfinClient,
|
|
'navidrome': NavidromeClient,
|
|
'soulsync': SoulSyncClient,
|
|
}
|
|
|
|
|
|
def test_default_registry_registers_all_four_servers():
|
|
"""Smoke check that the foundation registry knows about every
|
|
server SoulSync historically dispatched to."""
|
|
from core.media_server import build_default_registry
|
|
|
|
registry = build_default_registry()
|
|
expected = {'plex', 'jellyfin', 'navidrome', 'soulsync'}
|
|
assert set(registry.names()) == expected
|
|
|
|
|
|
@pytest.mark.parametrize('server_name', ['plex', 'jellyfin', 'navidrome', 'soulsync'])
|
|
def test_server_class_has_all_required_methods(server_name):
|
|
"""Every registered server class exposes every required protocol
|
|
method by name. Diagnostic-friendly: tells you WHICH method is
|
|
missing when a new server is added without all the required
|
|
methods."""
|
|
classes = _import_server_classes()
|
|
cls = classes[server_name]
|
|
|
|
missing = [m for m in REQUIRED_METHODS if not hasattr(cls, m)]
|
|
assert not missing, (
|
|
f"{server_name} ({cls.__name__}) missing required methods: {missing}"
|
|
)
|