Files
hoopscout/tests/test_players_views.py
2026-03-10 15:53:55 +01:00

196 lines
6.5 KiB
Python

from datetime import date
import pytest
from django.urls import reverse
from apps.competitions.models import Competition, Season
from apps.players.models import Nationality, Player, PlayerAlias, Position, Role
from apps.stats.models import PlayerSeason, PlayerSeasonStats
from apps.teams.models import Team
@pytest.mark.django_db
def test_player_search_filters_by_name_and_position(client):
nationality = Nationality.objects.create(name="Italy", iso2_code="IT", iso3_code="ITA")
guard = Position.objects.create(code="PG", name="Point Guard")
center = Position.objects.create(code="C", name="Center")
role = Role.objects.create(code="playmaker", name="Playmaker")
p1 = Player.objects.create(
first_name="Marco",
last_name="Rossi",
full_name="Marco Rossi",
birth_date=date(2001, 1, 10),
nationality=nationality,
nominal_position=guard,
inferred_role=role,
height_cm=190,
weight_kg=82,
)
PlayerAlias.objects.create(player=p1, alias="M. Rossi")
Player.objects.create(
first_name="Luca",
last_name="Bianchi",
full_name="Luca Bianchi",
birth_date=date(1998, 2, 3),
nationality=nationality,
nominal_position=center,
inferred_role=role,
height_cm=208,
weight_kg=105,
)
response = client.get(
reverse("players:index"),
data={"q": "ross", "nominal_position": guard.id},
)
assert response.status_code == 200
players = response.context["players"]
assert len(players) == 1
assert players[0].full_name == "Marco Rossi"
@pytest.mark.django_db
def test_player_search_htmx_returns_partial(client):
nationality = Nationality.objects.create(name="Spain", iso2_code="ES", iso3_code="ESP")
position = Position.objects.create(code="SG", name="Shooting Guard")
role = Role.objects.create(code="shooter", name="Shooter")
season = Season.objects.create(label="2025-2026", start_date=date(2025, 9, 1), end_date=date(2026, 6, 1))
competition = Competition.objects.create(
name="Liga ACB",
slug="liga-acb",
competition_type=Competition.CompetitionType.LEAGUE,
gender=Competition.Gender.MEN,
country=nationality,
)
team = Team.objects.create(name="Madrid", slug="madrid", country=nationality)
player = Player.objects.create(
first_name="Juan",
last_name="Perez",
full_name="Juan Perez",
birth_date=date(2000, 4, 12),
nationality=nationality,
nominal_position=position,
inferred_role=role,
height_cm=196,
weight_kg=90,
)
season_row = PlayerSeason.objects.create(
player=player,
season=season,
team=team,
competition=competition,
games_played=30,
minutes_played=900,
)
PlayerSeasonStats.objects.create(
player_season=season_row,
points=16.2,
rebounds=4.3,
assists=3.1,
steals=1.4,
blocks=0.3,
turnovers=1.8,
fg_pct=49.5,
three_pct=38.2,
ft_pct=84.1,
player_efficiency_rating=18.7,
)
response = client.get(
reverse("players:index"),
HTTP_HX_REQUEST="true",
data={"points_per_game_min": 15},
)
assert response.status_code == 200
assert "Results" in response.content.decode()
assert "Juan Perez" in response.content.decode()
@pytest.mark.django_db
def test_player_detail_page_loads(client):
nationality = Nationality.objects.create(name="France", iso2_code="FR", iso3_code="FRA")
position = Position.objects.create(code="SF", name="Small Forward")
role = Role.objects.create(code="wing", name="Wing")
player = Player.objects.create(
first_name="Paul",
last_name="Martin",
full_name="Paul Martin",
birth_date=date(1999, 8, 14),
nationality=nationality,
nominal_position=position,
inferred_role=role,
height_cm=201,
weight_kg=95,
)
PlayerAlias.objects.create(player=player, alias="P. Martin")
response = client.get(reverse("players:detail", kwargs={"pk": player.pk}))
assert response.status_code == 200
body = response.content.decode()
assert "Paul Martin" in body
assert "P. Martin" in body
@pytest.mark.django_db
def test_player_search_invalid_numeric_filter_shows_errors_and_no_broad_fallback(client):
nationality = Nationality.objects.create(name="Belgium", iso2_code="BE", iso3_code="BEL")
position = Position.objects.create(code="PG", name="Point Guard")
role = Role.objects.create(code="playmaker", name="Playmaker")
Player.objects.create(
first_name="Any",
last_name="Player",
full_name="Any Player",
birth_date=date(2000, 1, 1),
nationality=nationality,
nominal_position=position,
inferred_role=role,
)
response = client.get(reverse("players:index"), data={"points_per_game_min": "abc", "q": "Any"})
assert response.status_code == 200
assert list(response.context["players"]) == []
assert response.context["search_has_errors"] is True
assert "points per game min" in response.content.decode().lower()
assert response.context["search_form"]["q"].value() == "Any"
@pytest.mark.django_db
def test_player_search_invalid_choice_filter_shows_errors(client):
response = client.get(reverse("players:index"), data={"sort": "bad-sort"})
assert response.status_code == 200
assert list(response.context["players"]) == []
assert response.context["search_has_errors"] is True
assert "select a valid choice" in response.content.decode().lower()
@pytest.mark.django_db
def test_player_search_invalid_range_combination_shows_errors(client):
response = client.get(reverse("players:index"), data={"age_min": 30, "age_max": 20})
assert response.status_code == 200
assert list(response.context["players"]) == []
assert response.context["search_has_errors"] is True
body = response.content.decode().lower()
assert "age max" in body
assert "must be >=" in body or "must be >=" in body
@pytest.mark.django_db
def test_player_search_htmx_invalid_filters_return_validation_feedback(client):
response = client.get(
reverse("players:index"),
HTTP_HX_REQUEST="true",
data={"points_per_game_min": "abc"},
)
assert response.status_code == 200
body = response.content.decode().lower()
assert "search filters are invalid" in body
assert "points per game min" in body