""" Tiebreak ved likt resultat i individuelle turneringer (migrasjon 085, 2026-08-18). Bruker: "Ved likt resultat ... Skal laveste hcp plasseres foran, beste siste 18/9/6/3/siste hull plasseres foran, eller deles plasseringene. Her kan svaret være forskjellig for vinner og resten av feltet." Metoden er en fri organisator-innstilling, ikke fastlåst -- to UAVHENGIGE felt (vinner/felt), og påvirker ALDRI Cut-grensen (kun sluttresultatets plasseringsvisning). """ from app.routers.individual_tournaments import ( HoleUpdate as TournamentHoleUpdate, apply_cut, individual_leaderboard, update_hole as tournament_update_hole, ) from app.routers.tournaments import TournamentUpdate, update_tournament from app.auth import CurrentUser 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_single_round(org_id, owner_id, method="stroke_gross"): tournament_id = await create_tournament(org_id, name="Tiebreak-turnering") course_id = await create_course(org_id, name="Tiebreak 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, ) round_id = await create_tournament_round(org_id, tournament_id, course_id, sequence=1) return tournament_id, course_id, tee_id, round_id async def _set_tiebreak(org_id, tournament_id, winner: str, field: str): await update_tournament( tournament_id, TournamentUpdate(tiebreak_winner_method=winner, tiebreak_field_method=field), organization_id=org_id, ) async def test_countback_resolves_winner_tie_by_better_back_nine(pool): 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, round_id = await _setup_single_round(org_id, owner_id) await _set_tiebreak(org_id, tournament_id, winner="countback", field="none") p1 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P1")) p2 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P2")) p3 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P3")) rp1 = await create_tournament_round_participant(org_id, round_id, p1, tee_id) rp2 = await create_tournament_round_participant(org_id, round_id, p2, tee_id) rp3 = await create_tournament_round_participant(org_id, round_id, p3, tee_id) # P1: front9 alle par (36), back9 to birdier (34) -> total 70, back9=34. p1_scores = [4] * 9 + [3, 3] + [4] * 7 # P2: front9 to birdier (34), back9 alle par (36) -> total 70, back9=36 (dårligere enn P1 sin back9). p2_scores = [3, 3] + [4] * 7 + [4] * 9 # P3: klart bak, ingen uavgjort. p3_scores = [5] * 18 await _score_holes(tournament_id, round_id, rp1, org_id, owner_id, p1_scores) await _score_holes(tournament_id, round_id, rp2, org_id, owner_id, p2_scores) await _score_holes(tournament_id, round_id, rp3, org_id, owner_id, p3_scores) entries = await individual_leaderboard(tournament_id, organization_id=org_id) by_id = {e.tournament_participant_id: e for e in entries} assert by_id[p1].position == "1" # bedre back9 -> sole leder, IKKE "T1" assert by_id[p1].is_leader is True assert by_id[p2].position == "2" assert by_id[p2].is_leader is False assert by_id[p3].position == "3" async def test_lowest_hcp_resolves_field_tie_but_not_winner(pool): 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, round_id = await _setup_single_round(org_id, owner_id) # Vinner-tiebreak IKKE satt (none) -- kun felt-tiebreak skal virke. await _set_tiebreak(org_id, tournament_id, winner="none", field="lowest_hcp") leader = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="Leader")) p2 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P2")) p3 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P3")) async with app_db.org_connection(org_id) as conn: await conn.execute("UPDATE tournament_participant SET handicap_index_snapshot = 8.0 WHERE id = $1", p2) await conn.execute("UPDATE tournament_participant SET handicap_index_snapshot = 20.0 WHERE id = $1", p3) rp_leader = await create_tournament_round_participant(org_id, round_id, leader, tee_id) rp2 = await create_tournament_round_participant(org_id, round_id, p2, tee_id) rp3 = await create_tournament_round_participant(org_id, round_id, p3, tee_id) await _score_holes(tournament_id, round_id, rp_leader, org_id, owner_id, [3] * 18) # klar leder, -18 await _score_holes(tournament_id, round_id, rp2, org_id, owner_id, [4] * 18) # uavgjort med p3, E await _score_holes(tournament_id, round_id, rp3, org_id, owner_id, [4] * 18) # uavgjort med p2, E entries = await individual_leaderboard(tournament_id, organization_id=org_id) by_id = {e.tournament_participant_id: e for e in entries} assert by_id[leader].position == "1" assert by_id[leader].is_leader is True assert by_id[p2].position == "2" # lavere HCP (8.0 < 20.0) -> foran assert by_id[p3].position == "3" async def test_tiebreak_method_none_keeps_shared_position(pool): """Standard/uendret oppførsel -- ingen tiebreak konfigurert i det hele tatt (default 'none' for begge felt, ingen regresjon).""" 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, round_id = await _setup_single_round(org_id, owner_id) p1 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P1")) p2 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P2")) rp1 = await create_tournament_round_participant(org_id, round_id, p1, tee_id) rp2 = await create_tournament_round_participant(org_id, round_id, p2, tee_id) await _score_holes(tournament_id, round_id, rp1, org_id, owner_id, [4] * 18) await _score_holes(tournament_id, round_id, rp2, org_id, owner_id, [4] * 18) entries = await individual_leaderboard(tournament_id, organization_id=org_id) by_id = {e.tournament_participant_id: e for e in entries} assert by_id[p1].position == "T1" assert by_id[p2].position == "T1" assert by_id[p1].is_leader is True assert by_id[p2].is_leader is True async def test_tiebreak_never_affects_cut_boundary(pool): """Bruker bekreftet eksplisitt: tiebreak påvirker KUN sluttresultatet, ALDRI selve cut-grensen -- full delt plass på grensen uansett tiebreak-innstilling.""" 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 = await _setup_single_round(org_id, owner_id) round2_id = await create_tournament_round(org_id, tournament_id, course_id, sequence=2) # Countback konfigurert -- skal likevel IKKE påvirke cutten. await _set_tiebreak(org_id, tournament_id, winner="countback", field="countback") await update_tournament( tournament_id, TournamentUpdate(cut_after_round=1, cut_size=1), organization_id=org_id, ) p1 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P1")) p2 = await create_tournament_participant(org_id, tournament_id, await create_player(org_id, display_name="P2")) rp1 = await create_tournament_round_participant(org_id, round1_id, p1, tee_id) rp2 = await create_tournament_round_participant(org_id, round1_id, p2, tee_id) # Identisk totalt OG identisk back9 -- ekte fullt uavgjort, selv med countback slått på. await _score_holes(tournament_id, round1_id, rp1, org_id, owner_id, [4] * 18) await _score_holes(tournament_id, round1_id, rp2, org_id, owner_id, [4] * 18) result = await apply_cut(tournament_id, organization_id=org_id) assert result.cut_size == 1 assert result.survivors == 2 # begge slipper gjennom -- delt 1.-plass, IKKE brutt av countback assert result.cut_count == 0