fix: resolve 4 logic bugs found in code review
All checks were successful
CI / Lint (push) Successful in 5s
CI / Test (push) Successful in 5s
CI / Build & Push (push) Successful in 17s

- utc_to_eastern: use zoneinfo instead of hardcoded EDT offset (-4)
  so start times are correct in both EST and EDT
- standings: fetch before truncate so a failed API call doesn't wipe
  existing standings data
- routes: call parse_games() once per request instead of three times
- scheduler: wrap run_pending() in try/except so an unhandled exception
  doesn't kill the background thread

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-29 14:06:45 -04:00
parent 56feb0a5f2
commit 3169d1a1ff
7 changed files with 56 additions and 27 deletions

View File

@@ -101,10 +101,16 @@ class TestGetGameOutcome:
class TestUtcToEstTime:
def test_converts_utc_to_est(self):
def test_converts_utc_to_edt(self):
# April is EDT (UTC-4): 23:00 UTC → 07:00 PM EDT
result = utc_to_eastern("2024-04-10T23:00:00Z")
assert result == "07:00 PM"
def test_converts_utc_to_est(self):
# January is EST (UTC-5): 23:00 UTC → 06:00 PM EST
result = utc_to_eastern("2024-01-15T23:00:00Z")
assert result == "06:00 PM"
class TestParseGames:
def test_returns_empty_list_for_none(self):

View File

@@ -39,3 +39,20 @@ class TestStartScheduler:
start_scheduler()
assert mock_schedule.run_pending.call_count >= 2
def test_continues_after_exception_in_run_pending(self, mocker):
mock_schedule = mocker.patch("app.scheduler.schedule")
call_count = {"n": 0}
def raise_then_stop(_):
call_count["n"] += 1
if call_count["n"] >= 2:
raise StopIteration
mock_schedule.run_pending.side_effect = RuntimeError("boom")
mocker.patch("app.scheduler.time.sleep", side_effect=raise_then_stop)
with pytest.raises(StopIteration):
start_scheduler()
assert mock_schedule.run_pending.call_count >= 2

View File

@@ -209,12 +209,25 @@ class TestRefreshStandings:
assert count == 2
def test_does_not_insert_when_fetch_fails(self, mocker, tmp_path):
mocker.patch("app.standings.fetch_standings", return_value=None)
mocker.patch("app.standings.DB_PATH", str(tmp_path / "test.db"))
db_path = str(tmp_path / "test.db")
mocker.patch("app.standings.DB_PATH", db_path)
# Seed with existing data before the failed refresh
seed = [
{
"team_common_name": "Bruins",
"league_sequence": 1,
"league_l10_sequence": 2,
}
]
mocker.patch("app.standings.fetch_standings", return_value=seed)
refresh_standings()
conn = sqlite3.connect(str(tmp_path / "test.db"))
# Now simulate a fetch failure — existing data must be preserved
mocker.patch("app.standings.fetch_standings", return_value=None)
refresh_standings()
conn = sqlite3.connect(db_path)
count = conn.execute("SELECT COUNT(*) FROM standings").fetchone()[0]
conn.close()
assert count == 0
assert count == 1