mirror of
https://github.com/azaion/detections.git
synced 2026-06-21 22:51:09 +00:00
Fixed isolate Jetson manual e2e and stabilize SSE warmup
This commit is contained in:
+46
-4
@@ -133,10 +133,11 @@ def image_detect(http_client, auth_headers):
|
|||||||
headers=headers,
|
headers=headers,
|
||||||
timeout=timeout,
|
timeout=timeout,
|
||||||
)
|
)
|
||||||
done.wait(timeout=timeout)
|
completed = done.wait(timeout=timeout)
|
||||||
elapsed_ms = (time.perf_counter() - t0) * 1000.0
|
elapsed_ms = (time.perf_counter() - t0) * 1000.0
|
||||||
|
|
||||||
assert r.status_code == 202, f"Expected 202, got {r.status_code}: {r.text}"
|
assert r.status_code == 202, f"Expected 202, got {r.status_code}: {r.text}"
|
||||||
|
assert completed, f"Timed out waiting for terminal SSE event on channel {cid}"
|
||||||
assert not errors, f"SSE errors: {errors}"
|
assert not errors, f"SSE errors: {errors}"
|
||||||
|
|
||||||
th.join(timeout=1)
|
th.join(timeout=1)
|
||||||
@@ -214,6 +215,27 @@ def _read_media(name: str) -> bytes:
|
|||||||
return p.read_bytes()
|
return p.read_bytes()
|
||||||
|
|
||||||
|
|
||||||
|
def _health(http_client):
|
||||||
|
r = http_client.get("/health")
|
||||||
|
r.raise_for_status()
|
||||||
|
return r.json()
|
||||||
|
|
||||||
|
|
||||||
|
def _health_ai_active(data: dict) -> bool:
|
||||||
|
return data.get("aiAvailability") not in ("None", "Downloading", "Error")
|
||||||
|
|
||||||
|
|
||||||
|
def _wait_for_ai_active(http_client, timeout: float = 30) -> dict | None:
|
||||||
|
deadline = time.time() + timeout
|
||||||
|
last = None
|
||||||
|
while time.time() < deadline:
|
||||||
|
last = _health(http_client)
|
||||||
|
if _health_ai_active(last):
|
||||||
|
return last
|
||||||
|
time.sleep(1)
|
||||||
|
return last
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def media_dir():
|
def media_dir():
|
||||||
return str(_media_dir())
|
return str(_media_dir())
|
||||||
@@ -280,11 +302,20 @@ def warm_engine(http_client, image_small, auth_headers):
|
|||||||
consecutive_errors = 0
|
consecutive_errors = 0
|
||||||
|
|
||||||
while time.time() < deadline:
|
while time.time() < deadline:
|
||||||
|
try:
|
||||||
|
current_health = _health(http_client)
|
||||||
|
if _health_ai_active(current_health):
|
||||||
|
return
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
|
||||||
cid = str(uuid.uuid4())
|
cid = str(uuid.uuid4())
|
||||||
headers = {**auth_headers, "X-Channel-Id": cid}
|
headers = {**auth_headers, "X-Channel-Id": cid}
|
||||||
done = threading.Event()
|
done = threading.Event()
|
||||||
|
terminal_status = None
|
||||||
|
|
||||||
def _listen(cid=cid):
|
def _listen(cid=cid):
|
||||||
|
nonlocal terminal_status
|
||||||
try:
|
try:
|
||||||
with http_client.get(
|
with http_client.get(
|
||||||
f"/detect/events/{cid}",
|
f"/detect/events/{cid}",
|
||||||
@@ -297,7 +328,8 @@ def warm_engine(http_client, image_small, auth_headers):
|
|||||||
if not ev.data or not str(ev.data).strip():
|
if not ev.data or not str(ev.data).strip():
|
||||||
continue
|
continue
|
||||||
data = json.loads(ev.data)
|
data = json.loads(ev.data)
|
||||||
if data.get("mediaStatus") == "AIProcessed":
|
terminal_status = data.get("mediaStatus")
|
||||||
|
if terminal_status in ("AIProcessed", "Error"):
|
||||||
break
|
break
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
@@ -316,9 +348,19 @@ def warm_engine(http_client, image_small, auth_headers):
|
|||||||
)
|
)
|
||||||
last_status = r.status_code
|
last_status = r.status_code
|
||||||
if r.status_code == 202:
|
if r.status_code == 202:
|
||||||
done.wait(timeout=30)
|
completed = done.wait(timeout=30)
|
||||||
th.join(timeout=1)
|
th.join(timeout=1)
|
||||||
return
|
if completed and terminal_status == "AIProcessed":
|
||||||
|
if _health_ai_active(_wait_for_ai_active(http_client, timeout=30) or {}):
|
||||||
|
return
|
||||||
|
if completed and terminal_status == "Error":
|
||||||
|
consecutive_errors += 1
|
||||||
|
else:
|
||||||
|
active_health = _wait_for_ai_active(http_client, timeout=10)
|
||||||
|
if _health_ai_active(active_health or {}):
|
||||||
|
return
|
||||||
|
time.sleep(2)
|
||||||
|
continue
|
||||||
if r.status_code >= 500:
|
if r.status_code >= 500:
|
||||||
consecutive_errors += 1
|
consecutive_errors += 1
|
||||||
if consecutive_errors >= 5:
|
if consecutive_errors >= 5:
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ services:
|
|||||||
ANNOTATIONS_URL: http://mock-annotations:8081
|
ANNOTATIONS_URL: http://mock-annotations:8081
|
||||||
JWT_SECRET: test-secret-e2e-only
|
JWT_SECRET: test-secret-e2e-only
|
||||||
CLASSES_JSON_PATH: /app/classes.json
|
CLASSES_JSON_PATH: /app/classes.json
|
||||||
|
LOG_DIR: /app/Logs
|
||||||
volumes:
|
volumes:
|
||||||
- ./fixtures:/media
|
- ./fixtures:/media
|
||||||
- detections-logs:/app/Logs
|
- detections-logs:/app/Logs
|
||||||
@@ -50,6 +51,7 @@ services:
|
|||||||
ANNOTATIONS_URL: http://mock-annotations:8081
|
ANNOTATIONS_URL: http://mock-annotations:8081
|
||||||
JWT_SECRET: test-secret-e2e-only
|
JWT_SECRET: test-secret-e2e-only
|
||||||
CLASSES_JSON_PATH: /app/classes.json
|
CLASSES_JSON_PATH: /app/classes.json
|
||||||
|
LOG_DIR: /app/Logs
|
||||||
volumes:
|
volumes:
|
||||||
- ./fixtures:/media
|
- ./fixtures:/media
|
||||||
- detections-logs:/app/Logs
|
- detections-logs:/app/Logs
|
||||||
@@ -73,6 +75,7 @@ services:
|
|||||||
ANNOTATIONS_URL: http://mock-annotations:8081
|
ANNOTATIONS_URL: http://mock-annotations:8081
|
||||||
JWT_SECRET: test-secret-e2e-only
|
JWT_SECRET: test-secret-e2e-only
|
||||||
CLASSES_JSON_PATH: /app/classes.json
|
CLASSES_JSON_PATH: /app/classes.json
|
||||||
|
LOG_DIR: /app/Logs
|
||||||
volumes:
|
volumes:
|
||||||
- ./fixtures:/media:ro
|
- ./fixtures:/media:ro
|
||||||
- detections-logs:/app/Logs
|
- detections-logs:/app/Logs
|
||||||
|
|||||||
@@ -29,11 +29,11 @@ def test_ft_p08_immediate_async_response(
|
|||||||
assert r.status_code == 202
|
assert r.status_code == 202
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.timeout(10)
|
@pytest.mark.timeout(30)
|
||||||
def test_ft_p09_sse_event_delivery(
|
def test_ft_p09_sse_event_delivery(
|
||||||
warm_engine, http_client, jwt_token
|
warm_engine, http_client, jwt_token
|
||||||
):
|
):
|
||||||
media_id = f"sse-{uuid.uuid4().hex}"
|
media_id = f"event-{uuid.uuid4().hex}"
|
||||||
channel_id = str(uuid.uuid4())
|
channel_id = str(uuid.uuid4())
|
||||||
body = _ai_config_video()
|
body = _ai_config_video()
|
||||||
auth_header = {"Authorization": f"Bearer {jwt_token}"}
|
auth_header = {"Authorization": f"Bearer {jwt_token}"}
|
||||||
|
|||||||
@@ -409,6 +409,7 @@ async def detect_events(channel_id: str, request: Request, after_ts: Optional[in
|
|||||||
|
|
||||||
async def event_generator():
|
async def event_generator():
|
||||||
try:
|
try:
|
||||||
|
yield ": connected\n\n"
|
||||||
if after_ts is not None:
|
if after_ts is not None:
|
||||||
for ts_ms, data in list(_channel_buffers.get(channel_id, [])):
|
for ts_ms, data in list(_channel_buffers.get(channel_id, [])):
|
||||||
if ts_ms > after_ts:
|
if ts_ms > after_ts:
|
||||||
|
|||||||
Reference in New Issue
Block a user