mirror of https://github.com/Nezreka/SoulSync.git
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.
168 lines
6.4 KiB
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)
|