import pytest from sqlalchemy import select from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine import app.bets.confirmation # noqa: F401 (registers the "bet" handler) import app.rounds.confirmation # noqa: F401 (registers the "payout" handler) from app.db.base import Base from app.db.models import PendingTransaction, Round, RoundParticipant from app.tx.confirmation import poll_once class FakeClient: def __init__(self, confirmations_by_txid: dict[str, int]): self._confirmations = confirmations_by_txid async def get_transaction(self, txid: str, verbose: bool = False) -> dict: return {"confirmations": self._confirmations.get(txid, 0)} @pytest.fixture async def session_factory(): engine = create_async_engine("sqlite+aiosqlite:///:memory:") async with engine.begin() as conn: await conn.run_sync(Base.metadata.create_all) yield async_sessionmaker(engine, expire_on_commit=False) await engine.dispose() async def test_bet_confirmation_marks_participant_confirmed(session_factory): async with session_factory() as session: session.add(Round(id=1, status="open")) session.add( RoundParticipant( round_id=1, user_id=1, bet_amount_sats=1_000, bet_txid="tx1", status="broadcast" ) ) session.add( PendingTransaction(kind="bet", round_id=1, user_id=1, current_txid="tx1", fee_rate_sat_vb=1, raw_tx_hex="00", status="pending") ) await session.commit() client = FakeClient({"tx1": 1}) confirmed = await poll_once(session_factory, client) assert confirmed == 1 async with session_factory() as session: participant = (await session.scalars(select(RoundParticipant))).one() assert participant.status == "confirmed" assert participant.confirmed_at is not None pending = (await session.scalars(select(PendingTransaction))).one() assert pending.status == "confirmed" async def test_unconfirmed_tx_is_left_pending(session_factory): async with session_factory() as session: session.add(Round(id=2, status="open")) session.add(RoundParticipant(round_id=2, user_id=1, bet_amount_sats=1_000, bet_txid="tx2", status="broadcast")) session.add(PendingTransaction(kind="bet", round_id=2, user_id=1, current_txid="tx2", fee_rate_sat_vb=1, raw_tx_hex="00", status="pending")) await session.commit() client = FakeClient({"tx2": 0}) confirmed = await poll_once(session_factory, client) assert confirmed == 0 async with session_factory() as session: participant = (await session.scalars(select(RoundParticipant))).one() assert participant.status == "broadcast" async def test_payout_confirmation_closes_round(session_factory): async with session_factory() as session: session.add(Round(id=3, status="paying_out", payout_txid="tx3")) session.add(PendingTransaction(kind="payout", round_id=3, current_txid="tx3", fee_rate_sat_vb=1, raw_tx_hex="00", status="pending")) await session.commit() client = FakeClient({"tx3": 2}) confirmed = await poll_once(session_factory, client) assert confirmed == 1 async with session_factory() as session: round_ = await session.get(Round, 3) assert round_.status == "closed"