"""Tests for the Jellyseerr stats provider + generic stat widget source.""" from __future__ import annotations import asyncio from unittest.mock import MagicMock, patch from media_library_viewer_api.widgets.jellyseerr_stats import JellyseerrStatsProvider from media_library_viewer_api.widgets.sources import ServiceRecord, StatsWidgetSource from media_library_viewer_api.widgets.stats_provider import ( StatValue, get_stats_provider, ) def _service( *, jellyseerr_url: str = "https://js.example.com", jellyseerr_api_key: str = "key", ) -> ServiceRecord: return ServiceRecord( id="svc-jf", service_type="jellyfin", name="Jellyfin", config={"jellyseerr_url": jellyseerr_url, "jellyseerr_api_key": jellyseerr_api_key}, secrets={}, enabled=True, ) _COUNTS = {"total": 5, "pending": 2, "approved": 1, "declined": 0, "processing": 1, "available": 1} _RECENT = [{"id": 1, "name": "Inception", "status": "pending", "media_status": "available"}] def test_provider_registered_for_jellyfin(): assert isinstance(get_stats_provider("jellyfin"), JellyseerrStatsProvider) def test_provider_returns_normalized_stats_and_recent(): provider = JellyseerrStatsProvider(ttl=0) fake = MagicMock() fake.request_count.return_value = _COUNTS fake.recent_requests.return_value = _RECENT with patch("media_library_viewer_api.widgets.jellyseerr_stats._jellyseer_client", return_value=fake): result = provider.fetch_stats(_service()) assert [s.key for s in result.stats] == [ "total", "pending", "approved", "declined", "processing", "available", ] by_key = {s.key: s.value for s in result.stats} assert by_key["pending"] == 2 and by_key["total"] == 5 assert result.recent == _RECENT def test_provider_not_configured_returns_detail(): provider = JellyseerrStatsProvider() result = provider.fetch_stats(_service(jellyseerr_url="", jellyseerr_api_key="")) assert result.stats == [] assert "not configured" in (result.detail or "").lower() def test_provider_caches_within_ttl(): provider = JellyseerrStatsProvider(ttl=10) fake = MagicMock() fake.request_count.return_value = _COUNTS fake.recent_requests.return_value = _RECENT with patch("media_library_viewer_api.widgets.jellyseerr_stats._jellyseer_client", return_value=fake): provider.fetch_stats(_service()) provider.fetch_stats(_service()) # served from cache assert fake.request_count.call_count == 1 def test_stat_widget_returns_selected_value(): src = StatsWidgetSource() result = MagicMock() result.detail = None result.stats = [StatValue("total", "Total", 5), StatValue("pending", "Pending", 2)] result.recent = [] provider = MagicMock() provider.fetch_stats.return_value = result with patch("media_library_viewer_api.widgets.sources.get_stats_provider", return_value=provider): data = asyncio.run(src.fetch(_service(), "stat", {"stat": "pending"})) assert data == {"key": "pending", "label": "Pending", "value": 2} def test_stats_overview_widget_returns_all_stats_and_recent(): src = StatsWidgetSource() result = MagicMock() result.detail = None result.stats = [StatValue("total", "Total", 5), StatValue("pending", "Pending", 2)] result.recent = _RECENT provider = MagicMock() provider.fetch_stats.return_value = result with patch("media_library_viewer_api.widgets.sources.get_stats_provider", return_value=provider): data = asyncio.run(src.fetch(_service(), "stats_overview", {})) assert data["stats"] == [ {"key": "total", "label": "Total", "value": 5}, {"key": "pending", "label": "Pending", "value": 2}, ] assert data["recent"] == _RECENT def test_stat_widget_unknown_stat_returns_error(): src = StatsWidgetSource() result = MagicMock() result.detail = None result.stats = [StatValue("total", "Total", 5)] result.recent = [] provider = MagicMock() provider.fetch_stats.return_value = result with patch("media_library_viewer_api.widgets.sources.get_stats_provider", return_value=provider): data = asyncio.run(src.fetch(_service(), "stat", {"stat": "nope"})) assert "error" in data