"""Integration tests for Premium Purchase and PaymentProvider. docs/planning/economy.md US-2 (task 575fd14b5360). """ 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_payments_unauthorized(): client = TestClient(app) resp = client.post("/api/payments/purchase", json={"credits": 500}) assert resp.status_code == 401 resp = client.post("/api/payments/confirm", json={"session_id": "sess_123"}) assert resp.status_code == 401 def test_purchase_rejects_arbitrary_amounts(): client = TestClient(app) _, headers = _register(client, "pay_user_1") # 42 credits is not an allowed pack resp = client.post("/api/payments/purchase", json={"credits": 42}, headers=headers) assert resp.status_code == 422 # Negative / 0 credits resp = client.post("/api/payments/purchase", json={"credits": -100}, headers=headers) assert resp.status_code == 422 def test_purchase_and_confirm_flow(): client = TestClient(app) uid, headers = _register(client, "pay_user_2") # 1. Check initial balance w_resp = client.get("/api/wallet", headers=headers) assert w_resp.status_code == 200 assert w_resp.json()["balance"] == 0 # 2. Purchase session for 500 credits p_resp = client.post( "/api/payments/purchase", json={"credits": 500}, headers={"Idempotency-Key": "sess_req_1", **headers}, ) assert p_resp.status_code == 201, p_resp.text p_data = p_resp.json() assert p_data["credits"] == 500 assert p_data["session_id"].startswith("sess_") session_id = p_data["session_id"] # Balance is still 0 before confirmation w_resp = client.get("/api/wallet", headers=headers) assert w_resp.json()["balance"] == 0 # 3. Confirm purchase session c_resp = client.post( "/api/payments/confirm", json={"session_id": session_id}, headers=headers, ) assert c_resp.status_code == 200, c_resp.text c_data = c_resp.json() assert c_data["confirmed"] is True assert c_data["credits"] == 500 assert c_data["new_balance"] == 500 assert c_data["provider_ref"] != "" # 4. Idempotent re-confirm must not double-credit c_resp2 = client.post( "/api/payments/confirm", json={"session_id": session_id}, headers=headers, ) assert c_resp2.status_code == 200 assert c_resp2.json()["new_balance"] == 500 # Check transactions in ledger: exactly 1 transaction tx_resp = client.get("/api/wallet/transactions", headers=headers) assert tx_resp.status_code == 200 tx_data = tx_resp.json() assert tx_data["total"] == 1 assert tx_data["transactions"][0]["amount"] == 500 assert tx_data["transactions"][0]["type"] == "credit_purchase" def test_confirm_invalid_or_wrong_user_fails(): client = TestClient(app) uid1, headers1 = _register(client, "pay_user_3a") uid2, headers2 = _register(client, "pay_user_3b") # User 1 creates a session p_resp = client.post("/api/payments/purchase", json={"credits": 100}, headers=headers1) session_id = p_resp.json()["session_id"] # User 2 tries to confirm User 1's session -> must fail 400 bad_resp = client.post("/api/payments/confirm", json={"session_id": session_id}, headers=headers2) assert bad_resp.status_code == 400 # Non-existent session -> must fail 400 bad_resp2 = client.post("/api/payments/confirm", json={"session_id": "sess_nonexistent"}, headers=headers1) assert bad_resp2.status_code == 400