Config accepts structured host/port/channel/username/password (or the existing rtsp_url_env) and builds the RTSP URL with RFC 3986 percent- encoded credentials, so passwords containing @/:/ no longer break parsing. Credentials are excluded from config_version; a guard test keeps the public example credential-free. probe_stream does a bounded connection test. 59 tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
50 lines
1.4 KiB
Python
50 lines
1.4 KiB
Python
import numpy as np
|
|
|
|
from v1.camera import build_rtsp_url, probe_stream
|
|
|
|
|
|
class _FakeCapture:
|
|
def __init__(self, frames):
|
|
self._frames = list(frames) if frames is not None else None
|
|
|
|
def isOpened(self):
|
|
return self._frames is not None
|
|
|
|
def read(self):
|
|
if self._frames:
|
|
return True, self._frames.pop(0)
|
|
return False, None
|
|
|
|
def release(self):
|
|
pass
|
|
|
|
|
|
def test_build_rtsp_url_percent_encodes_special_characters_in_password():
|
|
# Example TEST-NET address and a synthetic password (never a real credential).
|
|
url = build_rtsp_url("192.0.2.10", 554, "admin", "p@ss/w:d", "102")
|
|
|
|
assert url == "rtsp://admin:p%40ss%2Fw%3Ad@192.0.2.10:554/Streaming/Channels/102"
|
|
|
|
|
|
def test_build_rtsp_url_defaults_to_main_channel():
|
|
url = build_rtsp_url("192.0.2.10", 554, "admin", "secret")
|
|
|
|
assert url.endswith("/Streaming/Channels/101")
|
|
|
|
|
|
def test_probe_stream_returns_a_frame_on_success():
|
|
frame = np.zeros((480, 640, 3), dtype=np.uint8)
|
|
|
|
result = probe_stream("rtsp://x", capture_factory=lambda _u: _FakeCapture([frame]))
|
|
|
|
assert result.ok is True
|
|
assert "640x480" in result.message
|
|
assert result.frame is not None
|
|
|
|
|
|
def test_probe_stream_reports_failure_without_raising():
|
|
result = probe_stream("rtsp://x", attempts=3, capture_factory=lambda _u: _FakeCapture(None))
|
|
|
|
assert result.ok is False
|
|
assert result.frame is None
|