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/test_debug_info_services.py

168 lines
6.4 KiB

"""Pin the `info['services']` block returned by /api/debug-info.
Pre-fix the `music_source` field always rendered as "unknown" because
the code read `_status_cache.get('spotify', {})` — but the cache only
ever holds 'media_server' and 'soulseek' keys, so the fallback always
fired. Same problem (silently) for `spotify_connected` and
`spotify_rate_limited`. Hydrabase was missing entirely.
Fix routes those reads through the canonical accessors:
- `music_source` → `core.metadata.registry.get_primary_source` (which
already accounts for the auth-fallback chain — Spotify → Deezer when
unauthenticated)
- `spotify_connected` / `spotify_rate_limited` →
`core.metadata.status.get_spotify_status`
- `hydrabase_connected` → `core.metadata.registry.is_hydrabase_enabled`
- `youtube_available` → constant True (URL-based, no auth)
- `hifi_instance_count` → `db.get_hifi_instances`
- `always_available_metadata_sources` → static list of public-API
sources (Deezer / iTunes / MusicBrainz)
"""
from __future__ import annotations
from contextlib import contextmanager
from unittest.mock import patch
import pytest
@pytest.fixture
def app_test_client():
import web_server
web_server.app.config['TESTING'] = True
with web_server.app.test_client() as client:
yield client
@contextmanager
def _patched_endpoint(
primary_source='spotify',
spotify_status=None,
hydrabase_enabled=False,
primary_source_raises=False,
spotify_status_raises=False,
hydrabase_raises=False,
):
"""Patch the three module-level lookups inside `core.debug_info`
and yield. Each can either return a fixed value or raise — the
`*_raises` flags select which."""
if spotify_status is None:
spotify_status = {'connected': True, 'rate_limited': False}
def _boom(*_a, **_k):
raise RuntimeError('forced failure for test')
primary_patch = patch(
'core.debug_info.get_primary_source',
side_effect=_boom if primary_source_raises else None,
return_value=None if primary_source_raises else primary_source,
)
spotify_patch = patch(
'core.debug_info.get_spotify_status',
side_effect=_boom if spotify_status_raises else None,
return_value=None if spotify_status_raises else spotify_status,
)
hydrabase_patch = patch(
'core.debug_info.is_hydrabase_enabled',
side_effect=_boom if hydrabase_raises else None,
return_value=None if hydrabase_raises else hydrabase_enabled,
)
with primary_patch, spotify_patch, hydrabase_patch:
yield
def _services(client):
resp = client.get('/api/debug-info')
assert resp.status_code == 200
return resp.get_json()['services']
def test_music_source_uses_primary_source_not_status_cache(app_test_client):
"""The bug: music_source always read 'unknown' because it pulled
from a non-existent 'spotify' key in `_status_cache`. Fix routes
it through `get_primary_source` which is the actual authority."""
with _patched_endpoint(primary_source='tidal'):
services = _services(app_test_client)
assert services['music_source'] == 'tidal'
def test_music_source_falls_back_to_unknown_when_lookup_raises(app_test_client):
"""Defensive: if `get_primary_source` itself blows up, the field
still renders as 'unknown' rather than crashing the whole endpoint."""
with _patched_endpoint(primary_source_raises=True):
services = _services(app_test_client)
assert services['music_source'] == 'unknown'
def test_spotify_connected_uses_get_spotify_status(app_test_client):
"""`spotify_connected` was reading `_status_cache.get('spotify', {})`,
which never had the key. Routed through `get_spotify_status` now."""
with _patched_endpoint(spotify_status={'connected': True, 'rate_limited': False}):
services = _services(app_test_client)
assert services['spotify_connected'] is True
def test_spotify_rate_limited_uses_get_spotify_status(app_test_client):
with _patched_endpoint(spotify_status={'connected': True, 'rate_limited': True}):
services = _services(app_test_client)
assert services['spotify_rate_limited'] is True
def test_spotify_status_lookup_failure_does_not_break_endpoint(app_test_client):
"""`get_spotify_status` raises → both spotify_* fields default to
False rather than 500'ing the whole debug dump."""
with _patched_endpoint(spotify_status_raises=True):
services = _services(app_test_client)
assert services['spotify_connected'] is False
assert services['spotify_rate_limited'] is False
def test_hydrabase_connected_present(app_test_client):
"""Hydrabase status was never surfaced in debug info even though
it's an active metadata source. Now reported."""
with _patched_endpoint(hydrabase_enabled=True):
services = _services(app_test_client)
assert services['hydrabase_connected'] is True
def test_hydrabase_disconnected_when_disabled(app_test_client):
with _patched_endpoint(hydrabase_enabled=False):
services = _services(app_test_client)
assert services['hydrabase_connected'] is False
def test_hydrabase_lookup_failure_defaults_false(app_test_client):
with _patched_endpoint(hydrabase_raises=True):
services = _services(app_test_client)
assert services['hydrabase_connected'] is False
def test_youtube_available_always_true(app_test_client):
"""YouTube is URL-based via yt-dlp, no auth, always available.
Surfaced so the dump documents it as a download source."""
with _patched_endpoint():
services = _services(app_test_client)
assert services['youtube_available'] is True
def test_always_available_metadata_sources_listed(app_test_client):
"""Public-API metadata sources (no auth, no per-user state) listed
so the debug dump reflects the full metadata surface."""
with _patched_endpoint():
services = _services(app_test_client)
available = services['always_available_metadata_sources']
assert 'deezer' in available
assert 'itunes' in available
assert 'musicbrainz' in available
def test_hifi_instance_count_present(app_test_client):
"""HiFi instance count exposed because each instance is a separate
endpoint with its own auth state — single connected/disconnected
bool wouldn't capture the actual config."""
with _patched_endpoint():
services = _services(app_test_client)
assert 'hifi_instance_count' in services
assert isinstance(services['hifi_instance_count'], int)