176 lines
5.4 KiB
Python
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()
|