""" AZ-178: True streaming video detection — e2e tests. Both tests upload video_test01.mp4 (12 MB), wait for the first SSE event, then stop. The goal is to prove the service starts and produces detections, not to process the whole file. Run with: pytest e2e/tests/test_streaming_video_upload.py -s -v """ import json import threading import time import uuid from pathlib import Path import pytest import sseclient FIXTURES_DIR = Path(__file__).resolve().parent.parent / "fixtures" _TIMEOUT = 5.0 _STOP_AFTER = 5 def _fixture_path(name: str) -> str: p = FIXTURES_DIR / name if not p.is_file(): pytest.skip(f"missing fixture {p}") return str(p) def _chunked_reader(path: str, chunk_size: int = 64 * 1024): with open(path, "rb") as f: while True: chunk = f.read(chunk_size) if not chunk: break yield chunk def _start_sse_listener( http_client, channel_id: str, auth_headers: dict ) -> tuple[list[dict], list[BaseException], threading.Event]: events: list[dict] = [] errors: list[BaseException] = [] first_event = threading.Event() connected = threading.Event() def _listen(): try: with http_client.get( f"/detect/events/{channel_id}", stream=True, timeout=_TIMEOUT + 2, headers=auth_headers, ) as resp: resp.raise_for_status() connected.set() for event in sseclient.SSEClient(resp).events(): if not event.data or not str(event.data).strip(): continue events.append(json.loads(event.data)) if len(events) >= _STOP_AFTER: first_event.set() break except BaseException as exc: errors.append(exc) finally: connected.set() first_event.set() th = threading.Thread(target=_listen, daemon=True) th.start() connected.wait(timeout=3) return events, errors, first_event @pytest.mark.timeout(10) def test_streaming_video_detections_appear_during_upload( warm_engine, http_client, auth_headers ): # Arrange video_path = _fixture_path("video_test01.mp4") channel_id = str(uuid.uuid4()) events, errors, first_event = _start_sse_listener(http_client, channel_id, auth_headers) # Act r = http_client.post( "/detect/video", data=_chunked_reader(video_path), headers={ **auth_headers, "X-Channel-Id": channel_id, "X-Filename": "video_test01.mp4", "Content-Type": "application/octet-stream", }, timeout=8, ) assert r.status_code == 202 first_event.wait(timeout=_TIMEOUT) # Assert assert not errors, f"SSE thread error: {errors}" assert len(events) >= 1, "Expected at least one SSE event within 5s" print(f"\n First {len(events)} SSE events:") for e in events: print(f" {e}") @pytest.mark.timeout(10) def test_non_faststart_video_still_works(warm_engine, http_client, auth_headers): # Arrange video_path = _fixture_path("video_test01.mp4") channel_id = str(uuid.uuid4()) events, errors, first_event = _start_sse_listener(http_client, channel_id, auth_headers) # Act r = http_client.post( "/detect/video", data=_chunked_reader(video_path), headers={ **auth_headers, "X-Channel-Id": channel_id, "X-Filename": "video_test01_plain.mp4", "Content-Type": "application/octet-stream", }, timeout=8, ) assert r.status_code == 202 first_event.wait(timeout=_TIMEOUT) # Assert assert not errors, f"SSE thread error: {errors}" assert len(events) >= 1, "Expected at least one SSE event within 5s" print(f"\n First {len(events)} SSE events:") for e in events: print(f" {e}")