""" 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_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