204 lines
7.2 KiB
Python
204 lines
7.2 KiB
Python
|
|
"""
|
||
|
|
ADR-060: samme optimistiske versjonssjekk som round_hole (ADR-057) fikk,
|
||
|
|
nå for org-turneringenes match-scoring (hole_score/match_hole_result,
|
||
|
|
app/routers/scoring.py). Kaller de faktiske router-funksjonene direkte --
|
||
|
|
samme kodesti som produksjon, ikke en SQL-gjenimplementering.
|
||
|
|
|
||
|
|
Autorisasjon: testbrukeren er satt som org-EIER (organization_membership
|
||
|
|
role='owner'), som tilfredsstiller user_is_match_participant sin
|
||
|
|
is_org_admin-fallback (team_authz.py) uten å måtte bygge ut hele
|
||
|
|
player/team_roster/match_participant-kjeden for HVER test -- kun
|
||
|
|
test_hole_score_individual_ball_participant_path bygger den fulle kjeden,
|
||
|
|
siden DEN grenen i submit_hole_score krever en ekte match_participant-rad
|
||
|
|
å slå opp uavhengig av org-admin-status.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from fastapi import HTTPException
|
||
|
|
|
||
|
|
from app import db as app_db
|
||
|
|
from app.auth import CurrentUser
|
||
|
|
from app.routers.scoring import (
|
||
|
|
HoleResultCreate,
|
||
|
|
HoleScoreCreate,
|
||
|
|
submit_hole_result,
|
||
|
|
submit_hole_score,
|
||
|
|
)
|
||
|
|
|
||
|
|
from tests.conftest import (
|
||
|
|
add_membership,
|
||
|
|
create_course,
|
||
|
|
create_match,
|
||
|
|
create_match_participant,
|
||
|
|
create_org,
|
||
|
|
create_player,
|
||
|
|
create_session,
|
||
|
|
create_team,
|
||
|
|
create_team_roster,
|
||
|
|
create_tee,
|
||
|
|
create_tournament,
|
||
|
|
create_user,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
async def _setup_match(scoring_mode: str = "stroke", format_: str = "foursome"):
|
||
|
|
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)
|
||
|
|
team_a = await create_team(org_id, tournament_id)
|
||
|
|
team_b = await create_team(org_id, tournament_id)
|
||
|
|
course_id = await create_course(org_id)
|
||
|
|
session_id = await create_session(
|
||
|
|
org_id, tournament_id, course_id, format=format_, scoring_mode=scoring_mode
|
||
|
|
)
|
||
|
|
match_id = await create_match(org_id, session_id, team_a, team_b)
|
||
|
|
return org_id, user_id, match_id, team_a, team_b
|
||
|
|
|
||
|
|
|
||
|
|
async def test_hole_score_shared_ball_succeeds_and_increments_version(pool):
|
||
|
|
org_id, user_id, match_id, _, _ = await _setup_match()
|
||
|
|
user = CurrentUser(user_id=user_id)
|
||
|
|
|
||
|
|
first = await submit_hole_score(
|
||
|
|
match_id,
|
||
|
|
HoleScoreCreate(team_side="a", hole_number=1, gross_strokes=5, expected_version=None),
|
||
|
|
org_id,
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
assert first.version == 1
|
||
|
|
assert first.gross_strokes == 5
|
||
|
|
|
||
|
|
second = await submit_hole_score(
|
||
|
|
match_id,
|
||
|
|
HoleScoreCreate(team_side="a", hole_number=1, gross_strokes=4, expected_version=1),
|
||
|
|
org_id,
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
assert second.version == 2
|
||
|
|
assert second.gross_strokes == 4
|
||
|
|
|
||
|
|
|
||
|
|
async def test_hole_score_stale_version_is_409(pool):
|
||
|
|
org_id, user_id, match_id, _, _ = await _setup_match()
|
||
|
|
user = CurrentUser(user_id=user_id)
|
||
|
|
|
||
|
|
await submit_hole_score(
|
||
|
|
match_id,
|
||
|
|
HoleScoreCreate(team_side="a", hole_number=1, gross_strokes=5, expected_version=None),
|
||
|
|
org_id,
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
# "Enhet 2" hadde OGSÅ lest versjon 1 (før dette skjedde), og prøver nå
|
||
|
|
# å skrive med en versjon som allerede er forbigått.
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
await submit_hole_score(
|
||
|
|
match_id,
|
||
|
|
HoleScoreCreate(team_side="a", hole_number=1, gross_strokes=6, expected_version=99),
|
||
|
|
org_id,
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
assert exc_info.value.status_code == 409
|
||
|
|
assert exc_info.value.detail["code"] == "STALE_VERSION"
|
||
|
|
|
||
|
|
# Og bekreft at den avviste skrivingen faktisk IKKE ble lagret.
|
||
|
|
async with app_db.org_connection(org_id) as conn:
|
||
|
|
row = await conn.fetchrow(
|
||
|
|
"SELECT gross_strokes, version FROM hole_score WHERE match_id = $1 AND hole_number = 1", match_id
|
||
|
|
)
|
||
|
|
assert row["gross_strokes"] == 5
|
||
|
|
assert row["version"] == 1
|
||
|
|
|
||
|
|
|
||
|
|
async def test_hole_score_expected_version_none_bypasses_check(pool):
|
||
|
|
org_id, user_id, match_id, _, _ = await _setup_match()
|
||
|
|
user = CurrentUser(user_id=user_id)
|
||
|
|
|
||
|
|
await submit_hole_score(
|
||
|
|
match_id,
|
||
|
|
HoleScoreCreate(team_side="a", hole_number=1, gross_strokes=5, expected_version=None),
|
||
|
|
org_id,
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
out = await submit_hole_score(
|
||
|
|
match_id,
|
||
|
|
HoleScoreCreate(team_side="a", hole_number=1, gross_strokes=7, expected_version=None),
|
||
|
|
org_id,
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
assert out.gross_strokes == 7
|
||
|
|
assert out.version == 2
|
||
|
|
|
||
|
|
|
||
|
|
async def test_hole_score_individual_ball_participant_path(pool):
|
||
|
|
"""Den ANDRE ON CONFLICT-grenen i submit_hole_score (match_participant_id
|
||
|
|
satt) -- egen kode, egen WHERE-versjonssjekk, verdt å dekke separat fra
|
||
|
|
delt-ball-testene over. Krever en ekte match_participant-rad (kylling-
|
||
|
|
og-egg: user_is_match_participant sin participant-sjekk er uavhengig av
|
||
|
|
org-admin-fallbacken for AKKURAT denne oppslags-grenen i
|
||
|
|
submit_hole_score, se linje ~534 i scoring.py)."""
|
||
|
|
org_id, user_id, match_id, team_a, _ = await _setup_match(format_="singles")
|
||
|
|
user = CurrentUser(user_id=user_id)
|
||
|
|
|
||
|
|
course_id = await create_course(org_id)
|
||
|
|
tee_id = await create_tee(org_id, course_id)
|
||
|
|
player_id = await create_player(org_id, user_id=user_id)
|
||
|
|
roster_id = await create_team_roster(org_id, team_a, player_id)
|
||
|
|
participant_id = await create_match_participant(org_id, match_id, "a", roster_id, tee_id)
|
||
|
|
|
||
|
|
first = await submit_hole_score(
|
||
|
|
match_id,
|
||
|
|
HoleScoreCreate(
|
||
|
|
team_side="a", match_participant_id=participant_id, hole_number=1,
|
||
|
|
gross_strokes=4, expected_version=None,
|
||
|
|
),
|
||
|
|
org_id,
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
assert first.version == 1
|
||
|
|
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
await submit_hole_score(
|
||
|
|
match_id,
|
||
|
|
HoleScoreCreate(
|
||
|
|
team_side="a", match_participant_id=participant_id, hole_number=1,
|
||
|
|
gross_strokes=9, expected_version=99,
|
||
|
|
),
|
||
|
|
org_id,
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
assert exc_info.value.status_code == 409
|
||
|
|
assert exc_info.value.detail["code"] == "STALE_VERSION"
|
||
|
|
|
||
|
|
|
||
|
|
async def test_hole_result_succeeds_and_increments_version(pool):
|
||
|
|
org_id, user_id, match_id, _, _ = await _setup_match(scoring_mode="hole_result")
|
||
|
|
user = CurrentUser(user_id=user_id)
|
||
|
|
|
||
|
|
first = await submit_hole_result(
|
||
|
|
match_id, HoleResultCreate(hole_number=1, winning_side="a", expected_version=None), org_id, user=user
|
||
|
|
)
|
||
|
|
assert first.version == 1
|
||
|
|
assert first.winning_side == "a"
|
||
|
|
|
||
|
|
second = await submit_hole_result(
|
||
|
|
match_id, HoleResultCreate(hole_number=1, winning_side="b", expected_version=1), org_id, user=user
|
||
|
|
)
|
||
|
|
assert second.version == 2
|
||
|
|
assert second.winning_side == "b"
|
||
|
|
|
||
|
|
|
||
|
|
async def test_hole_result_stale_version_is_409(pool):
|
||
|
|
org_id, user_id, match_id, _, _ = await _setup_match(scoring_mode="hole_result")
|
||
|
|
user = CurrentUser(user_id=user_id)
|
||
|
|
|
||
|
|
await submit_hole_result(
|
||
|
|
match_id, HoleResultCreate(hole_number=1, winning_side="a", expected_version=None), org_id, user=user
|
||
|
|
)
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
await submit_hole_result(
|
||
|
|
match_id, HoleResultCreate(hole_number=1, winning_side="b", expected_version=99), org_id, user=user
|
||
|
|
)
|
||
|
|
assert exc_info.value.status_code == 409
|
||
|
|
assert exc_info.value.detail["code"] == "STALE_VERSION"
|