maritime / tests /test_api.py
copilot-swe-agent[bot]
Add Maritime monorepo scaffold: backend FastAPI + frontend Next.js + CI/CD workflow
6e1c8c8
"""
pytest tests for the SUB-SENTINEL FastAPI backend.
Run:
cd backend
pytest tests/ -v
"""
import base64
import io
import os
import pytest
from fastapi.testclient import TestClient
from PIL import Image
# Ensure we do NOT need a real GROQ_API_KEY for tests
os.environ.setdefault("GROQ_API_KEY", "")
from main import app # noqa: E402 – import after env setup
client = TestClient(app)
# ---------------------------------------------------------------------------
# Helpers
# ---------------------------------------------------------------------------
def _make_png_bytes(width: int = 64, height: int = 64, color=(30, 80, 120)) -> bytes:
"""Create a small solid-colour PNG in memory."""
img = Image.new("RGB", (width, height), color)
buf = io.BytesIO()
img.save(buf, format="PNG")
return buf.getvalue()
# ---------------------------------------------------------------------------
# /health
# ---------------------------------------------------------------------------
def test_health_ok():
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
# ---------------------------------------------------------------------------
# /process – happy path
# ---------------------------------------------------------------------------
def test_process_returns_required_keys():
png = _make_png_bytes()
response = client.post(
"/process",
files={"file": ("test.png", png, "image/png")},
)
assert response.status_code == 200
data = response.json()
assert "enhanced_image_base64" in data
assert "heatmap_base64" in data
assert "detections" in data
assert "sitrep_text" in data
def test_process_enhanced_image_is_valid_base64():
png = _make_png_bytes()
response = client.post(
"/process",
files={"file": ("test.png", png, "image/png")},
)
assert response.status_code == 200
b64_str = response.json()["enhanced_image_base64"]
# Should be a data-URI
assert b64_str.startswith("data:image/")
# Extract the raw base64 portion and verify it decodes
raw = b64_str.split(",", 1)[1]
decoded = base64.b64decode(raw)
img = Image.open(io.BytesIO(decoded))
assert img.size[0] > 0
assert img.size[1] > 0
def test_process_heatmap_is_valid_base64():
png = _make_png_bytes()
response = client.post(
"/process",
files={"file": ("test.png", png, "image/png")},
)
assert response.status_code == 200
b64_str = response.json()["heatmap_base64"]
assert b64_str.startswith("data:image/")
raw = b64_str.split(",", 1)[1]
decoded = base64.b64decode(raw)
img = Image.open(io.BytesIO(decoded))
assert img.size[0] > 0
def test_process_detections_structure():
"""Each detection must have the required keys with correct types."""
png = _make_png_bytes()
response = client.post(
"/process",
files={"file": ("test.png", png, "image/png")},
)
assert response.status_code == 200
detections = response.json()["detections"]
assert isinstance(detections, list)
for det in detections:
assert "class" in det
assert "mapped_label" in det
assert "confidence" in det
assert "bbox" in det
assert isinstance(det["confidence"], float)
assert len(det["bbox"]) == 4
def test_process_sitrep_is_string():
png = _make_png_bytes()
response = client.post(
"/process",
files={"file": ("test.png", png, "image/png")},
)
assert response.status_code == 200
sitrep = response.json()["sitrep_text"]
assert isinstance(sitrep, str)
assert len(sitrep) > 0
# ---------------------------------------------------------------------------
# /process – error cases
# ---------------------------------------------------------------------------
def test_process_rejects_non_image():
response = client.post(
"/process",
files={"file": ("data.txt", b"hello world", "text/plain")},
)
assert response.status_code == 400
def test_process_rejects_empty_file():
response = client.post(
"/process",
files={"file": ("empty.png", b"", "image/png")},
)
assert response.status_code == 400
def test_process_jpeg_image():
"""Pipeline should work with JPEG input."""
img = Image.new("RGB", (80, 80), (50, 100, 150))
buf = io.BytesIO()
img.save(buf, format="JPEG")
response = client.post(
"/process",
files={"file": ("test.jpg", buf.getvalue(), "image/jpeg")},
)
assert response.status_code == 200
assert "enhanced_image_base64" in response.json()