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"