test: add full test suite with 100% coverage across all modules
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
117
tests/test_api.py
Normal file
117
tests/test_api.py
Normal 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()
|
||||
Reference in New Issue
Block a user