""" Tests for the commodity analytics API endpoints. """ import hashlib import secrets from datetime import datetime import pytest async def _create_api_key_for_user(db, user_id, plan="starter"): """Helper: create an API key and subscription, return the raw key.""" raw_key = f"sk_{secrets.token_urlsafe(32)}" key_hash = hashlib.sha256(raw_key.encode()).hexdigest() now = datetime.utcnow().isoformat() await db.execute( """INSERT INTO api_keys (user_id, name, key_hash, key_prefix, scopes, created_at) VALUES (?, ?, ?, ?, ?, ?)""", (user_id, "test-key", key_hash, raw_key[:12], "read,write", now), ) # Create subscription for plan if plan != "free": await db.execute( """INSERT OR REPLACE INTO subscriptions (user_id, plan, status, created_at, updated_at) VALUES (?, ?, 'active', ?, ?)""", (user_id, plan, now, now), ) await db.commit() return raw_key @pytest.mark.asyncio async def test_api_requires_auth(client): """API returns 401 without auth header.""" response = await client.get("/api/v1/commodities") assert response.status_code == 401 @pytest.mark.asyncio async def test_api_rejects_free_plan(client, db, test_user, mock_analytics): """API returns 403 for free plan users.""" raw_key = await _create_api_key_for_user(db, test_user["id"], plan="free") response = await client.get( "/api/v1/commodities", headers={"Authorization": f"Bearer {raw_key}"}, ) assert response.status_code == 403 @pytest.mark.asyncio async def test_list_commodities(client, db, test_user, mock_analytics): """GET /commodities returns commodity list.""" raw_key = await _create_api_key_for_user(db, test_user["id"]) response = await client.get( "/api/v1/commodities", headers={"Authorization": f"Bearer {raw_key}"}, ) assert response.status_code == 200 data = await response.get_json() assert "commodities" in data assert len(data["commodities"]) == 2 @pytest.mark.asyncio async def test_commodity_metrics(client, db, test_user, mock_analytics): """GET /commodities//metrics returns time series.""" raw_key = await _create_api_key_for_user(db, test_user["id"]) response = await client.get( "/api/v1/commodities/711100/metrics?metrics=Production&metrics=Exports", headers={"Authorization": f"Bearer {raw_key}"}, ) assert response.status_code == 200 data = await response.get_json() assert data["commodity_code"] == 711100 assert "Production" in data["metrics"] @pytest.mark.asyncio async def test_commodity_metrics_invalid_metric(client, db, test_user, mock_analytics): """GET /commodities//metrics rejects invalid metrics.""" raw_key = await _create_api_key_for_user(db, test_user["id"]) response = await client.get( "/api/v1/commodities/711100/metrics?metrics=DROP_TABLE", headers={"Authorization": f"Bearer {raw_key}"}, ) assert response.status_code == 400 @pytest.mark.asyncio async def test_commodity_countries(client, db, test_user, mock_analytics): """GET /commodities//countries returns ranking.""" raw_key = await _create_api_key_for_user(db, test_user["id"]) response = await client.get( "/api/v1/commodities/711100/countries?metric=Production&limit=5", headers={"Authorization": f"Bearer {raw_key}"}, ) assert response.status_code == 200 data = await response.get_json() assert data["metric"] == "Production" @pytest.mark.asyncio async def test_commodity_csv_export(client, db, test_user, mock_analytics): """GET /commodities//metrics.csv returns CSV.""" raw_key = await _create_api_key_for_user(db, test_user["id"]) response = await client.get( "/api/v1/commodities/711100/metrics.csv", headers={"Authorization": f"Bearer {raw_key}"}, ) assert response.status_code == 200 assert "text/csv" in response.content_type @pytest.mark.asyncio async def test_me_endpoint(client, db, test_user, mock_analytics): """GET /me returns user info.""" raw_key = await _create_api_key_for_user(db, test_user["id"]) response = await client.get( "/api/v1/me", headers={"Authorization": f"Bearer {raw_key}"}, ) assert response.status_code == 200 data = await response.get_json() assert data["email"] == "test@example.com" assert data["plan"] == "starter"