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