teecup/tests/test_eclectic.py
Erol Haagenrud 9b7ff8e2ef Eclectic-format for org-individuelle turneringer (Del C) + tre UI-rettelser
Del C (ADR-068, migrasjon 072, siste del av den tredelte utvidelsen som
startet med Flaggturnering GPS/kart, se ADR-066/067): nytt format
eclectic_gross/eclectic_net/eclectic_stableford -- beste resultat per
hull på tvers av en turnerings egne runder, krever samme bane (avvist
tydelig ved rundeopprettelse ellers). Regnes ut ved lesing, ingen nye
tabeller. Bevisst avvik fra opprinnelig plan: integrert som en ny gren
i eksisterende individual-leaderboard-endepunkt fremfor et nytt eget
endepunkt -- se ADR-068 for begrunnelsen.

Tre ikke-relaterte, brukerrapporterte UI-rettelser tatt med i samme
runde: avstandsindikatoren brukte "grønn"/"Midt" i stedet for riktige
golf-uttrykk "green"/"senter", og "Oppdateres live"-badgen fjernet.
"Antall hull"-bryteren i Ny runde-veiviseren fikk samme grønne
aksent-valgt-stil som resten av samme skjerm (delt Segmented-primitiv).

Se ARCHITECTURE_DECISIONS.md (ADR-068) og CHANGELOG.md (punkt 84) for
full begrunnelse og verifiseringslogg.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 11:12:25 +02:00

151 lines
6.1 KiB
Python

