139 lines
5.5 KiB
Python
139 lines
5.5 KiB
Python
|
|
"""
|
||
|
|
Automatiserer det som til nå (ADR-057) kun er verifisert manuelt i tre lag
|
||
|
|
i nettleser: den optimistiske versjonssjekken på round_hole. Kaller de
|
||
|
|
faktiske router-funksjonene i rounds.py direkte (samme SQL, samme
|
||
|
|
feilhåndtering som produksjon) -- IKKE en gjenimplementering av logikken.
|
||
|
|
"""
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
from fastapi import HTTPException
|
||
|
|
|
||
|
|
from app import db as app_db
|
||
|
|
from app.auth import CurrentUser
|
||
|
|
from app.routers.rounds import (
|
||
|
|
HoleUpdate,
|
||
|
|
SideHoleUpdate,
|
||
|
|
update_hole,
|
||
|
|
update_side_hole,
|
||
|
|
)
|
||
|
|
|
||
|
|
from tests.conftest import create_hole, create_participant, create_round
|
||
|
|
|
||
|
|
|
||
|
|
async def _setup_round_with_hole(hole_number: int = 1):
|
||
|
|
async with app_db.plain_connection() as conn:
|
||
|
|
from tests.conftest import create_user
|
||
|
|
|
||
|
|
owner_id = await create_user()
|
||
|
|
round_id = await create_round(conn, owner_id)
|
||
|
|
participant_id = await create_participant(conn, round_id, user_id=owner_id, is_owner=True)
|
||
|
|
await create_hole(conn, participant_id, hole_number=hole_number, par=4, stroke_index=9)
|
||
|
|
return owner_id, round_id, participant_id
|
||
|
|
|
||
|
|
|
||
|
|
async def test_update_succeeds_and_increments_version(pool):
|
||
|
|
owner_id, round_id, participant_id = await _setup_round_with_hole()
|
||
|
|
user = CurrentUser(user_id=owner_id)
|
||
|
|
|
||
|
|
out = await update_hole(
|
||
|
|
round_id, participant_id, 1,
|
||
|
|
HoleUpdate(score=5, expected_version=1),
|
||
|
|
user=user,
|
||
|
|
)
|
||
|
|
assert out.score == 5
|
||
|
|
assert out.version == 2
|
||
|
|
|
||
|
|
|
||
|
|
async def test_stale_version_is_rejected_with_409(pool):
|
||
|
|
owner_id, round_id, participant_id = await _setup_round_with_hole()
|
||
|
|
user = CurrentUser(user_id=owner_id)
|
||
|
|
|
||
|
|
# "Enhet 1" leser versjon 1, skriver -- lykkes, hullet er nå versjon 2.
|
||
|
|
await update_hole(round_id, participant_id, 1, HoleUpdate(score=4, expected_version=1), user=user)
|
||
|
|
|
||
|
|
# "Enhet 2" leste OGSÅ versjon 1 (før enhet 1 sin skriving nådde den),
|
||
|
|
# og prøver nå å skrive basert på den utdaterte versjonen.
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
await update_hole(round_id, participant_id, 1, HoleUpdate(score=6, expected_version=1), user=user)
|
||
|
|
|
||
|
|
assert exc_info.value.status_code == 409
|
||
|
|
assert exc_info.value.detail["code"] == "STALE_VERSION"
|
||
|
|
|
||
|
|
|
||
|
|
async def test_expected_version_none_bypasses_check(pool):
|
||
|
|
owner_id, round_id, participant_id = await _setup_round_with_hole()
|
||
|
|
user = CurrentUser(user_id=owner_id)
|
||
|
|
|
||
|
|
await update_hole(round_id, participant_id, 1, HoleUpdate(score=4, expected_version=1), user=user)
|
||
|
|
# Hullet er nå versjon 2 -- men vi sender ingen forventet versjon i det
|
||
|
|
# hele tatt (klient som ikke sender feltet, f.eks. eldre app-versjon).
|
||
|
|
out = await update_hole(round_id, participant_id, 1, HoleUpdate(score=7, expected_version=None), user=user)
|
||
|
|
assert out.score == 7
|
||
|
|
assert out.version == 3
|
||
|
|
|
||
|
|
|
||
|
|
async def test_nonexistent_hole_is_404_not_409(pool):
|
||
|
|
owner_id, round_id, participant_id = await _setup_round_with_hole(hole_number=1)
|
||
|
|
user = CurrentUser(user_id=owner_id)
|
||
|
|
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
await update_hole(round_id, participant_id, 18, HoleUpdate(score=4, expected_version=1), user=user)
|
||
|
|
assert exc_info.value.status_code == 404
|
||
|
|
assert exc_info.value.detail["code"] == "NOT_FOUND"
|
||
|
|
|
||
|
|
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
# round_side (delt-ball-format) -- update_side_hole har sin EGEN 404-vs-409-
|
||
|
|
# logikk (egen eksistenssjekk lagt til i samme runde som versjonskolonnen),
|
||
|
|
# adskilt kode fra update_hole -- verifiseres derfor separat, ikke antatt
|
||
|
|
# dekket av testene over.
|
||
|
|
# ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
async def _setup_round_with_side_hole(hole_number: int = 1):
|
||
|
|
async with app_db.plain_connection() as conn:
|
||
|
|
from tests.conftest import create_user, new_id
|
||
|
|
|
||
|
|
owner_id = await create_user()
|
||
|
|
round_id = await create_round(conn, owner_id)
|
||
|
|
side_id = new_id()
|
||
|
|
await conn.execute(
|
||
|
|
"INSERT INTO round_side (id, round_id, label) VALUES ($1, $2, 'Side A')",
|
||
|
|
side_id, round_id,
|
||
|
|
)
|
||
|
|
participant_id = await create_participant(
|
||
|
|
conn, round_id, user_id=owner_id, is_owner=True,
|
||
|
|
)
|
||
|
|
await conn.execute(
|
||
|
|
"UPDATE round_participant SET round_side_id = $1 WHERE id = $2", side_id, participant_id
|
||
|
|
)
|
||
|
|
await conn.execute(
|
||
|
|
"""
|
||
|
|
INSERT INTO round_hole (round_side_id, hole_number, par, stroke_index, version)
|
||
|
|
VALUES ($1, $2, 4, 9, 1)
|
||
|
|
""",
|
||
|
|
side_id, hole_number,
|
||
|
|
)
|
||
|
|
return owner_id, round_id, side_id
|
||
|
|
|
||
|
|
|
||
|
|
async def test_side_hole_stale_version_is_409(pool):
|
||
|
|
owner_id, round_id, side_id = await _setup_round_with_side_hole()
|
||
|
|
user = CurrentUser(user_id=owner_id)
|
||
|
|
|
||
|
|
await update_side_hole(round_id, side_id, 1, SideHoleUpdate(score=4, expected_version=1), user=user)
|
||
|
|
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
await update_side_hole(round_id, side_id, 1, SideHoleUpdate(score=6, expected_version=1), user=user)
|
||
|
|
assert exc_info.value.status_code == 409
|
||
|
|
assert exc_info.value.detail["code"] == "STALE_VERSION"
|
||
|
|
|
||
|
|
|
||
|
|
async def test_side_hole_nonexistent_is_404_not_409(pool):
|
||
|
|
owner_id, round_id, side_id = await _setup_round_with_side_hole(hole_number=1)
|
||
|
|
user = CurrentUser(user_id=owner_id)
|
||
|
|
|
||
|
|
with pytest.raises(HTTPException) as exc_info:
|
||
|
|
await update_side_hole(round_id, side_id, 18, SideHoleUpdate(score=4, expected_version=1), user=user)
|
||
|
|
assert exc_info.value.status_code == 404
|
||
|
|
assert exc_info.value.detail["code"] == "NOT_FOUND"
|