Files
modbison/backend/tests/test_marketplace_models.py

176 lines
5.4 KiB
Python

"""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()