test: add full test suite with 100% coverage across all modules
All checks were successful
CI / Lint (push) Successful in 6s
CI / Test (push) Successful in 7s
CI / Build & Push (push) Successful in 15s

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 13:17:20 -04:00
parent dd5ac945bd
commit def491a4d4
6 changed files with 516 additions and 0 deletions

117
tests/test_api.py Normal file
View File

@@ -0,0 +1,117 @@
import json
from datetime import datetime
from zoneinfo import ZoneInfo
import requests as req
from app.api import fetch_scores, refresh_scores
EASTERN = ZoneInfo("America/New_York")
class TestFetchScores:
def test_uses_now_url_during_evening(self, mocker):
"""7:30 PM ET → /score/now"""
mock_dt = mocker.patch("app.api.datetime")
mock_dt.now.return_value = datetime(2024, 4, 10, 19, 30, tzinfo=EASTERN)
mock_get = mocker.patch("app.api.requests.get")
mock_get.return_value.json.return_value = {"games": []}
fetch_scores()
url = mock_get.call_args[0][0]
assert url == "https://api-web.nhle.com/v1/score/now"
def test_uses_now_url_after_midnight(self, mocker):
"""1:00 AM ET → /score/now (still considered game hours)"""
mock_dt = mocker.patch("app.api.datetime")
mock_dt.now.return_value = datetime(2024, 4, 11, 1, 0, tzinfo=EASTERN)
mock_get = mocker.patch("app.api.requests.get")
mock_get.return_value.json.return_value = {"games": []}
fetch_scores()
url = mock_get.call_args[0][0]
assert url == "https://api-web.nhle.com/v1/score/now"
def test_uses_date_url_during_afternoon(self, mocker):
"""2:00 PM ET → date-based endpoint"""
mock_dt = mocker.patch("app.api.datetime")
mock_dt.now.return_value = datetime(2024, 4, 10, 14, 0, tzinfo=EASTERN)
mock_get = mocker.patch("app.api.requests.get")
mock_get.return_value.json.return_value = {"games": []}
fetch_scores()
url = mock_get.call_args[0][0]
assert "2024-04-10" in url
assert "now" not in url
def test_returns_json_on_success(self, mocker):
mock_dt = mocker.patch("app.api.datetime")
mock_dt.now.return_value = datetime(2024, 4, 10, 14, 0, tzinfo=EASTERN)
expected = {"games": [{"id": 1}]}
mock_get = mocker.patch("app.api.requests.get")
mock_get.return_value.json.return_value = expected
result = fetch_scores()
assert result == expected
def test_returns_none_on_request_exception(self, mocker):
mock_dt = mocker.patch("app.api.datetime")
mock_dt.now.return_value = datetime(2024, 4, 10, 14, 0, tzinfo=EASTERN)
mocker.patch(
"app.api.requests.get", side_effect=req.RequestException("timeout")
)
result = fetch_scores()
assert result is None
def test_returns_none_on_bad_status(self, mocker):
mock_dt = mocker.patch("app.api.datetime")
mock_dt.now.return_value = datetime(2024, 4, 10, 14, 0, tzinfo=EASTERN)
mock_get = mocker.patch("app.api.requests.get")
mock_get.return_value.raise_for_status.side_effect = req.HTTPError("404")
result = fetch_scores()
assert result is None
class TestRefreshScores:
def test_writes_data_to_file(self, mocker, tmp_path):
data = {"games": [{"id": 1}]}
mocker.patch("app.api.fetch_scores", return_value=data)
score_file = tmp_path / "scoreboard_data.json"
mocker.patch("app.api.SCOREBOARD_DATA_FILE", str(score_file))
result = refresh_scores()
assert result == data
assert score_file.exists()
assert json.loads(score_file.read_text()) == data
def test_returns_none_when_fetch_fails(self, mocker):
mocker.patch("app.api.fetch_scores", return_value=None)
result = refresh_scores()
assert result is None
def test_does_not_write_file_when_fetch_fails(self, mocker, tmp_path):
mocker.patch("app.api.fetch_scores", return_value=None)
score_file = tmp_path / "scoreboard_data.json"
mocker.patch("app.api.SCOREBOARD_DATA_FILE", str(score_file))
refresh_scores()
assert not score_file.exists()