"""Order creation, stock deduction, and webhook handling.""" from unittest.mock import AsyncMock from tests.conftest import PRODUCT_ID, ORDER_ID SAMPLE_PRODUCT_ROW = { "id": PRODUCT_ID, "name": "Extensions Clip-In Luxe", "price": 189.0, "stock_quantity": 10, } SAMPLE_ORDER_ROW = { "id": ORDER_ID, "user_id": "11111111-1111-1111-1111-111111111111", "status": "pending", "total_amount": 189.0, "stripe_payment_intent_id": "pi_test123", "shipping_address": None, "notes": None, "created_at": "2026-01-01T00:00:00", "updated_at": "2026-01-01T00:00:00", } async def test_create_order_success(auth_client, mock_db): mock_db.fetchrow = AsyncMock(side_effect=[ SAMPLE_PRODUCT_ROW, # product lookup {"id": ORDER_ID, **SAMPLE_ORDER_ROW}, # INSERT order ]) mock_db.execute = AsyncMock(return_value="UPDATE 1") r = await auth_client.post("/api/v1/orders", json={ "items": [{"product_id": PRODUCT_ID, "quantity": 1}], }) assert r.status_code == 201 body = r.json() assert body["success"] is True assert "client_secret" in body["data"] assert "order_id" in body["data"] assert body["data"]["amount"] == 189.0 async def test_create_order_requires_auth(anon_client): r = await anon_client.post("/api/v1/orders", json={ "items": [{"product_id": PRODUCT_ID, "quantity": 1}], }) assert r.status_code == 401 async def test_create_order_empty_items_rejected(auth_client): r = await auth_client.post("/api/v1/orders", json={"items": []}) assert r.status_code == 422 async def test_create_order_product_not_found(auth_client, mock_db): mock_db.fetchrow = AsyncMock(return_value=None) r = await auth_client.post("/api/v1/orders", json={ "items": [{"product_id": "00000000-0000-0000-0000-000000000000", "quantity": 1}], }) assert r.status_code == 404 assert r.json()["error"]["code"] == "PRODUCT_NOT_FOUND" async def test_create_order_out_of_stock(auth_client, mock_db): low_stock = {**SAMPLE_PRODUCT_ROW, "stock_quantity": 0} mock_db.fetchrow = AsyncMock(return_value=low_stock) r = await auth_client.post("/api/v1/orders", json={ "items": [{"product_id": PRODUCT_ID, "quantity": 1}], }) assert r.status_code == 409 assert r.json()["error"]["code"] == "OUT_OF_STOCK" async def test_list_my_orders(auth_client, mock_db): mock_db.fetchval = AsyncMock(return_value=1) mock_db.fetch = AsyncMock(return_value=[SAMPLE_ORDER_ROW]) r = await auth_client.get("/api/v1/orders") assert r.status_code == 200 body = r.json() assert body["meta"]["total"] == 1 async def test_get_order_by_id(auth_client, mock_db): mock_db.fetchrow = AsyncMock(return_value=SAMPLE_ORDER_ROW) mock_db.fetch = AsyncMock(return_value=[]) # order items r = await auth_client.get(f"/api/v1/orders/{ORDER_ID}") assert r.status_code == 200 async def test_get_order_not_found(auth_client, mock_db): mock_db.fetchrow = AsyncMock(return_value=None) r = await auth_client.get(f"/api/v1/orders/00000000-0000-0000-0000-000000000000") assert r.status_code == 404 # ── Admin order management ──────────────────────────────────────────────────── async def test_admin_list_orders(admin_client, mock_db): mock_db.fetchval = AsyncMock(return_value=1) mock_db.fetch = AsyncMock(return_value=[SAMPLE_ORDER_ROW]) r = await admin_client.get("/api/v1/admin/orders") assert r.status_code == 200 async def test_admin_update_order_status(admin_client, mock_db): updated = {**SAMPLE_ORDER_ROW, "status": "shipped"} mock_db.fetchrow = AsyncMock(return_value=updated) mock_db.execute = AsyncMock(return_value="INSERT 1") r = await admin_client.patch(f"/api/v1/admin/orders/{ORDER_ID}/status", json={"status": "shipped"}) assert r.status_code == 200 assert r.json()["data"]["status"] == "shipped" async def test_admin_update_order_invalid_status(admin_client): r = await admin_client.patch(f"/api/v1/admin/orders/{ORDER_ID}/status", json={"status": "exploded"}) assert r.status_code == 422