Skip to content

Commit 1b1f9f2

Browse files
authored
Merge pull request #90 from Climate-Vision/merge/pr-50
fix: validate start_date <= end_date in PredictRequest (#50, rebased)
2 parents 3e0abe7 + 48cb5f2 commit 1b1f9f2

2 files changed

Lines changed: 79 additions & 5 deletions

File tree

src/climatevision/api/main.py

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -121,8 +121,14 @@ class PredictRequest(BaseModel):
121121
kind: str = Field(default="demo")
122122
analysis_type: AnalysisType = Field(default="deforestation")
123123
bbox: Optional[list[float]] = None
124-
start_date: Optional[str] = None
125-
end_date: Optional[str] = None
124+
start_date: Optional[str] = Field(
125+
default=None,
126+
description="Start date in YYYY-MM-DD format. Must be earlier than end_date.",
127+
)
128+
end_date: Optional[str] = Field(
129+
default=None,
130+
description="End date in YYYY-MM-DD format. Must be later than start_date.",
131+
)
126132

127133
@field_validator("bbox")
128134
@classmethod
@@ -651,9 +657,6 @@ async def predict_json(
651657
org: dict[str, Any] = Depends(require_api_key),
652658
) -> dict[str, Any]:
653659
"""Run prediction using bounding box and date range."""
654-
if body.start_date and body.end_date and body.start_date > body.end_date:
655-
raise HTTPException(status_code=400, detail="start_date must be before end_date")
656-
657660
created_at = _utc_now_iso()
658661
bbox_json = json.dumps(body.bbox) if body.bbox else None
659662

tests/test_api.py

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
"""Tests for ClimateVision API endpoints."""
22

3+
from unittest.mock import patch
4+
35
import pytest
46
from fastapi.testclient import TestClient
57

@@ -47,3 +49,72 @@ def test_predict_json_accepts_dev_key(
4749
)
4850
# Should pass auth; inference may fail due to missing models/GEE
4951
assert response.status_code in (200, 500)
52+
53+
54+
def test_predict_valid_date_range_reaches_inference(
55+
client: TestClient, monkeypatch: pytest.MonkeyPatch
56+
) -> None:
57+
"""POST /api/predict with valid date range should reach the inference layer."""
58+
monkeypatch.setenv("CLIMATEVISION_ALLOW_DEV_KEY", "1")
59+
payload = {
60+
"bbox": [-60.0, -15.0, -45.0, -5.0],
61+
"start_date": "2023-01-01",
62+
"end_date": "2023-06-30",
63+
"analysis_type": "deforestation",
64+
}
65+
fake_result = {
66+
"region": {"bbox": payload["bbox"]},
67+
"inference": {"forest_percentage": 72.3},
68+
"analysis_type": "deforestation",
69+
}
70+
with patch(
71+
"climatevision.api.main.run_inference_from_gee", return_value=fake_result
72+
) as mock_infer:
73+
response = client.post(
74+
"/api/predict",
75+
json=payload,
76+
headers={"X-API-Key": "cv_dev"},
77+
)
78+
assert response.status_code == 200
79+
mock_infer.assert_called_once()
80+
81+
82+
def test_predict_reversed_date_range_returns_422(
83+
client: TestClient, monkeypatch: pytest.MonkeyPatch
84+
) -> None:
85+
"""POST /api/predict with start_date > end_date should return 422."""
86+
monkeypatch.setenv("CLIMATEVISION_ALLOW_DEV_KEY", "1")
87+
payload = {
88+
"bbox": [-60.0, -15.0, -45.0, -5.0],
89+
"start_date": "2026-06-01",
90+
"end_date": "2026-01-01",
91+
"analysis_type": "deforestation",
92+
}
93+
response = client.post(
94+
"/api/predict",
95+
json=payload,
96+
headers={"X-API-Key": "cv_dev"},
97+
)
98+
assert response.status_code == 422
99+
body = response.json()
100+
error_messages = [e["msg"] for e in body["detail"]]
101+
assert any("start_date" in msg or "end_date" in msg for msg in error_messages)
102+
103+
104+
def test_predict_equal_dates_returns_422(
105+
client: TestClient, monkeypatch: pytest.MonkeyPatch
106+
) -> None:
107+
"""POST /api/predict with start_date == end_date should return 422."""
108+
monkeypatch.setenv("CLIMATEVISION_ALLOW_DEV_KEY", "1")
109+
payload = {
110+
"bbox": [-60.0, -15.0, -45.0, -5.0],
111+
"start_date": "2023-06-01",
112+
"end_date": "2023-06-01",
113+
"analysis_type": "deforestation",
114+
}
115+
response = client.post(
116+
"/api/predict",
117+
json=payload,
118+
headers={"X-API-Key": "cv_dev"},
119+
)
120+
assert response.status_code == 422

0 commit comments

Comments
 (0)