"""
Eclectic (org individuell turnering, ADR-067-tillegget "Del C", migrasjon
072) -- "drømmerunde" satt sammen av beste resultat per hull på tvers av
turneringens runder. Samme kall-direkte-mønster som
test_flag_plant_individual_tournament.py.
"""
import pytest
from fastapi import HTTPException
from app.auth import CurrentUser
from app.routers.individual_tournaments import (
HoleUpdate,
TournamentRoundCreate,
create_round,
individual_leaderboard,
update_hole,
)
from tests.conftest import (
add_membership,
create_org,
create_org_hole,
create_course,
create_player,
create_tee,
create_tournament,
create_tournament_participant,
create_tournament_round,
create_tournament_round_participant,
create_user,
)
async def _setup_eclectic_tournament(scoring_method: str = "eclectic_gross"):
org_id = await create_org()
user_id = await create_user()
await add_membership(org_id, user_id, role="owner")
tournament_id = await create_tournament(org_id)
course_id = await create_course(org_id)
for n in range(1, 19):
await create_org_hole(org_id, course_id, hole_number=n, par=4, stroke_index=n)
tee_id = await create_tee(org_id, course_id)
import app.db as app_db
async with app_db.org_connection(org_id) as conn:
await conn.execute(
"UPDATE tournament SET format_type = 'individual', scoring_method = $2 WHERE id = $1",
tournament_id, scoring_method,
)
round1_id = await create_tournament_round(org_id, tournament_id, course_id, sequence=1)
round2_id = await create_tournament_round(org_id, tournament_id, course_id, sequence=2)
player_id = await create_player(org_id, user_id=user_id)
tp_id = await create_tournament_participant(org_id, tournament_id, player_id)
# course_handicap=0 -- gjør netto/Stableford-tallene enkle å regne for hånd.
rp1_id = await create_tournament_round_participant(org_id, round1_id, tp_id, tee_id, course_handicap=0)
rp2_id = await create_tournament_round_participant(org_id, round2_id, tp_id, tee_id, course_handicap=0)
return org_id, user_id, tournament_id, round1_id, round2_id, rp1_id, rp2_id, tp_id
async def _play_hole(tournament_id, round_id, rp_id, hole_number, score, org_id, user):
await update_hole(
tournament_id, round_id, rp_id, hole_number,
HoleUpdate(gross_strokes=score),
organization_id=org_id, user=user,
)
async def test_eclectic_gross_picks_best_per_hole_across_rounds(pool):
org_id, user_id, tournament_id, round1_id, round2_id, rp1_id, rp2_id, tp_id = (
await _setup_eclectic_tournament("eclectic_gross")
)
user = CurrentUser(user_id=user_id)
# Hull 1: runde 1 -> 5, runde 2 -> 6 (runde 1 vinner).
# Hull 2: runde 1 -> 6, runde 2 -> 4 (runde 2 vinner).
await _play_hole(tournament_id, round1_id, rp1_id, 1, 5, org_id, user)
await _play_hole(tournament_id, round1_id, rp1_id, 2, 6, org_id, user)
await _play_hole(tournament_id, round2_id, rp2_id, 1, 6, org_id, user)
await _play_hole(tournament_id, round2_id, rp2_id, 2, 4, org_id, user)
entries = await individual_leaderboard(tournament_id, organization_id=org_id)
entry = next(e for e in entries if e.tournament_participant_id == tp_id)
assert entry.eclectic_total == 5 + 4
holes_by_number = {h.hole_number: h for h in entry.eclectic_holes}
assert holes_by_number[1].value == 5
assert holes_by_number[1].round_number == 1
assert holes_by_number[2].value == 4
assert holes_by_number[2].round_number == 2
async def test_eclectic_net_subtracts_strokes_received(pool):
org_id, user_id, tournament_id, round1_id, round2_id, rp1_id, rp2_id, tp_id = (
await _setup_eclectic_tournament("eclectic_net")
)
user = CurrentUser(user_id=user_id)
await _play_hole(tournament_id, round1_id, rp1_id, 1, 5, org_id, user)
entries = await individual_leaderboard(tournament_id, organization_id=org_id)
entry = next(e for e in entries if e.tournament_participant_id == tp_id)
# course_handicap=0 -- ingen mottatte slag, netto == brutto.
assert entry.eclectic_total == 5
async def test_eclectic_stableford_picks_highest_points(pool):
org_id, user_id, tournament_id, round1_id, round2_id, rp1_id, rp2_id, tp_id = (
await _setup_eclectic_tournament("eclectic_stableford")
)
user = CurrentUser(user_id=user_id)
# Par 4, HCP 0: gross 4 -> netto par -> 2 poeng. gross 3 -> netto birdie -> 3 poeng.
await _play_hole(tournament_id, round1_id, rp1_id, 1, 4, org_id, user)
await _play_hole(tournament_id, round2_id, rp2_id, 1, 3, org_id, user)
entries = await individual_leaderboard(tournament_id, organization_id=org_id)
entry = next(e for e in entries if e.tournament_participant_id == tp_id)
assert entry.eclectic_total == 3
assert entry.eclectic_holes[0].round_number == 2
async def test_eclectic_rejects_round_on_different_course(pool):
org_id, user_id, tournament_id, round1_id, round2_id, rp1_id, rp2_id, tp_id = (
await _setup_eclectic_tournament("eclectic_gross")
)
other_course_id = await create_course(org_id)
for n in range(1, 19):
await create_org_hole(org_id, other_course_id, hole_number=n, par=4, stroke_index=n)
with pytest.raises(HTTPException) as exc_info:
await create_round(
tournament_id,
TournamentRoundCreate(sequence=3, course_id=other_course_id),
organization_id=org_id,
)
assert exc_info.value.status_code == 400
assert exc_info.value.detail["code"] == "VALIDATION_FAILED"
async def test_non_eclectic_tournament_allows_different_courses(pool):
org_id, user_id, tournament_id, round1_id, round2_id, rp1_id, rp2_id, tp_id = (
await _setup_eclectic_tournament("stroke_gross")
)
other_course_id = await create_course(org_id)
for n in range(1, 19):
await create_org_hole(org_id, other_course_id, hole_number=n, par=4, stroke_index=n)
result = await create_round(
tournament_id,
TournamentRoundCreate(sequence=3, course_id=other_course_id),
organization_id=org_id,
)
assert result.course_id == other_course_id