cmvr_edge_ai/tests/integration/test_onnx_server_config.py

116 lines
4.0 KiB
Python
Raw Normal View History

from __future__ import annotations
import asyncio
import base64
from io import BytesIO
from pathlib import Path
from typing import Any
import httpx
import pytest
from PIL import Image
pytest.importorskip("onnxruntime")
from cmvr_edge_ai.application import (
create_default_capability_registry,
create_default_model_registry,
create_default_registry,
)
from cmvr_edge_ai.config import load_config
from cmvr_edge_ai.server.application import EdgeAIServerApplication
PROJECT_ROOT = Path(__file__).resolve().parents[2]
def _jpeg() -> bytes:
image = Image.new("RGB", (96, 64))
image.putdata(
[
((x * 3) % 256, (y * 5) % 256, ((x + y) * 7) % 256)
for y in range(image.height)
for x in range(image.width)
]
)
output = BytesIO()
image.save(output, format="JPEG", quality=90)
return output.getvalue()
async def _ticker() -> None:
# A timer keeps restricted CI event loops responsive while native ORT and
# bounded executor threads complete setup/inference.
while True:
await asyncio.sleep(0.01)
def test_real_server_detect_config_serves_both_onnx_routes() -> None:
async def exercise() -> tuple[list[dict[str, Any]], bool]:
config = load_config(PROJECT_ROOT / "configs" / "server_detect.yaml")
models = create_default_model_registry(discover_entry_points=False)
application = EdgeAIServerApplication(
config,
create_default_registry(
discover_entry_points=False,
model_registry=models,
),
create_default_capability_registry(
discover_entry_points=False,
model_registry=models,
),
)
ticker = asyncio.create_task(_ticker())
try:
await application.start()
responses: list[dict[str, Any]] = []
async with httpx.AsyncClient(
transport=httpx.ASGITransport(app=application.asgi_app),
base_url="http://test",
timeout=30,
) as client:
encoded = base64.b64encode(_jpeg()).decode("ascii")
for index, category in enumerate(
("detect.ppe", "detect.mobile_phone")
):
response = await client.post(
"/v1/inference",
json={
"schema_version": "cmvr.inference-request/v1",
"request_id": f"onnx-smoke-{index}",
"category": category,
"inputs": [
{
"kind": "image",
"name": "image",
"media_type": "image/jpeg",
"encoding": "base64",
"data": encoded,
}
],
},
)
assert response.status_code == 200, response.text
responses.append(response.json())
ready = await client.get("/health/ready")
return responses, ready.status_code == 200
finally:
await application.stop()
ticker.cancel()
await asyncio.gather(ticker, return_exceptions=True)
responses, ready = asyncio.run(exercise())
expected = (
("detect.ppe", "construction-ppe-yolov8@2"),
("detect.mobile_phone", "yolov8n-mobile-phone@2"),
)
assert ready is True
for response, (category, model_id) in zip(responses, expected):
assert response["schema_version"] == "cmvr.inference-response/v1"
assert response["category"] == category
assert response["status"] in {"succeeded", "no_result"}
assert response["model"]["model_id"] == model_id
assert response["model"]["backend"] == "onnxruntime-yolov8"
assert response["outputs"][0]["kind"] == "detections"