73 lines
1.9 KiB
Python
73 lines
1.9 KiB
Python
"""Tests for the optional MJPEG web viewer (stdlib only -- no cv2 / hardware).
|
|
|
|
Binds an ephemeral localhost port, hits each route, and checks the framing.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import time
|
|
import urllib.request
|
|
|
|
import pytest
|
|
|
|
from gowelcome.web import MjpegServer
|
|
|
|
# A minimal valid JPEG (SOI ... EOI) used as the stub frame.
|
|
_FAKE_JPEG = bytes.fromhex("ffd8ffe000104a46494600010100000100010000ffd9")
|
|
|
|
|
|
@pytest.fixture()
|
|
def server():
|
|
srv = MjpegServer(lambda: _FAKE_JPEG, host="127.0.0.1", port=0, fps=30)
|
|
srv.start()
|
|
time.sleep(0.2)
|
|
try:
|
|
yield srv
|
|
finally:
|
|
srv.stop()
|
|
|
|
|
|
def _get(port: int, path: str, n: int | None = None):
|
|
resp = urllib.request.urlopen(f"http://127.0.0.1:{port}{path}", timeout=3)
|
|
return resp.status, resp.headers.get("Content-Type"), (resp.read(n) if n else resp.read())
|
|
|
|
|
|
def test_healthz(server):
|
|
status, ctype, body = _get(server.port, "/healthz")
|
|
assert status == 200
|
|
assert body == b"ok"
|
|
|
|
|
|
def test_index_embeds_stream(server):
|
|
status, ctype, body = _get(server.port, "/")
|
|
assert status == 200
|
|
assert "text/html" in ctype
|
|
assert b"/stream.mjpg" in body
|
|
|
|
|
|
def test_snapshot_returns_jpeg(server):
|
|
status, ctype, body = _get(server.port, "/snapshot.jpg")
|
|
assert status == 200
|
|
assert ctype == "image/jpeg"
|
|
assert body[:2] == b"\xff\xd8" # JPEG SOI
|
|
|
|
|
|
def test_stream_is_multipart_mjpeg(server):
|
|
resp = urllib.request.urlopen(f"http://127.0.0.1:{server.port}/stream.mjpg", timeout=3)
|
|
assert "multipart/x-mixed-replace" in resp.headers.get("Content-Type", "")
|
|
chunk = resp.read(400)
|
|
resp.close()
|
|
assert b"--gowelcomeframe" in chunk
|
|
assert b"image/jpeg" in chunk
|
|
|
|
|
|
def test_unknown_path_404(server):
|
|
with pytest.raises(urllib.error.HTTPError) as exc:
|
|
_get(server.port, "/nope")
|
|
assert exc.value.code == 404
|
|
|
|
|
|
def test_stop_is_idempotent(server):
|
|
server.stop()
|
|
server.stop() # must not raise
|