Files
CA/backend/tests/test_error_handling.py
Akiba So e95e2f1338 feat: add analysis pages and raster risk map
Ship a new app version with broader analytics, restructured
dashboards, and a server-rendered risk map.

Frontend:
- Add Overview, Demographic, Disease, and Environmental Health
  analysis pages
- Add AnomalyMarkers, CalendarHeatmap, and MetricHeatmapTable
  components
- Rebuild Alerts map onto server-rendered raster risk tiles;
  expand Monitoring, Trend, and District Comparison views
- Extend API client, stores, and TypeScript types

Backend:
- Add environment router (pollutants, lag correlations)
- Add risk_raster util serving XYZ 100m risk tiles
- Expand cases endpoints (demographics, seasonality, diagnoses)
  and insights; harden auth and file-based loaders

Data & tooling:
- Add processed outpatient/inpatient/combined case parquet (LFS)
- Add nested CLAUDE.md guides, pyrightconfig, and test updates
2026-06-21 17:35:03 +08:00

116 lines
4.3 KiB
Python

"""US-002: Error handling and edge case tests."""
import pytest
from fastapi.testclient import TestClient
class TestDateValidation:
def test_invalid_date_format_alerts(self, client: TestClient):
resp = client.get("/api/alerts?date=invalid")
assert resp.status_code == 400
data = resp.json()
assert "detail" in data
def test_invalid_alert_date_format_detail(self, client: TestClient):
resp = client.get("/api/alerts/nonexistent?date=notadate")
assert resp.status_code == 400
def test_malformed_query_params(self, client: TestClient):
"""Non-numeric value for numeric param should return 422."""
resp = client.get("/api/risk/lod-grid?zoom=abc")
assert resp.status_code == 422
class TestMissingResources:
def test_nonexistent_endpoint(self, client: TestClient):
resp = client.get("/api/nonexistent_endpoint_xyz")
assert resp.status_code == 404
def test_nonexistent_grid_history(self, client: TestClient):
resp = client.get("/api/risk/history/nonexistent_grid_99999")
assert resp.status_code == 404
def test_nonexistent_date(self, client: TestClient):
resp = client.get("/api/risk/map?date=20990101")
assert resp.status_code == 404
assert "detail" in resp.json()
class TestInvalidForecastDay:
def test_forecast_out_of_range(self, client: TestClient):
# days=0 violates the ge=1 bound on /forecast/{days}, so FastAPI returns 422.
# (This previously returned 200 because `Path` was shadowed by `pathlib.Path`,
# silently disabling validation — fixed by the risk.py import correction.)
resp = client.get("/api/risk/forecast/0")
assert resp.status_code == 422
def test_forecast_too_large(self, client: TestClient):
resp = client.get("/api/risk/forecast/999")
assert resp.status_code in (200, 404, 422)
def test_lod_grid_invalid_zoom(self, client: TestClient):
resp = client.get("/api/risk/lod-grid?zoom=0")
assert resp.status_code == 422
def test_lod_grid_zoom_too_high(self, client: TestClient):
resp = client.get("/api/risk/lod-grid?zoom=21")
assert resp.status_code == 422
def test_lod_tile_below_min_zoom(self, client: TestClient):
resp = client.get("/api/risk/lod-grid/tile?zoom=10&tile_x=0&tile_y=0&forecast_day=1")
assert resp.status_code in (400, 422)
class TestAuthRequiredEndpoints:
def test_me_without_token(self, client: TestClient):
resp = client.get("/api/auth/me")
assert resp.status_code in (401, 403)
class TestMalformedRequestBody:
def test_login_missing_fields(self, client: TestClient):
resp = client.post("/api/auth/login", json={})
assert resp.status_code == 422
def test_login_empty_body(self, client: TestClient):
resp = client.post("/api/auth/login")
assert resp.status_code == 422
def test_chat_missing_messages(self, client: TestClient):
resp = client.post("/api/chat", json={})
assert resp.status_code in (403, 422)
def test_predict_missing_date(self, client: TestClient):
resp = client.post("/api/predict/multi-day", json={"days": 3})
assert resp.status_code == 422
def test_predict_invalid_days(self, client: TestClient):
resp = client.post("/api/predict/multi-day", json={"date": "2022-12-15", "days": 0})
assert resp.status_code == 422
class TestGlobalErrorHandler:
def test_internal_error_returns_json(self, client: TestClient):
"""Global exception handler should return JSON, not HTML, on 500."""
resp = client.get("/api/risk/lod-grid?zoom=10&forecast_day=1")
assert resp.status_code == 200 # valid request should pass
class TestCORSAvailability:
def test_cors_preflight(self, client: TestClient):
resp = client.options(
"/api/risk/current",
headers={
"Origin": "http://localhost:5173",
"Access-Control-Request-Method": "GET",
},
)
assert resp.status_code == 200
def test_cors_origin_header(self, client: TestClient):
resp = client.get(
"/api/risk/current",
headers={"Origin": "http://localhost:5173"},
)
assert resp.status_code == 200
assert "access-control-allow-origin" in resp.headers