2026-08-18 15:43:39 +02:00
|
|
|
"""
|
|
|
|
|
Utslagsgrupper (migrasjon 084, 2026-08-18, ADR-088): hvem spiller med
|
|
|
|
|
hvem, klokka når -- for individuelle turneringer. Bruker: "Jeg må jo
|
|
|
|
|
kunne bulksjekke hvem som skal spille fra hvor, med hvem, klokka når?"
|
|
|
|
|
Design bekreftet: tradisjonelt utslag (samme starthull, staggerte
|
|
|
|
|
klokkeslett med fast intervall), manuell gruppesammensetning med et
|
|
|
|
|
forslag å justere, fast gruppestørrelse.
|
|
|
|
|
"""
|
|
|
|
|
|
|
|
|
|
from app.routers.individual_tournaments import (
|
|
|
|
|
RoundGroupIn,
|
|
|
|
|
SaveRoundGroupsIn,
|
|
|
|
|
SuggestGroupsIn,
|
|
|
|
|
TournamentRoundUpdate,
|
|
|
|
|
list_round_groups,
|
|
|
|
|
save_round_groups,
|
|
|
|
|
suggest_round_groups,
|
|
|
|
|
update_round,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
from tests.conftest import (
|
|
|
|
|
create_org,
|
|
|
|
|
create_course,
|
|
|
|
|
create_player,
|
|
|
|
|
create_tee,
|
|
|
|
|
create_tournament,
|
|
|
|
|
create_tournament_participant,
|
|
|
|
|
create_tournament_round,
|
|
|
|
|
create_tournament_round_participant,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
import app.db as app_db
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def _setup(org_id, n_players=5):
|
|
|
|
|
tournament_id = await create_tournament(org_id, name="Gruppe-turnering")
|
|
|
|
|
course_id = await create_course(org_id, name="Gruppe Links")
|
|
|
|
|
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 = 'stroke_gross' WHERE id = $1",
|
|
|
|
|
tournament_id,
|
|
|
|
|
)
|
|
|
|
|
round_id = await create_tournament_round(org_id, tournament_id, course_id, sequence=1)
|
|
|
|
|
|
|
|
|
|
rp_ids = []
|
|
|
|
|
for i in range(n_players):
|
|
|
|
|
player_id = await create_player(org_id, display_name=f"Spiller {i:02d}")
|
|
|
|
|
tp_id = await create_tournament_participant(org_id, tournament_id, player_id)
|
|
|
|
|
rp_ids.append(await create_tournament_round_participant(org_id, round_id, tp_id, tee_id))
|
|
|
|
|
return tournament_id, round_id, rp_ids
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_update_round_sets_scheduling_fields(pool):
|
|
|
|
|
org_id = await create_org()
|
|
|
|
|
tournament_id, round_id, _ = await _setup(org_id, n_players=1)
|
|
|
|
|
|
|
|
|
|
updated = await update_round(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
TournamentRoundUpdate(scheduled_at="2026-08-19T10:00:00", tee_interval_minutes=10, start_hole=1),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
assert updated.tee_interval_minutes == 10
|
|
|
|
|
assert updated.start_hole == 1
|
|
|
|
|
assert "2026-08-19" in (updated.scheduled_at or "")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_suggest_round_groups_chunks_by_size_and_computes_tee_times_without_saving(pool):
|
|
|
|
|
org_id = await create_org()
|
|
|
|
|
tournament_id, round_id, rp_ids = await _setup(org_id, n_players=5)
|
|
|
|
|
await update_round(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
TournamentRoundUpdate(scheduled_at="2026-08-19T10:00:00", tee_interval_minutes=10),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
suggestion = await suggest_round_groups(
|
|
|
|
|
tournament_id, round_id, SuggestGroupsIn(group_size=2), organization_id=org_id
|
|
|
|
|
)
|
|
|
|
|
assert [len(g.participants) for g in suggestion.groups] == [2, 2, 1]
|
|
|
|
|
assert [g.sequence for g in suggestion.groups] == [1, 2, 3]
|
|
|
|
|
assert suggestion.groups[0].tee_time is not None and "10:00" in suggestion.groups[0].tee_time
|
|
|
|
|
assert suggestion.groups[1].tee_time is not None and "10:10" in suggestion.groups[1].tee_time
|
|
|
|
|
assert suggestion.groups[2].tee_time is not None and "10:20" in suggestion.groups[2].tee_time
|
|
|
|
|
# Alle grupper i forslaget er usaved (id=None).
|
|
|
|
|
assert all(g.id is None for g in suggestion.groups)
|
|
|
|
|
|
|
|
|
|
# Ingenting skal faktisk ha blitt lagret av selve forslaget.
|
|
|
|
|
saved = await list_round_groups(tournament_id, round_id, organization_id=org_id)
|
|
|
|
|
assert saved.groups == []
|
|
|
|
|
assert len(saved.ungrouped) == 5
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_save_round_groups_persists_and_fully_replaces_previous(pool):
|
|
|
|
|
org_id = await create_org()
|
|
|
|
|
tournament_id, round_id, rp_ids = await _setup(org_id, n_players=4)
|
|
|
|
|
await update_round(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
TournamentRoundUpdate(scheduled_at="2026-08-19T10:00:00", tee_interval_minutes=10),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
first = await save_round_groups(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
SaveRoundGroupsIn(groups=[
|
|
|
|
|
RoundGroupIn(sequence=1, round_participant_ids=[rp_ids[0], rp_ids[1]]),
|
|
|
|
|
RoundGroupIn(sequence=2, round_participant_ids=[rp_ids[2], rp_ids[3]]),
|
|
|
|
|
]),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
assert len(first.groups) == 2
|
|
|
|
|
assert len(first.ungrouped) == 0
|
|
|
|
|
|
|
|
|
|
# Lagre på nytt med en HELT ANNEN sammensetning -- skal erstatte, ikke legge til.
|
|
|
|
|
second = await save_round_groups(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
SaveRoundGroupsIn(groups=[
|
|
|
|
|
RoundGroupIn(sequence=1, round_participant_ids=[rp_ids[0], rp_ids[2], rp_ids[3]]),
|
|
|
|
|
]),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
assert len(second.groups) == 1
|
|
|
|
|
assert len(second.groups[0].participants) == 3
|
|
|
|
|
assert len(second.ungrouped) == 1
|
|
|
|
|
assert second.ungrouped[0].round_participant_id == rp_ids[1]
|
|
|
|
|
|
|
|
|
|
async with app_db.org_connection(org_id) as conn:
|
|
|
|
|
group_count = await conn.fetchval(
|
|
|
|
|
"SELECT count(*) FROM tournament_round_group WHERE tournament_round_id = $1", round_id
|
|
|
|
|
)
|
|
|
|
|
assert group_count == 1 # den gamle gruppen faktisk slettet, ikke bare "løsrevet"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_save_round_groups_leaves_unlisted_participants_ungrouped(pool):
|
|
|
|
|
org_id = await create_org()
|
|
|
|
|
tournament_id, round_id, rp_ids = await _setup(org_id, n_players=3)
|
|
|
|
|
|
|
|
|
|
result = await save_round_groups(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
SaveRoundGroupsIn(groups=[RoundGroupIn(sequence=1, round_participant_ids=[rp_ids[0]])]),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
assert len(result.groups) == 1
|
|
|
|
|
assert {p.round_participant_id for p in result.ungrouped} == {rp_ids[1], rp_ids[2]}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_group_tee_time_override_wins_over_computed(pool):
|
|
|
|
|
org_id = await create_org()
|
|
|
|
|
tournament_id, round_id, rp_ids = await _setup(org_id, n_players=2)
|
|
|
|
|
await update_round(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
TournamentRoundUpdate(scheduled_at="2026-08-19T10:00:00", tee_interval_minutes=10),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await save_round_groups(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
SaveRoundGroupsIn(groups=[
|
|
|
|
|
RoundGroupIn(sequence=1, tee_time_override="2026-08-19T09:15:00", round_participant_ids=rp_ids),
|
|
|
|
|
]),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
assert result.groups[0].tee_time is not None
|
|
|
|
|
assert "09:15" in result.groups[0].tee_time # override, IKKE den utledede 10:00
|
2026-08-21 08:59:48 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_shotgun_mode_gives_all_groups_the_same_tee_time(pool):
|
|
|
|
|
"""Migrasjon 088, ADR-096 -- intervallet er meningsløst i shotgun,
|
|
|
|
|
alle grupper går ut SAMTIDIG uansett sequence."""
|
|
|
|
|
org_id = await create_org()
|
|
|
|
|
tournament_id, round_id, rp_ids = await _setup(org_id, n_players=4)
|
|
|
|
|
await update_round(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
TournamentRoundUpdate(
|
|
|
|
|
scheduled_at="2026-08-19T08:00:00", tee_interval_minutes=10, start_mode="shotgun",
|
|
|
|
|
),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await save_round_groups(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
SaveRoundGroupsIn(groups=[
|
|
|
|
|
RoundGroupIn(sequence=1, start_hole=5, round_participant_ids=[rp_ids[0], rp_ids[1]]),
|
|
|
|
|
RoundGroupIn(sequence=2, start_hole=1, round_participant_ids=[rp_ids[2], rp_ids[3]]),
|
|
|
|
|
]),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
# Begge grupper går ut kl 08:00, uavhengig av sequence/intervall.
|
|
|
|
|
for g in result.groups:
|
|
|
|
|
assert g.tee_time is not None and "08:00" in g.tee_time
|
|
|
|
|
|
|
|
|
|
# Vises sortert etter STARTHULL (1 før 5), ikke etter sequence (1 før 2).
|
|
|
|
|
assert [g.start_hole for g in result.groups] == [1, 5]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def test_consecutive_mode_still_sorts_by_sequence_not_start_hole(pool):
|
|
|
|
|
"""Regresjonsvern -- start_hole (uansett verdi, normalt None) skal
|
|
|
|
|
IKKE påvirke rekkefølgen når runden fortsatt er 'consecutive'."""
|
|
|
|
|
org_id = await create_org()
|
|
|
|
|
tournament_id, round_id, rp_ids = await _setup(org_id, n_players=4)
|
|
|
|
|
await update_round(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
TournamentRoundUpdate(scheduled_at="2026-08-19T08:00:00", tee_interval_minutes=10),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
result = await save_round_groups(
|
|
|
|
|
tournament_id, round_id,
|
|
|
|
|
SaveRoundGroupsIn(groups=[
|
|
|
|
|
RoundGroupIn(sequence=1, round_participant_ids=[rp_ids[0], rp_ids[1]]),
|
|
|
|
|
RoundGroupIn(sequence=2, round_participant_ids=[rp_ids[2], rp_ids[3]]),
|
|
|
|
|
]),
|
|
|
|
|
organization_id=org_id,
|
|
|
|
|
)
|
|
|
|
|
assert [g.sequence for g in result.groups] == [1, 2]
|
|
|
|
|
assert "08:00" in result.groups[0].tee_time
|
|
|
|
|
assert "08:10" in result.groups[1].tee_time
|