266 lines
9.5 KiB
Python
266 lines
9.5 KiB
Python
|
|
"""US-001: API endpoint tests covering all 10 routers."""
|
||
|
|
import pytest
|
||
|
|
from fastapi.testclient import TestClient
|
||
|
|
|
||
|
|
|
||
|
|
class TestRootAndHealth:
|
||
|
|
def test_root_returns_status(self, client: TestClient):
|
||
|
|
resp = client.get("/")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert data["status"] == "running"
|
||
|
|
assert data["version"] == "1.0.0"
|
||
|
|
|
||
|
|
def test_health_check(self, client: TestClient):
|
||
|
|
resp = client.get("/health")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert resp.json()["status"] == "healthy"
|
||
|
|
|
||
|
|
|
||
|
|
class TestRiskEndpoints:
|
||
|
|
def test_current_risk_map(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/current")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "grids" in data
|
||
|
|
assert isinstance(data["grids"], list)
|
||
|
|
assert "total_count" in data
|
||
|
|
assert "timestamp" in data
|
||
|
|
if data["grids"]:
|
||
|
|
g = data["grids"][0]
|
||
|
|
assert "grid_id" in g
|
||
|
|
assert "risk_value" in g
|
||
|
|
assert "risk_level" in g
|
||
|
|
assert g["risk_level"] in ("high", "medium_high", "medium", "medium_low", "low")
|
||
|
|
|
||
|
|
def test_risk_map_with_date(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/map?date=20231201")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert data["total_count"] > 0
|
||
|
|
|
||
|
|
def test_risk_map_missing_date(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/map?date=20990101")
|
||
|
|
assert resp.status_code == 404
|
||
|
|
|
||
|
|
def test_forecast_1d(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/forecast/1")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "grids" in data
|
||
|
|
|
||
|
|
def test_forecast_3d(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/forecast/3")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
|
||
|
|
def test_forecast_7d(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/forecast/7")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
|
||
|
|
def test_stats(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/stats")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "total_grids" in data
|
||
|
|
assert "avg_risk" in data
|
||
|
|
assert "distribution" in data
|
||
|
|
assert "high_risk_count" in data
|
||
|
|
dist = data["distribution"]
|
||
|
|
for k in ("high", "medium_high", "medium", "medium_low", "low"):
|
||
|
|
assert k in dist
|
||
|
|
|
||
|
|
def test_lod_grid(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/lod-grid?zoom=10&forecast_day=1")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "lod" in data
|
||
|
|
assert "grids" in data
|
||
|
|
assert "total_count" in data
|
||
|
|
|
||
|
|
def test_lod_tile(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/lod-grid/tile?zoom=14&tile_x=0&tile_y=0&forecast_day=1")
|
||
|
|
assert resp.status_code in (200, 422)
|
||
|
|
|
||
|
|
def test_fullgrid(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/fullgrid")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "columns" in data
|
||
|
|
assert data["columns"] == ["lat", "lon", "risk_1d", "risk_3d", "risk_7d"]
|
||
|
|
|
||
|
|
def test_precomputed(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/precomputed")
|
||
|
|
assert resp.status_code in (200, 404)
|
||
|
|
|
||
|
|
def test_risk_history(self, client: TestClient):
|
||
|
|
resp = client.get("/api/risk/history/r0_c0?days=7")
|
||
|
|
assert resp.status_code in (200, 404)
|
||
|
|
if resp.status_code == 200:
|
||
|
|
data = resp.json()
|
||
|
|
assert "grid_id" in data
|
||
|
|
assert "history" in data
|
||
|
|
|
||
|
|
|
||
|
|
class TestAlertEndpoints:
|
||
|
|
def test_list_alerts(self, client: TestClient):
|
||
|
|
resp = client.get("/api/alerts")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "alerts" in data
|
||
|
|
assert "total" in data
|
||
|
|
assert isinstance(data["alerts"], list)
|
||
|
|
|
||
|
|
def test_alerts_with_min_risk_filter(self, client: TestClient):
|
||
|
|
resp_all = client.get("/api/alerts")
|
||
|
|
resp_filtered = client.get("/api/alerts?min_risk=0.9")
|
||
|
|
assert resp_filtered.status_code == 200
|
||
|
|
assert resp_filtered.json()["total"] <= resp_all.json()["total"]
|
||
|
|
|
||
|
|
def test_alerts_with_priority_filter(self, client: TestClient):
|
||
|
|
resp = client.get("/api/alerts?priority=P1")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
for alert in resp.json()["alerts"]:
|
||
|
|
assert alert["priority"] == "P1"
|
||
|
|
|
||
|
|
def test_p1_alerts(self, client: TestClient):
|
||
|
|
resp = client.get("/api/alerts/priority/p1")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
for alert in resp.json()["alerts"]:
|
||
|
|
assert alert["priority"] == "P1"
|
||
|
|
|
||
|
|
def test_p2_alerts(self, client: TestClient):
|
||
|
|
resp = client.get("/api/alerts/priority/p2")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
for alert in resp.json()["alerts"]:
|
||
|
|
assert alert["priority"] == "P2"
|
||
|
|
|
||
|
|
def test_get_single_alert(self, client: TestClient):
|
||
|
|
alerts_resp = client.get("/api/alerts")
|
||
|
|
alerts = alerts_resp.json().get("alerts", [])
|
||
|
|
if alerts:
|
||
|
|
alert_id = alerts[0]["alert_id"]
|
||
|
|
resp = client.get(f"/api/alerts/{alert_id}")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert resp.json()["alert_id"] == alert_id
|
||
|
|
|
||
|
|
def test_alert_not_found(self, client: TestClient):
|
||
|
|
resp = client.get("/api/alerts/nonexistent_alert_id")
|
||
|
|
assert resp.status_code == 404
|
||
|
|
|
||
|
|
|
||
|
|
class TestGridEndpoints:
|
||
|
|
def test_grids_geojson(self, client: TestClient):
|
||
|
|
resp = client.get("/api/grids/geojson?date=2022-12-15")
|
||
|
|
assert resp.status_code in (200, 500, 503)
|
||
|
|
|
||
|
|
def test_grid_history(self, client: TestClient):
|
||
|
|
resp = client.get("/api/grids/r100_c200/history?days=7")
|
||
|
|
assert resp.status_code in (200, 404, 500, 503)
|
||
|
|
|
||
|
|
def test_historical_aggregated(self, client: TestClient):
|
||
|
|
resp = client.get("/api/history/aggregated?start_date=2022-12-01&end_date=2022-12-31")
|
||
|
|
assert resp.status_code in (200, 500, 503)
|
||
|
|
|
||
|
|
def test_multi_day_prediction(self, client: TestClient):
|
||
|
|
resp = client.post("/api/predict/multi-day", json={"date": "2022-12-15", "days": 3})
|
||
|
|
assert resp.status_code in (200, 500, 503)
|
||
|
|
|
||
|
|
|
||
|
|
class TestCaseEndpoints:
|
||
|
|
def test_case_trend(self, client: TestClient):
|
||
|
|
resp = client.get("/api/cases/trend")
|
||
|
|
assert resp.status_code in (200, 500, 503)
|
||
|
|
|
||
|
|
def test_case_trend_with_params(self, client: TestClient):
|
||
|
|
resp = client.get(
|
||
|
|
"/api/cases/trend?start_date=2022-12-01&end_date=2022-12-31&group_by=week"
|
||
|
|
)
|
||
|
|
assert resp.status_code in (200, 500, 503)
|
||
|
|
|
||
|
|
def test_case_districts(self, client: TestClient):
|
||
|
|
resp = client.get("/api/cases/districts")
|
||
|
|
assert resp.status_code in (200, 500, 503)
|
||
|
|
|
||
|
|
def test_case_stats(self, client: TestClient):
|
||
|
|
resp = client.get("/api/cases/stats")
|
||
|
|
assert resp.status_code in (200, 500, 503)
|
||
|
|
|
||
|
|
def test_case_diagnoses(self, client: TestClient):
|
||
|
|
resp = client.get("/api/cases/diagnoses")
|
||
|
|
assert resp.status_code in (200, 500, 503)
|
||
|
|
|
||
|
|
|
||
|
|
class TestGeocodedEndpoints:
|
||
|
|
def test_geocoded_grid(self, client: TestClient):
|
||
|
|
resp = client.get("/api/geocoded/grid")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "grids" in data
|
||
|
|
|
||
|
|
def test_geocoded_cases(self, client: TestClient):
|
||
|
|
resp = client.get("/api/geocoded/geocoded?limit=10")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "cases" in data
|
||
|
|
|
||
|
|
def test_geocoded_count(self, client: TestClient):
|
||
|
|
resp = client.get("/api/geocoded/geocoded/count")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "total" in data
|
||
|
|
|
||
|
|
|
||
|
|
class TestInsightsEndpoints:
|
||
|
|
def test_insights_overview(self, client: TestClient):
|
||
|
|
resp = client.get("/api/insights/overview")
|
||
|
|
assert resp.status_code in (200, 404, 500)
|
||
|
|
|
||
|
|
def test_insights_cards(self, client: TestClient):
|
||
|
|
resp = client.get("/api/insights/cards")
|
||
|
|
assert resp.status_code in (200, 404, 500)
|
||
|
|
|
||
|
|
|
||
|
|
class TestReportsEndpoints:
|
||
|
|
def test_reports_list(self, client: TestClient):
|
||
|
|
resp = client.get("/api/reports/list")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "reports" in data
|
||
|
|
assert "total" in data
|
||
|
|
|
||
|
|
def test_report_by_id(self, client: TestClient):
|
||
|
|
list_resp = client.get("/api/reports/list")
|
||
|
|
reports = list_resp.json().get("reports", [])
|
||
|
|
if reports:
|
||
|
|
rid = reports[0]["report_id"]
|
||
|
|
resp = client.get(f"/api/reports/{rid}")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
data = resp.json()
|
||
|
|
assert "metadata" in data
|
||
|
|
|
||
|
|
def test_report_not_found(self, client: TestClient):
|
||
|
|
resp = client.get("/api/reports/nonexistent")
|
||
|
|
assert resp.status_code in (400, 404)
|
||
|
|
|
||
|
|
def test_report_summary_latest(self, client: TestClient):
|
||
|
|
resp = client.get("/api/reports/summary/latest")
|
||
|
|
assert resp.status_code in (200, 404)
|
||
|
|
|
||
|
|
|
||
|
|
class TestAnalysisEndpoints:
|
||
|
|
def test_analysis_trend(self, client: TestClient):
|
||
|
|
resp = client.get("/api/analysis/trend?days=7")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
|
||
|
|
def test_analysis_districts(self, client: TestClient):
|
||
|
|
resp = client.get("/api/analysis/districts")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
|
||
|
|
|
||
|
|
class TestChatEndpoint:
|
||
|
|
def test_chat_post(self, client: TestClient):
|
||
|
|
resp = client.post("/api/chat", json={
|
||
|
|
"messages": [{"role": "user", "content": "你好"}]
|
||
|
|
})
|
||
|
|
assert resp.status_code in (200, 401, 403, 503)
|