171 lines
7.2 KiB
Python
171 lines
7.2 KiB
Python
|
|
"""
|
||
|
|
Manuell status per deltaker (migrasjon 087, 2026-08-18). Bruker: "Vi må
|
||
|
|
manuelt kunne sette Dsq, Rtd, Dnf, og fns [DNS]." Tournament-bredt
|
||
|
|
(ikke per runde), overstyrer BÅDE det beregnede til-par-tallet og en
|
||
|
|
ev. Cut-status, og blokkerer videre score-registrering i ALLE runder
|
||
|
|
(strengere enn Cut, som kun blokkerer runder ETTER cut-punktet).
|
||
|
|
"""
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from fastapi import HTTPException
|
||
|
|
|
||
|
|
from app.auth import CurrentUser
|
||
|
|
from app.routers.individual_tournaments import (
|
||
|
|
HoleUpdate as TournamentHoleUpdate,
|
||
|
|
TournamentParticipantUpdate,
|
||
|
|
individual_leaderboard,
|
||
|
|
update_hole as tournament_update_hole,
|
||
|
|
update_tournament_participant,
|
||
|
|
)
|
||
|
|
|
||
|
|
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,
|
||
|
|
)
|
||
|
|
|
||
|
|
import app.db as app_db
|
||
|
|
|
||
|
|
|
||
|
|
async def _score_holes(tournament_id, round_id, rp_id, org_id, user_id, scores: list[int]) -> None:
|
||
|
|
user = CurrentUser(user_id=user_id)
|
||
|
|
for n, gross in enumerate(scores, start=1):
|
||
|
|
await tournament_update_hole(
|
||
|
|
tournament_id, round_id, rp_id, n,
|
||
|
|
TournamentHoleUpdate(gross_strokes=gross, expected_version=None),
|
||
|
|
organization_id=org_id, user=user,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
async def _setup(org_id, owner_id, method="stroke_gross"):
|
||
|
|
tournament_id = await create_tournament(org_id, name="Status-turnering")
|
||
|
|
course_id = await create_course(org_id, name="Status Links")
|
||
|
|
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)
|
||
|
|
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, 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)
|
||
|
|
return tournament_id, course_id, tee_id, round1_id, round2_id
|
||
|
|
|
||
|
|
|
||
|
|
@pytest.mark.parametrize("status", ["dsq", "rtd", "dnf", "dns"])
|
||
|
|
async def test_status_overrides_computed_total_label(pool, status):
|
||
|
|
org_id = await create_org()
|
||
|
|
owner_id = await create_user()
|
||
|
|
await add_membership(org_id, owner_id, role="owner")
|
||
|
|
tournament_id, course_id, tee_id, round1_id, round2_id = await _setup(org_id, owner_id)
|
||
|
|
|
||
|
|
player_id = await create_player(org_id, display_name="Spiller")
|
||
|
|
tp_id = await create_tournament_participant(org_id, tournament_id, player_id)
|
||
|
|
rp_id = await create_tournament_round_participant(org_id, round1_id, tp_id, tee_id)
|
||
|
|
await _score_holes(tournament_id, round1_id, rp_id, org_id, owner_id, [4] * 18)
|
||
|
|
|
||
|
|
await update_tournament_participant(
|
||
|
|
tournament_id, tp_id, TournamentParticipantUpdate(status=status), organization_id=org_id
|
||
|
|
)
|
||
|
|
|
||
|
|
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.total_label == status.upper()
|
||
|
|
assert entry.is_leader is False
|
||
|
|
assert entry.position is None
|
||
|
|
assert entry.status == status
|
||
|
|
|
||
|
|
|
||
|
|
async def test_status_overrides_even_a_cut_survivor(pool):
|
||
|
|
"""En DSQ-et spiller skal vises som DSQ, ikke CUT, selv om de
|
||
|
|
tilfeldigvis også overlevde cutten."""
|
||
|
|
org_id = await create_org()
|
||
|
|
owner_id = await create_user()
|
||
|
|
await add_membership(org_id, owner_id, role="owner")
|
||
|
|
tournament_id, course_id, tee_id, round1_id, round2_id = await _setup(org_id, owner_id)
|
||
|
|
|
||
|
|
from app.routers.tournaments import TournamentUpdate, update_tournament
|
||
|
|
from app.routers.individual_tournaments import apply_cut
|
||
|
|
|
||
|
|
await update_tournament(
|
||
|
|
tournament_id, TournamentUpdate(cut_after_round=1, cut_size=5), organization_id=org_id,
|
||
|
|
)
|
||
|
|
|
||
|
|
player_id = await create_player(org_id, display_name="Spiller")
|
||
|
|
tp_id = await create_tournament_participant(org_id, tournament_id, player_id)
|
||
|
|
rp_id = await create_tournament_round_participant(org_id, round1_id, tp_id, tee_id)
|
||
|
|
await _score_holes(tournament_id, round1_id, rp_id, org_id, owner_id, [4] * 18)
|
||
|
|
|
||
|
|
await apply_cut(tournament_id, organization_id=org_id)
|
||
|
|
await update_tournament_participant(
|
||
|
|
tournament_id, tp_id, TournamentParticipantUpdate(status="dsq"), organization_id=org_id
|
||
|
|
)
|
||
|
|
|
||
|
|
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.total_label == "DSQ"
|
||
|
|
assert entry.cut is False # var faktisk innenfor cut-grensen
|
||
|
|
|
||
|
|
|
||
|
|
async def test_status_blocks_scoring_in_any_round_not_just_after_a_point(pool):
|
||
|
|
"""Strengere enn Cut: en DSQ-et/RTD-et/DNF-et/DNS-et spiller er
|
||
|
|
blokkert i ALLE runder, ikke bare etter et gitt punkt."""
|
||
|
|
org_id = await create_org()
|
||
|
|
owner_id = await create_user()
|
||
|
|
await add_membership(org_id, owner_id, role="owner")
|
||
|
|
tournament_id, course_id, tee_id, round1_id, round2_id = await _setup(org_id, owner_id)
|
||
|
|
|
||
|
|
player_id = await create_player(org_id, display_name="Spiller")
|
||
|
|
tp_id = await create_tournament_participant(org_id, tournament_id, player_id)
|
||
|
|
rp1_id = await create_tournament_round_participant(org_id, round1_id, tp_id, tee_id)
|
||
|
|
rp2_id = await create_tournament_round_participant(org_id, round2_id, tp_id, tee_id)
|
||
|
|
|
||
|
|
await update_tournament_participant(
|
||
|
|
tournament_id, tp_id, TournamentParticipantUpdate(status="dsq"), organization_id=org_id
|
||
|
|
)
|
||
|
|
|
||
|
|
user = CurrentUser(user_id=owner_id)
|
||
|
|
with pytest.raises(HTTPException) as exc_round1:
|
||
|
|
await tournament_update_hole(
|
||
|
|
tournament_id, round1_id, rp1_id, 1,
|
||
|
|
TournamentHoleUpdate(gross_strokes=4, expected_version=None),
|
||
|
|
organization_id=org_id, user=user,
|
||
|
|
)
|
||
|
|
assert exc_round1.value.status_code == 403
|
||
|
|
|
||
|
|
with pytest.raises(HTTPException) as exc_round2:
|
||
|
|
await tournament_update_hole(
|
||
|
|
tournament_id, round2_id, rp2_id, 1,
|
||
|
|
TournamentHoleUpdate(gross_strokes=4, expected_version=None),
|
||
|
|
organization_id=org_id, user=user,
|
||
|
|
)
|
||
|
|
assert exc_round2.value.status_code == 403
|
||
|
|
|
||
|
|
|
||
|
|
async def test_active_status_is_unaffected_default(pool):
|
||
|
|
"""Ingen regresjon -- default 'active' oppfører seg akkurat som før."""
|
||
|
|
org_id = await create_org()
|
||
|
|
owner_id = await create_user()
|
||
|
|
await add_membership(org_id, owner_id, role="owner")
|
||
|
|
tournament_id, course_id, tee_id, round1_id, round2_id = await _setup(org_id, owner_id)
|
||
|
|
|
||
|
|
player_id = await create_player(org_id, display_name="Spiller")
|
||
|
|
tp_id = await create_tournament_participant(org_id, tournament_id, player_id)
|
||
|
|
rp_id = await create_tournament_round_participant(org_id, round1_id, tp_id, tee_id)
|
||
|
|
await _score_holes(tournament_id, round1_id, rp_id, org_id, owner_id, [4] * 18)
|
||
|
|
|
||
|
|
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.status == "active"
|
||
|
|
assert entry.total_label == "E"
|
||
|
|
assert entry.position == "1"
|