""" Flaggturnering: GPS-flaggplanting + runde 2+, org-individuelle turneringer (migrasjon 070, 2026-08-14) -- speiler test_flag_plant.py (frittstående runder) presist, men gjennom individual_tournaments.py sin self-only autorisasjon (user_is_own_tournament_participant), ikke rounds.py sin flight-styrte modell. Kaller de faktiske router-funksjonene direkte (samme mønster som test_scoring_concurrency.py) -- organization_id sendes som vanlig funksjonsargument her, ikke via FastAPIs Depends (som kun løses når appen faktisk kjører via HTTP). """ import pytest from fastapi import HTTPException from app.auth import CurrentUser from app.routers.individual_tournaments import ( FlagOverflowHoleIn, FlagPlantIn, HoleUpdate, get_flag_map, get_flag_plant, list_flag_overflow_holes, plant_flag, unplant_flag, update_flag_overflow_hole, 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_flag_tournament(): 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 = 'flag' WHERE id = $1", tournament_id, ) tournament_round_id = await create_tournament_round(org_id, tournament_id, course_id) player_id = await create_player(org_id, user_id=user_id) tp_id = await create_tournament_participant(org_id, tournament_id, player_id) rp_id = await create_tournament_round_participant(org_id, tournament_round_id, tp_id, tee_id) return org_id, user_id, tournament_id, tournament_round_id, rp_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_plant_flag_succeeds_on_first_unplayed_hole(pool): org_id, user_id, tournament_id, round_id, rp_id = await _setup_flag_tournament() user = CurrentUser(user_id=user_id) await _play_hole(tournament_id, round_id, rp_id, 1, 5, org_id, user) result = await plant_flag( tournament_id, round_id, rp_id, FlagPlantIn(lat=59.9, lng=10.7, hole_number=2, lap=1, on_green=False), organization_id=org_id, user=user, ) assert result.lap == 1 assert result.hole_number == 2 async def test_plant_flag_rejects_non_owning_player(pool): org_id, user_id, tournament_id, round_id, rp_id = await _setup_flag_tournament() stranger_id = await create_user() await add_membership(org_id, stranger_id, role="member") stranger = CurrentUser(user_id=stranger_id) with pytest.raises(HTTPException) as exc_info: await plant_flag( tournament_id, round_id, rp_id, FlagPlantIn(lat=59.9, lng=10.7, hole_number=1, lap=1, on_green=False), organization_id=org_id, user=stranger, ) assert exc_info.value.status_code == 403 assert exc_info.value.detail["code"] == "NOT_TOURNAMENT_PARTICIPANT" async def test_plant_flag_rejects_mismatched_hole(pool): org_id, user_id, tournament_id, round_id, rp_id = await _setup_flag_tournament() user = CurrentUser(user_id=user_id) with pytest.raises(HTTPException) as exc_info: await plant_flag( tournament_id, round_id, rp_id, FlagPlantIn(lat=59.9, lng=10.7, hole_number=5, lap=1, on_green=False), organization_id=org_id, user=user, ) assert exc_info.value.status_code == 400 assert exc_info.value.detail["code"] == "VALIDATION_FAILED" async def test_unplant_and_replant(pool): org_id, user_id, tournament_id, round_id, rp_id = await _setup_flag_tournament() user = CurrentUser(user_id=user_id) await plant_flag( tournament_id, round_id, rp_id, FlagPlantIn(lat=59.9, lng=10.7, hole_number=1, lap=1, on_green=True, distance_to_pin_cm=120), organization_id=org_id, user=user, ) current = await get_flag_plant(tournament_id, round_id, rp_id, organization_id=org_id) assert current is not None and current.distance_to_pin_cm == 120 await unplant_flag(tournament_id, round_id, rp_id, organization_id=org_id, user=user) assert await get_flag_plant(tournament_id, round_id, rp_id, organization_id=org_id) is None async def test_full_lap_two_flow(pool): org_id, user_id, tournament_id, round_id, rp_id = await _setup_flag_tournament() user = CurrentUser(user_id=user_id) for n in range(1, 19): await _play_hole(tournament_id, round_id, rp_id, n, 4, org_id, user) with pytest.raises(HTTPException): await plant_flag( tournament_id, round_id, rp_id, FlagPlantIn(lat=59.9, lng=10.7, hole_number=1, lap=1, on_green=False), organization_id=org_id, user=user, ) overflow_hole = await update_flag_overflow_hole( tournament_id, round_id, rp_id, 2, 1, FlagOverflowHoleIn(gross_strokes=5), organization_id=org_id, user=user, ) assert overflow_hole.lap == 2 assert overflow_hole.gross_strokes == 5 holes = await list_flag_overflow_holes(tournament_id, round_id, rp_id, organization_id=org_id) assert len(holes) == 1 lap2_plant = await plant_flag( tournament_id, round_id, rp_id, FlagPlantIn(lat=59.9, lng=10.7, hole_number=2, lap=2, on_green=False), organization_id=org_id, user=user, ) assert lap2_plant.lap == 2 assert lap2_plant.hole_number == 2 async def test_flag_map_shows_only_own_flag_when_switch_off(pool): org_id, user_id, tournament_id, round_id, rp_id = await _setup_flag_tournament() other_user_id = await create_user() await add_membership(org_id, other_user_id, role="member") other_player_id = await create_player(org_id, user_id=other_user_id) other_tp_id = await create_tournament_participant(org_id, tournament_id, other_player_id) import app.db as app_db async with app_db.org_connection(org_id) as conn: tee_id = await conn.fetchval("SELECT tee_id FROM tournament_round_participant WHERE id = $1", rp_id) other_rp_id = await create_tournament_round_participant(org_id, round_id, other_tp_id, tee_id) user = CurrentUser(user_id=user_id) other = CurrentUser(user_id=other_user_id) await plant_flag( tournament_id, round_id, rp_id, FlagPlantIn(lat=59.9, lng=10.7, hole_number=1, lap=1, on_green=False), organization_id=org_id, user=user, ) await plant_flag( tournament_id, round_id, other_rp_id, FlagPlantIn(lat=60.0, lng=10.8, hole_number=1, lap=1, on_green=False), organization_id=org_id, user=other, ) result = await get_flag_map(tournament_id, round_id, organization_id=org_id, user=user) assert result.visible_to_all is False assert [f.participant_id for f in result.flags] == [rp_id] async def test_flag_map_shows_all_flags_when_switch_on(pool): org_id, user_id, tournament_id, round_id, rp_id = await _setup_flag_tournament() other_user_id = await create_user() await add_membership(org_id, other_user_id, role="member") other_player_id = await create_player(org_id, user_id=other_user_id) other_tp_id = await create_tournament_participant(org_id, tournament_id, other_player_id) import app.db as app_db async with app_db.org_connection(org_id) as conn: tee_id = await conn.fetchval("SELECT tee_id FROM tournament_round_participant WHERE id = $1", rp_id) await conn.execute("UPDATE tournament SET flag_map_visible = true WHERE id = $1", tournament_id) other_rp_id = await create_tournament_round_participant(org_id, round_id, other_tp_id, tee_id) user = CurrentUser(user_id=user_id) other = CurrentUser(user_id=other_user_id) await plant_flag( tournament_id, round_id, rp_id, FlagPlantIn(lat=59.9, lng=10.7, hole_number=1, lap=1, on_green=False), organization_id=org_id, user=user, ) await plant_flag( tournament_id, round_id, other_rp_id, FlagPlantIn(lat=60.0, lng=10.8, hole_number=1, lap=1, on_green=False), organization_id=org_id, user=other, ) result = await get_flag_map(tournament_id, round_id, organization_id=org_id, user=other) assert result.visible_to_all is True assert {f.participant_id for f in result.flags} == {rp_id, other_rp_id}