teecup/tests/conftest.py

314 lines
13 KiB
Python
Raw Permalink Normal View History

"""
Delt testoppsett for backend-integrasjonstestene.
Kjøres ALLTID mot en scratch-database (se scripts/run_backend_tests.sh) --
aldri mot ekte teecup_db. Poolen initialiseres per test (funksjonsscope,
ikke sesjonsscope) for å unngå event loop-fallgruver med pytest-asyncio --
en full pool-oppstart tar millisekunder og suiten er liten nok til at det
ikke koster noe i praksis.
Hjelpefunksjonene under (create_user, create_org, ...) setter kun de
kolonnene testene faktisk trenger å kontrollere, med fornuftige defaults
for resten -- se \\d-utskrift av schema for hvilke felt som er NOT NULL.
"""
import uuid
import pytest_asyncio
from app import db as app_db
def new_id() -> str:
return str(uuid.uuid4())
@pytest_asyncio.fixture
async def pool():
await app_db.init_pool()
yield
await app_db.close_pool()
async def create_user(**overrides) -> str:
"""Egen tilkobling, samme begrunnelse som create_org -- app_user har
ingen RLS (ikke org-scopet), det er trygt/enkelt å ikke kreve en
ferdig-åpnet `conn` fra kalleren."""
user_id = overrides.get("id", new_id())
display_name = overrides.get("display_name", f"Testbruker {user_id[:8]}")
birth_date = overrides.get("birth_date")
email = overrides.get("email", f"{user_id}@pytest.invalid")
async with app_db.plain_connection() as conn:
await conn.execute(
"""
INSERT INTO app_user (id, display_name, birth_date, email)
VALUES ($1, $2, $3, $4)
""",
user_id, display_name, birth_date, email,
)
return user_id
async def create_org(**overrides) -> str:
"""Egen tilkobling (ikke `conn`-parameter som de andre hjelperne) --
speiler den ekte selvrefererende RLS-bootstrappen i
routers/organizations.py::create_organization: `app.current_org`
være satt til organisasjonens EGEN, ennå-ikke-eksisterende id FØR
innsettingen, ellers avviser org_self-policyen den (teecup_app er
NOSUPERUSER/NOBYPASSRLS, akkurat som i produksjon)."""
org_id = overrides.get("id", new_id())
name = overrides.get("name", f"Testklubb {org_id[:8]}")
async with app_db.org_connection(org_id) as conn:
await conn.execute("INSERT INTO organization (id, name) VALUES ($1, $2)", org_id, name)
return org_id
async def add_membership(organization_id: str, user_id: str, role: str = "member") -> None:
"""organization_membership er org-scopet (RLS), samme begrunnelse som
create_org over -- skrives med riktig app.current_org satt."""
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO organization_membership (organization_id, user_id, role) VALUES ($1, $2, $3)",
organization_id, user_id, role,
)
async def create_round(conn, owner_user_id: str, **overrides) -> str:
round_id = overrides.get("id", new_id())
# course_source='teeoff' krever KUN teeoff_facility_slug/teeoff_course_id
# (fritekst, ingen FK mot en ekte teeoff-bane) -- enklere for et testfixtur
# enn 'custom', som (round_check-constrainten) også krever en ekte rad i
# personal_course.
await conn.execute(
"""
INSERT INTO round (id, owner_user_id, course_source, teeoff_facility_slug,
teeoff_course_id, course_name_snapshot, tee_name_snapshot, played_at)
VALUES ($1, $2, 'teeoff', 'test-facility', 'test-course', 'Testbanen', 'Gul', CURRENT_DATE)
""",
round_id, owner_user_id,
)
return round_id
async def create_participant(conn, round_id: str, **overrides) -> str:
participant_id = overrides.get("id", new_id())
user_id = overrides.get("user_id")
is_owner = overrides.get("is_owner", True)
course_handicap_snapshot = overrides.get("course_handicap_snapshot")
await conn.execute(
"""
INSERT INTO round_participant (id, round_id, user_id, is_owner, gender,
tee_name_snapshot, course_handicap_snapshot)
VALUES ($1, $2, $3, $4, 'x', 'Gul', $5)
""",
participant_id, round_id, user_id, is_owner, course_handicap_snapshot,
)
return participant_id
async def create_friendship(requester_user_id: str, addressee_user_id: str, **overrides) -> str:
friendship_id = overrides.get("id", new_id())
status = overrides.get("status", "accepted")
async with app_db.plain_connection() as conn:
await conn.execute(
"INSERT INTO friendship (id, requester_user_id, addressee_user_id, status) VALUES ($1, $2, $3, $4)",
friendship_id, requester_user_id, addressee_user_id, status,
)
return friendship_id
async def create_hole(conn, participant_id: str, hole_number: int, par: int = 4,
stroke_index: int = 9, **overrides) -> str:
hole_id = overrides.get("id", new_id())
version = overrides.get("version", 1)
await conn.execute(
"""
INSERT INTO round_hole (id, round_participant_id, hole_number, par, stroke_index, version)
VALUES ($1, $2, $3, $4, $5, $6)
""",
hole_id, participant_id, hole_number, par, stroke_index, version,
)
return hole_id
# ---------------------------------------------------------------------------
# Org-turnering-kjeden (organisasjon -> turnering -> lag/bane/tee -> økt ->
# match[-deltaker]) -- brukt av test_scoring_concurrency.py. Alle disse
# tabellene er org-scopet (RLS), derfor org_connection(organization_id) i
# hver hjelper i stedet for en delt `conn`-parameter (samme mønster som
# create_org/add_membership over).
# ---------------------------------------------------------------------------
async def create_tournament(organization_id: str, **overrides) -> str:
tournament_id = overrides.get("id", new_id())
name = overrides.get("name", f"Testturnering {tournament_id[:8]}")
join_code = overrides.get("join_code", tournament_id[:8])
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO tournament (id, organization_id, name, join_code) VALUES ($1, $2, $3, $4)",
tournament_id, organization_id, name, join_code,
)
return tournament_id
async def create_team(organization_id: str, tournament_id: str, **overrides) -> str:
team_id = overrides.get("id", new_id())
name = overrides.get("name", f"Testlag {team_id[:8]}")
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO team (id, organization_id, tournament_id, name) VALUES ($1, $2, $3, $4)",
team_id, organization_id, tournament_id, name,
)
return team_id
async def create_course(organization_id: str, **overrides) -> str:
course_id = overrides.get("id", new_id())
name = overrides.get("name", f"Testbanen {course_id[:8]}")
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO course (id, organization_id, name) VALUES ($1, $2, $3)",
course_id, organization_id, name,
)
return course_id
async def create_tee(organization_id: str, course_id: str, **overrides) -> str:
tee_id = overrides.get("id", new_id())
name = overrides.get("name", "Gul")
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO tee (id, organization_id, course_id, name) VALUES ($1, $2, $3, $4)",
tee_id, organization_id, course_id, name,
)
return tee_id
async def create_session(organization_id: str, tournament_id: str, course_id: str, **overrides) -> str:
session_id = overrides.get("id", new_id())
sequence = overrides.get("sequence", 1)
format_ = overrides.get("format", "singles")
scoring_mode = overrides.get("scoring_mode", "stroke")
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"""
INSERT INTO session (id, organization_id, tournament_id, sequence, format,
course_id, scoring_mode)
VALUES ($1, $2, $3, $4, $5, $6, $7)
""",
session_id, organization_id, tournament_id, sequence, format_, course_id, scoring_mode,
)
return session_id
async def create_match(organization_id: str, session_id: str, team_a_id: str, team_b_id: str,
**overrides) -> str:
match_id = overrides.get("id", new_id())
sequence = overrides.get("sequence", 1)
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"""
INSERT INTO match (id, organization_id, session_id, sequence, team_a_id, team_b_id)
VALUES ($1, $2, $3, $4, $5, $6)
""",
match_id, organization_id, session_id, sequence, team_a_id, team_b_id,
)
return match_id
async def create_player(organization_id: str, **overrides) -> str:
player_id = overrides.get("id", new_id())
user_id = overrides.get("user_id")
display_name = overrides.get("display_name", f"Testspiller {player_id[:8]}")
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO player (id, organization_id, user_id, display_name) VALUES ($1, $2, $3, $4)",
player_id, organization_id, user_id, display_name,
)
return player_id
async def create_team_roster(organization_id: str, team_id: str, player_id: str, **overrides) -> str:
roster_id = overrides.get("id", new_id())
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO team_roster (id, organization_id, team_id, player_id) VALUES ($1, $2, $3, $4)",
roster_id, organization_id, team_id, player_id,
)
return roster_id
async def create_match_participant(organization_id: str, match_id: str, team_side: str,
team_roster_id: str, tee_id: str, **overrides) -> str:
participant_id = overrides.get("id", new_id())
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"""
INSERT INTO match_participant (id, organization_id, match_id, team_side, team_roster_id, tee_id)
VALUES ($1, $2, $3, $4, $5, $6)
""",
participant_id, organization_id, match_id, team_side, team_roster_id, tee_id,
)
return participant_id
# ---------------------------------------------------------------------------
# Individuelle/flerrunde-turneringer (ADR-037, migrasjon 040) -- brukt av
# test_flag_plant_individual_tournament.py. Samme org_connection()-mønster
# som resten av org-turnering-kjeden over.
# ---------------------------------------------------------------------------
async def create_org_hole(organization_id: str, course_id: str, hole_number: int, par: int = 4,
stroke_index: int | None = None, **overrides) -> str:
hole_id = overrides.get("id", new_id())
stroke_index = stroke_index if stroke_index is not None else hole_number
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO hole (id, organization_id, course_id, hole_number, par, stroke_index) "
"VALUES ($1, $2, $3, $4, $5, $6)",
hole_id, organization_id, course_id, hole_number, par, stroke_index,
)
return hole_id
async def create_tournament_round(organization_id: str, tournament_id: str, course_id: str,
**overrides) -> str:
round_id = overrides.get("id", new_id())
sequence = overrides.get("sequence", 1)
hole_config = overrides.get("hole_config", "full_18")
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO tournament_round (id, organization_id, tournament_id, sequence, course_id, hole_config) "
"VALUES ($1, $2, $3, $4, $5, $6)",
round_id, organization_id, tournament_id, sequence, course_id, hole_config,
)
return round_id
async def create_tournament_participant(organization_id: str, tournament_id: str, player_id: str,
**overrides) -> str:
participant_id = overrides.get("id", new_id())
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO tournament_participant (id, organization_id, tournament_id, player_id) "
"VALUES ($1, $2, $3, $4)",
participant_id, organization_id, tournament_id, player_id,
)
return participant_id
async def create_tournament_round_participant(organization_id: str, tournament_round_id: str,
tournament_participant_id: str, tee_id: str,
**overrides) -> str:
round_participant_id = overrides.get("id", new_id())
course_handicap = overrides.get("course_handicap", 10)
async with app_db.org_connection(organization_id) as conn:
await conn.execute(
"INSERT INTO tournament_round_participant "
"(id, organization_id, tournament_round_id, tournament_participant_id, tee_id, course_handicap) "
"VALUES ($1, $2, $3, $4, $5, $6)",
round_participant_id, organization_id, tournament_round_id, tournament_participant_id,
tee_id, course_handicap,
)
return round_participant_id