good luck
This commit is contained in:
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
BIN
tests/__pycache__/__init__.cpython-313.pyc
Normal file
BIN
tests/__pycache__/__init__.cpython-313.pyc
Normal file
Binary file not shown.
BIN
tests/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc
Normal file
BIN
tests/__pycache__/conftest.cpython-313-pytest-8.3.4.pyc
Normal file
Binary file not shown.
BIN
tests/__pycache__/test_process_data.cpython-313-pytest-8.3.4.pyc
Normal file
BIN
tests/__pycache__/test_process_data.cpython-313-pytest-8.3.4.pyc
Normal file
Binary file not shown.
BIN
tests/__pycache__/test_routes.cpython-313-pytest-8.3.4.pyc
Normal file
BIN
tests/__pycache__/test_routes.cpython-313-pytest-8.3.4.pyc
Normal file
Binary file not shown.
93
tests/conftest.py
Normal file
93
tests/conftest.py
Normal file
@@ -0,0 +1,93 @@
|
||||
import json
|
||||
import sqlite3
|
||||
|
||||
import pytest
|
||||
|
||||
|
||||
def make_game(
|
||||
game_state="LIVE",
|
||||
home_name="Maple Leafs",
|
||||
away_name="Bruins",
|
||||
home_score=2,
|
||||
away_score=1,
|
||||
period=3,
|
||||
seconds_remaining=300,
|
||||
in_intermission=False,
|
||||
start_time_utc="2024-04-10T23:00:00Z",
|
||||
home_record="40-25-10",
|
||||
away_record="38-27-09",
|
||||
):
|
||||
clock = {
|
||||
"timeRemaining": f"{seconds_remaining // 60:02d}:{seconds_remaining % 60:02d}",
|
||||
"secondsRemaining": seconds_remaining,
|
||||
"running": game_state == "LIVE",
|
||||
"inIntermission": in_intermission,
|
||||
}
|
||||
return {
|
||||
"gameState": game_state,
|
||||
"startTimeUTC": start_time_utc,
|
||||
"periodDescriptor": {"number": period},
|
||||
"clock": clock,
|
||||
"homeTeam": {
|
||||
"name": {"default": home_name},
|
||||
"score": home_score,
|
||||
"sog": 15,
|
||||
"logo": "https://example.com/home.png",
|
||||
"record": home_record,
|
||||
},
|
||||
"awayTeam": {
|
||||
"name": {"default": away_name},
|
||||
"score": away_score,
|
||||
"sog": 12,
|
||||
"logo": "https://example.com/away.png",
|
||||
"record": away_record,
|
||||
},
|
||||
"gameOutcome": {"lastPeriodType": "REG"},
|
||||
}
|
||||
|
||||
|
||||
LIVE_GAME = make_game()
|
||||
PRE_GAME = make_game(
|
||||
game_state="FUT", home_score=0, away_score=0, period=0, seconds_remaining=1200
|
||||
)
|
||||
FINAL_GAME = make_game(game_state="OFF", period=3, seconds_remaining=0)
|
||||
|
||||
SAMPLE_SCOREBOARD = {"games": [LIVE_GAME, PRE_GAME, FINAL_GAME]}
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def sample_scoreboard():
|
||||
return SAMPLE_SCOREBOARD
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def flask_client(tmp_path, monkeypatch):
|
||||
data_dir = tmp_path / "data"
|
||||
data_dir.mkdir()
|
||||
|
||||
# Write sample scoreboard JSON
|
||||
scoreboard_file = data_dir / "scoreboard_data.json"
|
||||
scoreboard_file.write_text(json.dumps(SAMPLE_SCOREBOARD))
|
||||
|
||||
# Create minimal SQLite DB so get_team_standings doesn't crash
|
||||
db_path = data_dir / "nhl_standings.db"
|
||||
conn = sqlite3.connect(str(db_path))
|
||||
conn.execute(
|
||||
"CREATE TABLE standings "
|
||||
"(team_common_name TEXT, league_sequence INTEGER, league_l10_sequence INTEGER)"
|
||||
)
|
||||
conn.commit()
|
||||
conn.close()
|
||||
|
||||
# Patch module-level path constants so no reloads are needed
|
||||
import app.routes as routes
|
||||
import app.scoreboard.process_data as process_data
|
||||
|
||||
monkeypatch.setattr(routes, "SCOREBOARD_DATA_FILE", str(scoreboard_file))
|
||||
monkeypatch.setattr(process_data, "DB_PATH", str(db_path))
|
||||
|
||||
from app import app as flask_app
|
||||
|
||||
flask_app.config["TESTING"] = True
|
||||
with flask_app.test_client() as client:
|
||||
yield client
|
||||
103
tests/test_process_data.py
Normal file
103
tests/test_process_data.py
Normal file
@@ -0,0 +1,103 @@
|
||||
from tests.conftest import make_game
|
||||
from app.scoreboard.process_data import (
|
||||
convert_game_state,
|
||||
get_game_outcome,
|
||||
process_period,
|
||||
process_record,
|
||||
process_start_time,
|
||||
process_time_remaining,
|
||||
utc_to_est_time,
|
||||
)
|
||||
|
||||
|
||||
class TestConvertGameState:
|
||||
def test_off_maps_to_final(self):
|
||||
assert convert_game_state("OFF") == "FINAL"
|
||||
|
||||
def test_crit_maps_to_live(self):
|
||||
assert convert_game_state("CRIT") == "LIVE"
|
||||
|
||||
def test_fut_maps_to_pre(self):
|
||||
assert convert_game_state("FUT") == "PRE"
|
||||
|
||||
def test_unknown_state_passes_through(self):
|
||||
assert convert_game_state("LIVE") == "LIVE"
|
||||
|
||||
|
||||
class TestProcessRecord:
|
||||
def test_na_returns_na(self):
|
||||
assert process_record("N/A") == "N/A"
|
||||
|
||||
def test_pads_single_digit_parts(self):
|
||||
assert process_record("5-3-1") == "05-03-01"
|
||||
|
||||
def test_already_padded_unchanged(self):
|
||||
assert process_record("40-25-10") == "40-25-10"
|
||||
|
||||
|
||||
class TestProcessPeriod:
|
||||
def test_pre_game_returns_zero(self):
|
||||
game = make_game(game_state="PRE")
|
||||
assert process_period(game) == 0
|
||||
|
||||
def test_fut_game_returns_zero(self):
|
||||
game = make_game(game_state="FUT")
|
||||
assert process_period(game) == 0
|
||||
|
||||
def test_final_game_returns_na(self):
|
||||
game = make_game(game_state="OFF")
|
||||
assert process_period(game) == "N/A"
|
||||
|
||||
def test_live_game_returns_period_number(self):
|
||||
game = make_game(game_state="LIVE", period=2)
|
||||
assert process_period(game) == 2
|
||||
|
||||
|
||||
class TestProcessTimeRemaining:
|
||||
def test_pre_game_returns_2000(self):
|
||||
game = make_game(game_state="FUT")
|
||||
assert process_time_remaining(game) == "20:00"
|
||||
|
||||
def test_final_game_returns_0000(self):
|
||||
game = make_game(game_state="OFF")
|
||||
assert process_time_remaining(game) == "00:00"
|
||||
|
||||
def test_live_game_returns_clock(self):
|
||||
game = make_game(game_state="LIVE", seconds_remaining=305)
|
||||
assert process_time_remaining(game) == "05:05"
|
||||
|
||||
def test_live_game_at_zero_returns_end(self):
|
||||
game = make_game(game_state="LIVE", seconds_remaining=0)
|
||||
assert process_time_remaining(game) == "END"
|
||||
|
||||
|
||||
class TestProcessStartTime:
|
||||
def test_pre_game_returns_est_time(self):
|
||||
game = make_game(game_state="FUT", start_time_utc="2024-04-10T23:00:00Z")
|
||||
result = process_start_time(game)
|
||||
assert result == "7:00 PM"
|
||||
|
||||
def test_pre_game_strips_leading_zero(self):
|
||||
game = make_game(game_state="FUT", start_time_utc="2024-04-10T22:00:00Z")
|
||||
result = process_start_time(game)
|
||||
assert not result.startswith("0")
|
||||
|
||||
def test_live_game_returns_na(self):
|
||||
game = make_game(game_state="LIVE")
|
||||
assert process_start_time(game) == "N/A"
|
||||
|
||||
|
||||
class TestGetGameOutcome:
|
||||
def test_final_game_returns_last_period_type(self):
|
||||
game = make_game(game_state="OFF")
|
||||
assert get_game_outcome(game, "FINAL") == "REG"
|
||||
|
||||
def test_live_game_returns_na(self):
|
||||
game = make_game(game_state="LIVE")
|
||||
assert get_game_outcome(game, "LIVE") == "N/A"
|
||||
|
||||
|
||||
class TestUtcToEstTime:
|
||||
def test_converts_utc_to_est(self):
|
||||
result = utc_to_est_time("2024-04-10T23:00:00Z")
|
||||
assert result == "07:00 PM"
|
||||
44
tests/test_routes.py
Normal file
44
tests/test_routes.py
Normal file
@@ -0,0 +1,44 @@
|
||||
import json
|
||||
|
||||
|
||||
class TestIndexRoute:
|
||||
def test_returns_200(self, flask_client):
|
||||
response = flask_client.get("/")
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_returns_html(self, flask_client):
|
||||
response = flask_client.get("/")
|
||||
assert b"NHL Scoreboard" in response.data
|
||||
|
||||
|
||||
class TestScoreboardRoute:
|
||||
def test_returns_200(self, flask_client):
|
||||
response = flask_client.get("/scoreboard")
|
||||
assert response.status_code == 200
|
||||
|
||||
def test_returns_json_with_expected_keys(self, flask_client):
|
||||
response = flask_client.get("/scoreboard")
|
||||
data = json.loads(response.data)
|
||||
assert "live_games" in data
|
||||
assert "pre_games" in data
|
||||
assert "final_games" in data
|
||||
|
||||
def test_live_games_have_required_fields(self, flask_client):
|
||||
response = flask_client.get("/scoreboard")
|
||||
data = json.loads(response.data)
|
||||
if data["live_games"]:
|
||||
game = data["live_games"][0]
|
||||
assert "Home Team" in game
|
||||
assert "Away Team" in game
|
||||
assert "Home Score" in game
|
||||
assert "Away Score" in game
|
||||
assert "Game State" in game
|
||||
assert game["Game State"] == "LIVE"
|
||||
|
||||
def test_missing_file_returns_error(self, flask_client, monkeypatch):
|
||||
import app.routes as routes
|
||||
|
||||
monkeypatch.setattr(routes, "SCOREBOARD_DATA_FILE", "/nonexistent/path.json")
|
||||
response = flask_client.get("/scoreboard")
|
||||
data = json.loads(response.data)
|
||||
assert "error" in data
|
||||
Reference in New Issue
Block a user