116 lines
4.0 KiB
Python
116 lines
4.0 KiB
Python
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"
|