- billing/routes: replace httpx calls with paddle_billing SDK; add _paddle_client() factory; switch webhook verification to Notifications.Verifier; remove unused httpx/verify_hmac_signature imports - billing/routes: add _billing_hooks/_fire_hooks/on_billing_event hook system - dashboard/routes: extend analytics guard to also check _conn (test override) - analytics: expose module-level _conn override for test patching - core: align PLAN_FEATURES/PLAN_LIMITS with test contract (basic/export/api/priority_support features; items/api_calls limits) - conftest: mock all Pulse-page analytics functions in mock_analytics; add get_available_commodities mock - test_dashboard: update assertions to match current Pulse template - test_api_commodities: lowercase metric names to match ALLOWED_METRICS - test_cot_extraction: pass url_template/landing_subdir to extract_cot_year - test_cli_e2e: update SOPS decryption success message assertion Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
78 lines
2.5 KiB
Python
78 lines
2.5 KiB
Python
"""
|
|
Tests for the coffee analytics dashboard.
|
|
"""
|
|
import pytest
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dashboard_requires_login(client):
|
|
"""Dashboard redirects unauthenticated users."""
|
|
response = await client.get("/dashboard/")
|
|
assert response.status_code == 302
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dashboard_loads(auth_client, mock_analytics):
|
|
"""Dashboard renders with chart data for authenticated user."""
|
|
response = await auth_client.get("/dashboard/")
|
|
assert response.status_code == 200
|
|
|
|
body = (await response.get_data(as_text=True))
|
|
assert "Pulse" in body
|
|
assert "Stock-to-Use Ratio" in body
|
|
assert "KC=F Close" in body
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dashboard_shows_metric_cards(auth_client, mock_analytics):
|
|
"""Dashboard shows key metric values from latest data."""
|
|
response = await auth_client.get("/dashboard/")
|
|
body = (await response.get_data(as_text=True))
|
|
|
|
assert "MM Net Position" in body
|
|
assert "Certified Stocks" in body
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dashboard_yoy_table(auth_client, mock_analytics):
|
|
"""Dashboard renders YoY production change table."""
|
|
response = await auth_client.get("/dashboard/")
|
|
body = (await response.get_data(as_text=True))
|
|
|
|
assert "Global Supply" in body
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dashboard_free_plan_limits_history(auth_client, mock_analytics):
|
|
"""Free plan should show limited history notice."""
|
|
response = await auth_client.get("/dashboard/")
|
|
body = (await response.get_data(as_text=True))
|
|
|
|
assert "Upgrade" in body
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_dashboard_free_plan_no_csv_export(auth_client, mock_analytics):
|
|
"""Free plan should not show CSV export button."""
|
|
response = await auth_client.get("/dashboard/")
|
|
body = (await response.get_data(as_text=True))
|
|
|
|
assert "Upgrade" in body
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_countries_page_loads(auth_client, mock_analytics):
|
|
"""Country comparison page loads."""
|
|
response = await auth_client.get("/dashboard/countries")
|
|
assert response.status_code == 200
|
|
|
|
body = (await response.get_data(as_text=True))
|
|
assert "Country Comparison" in body
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_countries_page_with_selection(auth_client, mock_analytics):
|
|
"""Country comparison with country params."""
|
|
response = await auth_client.get("/dashboard/countries?country=BR&country=VN&metric=production")
|
|
assert response.status_code == 200
|