import pytest from httpx import ASGITransport, AsyncClient from proxy_pool.app import create_app @pytest.fixture async def client(): app = create_app() async with app.router.lifespan_context(app): transport = ASGITransport(app=app) async with AsyncClient(transport=transport, base_url="http://test") as client: yield client async def register_user(client, email=None): """Helper to register and return (user_data, raw_api_key).""" import uuid email = email or f"test-{uuid.uuid4().hex[:8]}@example.com" resp = await client.post( "/auth/register", json={"email": email, "display_name": "Test User"}, ) data = resp.json() return data, data["api_key"]["key"] def auth_header(api_key: str) -> dict: return {"Authorization": f"Bearer {api_key}"} class TestRegistration: async def test_register_returns_user_and_key(self, client): data, raw_key = await register_user(client) assert data["user"]["email"].endswith("@example.com") assert data["user"]["is_active"] is True assert raw_key.startswith("pp_") assert data["api_key"]["label"] == "default" async def test_register_duplicate_email(self, client): email = "dupe@example.com" await register_user(client, email=email) resp = await client.post( "/auth/register", json={"email": email}, ) assert resp.status_code == 409 class TestAuthentication: async def test_authenticated_request(self, client): _, api_key = await register_user(client) resp = await client.get("/account", headers=auth_header(api_key)) assert resp.status_code == 200 assert resp.json()["is_active"] is True async def test_invalid_key_rejected(self, client): resp = await client.get( "/account", headers=auth_header("pp_bogus_key_that_does_not_exist"), ) assert resp.status_code == 401 async def test_missing_key_rejected(self, client): resp = await client.get("/account") assert resp.status_code == 401 class TestCredits: async def test_initial_credits(self, client): _, api_key = await register_user(client) resp = await client.get( "/account/credits", headers=auth_header(api_key), ) assert resp.status_code == 200 data = resp.json() assert data["balance"] == 100 assert len(data["recent_transactions"]) == 1 assert data["recent_transactions"][0]["tx_type"] == "purchase" class TestApiKeyManagement: async def test_create_additional_key(self, client): _, api_key = await register_user(client) resp = await client.post( "/auth/keys", headers=auth_header(api_key), json={"label": "ci-server"}, ) assert resp.status_code == 201 assert resp.json()["label"] == "ci-server" assert resp.json()["key"].startswith("pp_") async def test_list_keys(self, client): _, api_key = await register_user(client) resp = await client.get( "/auth/keys", headers=auth_header(api_key), ) assert resp.status_code == 200 assert len(resp.json()) >= 1 async def test_revoke_key(self, client): _, api_key = await register_user(client) # Create a second key so we can revoke it create_resp = await client.post( "/auth/keys", headers=auth_header(api_key), json={"label": "throwaway"}, ) key_id = create_resp.json()["id"] # Revoke it del_resp = await client.delete( f"/auth/keys/{key_id}", headers=auth_header(api_key), ) assert del_resp.status_code == 204