Add BeanFlows MVP: coffee analytics dashboard, API, and web app
- Fix pipeline granularity: add market_year to cleaned/serving SQL models - Add DuckDB data access layer with async query functions (analytics.py) - Build Chart.js dashboard: supply/demand, STU ratio, top producers, YoY table - Add country comparison page with multi-select picker - Replace items CRUD with read-only commodity API (list, metrics, countries, CSV) - Configure BeanFlows plan tiers (Free/Starter/Pro) with feature gating - Rewrite public pages for coffee market intelligence positioning - Remove boilerplate items schema, update health check for DuckDB - Add test suite: 139 tests passing (dashboard, API, billing) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
79
web/tests/test_dashboard.py
Normal file
79
web/tests/test_dashboard.py
Normal file
@@ -0,0 +1,79 @@
|
||||
"""
|
||||
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 "Coffee Dashboard" in body
|
||||
assert "Global Supply" in body
|
||||
assert "Stock-to-Use" in body
|
||||
assert "Top Producing Countries" 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))
|
||||
|
||||
# Latest production from mock: 172,000
|
||||
assert "172,000" 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 "Brazil" in body
|
||||
assert "Vietnam" 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 "CSV export available on Starter" 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
|
||||
Reference in New Issue
Block a user