NBA-Fantasy-Game / tests /test_database.py
HatmanStack
ci(hooks): add pre-commit config with ruff and mypy hooks
56bab24
Raw
History Blame Contribute Delete
6.72 kB
"""Tests for database module using local pandas data."""
from unittest.mock import patch
import pandas as pd
import pytest
from src.config import PLAYER_COLUMNS
from src.database.connection import (
DatabaseConnectionError,
QueryExecutionError,
get_data,
load_data,
)
from src.database.queries import (
get_away_team_by_stats,
get_players_by_full_names,
search_player_by_name,
)
class TestLoadData:
"""Tests for load_data and get_data functions."""
def test_load_data_returns_dataframe(self) -> None:
"""Test that load_data returns a DataFrame with uppercase columns."""
df = load_data()
assert isinstance(df, pd.DataFrame)
assert not df.empty
# All columns should be uppercase
for col in df.columns:
assert col == col.upper()
def test_get_data_returns_dataframe(self) -> None:
"""Test that get_data returns a DataFrame."""
df = get_data()
assert isinstance(df, pd.DataFrame)
assert not df.empty
@patch("src.database.connection.CSV_PATH")
def test_load_data_missing_file_raises_error(self, mock_path) -> None: # type: ignore[no-untyped-def]
"""Test that missing CSV raises DatabaseConnectionError."""
mock_path.exists.return_value = False
with pytest.raises(DatabaseConnectionError, match="not found"):
load_data()
@patch("src.database.connection.pd.read_csv")
@patch("src.database.connection.CSV_PATH")
def test_load_data_parser_error_raises_connection_error(
self,
mock_path,
mock_read_csv, # type: ignore[no-untyped-def]
) -> None:
"""Test that CSV parse errors raise DatabaseConnectionError."""
mock_path.exists.return_value = True
mock_read_csv.side_effect = pd.errors.ParserError("bad csv")
with pytest.raises(DatabaseConnectionError):
load_data()
class TestSearchPlayerByName:
"""Tests for search_player_by_name function."""
def test_search_by_full_name(self, sample_player_df: pd.DataFrame) -> None:
"""Verify search finds player by full name."""
result = search_player_by_name(sample_player_df, "LeBron James")
assert result == [("LeBron James",)]
def test_search_by_first_name(self, sample_player_df: pd.DataFrame) -> None:
"""Verify search finds player by first name."""
result = search_player_by_name(sample_player_df, "LeBron")
assert result == [("LeBron James",)]
def test_search_by_last_name(self, sample_player_df: pd.DataFrame) -> None:
"""Verify search finds player by last name."""
result = search_player_by_name(sample_player_df, "Jordan")
assert result == [("Michael Jordan",)]
def test_search_case_insensitive(self, sample_player_df: pd.DataFrame) -> None:
"""Verify search is case-insensitive."""
result = search_player_by_name(sample_player_df, "lebron")
assert result == [("LeBron James",)]
def test_search_partial_name(self, sample_player_df: pd.DataFrame) -> None:
"""Verify search finds player by partial name."""
result = search_player_by_name(sample_player_df, "Jord")
assert result == [("Michael Jordan",)]
def test_returns_empty_on_no_match(self, sample_player_df: pd.DataFrame) -> None:
"""Verify empty list returned when no player found."""
result = search_player_by_name(sample_player_df, "NonExistent Player")
assert result == []
class TestGetPlayersByFullNames:
"""Tests for get_players_by_full_names batch query."""
def test_returns_correct_players(self, sample_player_df: pd.DataFrame) -> None:
"""Verify correct players are returned in DataFrame."""
names = ["LeBron James", "Michael Jordan"]
result = get_players_by_full_names(sample_player_df, names)
assert isinstance(result, pd.DataFrame)
assert len(result) == 2
assert set(result["FULL_NAME"]) == set(names)
assert list(result.columns) == PLAYER_COLUMNS
def test_empty_names_returns_empty_dataframe(
self, sample_player_df: pd.DataFrame
) -> None:
"""Test that empty input returns empty DataFrame."""
result = get_players_by_full_names(sample_player_df, [])
assert isinstance(result, pd.DataFrame)
assert result.empty
assert list(result.columns) == PLAYER_COLUMNS
class TestGetAwayTeamByStats:
"""Tests for get_away_team_by_stats."""
def test_max_attempts_raises_error(self) -> None:
"""Test that max_attempts limit works when population is too small."""
# Create a DF with only 2 players
df = pd.DataFrame(
[
{"FULL_NAME": "P1", "PTS": 1001, "REB": 501, "AST": 301, "STL": 101},
{"FULL_NAME": "P2", "PTS": 1001, "REB": 501, "AST": 301, "STL": 101},
]
)
# Add missing columns to avoid errors if needed, though queries only use these
for col in PLAYER_COLUMNS:
if col not in df.columns:
df[col] = 0
with pytest.raises(QueryExecutionError) as exc_info:
get_away_team_by_stats(
df,
pts_threshold=1000,
reb_threshold=500,
ast_threshold=300,
stl_threshold=100,
max_attempts=3,
)
assert "3 attempts" in str(exc_info.value)
def test_success_with_enough_players(self) -> None:
"""Test successful generation with sufficient population."""
# Create a DF with 10 players meeting criteria
data = []
for i in range(10):
data.append(
{
"FULL_NAME": f"Player{i}",
"PTS": 2000,
"REB": 1000,
"AST": 500,
"STL": 200,
}
)
df = pd.DataFrame(data)
for col in PLAYER_COLUMNS:
if col not in df.columns:
df[col] = 0
result = get_away_team_by_stats(
df,
pts_threshold=1000,
reb_threshold=500,
ast_threshold=300,
stl_threshold=100,
)
assert isinstance(result, pd.DataFrame)
assert len(result) == 5
class TestCsvColumnValidation:
"""Integration tests validating CSV data matches config."""
def test_csv_columns_match_config(self) -> None:
"""Verify that actual CSV columns match PLAYER_COLUMNS in config."""
df = load_data()
assert not df.empty, "CSV file should not be empty"
assert list(df.columns) == PLAYER_COLUMNS