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>
151 lines
6.1 KiB
Python
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
|