feat(marketplace): add marketplace listings API and catalog items CRUD
This commit is contained in:
@@ -0,0 +1,167 @@
|
||||
"""Integration tests for Marketplace Listings API.
|
||||
|
||||
docs/planning/economy.md US-3 (task f51c7d95e237).
|
||||
"""
|
||||
|
||||
import pytest
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from app.main import app
|
||||
|
||||
|
||||
def _register(client: TestClient, username: str) -> tuple[int, dict[str, str]]:
|
||||
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
|
||||
token = resp.json()["access_token"]
|
||||
headers = {"Authorization": f"Bearer {token}"}
|
||||
me = client.get("/api/users/me", headers=headers)
|
||||
assert me.status_code == 200, me.text
|
||||
return me.json()["id"], headers
|
||||
|
||||
|
||||
def test_catalog_items_and_listings_crud():
|
||||
client = TestClient(app)
|
||||
uid1, h1 = _register(client, "mkt_user_1")
|
||||
uid2, h2 = _register(client, "mkt_user_2")
|
||||
|
||||
# 1. Create catalog item by user 1
|
||||
resp = client.post(
|
||||
"/api/marketplace/items",
|
||||
headers=h1,
|
||||
json={
|
||||
"name": "Wizard Hat",
|
||||
"description": "A pointy blue hat with stars",
|
||||
"item_type": "avatar_item",
|
||||
"metadata_json": {"color": "blue", "rarity": "rare"},
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 201, resp.text
|
||||
item_data = resp.json()
|
||||
assert item_data["name"] == "Wizard Hat"
|
||||
assert item_data["owner_id"] == uid1
|
||||
item_id = item_data["id"]
|
||||
|
||||
# 2. List catalog items
|
||||
resp = client.get("/api/marketplace/items", headers=h1, params={"mine": "true"})
|
||||
assert resp.status_code == 200
|
||||
my_items = resp.json()
|
||||
assert len(my_items) >= 1
|
||||
assert any(i["id"] == item_id for i in my_items)
|
||||
|
||||
resp = client.get("/api/marketplace/items", headers=h2, params={"mine": "true"})
|
||||
assert resp.status_code == 200
|
||||
assert not any(i["id"] == item_id for i in resp.json())
|
||||
|
||||
# 3. User 2 tries to list User 1's item -> 403
|
||||
resp = client.post(
|
||||
"/api/marketplace/listings",
|
||||
headers=h2,
|
||||
json={
|
||||
"catalog_item_id": item_id,
|
||||
"price": 100,
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 403, resp.text
|
||||
|
||||
# Invalid price <= 0 -> 422
|
||||
resp = client.post(
|
||||
"/api/marketplace/listings",
|
||||
headers=h1,
|
||||
json={
|
||||
"catalog_item_id": item_id,
|
||||
"price": 0,
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 422
|
||||
|
||||
# 4. User 1 successfully lists item
|
||||
resp = client.post(
|
||||
"/api/marketplace/listings",
|
||||
headers=h1,
|
||||
json={
|
||||
"catalog_item_id": item_id,
|
||||
"price": 250,
|
||||
},
|
||||
)
|
||||
assert resp.status_code == 201, resp.text
|
||||
listing = resp.json()
|
||||
assert listing["price"] == 250
|
||||
assert listing["seller_id"] == uid1
|
||||
assert listing["status"] == "active"
|
||||
assert listing["catalog_item"]["name"] == "Wizard Hat"
|
||||
listing_id = listing["id"]
|
||||
|
||||
# 5. Search / browse listings
|
||||
resp = client.get("/api/marketplace/listings", params={"q": "Wizard"})
|
||||
assert resp.status_code == 200, resp.text
|
||||
data = resp.json()
|
||||
assert data["total"] >= 1
|
||||
assert any(l["id"] == listing_id for l in data["listings"])
|
||||
|
||||
# Filter by item_type
|
||||
resp = client.get("/api/marketplace/listings", params={"item_type": "avatar_item"})
|
||||
assert resp.status_code == 200
|
||||
assert any(l["id"] == listing_id for l in resp.json()["listings"])
|
||||
|
||||
resp = client.get("/api/marketplace/listings", params={"item_type": "experience_pass"})
|
||||
assert resp.status_code == 200
|
||||
assert not any(l["id"] == listing_id for l in resp.json()["listings"])
|
||||
|
||||
# Price range filter
|
||||
resp = client.get("/api/marketplace/listings", params={"min_price": 300})
|
||||
assert resp.status_code == 200
|
||||
assert not any(l["id"] == listing_id for l in resp.json()["listings"])
|
||||
|
||||
resp = client.get("/api/marketplace/listings", params={"max_price": 260})
|
||||
assert resp.status_code == 200
|
||||
assert any(l["id"] == listing_id for l in resp.json()["listings"])
|
||||
|
||||
# 6. Listing detail
|
||||
resp = client.get(f"/api/marketplace/listings/{listing_id}")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["id"] == listing_id
|
||||
|
||||
# 7. Update listing
|
||||
# User 2 cannot update User 1's listing
|
||||
resp = client.patch(
|
||||
f"/api/marketplace/listings/{listing_id}",
|
||||
headers=h2,
|
||||
json={"price": 500},
|
||||
)
|
||||
assert resp.status_code == 403
|
||||
|
||||
# User 1 updates price
|
||||
resp = client.patch(
|
||||
f"/api/marketplace/listings/{listing_id}",
|
||||
headers=h1,
|
||||
json={"price": 350},
|
||||
)
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["price"] == 350
|
||||
|
||||
# 8. Delist listing
|
||||
# User 2 cannot delist
|
||||
resp = client.delete(f"/api/marketplace/listings/{listing_id}", headers=h2)
|
||||
assert resp.status_code == 403
|
||||
|
||||
# User 1 delists
|
||||
resp = client.delete(f"/api/marketplace/listings/{listing_id}", headers=h1)
|
||||
assert resp.status_code == 204
|
||||
|
||||
# Now detail shows delisted
|
||||
resp = client.get(f"/api/marketplace/listings/{listing_id}")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["status"] == "delisted"
|
||||
|
||||
# Default search only shows active
|
||||
resp = client.get("/api/marketplace/listings")
|
||||
assert resp.status_code == 200
|
||||
assert not any(l["id"] == listing_id for l in resp.json()["listings"])
|
||||
Reference in New Issue
Block a user