mirror of
https://github.com/ChuckBuilds/LEDMatrix.git
synced 2026-05-24 13:23:32 +00:00
Adds test coverage for six major untested areas: - src/base_classes/api_extractors.py — ESPN football, baseball, hockey, soccer extractors - src/base_classes/data_sources.py — ESPN, MLB, and soccer API data sources (HTTP mocked) - src/common/game_helper.py — game extraction, filtering, sorting, and summaries - src/common/utils.py — all utility functions (normalise, format, validate, parse) - src/common/scroll_helper.py — ScrollHelper init, create, update, visible portion, duration - src/background_data_service.py — cache hit/miss paths, retry, cancel, cleanup, singleton - src/vegas_mode/config.py — VegasModeConfig from_config, validate, update, ordering - src/logo_downloader.py — normalize_abbreviation, filename variations, directory helpers - src/plugin_system/health_monitor.py — HealthStatus determination, metrics, suggestions, lifecycle https://claude.ai/code/session_015792DiGo27JbgH5mk3KBjk
210 lines
8.4 KiB
Python
210 lines
8.4 KiB
Python
"""
|
|
Tests for src/base_classes/data_sources.py
|
|
|
|
Covers ESPNDataSource, MLBAPIDataSource, SoccerAPIDataSource.
|
|
All HTTP calls are mocked to avoid network access.
|
|
"""
|
|
|
|
import logging
|
|
from datetime import datetime, date
|
|
from unittest.mock import MagicMock, patch, Mock
|
|
import pytest
|
|
import requests
|
|
|
|
from src.base_classes.data_sources import ESPNDataSource, MLBAPIDataSource, SoccerAPIDataSource
|
|
|
|
|
|
def _make_logger() -> logging.Logger:
|
|
return logging.getLogger("test_data_sources")
|
|
|
|
|
|
def _mock_response(json_data: dict, status_code: int = 200):
|
|
resp = Mock(spec=requests.Response)
|
|
resp.status_code = status_code
|
|
resp.json.return_value = json_data
|
|
resp.raise_for_status = Mock()
|
|
if status_code >= 400:
|
|
resp.raise_for_status.side_effect = requests.HTTPError(response=resp)
|
|
return resp
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# ESPNDataSource
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestESPNDataSource:
|
|
def setup_method(self):
|
|
self.source = ESPNDataSource(_make_logger())
|
|
|
|
def test_get_headers(self):
|
|
headers = self.source.get_headers()
|
|
assert headers["Accept"] == "application/json"
|
|
assert "LEDMatrix" in headers["User-Agent"]
|
|
|
|
def test_fetch_live_games_returns_live_events(self):
|
|
live_event = {
|
|
"competitions": [{"status": {"type": {"state": "in"}}}]
|
|
}
|
|
non_live_event = {
|
|
"competitions": [{"status": {"type": {"state": "pre"}}}]
|
|
}
|
|
payload = {"events": [live_event, non_live_event]}
|
|
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_live_games("football", "nfl")
|
|
|
|
assert len(result) == 1
|
|
assert result[0] is live_event
|
|
|
|
def test_fetch_live_games_empty_when_none_live(self):
|
|
payload = {"events": [
|
|
{"competitions": [{"status": {"type": {"state": "post"}}}]}
|
|
]}
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_live_games("football", "nfl")
|
|
assert result == []
|
|
|
|
def test_fetch_live_games_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("network failure")):
|
|
result = self.source.fetch_live_games("football", "nfl")
|
|
assert result == []
|
|
|
|
def test_fetch_schedule_returns_all_events(self):
|
|
events = [{"id": "1"}, {"id": "2"}]
|
|
payload = {"events": events}
|
|
start = datetime(2024, 1, 1)
|
|
end = datetime(2024, 1, 7)
|
|
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_schedule("football", "nfl", (start, end))
|
|
|
|
assert len(result) == 2
|
|
|
|
def test_fetch_schedule_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("timeout")):
|
|
result = self.source.fetch_schedule("football", "nfl", (datetime.now(), datetime.now()))
|
|
assert result == []
|
|
|
|
def test_fetch_standings_success(self):
|
|
payload = {"standings": []}
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_standings("football", "nfl")
|
|
assert result == payload
|
|
|
|
def test_fetch_standings_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("error")):
|
|
result = self.source.fetch_standings("football", "nfl")
|
|
assert result == {}
|
|
|
|
def test_base_url_set_correctly(self):
|
|
assert "espn.com" in self.source.base_url
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# MLBAPIDataSource
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestMLBAPIDataSource:
|
|
def setup_method(self):
|
|
self.source = MLBAPIDataSource(_make_logger())
|
|
|
|
def test_fetch_live_games_filters_live(self):
|
|
live_game = {"status": {"abstractGameState": "Live"}}
|
|
final_game = {"status": {"abstractGameState": "Final"}}
|
|
payload = {"dates": [{"games": [live_game, final_game]}]}
|
|
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_live_games("baseball", "mlb")
|
|
|
|
assert len(result) == 1
|
|
assert result[0] is live_game
|
|
|
|
def test_fetch_live_games_empty_dates(self):
|
|
payload = {"dates": []}
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_live_games("baseball", "mlb")
|
|
assert result == []
|
|
|
|
def test_fetch_live_games_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("err")):
|
|
result = self.source.fetch_live_games("baseball", "mlb")
|
|
assert result == []
|
|
|
|
def test_fetch_schedule_aggregates_all_dates(self):
|
|
payload = {
|
|
"dates": [
|
|
{"games": [{"id": "1"}, {"id": "2"}]},
|
|
{"games": [{"id": "3"}]},
|
|
]
|
|
}
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_schedule("baseball", "mlb", (datetime.now(), datetime.now()))
|
|
assert len(result) == 3
|
|
|
|
def test_fetch_schedule_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("err")):
|
|
result = self.source.fetch_schedule("baseball", "mlb", (datetime.now(), datetime.now()))
|
|
assert result == []
|
|
|
|
def test_fetch_standings_success(self):
|
|
payload = {"records": []}
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_standings("baseball", "mlb")
|
|
assert result == payload
|
|
|
|
def test_fetch_standings_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("err")):
|
|
result = self.source.fetch_standings("baseball", "mlb")
|
|
assert result == {}
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# SoccerAPIDataSource
|
|
# ---------------------------------------------------------------------------
|
|
|
|
class TestSoccerAPIDataSource:
|
|
def setup_method(self):
|
|
self.source = SoccerAPIDataSource(_make_logger(), api_key="test-key-123")
|
|
|
|
def test_headers_include_api_key(self):
|
|
headers = self.source.get_headers()
|
|
assert headers["X-Auth-Token"] == "test-key-123"
|
|
|
|
def test_headers_without_api_key(self):
|
|
source = SoccerAPIDataSource(_make_logger())
|
|
headers = source.get_headers()
|
|
assert "X-Auth-Token" not in headers
|
|
|
|
def test_fetch_live_games_success(self):
|
|
payload = {"matches": [{"id": "m1"}, {"id": "m2"}]}
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_live_games("soccer", "eng.1")
|
|
assert len(result) == 2
|
|
|
|
def test_fetch_live_games_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("err")):
|
|
result = self.source.fetch_live_games("soccer", "eng.1")
|
|
assert result == []
|
|
|
|
def test_fetch_schedule_success(self):
|
|
payload = {"matches": [{"id": "m1"}]}
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_schedule("soccer", "eng.1", (datetime.now(), datetime.now()))
|
|
assert len(result) == 1
|
|
|
|
def test_fetch_schedule_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("err")):
|
|
result = self.source.fetch_schedule("soccer", "eng.1", (datetime.now(), datetime.now()))
|
|
assert result == []
|
|
|
|
def test_fetch_standings_success(self):
|
|
payload = {"standings": []}
|
|
with patch.object(self.source.session, "get", return_value=_mock_response(payload)):
|
|
result = self.source.fetch_standings("soccer", "PL")
|
|
assert result == payload
|
|
|
|
def test_fetch_standings_returns_empty_on_error(self):
|
|
with patch.object(self.source.session, "get", side_effect=Exception("err")):
|
|
result = self.source.fetch_standings("soccer", "PL")
|
|
assert result == {}
|