reachy-vision-api / tests /test_api.py
Chris
feat: add YOLOv8 object detection API
00340de unverified
import io
from unittest.mock import MagicMock
import pytest
import torch
from PIL import Image
import app as app_module
def test_health(client):
response = client.get("/health")
assert response.status_code == 200
assert response.json() == {"status": "ok"}
def test_detect_returns_detections(client):
img = Image.new("RGB", (640, 480), color="red")
buffer = io.BytesIO()
img.save(buffer, format="PNG")
buffer.seek(0)
response = client.post("/detect", files={"file": ("test.png", buffer, "image/png")})
assert response.status_code == 200
data = response.json()
assert "num_detections" in data
assert "detections" in data
assert isinstance(data["detections"], list)
def test_detect_with_jpeg(client):
img = Image.new("RGB", (640, 480), color="blue")
buffer = io.BytesIO()
img.save(buffer, format="JPEG")
buffer.seek(0)
response = client.post(
"/detect", files={"file": ("test.jpg", buffer, "image/jpeg")}
)
assert response.status_code == 200
data = response.json()
assert "num_detections" in data
assert "detections" in data
def test_detect_missing_file(client):
response = client.post("/detect")
assert response.status_code == 422
def test_detect_response_structure(client):
img = Image.new("RGB", (100, 100), color="green")
buffer = io.BytesIO()
img.save(buffer, format="PNG")
buffer.seek(0)
response = client.post("/detect", files={"file": ("test.png", buffer, "image/png")})
assert response.status_code == 200
data = response.json()
assert isinstance(data["num_detections"], int)
assert data["num_detections"] >= 0
assert data["num_detections"] == len(data["detections"])
for detection in data["detections"]:
assert "class_id" in detection
assert "class_name" in detection
assert "confidence" in detection
assert "bbox_xyxy" in detection
assert isinstance(detection["class_id"], int)
assert isinstance(detection["class_name"], str)
assert 0 <= detection["confidence"] <= 1
assert len(detection["bbox_xyxy"]) == 4
def test_detect_model_not_loaded(client):
"""Test that detect raises error when model is not loaded."""
original_model = app_module.model
app_module.model = None
try:
img = Image.new("RGB", (100, 100), color="red")
buffer = io.BytesIO()
img.save(buffer, format="PNG")
buffer.seek(0)
with pytest.raises(RuntimeError, match="Model not loaded"):
client.post("/detect", files={"file": ("test.png", buffer, "image/png")})
finally:
app_module.model = original_model
def test_detect_with_detections(client):
"""Test detection with mocked YOLO results containing detections."""
original_model = app_module.model
# Create mock model
mock_model = MagicMock()
mock_model.names = {0: "person", 1: "car"}
# Create mock box
mock_box = MagicMock()
mock_box.cls = torch.tensor([0])
mock_box.conf = torch.tensor([0.95])
mock_box.xyxy = torch.tensor([[100.0, 150.0, 300.0, 400.0]])
# Create mock result
mock_result = MagicMock()
mock_result.boxes = [mock_box]
mock_model.return_value = [mock_result]
app_module.model = mock_model
try:
img = Image.new("RGB", (640, 480), color="red")
buffer = io.BytesIO()
img.save(buffer, format="PNG")
buffer.seek(0)
response = client.post(
"/detect", files={"file": ("test.png", buffer, "image/png")}
)
assert response.status_code == 200
data = response.json()
assert data["num_detections"] == 1
assert len(data["detections"]) == 1
assert data["detections"][0]["class_id"] == 0
assert data["detections"][0]["class_name"] == "person"
assert data["detections"][0]["confidence"] == pytest.approx(0.95)
assert data["detections"][0]["bbox_xyxy"] == pytest.approx(
[100.0, 150.0, 300.0, 400.0]
)
finally:
app_module.model = original_model