"""Model-level integration tests for Marketplace tables. docs/planning/economy.md US-3, US-4 (task 166836d55365). """ import pytest from fastapi.testclient import TestClient from sqlalchemy import select from sqlalchemy.exc import IntegrityError from sqlalchemy.ext.asyncio import async_sessionmaker, create_async_engine from sqlalchemy.pool import NullPool from app.config import settings from app.main import app from app.models.marketplace import ( CatalogItem, CreatorBalance, ItemType, ListingStatus, MarketplaceListing, Purchase, PurchaseStatus, ) def _register(client: TestClient, username: str) -> int: resp = client.post( "/api/auth/register", json={ "username": username, "email": f"{username}@example.com", "password": "secret123", "date_of_birth": "1990-01-01", }, ) assert resp.status_code == 201, resp.text headers = {"Authorization": f"Bearer {resp.json()['access_token']}"} me = client.get("/api/users/me", headers=headers) assert me.status_code == 200, me.text return me.json()["id"] def _session_factory(): engine = create_async_engine(settings.DATABASE_URL, poolclass=NullPool) return engine, async_sessionmaker(engine, expire_on_commit=False) @pytest.mark.asyncio async def test_catalog_item_and_listing_creation(): client = TestClient(app) seller_id = _register(client, "market_seller_1") engine, sf = _session_factory() try: async with sf() as session: item = CatalogItem( owner_id=seller_id, item_type=ItemType.avatar_item, name="Cyber Wings", description="Glow in the dark wings", metadata_json={"slot": "back"}, ) session.add(item) await session.commit() await session.refresh(item) assert item.id is not None assert item.name == "Cyber Wings" listing = MarketplaceListing( catalog_item_id=item.id, seller_id=seller_id, price=250, status=ListingStatus.active, ) session.add(listing) await session.commit() await session.refresh(listing) assert listing.id is not None assert listing.price == 250 assert listing.status == ListingStatus.active finally: await engine.dispose() @pytest.mark.asyncio async def test_listing_price_positive_constraint(): client = TestClient(app) seller_id = _register(client, "market_seller_2") engine, sf = _session_factory() try: async with sf() as session: item = CatalogItem( owner_id=seller_id, item_type=ItemType.asset, name="Free Prop", ) session.add(item) await session.commit() await session.refresh(item) bad_listing = MarketplaceListing( catalog_item_id=item.id, seller_id=seller_id, price=0, # Must be > 0 ) session.add(bad_listing) with pytest.raises(IntegrityError): await session.commit() finally: await engine.dispose() @pytest.mark.asyncio async def test_creator_balance_uniqueness_and_constraints(): client = TestClient(app) creator_id = _register(client, "market_creator_1") engine, sf = _session_factory() try: async with sf() as session: cb1 = CreatorBalance(user_id=creator_id, available=100, pending=50, lifetime_earned=150) session.add(cb1) await session.commit() await session.refresh(cb1) assert cb1.available == 100 assert cb1.pending == 50 assert cb1.lifetime_earned == 150 # Unique user_id constraint async with sf() as session: cb2 = CreatorBalance(user_id=creator_id) session.add(cb2) with pytest.raises(IntegrityError): await session.commit() finally: await engine.dispose() @pytest.mark.asyncio async def test_purchase_record_creation(): client = TestClient(app) seller_id = _register(client, "market_seller_3") buyer_id = _register(client, "market_buyer_3") engine, sf = _session_factory() try: async with sf() as session: item = CatalogItem(owner_id=seller_id, item_type=ItemType.asset, name="Sword Prop") session.add(item) await session.commit() await session.refresh(item) listing = MarketplaceListing(catalog_item_id=item.id, seller_id=seller_id, price=100) session.add(listing) await session.commit() await session.refresh(listing) purchase = Purchase( listing_id=listing.id, buyer_id=buyer_id, seller_id=seller_id, price=100, platform_fee=30, seller_net=70, status=PurchaseStatus.pending_escrow, ) session.add(purchase) await session.commit() await session.refresh(purchase) assert purchase.id is not None assert purchase.seller_net == 70 assert purchase.status == PurchaseStatus.pending_escrow finally: await engine.dispose()