import pytest from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from app.db.base import Base from app.db.models import User from app.deposits.service import credit_confirmed_utxos @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() @pytest.fixture async def user_id(session_factory): async with session_factory() as session: user = User(username="alice", password_hash="x", derivation_index=0, address="plm1qxxx") session.add(user) await session.commit() return user.id async def test_credits_confirmed_utxo_and_updates_balance(session_factory, user_id): entries = [{"tx_hash": "aa" * 32, "tx_pos": 0, "height": 100, "value": 10_000_000}] async with session_factory() as session: credited = await credit_confirmed_utxos(session, user_id, entries) assert credited == 1 user = await session.get(User, user_id) assert user.cached_balance_sats == 10_000_000 async def test_unconfirmed_entry_is_ignored(session_factory, user_id): entries = [{"tx_hash": "bb" * 32, "tx_pos": 0, "height": 0, "value": 5_000_000}] async with session_factory() as session: credited = await credit_confirmed_utxos(session, user_id, entries) assert credited == 0 user = await session.get(User, user_id) assert user.cached_balance_sats == 0 async def test_idempotent_on_repeated_notification(session_factory, user_id): entries = [{"tx_hash": "cc" * 32, "tx_pos": 0, "height": 100, "value": 7_000_000}] async with session_factory() as session: first = await credit_confirmed_utxos(session, user_id, entries) async with session_factory() as session: second = await credit_confirmed_utxos(session, user_id, entries) user = await session.get(User, user_id) assert first == 1 assert second == 0 assert user.cached_balance_sats == 7_000_000