121 lines
4.6 KiB
Python
121 lines
4.6 KiB
Python
|
|
"""
|
||
|
|
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), så 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` MÅ
|
||
|
|
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 -- må 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_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
|