You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
SoulSync/tests/media_server/test_conformance.py

73 lines
2.7 KiB

"""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.registry 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}"
)
@pytest.mark.parametrize('server_name', ['plex', 'jellyfin', 'navidrome', 'soulsync'])
def test_server_class_explicitly_inherits_contract(server_name):
"""Per Cin's standard from the download refactor: clients must
explicitly inherit ``MediaServerClient`` so the contract conformance
is obvious from reading the class declaration. Structural
typing alone (which would still pass `hasattr` checks) leaves
the contract invisible to anyone reading the code — drift in a
future client class wouldn't fail at the contract boundary."""
from core.media_server.contract import MediaServerClient
classes = _import_server_classes()
cls = classes[server_name]
assert issubclass(cls, MediaServerClient), (
f"{cls.__name__} must explicitly inherit MediaServerClient — "
f"structural typing isn't enough"
)