92 lines
2.6 KiB
Python
92 lines
2.6 KiB
Python
|
|
"""Integration tests for GET /api/fires (bbox + since filters, JSON contract)."""
|
||
|
|
|
||
|
|
import asyncio
|
||
|
|
|
||
|
|
import httpx
|
||
|
|
|
||
|
|
from conftest import make_fire_msg, requires_db
|
||
|
|
|
||
|
|
from ingestor import ingest_fire_row
|
||
|
|
from main import app
|
||
|
|
|
||
|
|
BASE = "http://test"
|
||
|
|
|
||
|
|
|
||
|
|
def _seed(*msgs) -> None:
|
||
|
|
"""Insert fire rows through the real ingestor path (idempotent)."""
|
||
|
|
|
||
|
|
async def run():
|
||
|
|
for m in msgs:
|
||
|
|
await ingest_fire_row(m)
|
||
|
|
|
||
|
|
asyncio.run(run())
|
||
|
|
|
||
|
|
|
||
|
|
def _get(path: str) -> httpx.Response:
|
||
|
|
return asyncio.run(_get_async(path))
|
||
|
|
|
||
|
|
|
||
|
|
async def _get_async(path: str) -> httpx.Response:
|
||
|
|
transport = httpx.ASGITransport(app=app)
|
||
|
|
async with httpx.AsyncClient(transport=transport, base_url=BASE) as client:
|
||
|
|
return await client.get(path)
|
||
|
|
|
||
|
|
|
||
|
|
@requires_db
|
||
|
|
def test_api_fires_bbox_filter(clean_fires):
|
||
|
|
_seed(
|
||
|
|
make_fire_msg(latitude=39.45678, longitude=-121.12345, satellite="N"),
|
||
|
|
make_fire_msg(latitude=34.00000, longitude=-118.20000, satellite="N20"),
|
||
|
|
make_fire_msg(latitude=10.00000, longitude=20.00000, satellite="N"),
|
||
|
|
)
|
||
|
|
# bbox covering only the California-ish points
|
||
|
|
resp = _get("/api/fires?bbox=-125,30,-115,42")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
body = resp.json()
|
||
|
|
assert isinstance(body, list)
|
||
|
|
assert len(body) == 2
|
||
|
|
lats = sorted(p["latitude"] for p in body)
|
||
|
|
assert lats == [34.0, 39.45678]
|
||
|
|
|
||
|
|
|
||
|
|
@requires_db
|
||
|
|
def test_api_fires_since_filter(clean_fires):
|
||
|
|
_seed(
|
||
|
|
make_fire_msg(acq_time="2026-08-24T18:10:00+00:00"),
|
||
|
|
make_fire_msg(acq_time="2026-08-24T19:10:00+00:00"),
|
||
|
|
)
|
||
|
|
resp = _get("/api/fires?since=2026-08-24T18:30:00Z")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
body = resp.json()
|
||
|
|
assert len(body) == 1
|
||
|
|
assert body[0]["acq_time"].startswith("2026-08-24T19:10")
|
||
|
|
|
||
|
|
|
||
|
|
@requires_db
|
||
|
|
def test_api_fires_json_contract(clean_fires):
|
||
|
|
_seed(make_fire_msg())
|
||
|
|
body = _get("/api/fires").json()
|
||
|
|
assert len(body) == 1
|
||
|
|
point = body[0]
|
||
|
|
# The exact fields the frontend heatmap needs — nothing more, nothing less.
|
||
|
|
assert set(point.keys()) == {
|
||
|
|
"latitude", "longitude", "brightness", "confidence", "acq_time",
|
||
|
|
"satellite", "instrument", "bright_ti5", "frp", "daynight",
|
||
|
|
}
|
||
|
|
assert point["latitude"] == 39.45678
|
||
|
|
assert point["confidence"] == "h"
|
||
|
|
assert point["satellite"] == "N"
|
||
|
|
|
||
|
|
|
||
|
|
@requires_db
|
||
|
|
def test_api_fires_invalid_bbox_422(clean_fires):
|
||
|
|
assert _get("/api/fires?bbox=-125,30").status_code == 422
|
||
|
|
assert _get("/api/fires?bbox=-125,abc,-115,42").status_code == 422
|
||
|
|
|
||
|
|
|
||
|
|
@requires_db
|
||
|
|
def test_api_fires_empty(clean_fires):
|
||
|
|
resp = _get("/api/fires")
|
||
|
|
assert resp.status_code == 200
|
||
|
|
assert resp.json() == []
|