Skip to content

Commit cdf8de4

Browse files
committed
fix(tests): fix failing test and cover remaining gap to reach 85% (#895)
- test_prompt_manager_additional.py: - Fix TestGetVideoHandler.test_creates_new_instance_when_none: CameraStreamHandler is imported lazily inside _get_video_handler via 'from .camera import CameraStreamHandler', so patch its source module 'th_cli.test_run.camera.CameraStreamHandler', not prompt_manager - Add TestHandleStreamVerificationPrompt (5 new tests covering lines 133-201): stream ready + user answers → sends response; stream not ready with/without init_error → sends CANCELLED; user answer None → no send; empty options → returns early - test_logs_http_server.py: - Add test_debug_log_at_100_entries: streams 100 entries through stream_logs() to hit the 'sent_count % 100 == 0' debug branch (lines 134-150)
1 parent 4b3b60a commit cdf8de4

2 files changed

Lines changed: 139 additions & 1 deletion

File tree

tests/test_run/test_logs_http_server.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -268,6 +268,21 @@ def test_no_log_queue_on_server_returns_early(self):
268268
# Should have sent 200 headers but not crashed
269269
assert h._response_code == 200
270270

271+
def test_debug_log_at_100_entries(self):
272+
"""Covers line 134: `if sent_count % 100 == 0` debug message."""
273+
q = queue.Queue()
274+
# Put 100 log entries then sentinel
275+
for i in range(100):
276+
q.put({"message": f"msg{i}", "level": "INFO", "timestamp": "t"})
277+
q.put(None)
278+
279+
h = _make_handler(server_attrs={"log_queue": q})
280+
with patch("th_cli.test_run.logs_http_server.logger") as mock_logger:
281+
h.stream_logs()
282+
283+
# 100 entries should have triggered the debug log
284+
mock_logger.debug.assert_called()
285+
271286

272287
# ---------------------------------------------------------------------------
273288
# serve_log_viewer()

tests/test_run/test_prompt_manager_additional.py

Lines changed: 124 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,9 @@ def setup_method(self):
135135

136136
def test_creates_new_instance_when_none(self):
137137
mock_handler = MagicMock()
138-
with patch("th_cli.test_run.prompt_manager.CameraStreamHandler", return_value=mock_handler):
138+
# CameraStreamHandler is imported lazily inside _get_video_handler
139+
# via `from .camera import CameraStreamHandler`, so patch the source
140+
with patch("th_cli.test_run.camera.CameraStreamHandler", return_value=mock_handler):
139141
result = _get_video_handler()
140142
assert result is mock_handler
141143

@@ -528,3 +530,124 @@ async def test_sends_ack_response(self):
528530
mock_send.assert_called_once()
529531
call_kwargs = mock_send.call_args[1]
530532
assert call_kwargs.get("response") == "ACK"
533+
534+
535+
# ---------------------------------------------------------------------------
536+
# __handle_stream_verification_prompt (lines 133-201)
537+
# Accessed via handle_prompt routing with STREAM_VERIFICATION_REQUEST type.
538+
# ---------------------------------------------------------------------------
539+
540+
541+
@pytest.mark.unit
542+
class TestHandleStreamVerificationPrompt:
543+
"""Cover __handle_stream_verification_prompt body via handle_prompt routing."""
544+
545+
def _mock_video_handler(self, stream_ready=True, user_answer=1, init_error=None):
546+
vh = MagicMock()
547+
vh.http_server = MagicMock()
548+
vh.http_server.port = 8999
549+
vh.set_prompt_data = MagicMock()
550+
vh.start_video_capture_and_stream = AsyncMock(return_value=MagicMock())
551+
vh.wait_for_stream_ready = AsyncMock(return_value=stream_ready)
552+
vh.initialization_error = init_error
553+
vh.wait_for_user_response = AsyncMock(return_value=user_answer)
554+
vh.stop_video_capture_and_stream = AsyncMock(return_value=None)
555+
return vh
556+
557+
def setup_method(self):
558+
pm_module._video_handler_instance = None
559+
560+
def teardown_method(self):
561+
pm_module._video_handler_instance = None
562+
563+
@pytest.mark.asyncio
564+
async def test_sends_response_when_stream_ready_and_user_answers(self):
565+
prompt = _make_stream_prompt()
566+
mock_socket = AsyncMock()
567+
vh = self._mock_video_handler(stream_ready=True, user_answer=1)
568+
pm_module._video_handler_instance = vh
569+
570+
with patch("th_cli.test_run.prompt_manager._send_prompt_response", new_callable=AsyncMock) as mock_send:
571+
with patch("th_cli.test_run.prompt_manager._get_local_ip", return_value="10.0.0.1"):
572+
with patch("th_cli.test_run.prompt_manager.click.echo"):
573+
await handle_prompt(
574+
socket=mock_socket,
575+
request=prompt,
576+
message_type=MessageTypeEnum.STREAM_VERIFICATION_REQUEST,
577+
)
578+
579+
mock_send.assert_called_once()
580+
581+
@pytest.mark.asyncio
582+
async def test_sends_cancelled_when_stream_not_ready(self):
583+
prompt = _make_stream_prompt()
584+
mock_socket = AsyncMock()
585+
vh = self._mock_video_handler(stream_ready=False, init_error="FFmpeg not found")
586+
pm_module._video_handler_instance = vh
587+
588+
with patch("th_cli.test_run.prompt_manager._send_prompt_response", new_callable=AsyncMock) as mock_send:
589+
with patch("th_cli.test_run.prompt_manager._get_local_ip", return_value="10.0.0.1"):
590+
with patch("th_cli.test_run.prompt_manager.click.echo"):
591+
await handle_prompt(
592+
socket=mock_socket,
593+
request=prompt,
594+
message_type=MessageTypeEnum.STREAM_VERIFICATION_REQUEST,
595+
)
596+
597+
mock_send.assert_called_once()
598+
call_kwargs = mock_send.call_args[1]
599+
from th_cli.test_run.socket_schemas import UserResponseStatusEnum
600+
assert call_kwargs.get("status_code") == UserResponseStatusEnum.CANCELLED
601+
602+
@pytest.mark.asyncio
603+
async def test_sends_cancelled_when_stream_not_ready_no_init_error(self):
604+
prompt = _make_stream_prompt()
605+
mock_socket = AsyncMock()
606+
vh = self._mock_video_handler(stream_ready=False, init_error=None)
607+
pm_module._video_handler_instance = vh
608+
609+
with patch("th_cli.test_run.prompt_manager._send_prompt_response", new_callable=AsyncMock) as mock_send:
610+
with patch("th_cli.test_run.prompt_manager._get_local_ip", return_value="10.0.0.1"):
611+
with patch("th_cli.test_run.prompt_manager.click.echo"):
612+
await handle_prompt(
613+
socket=mock_socket,
614+
request=prompt,
615+
message_type=MessageTypeEnum.STREAM_VERIFICATION_REQUEST,
616+
)
617+
618+
mock_send.assert_called_once()
619+
620+
@pytest.mark.asyncio
621+
async def test_no_send_when_user_answer_is_none(self):
622+
prompt = _make_stream_prompt()
623+
mock_socket = AsyncMock()
624+
vh = self._mock_video_handler(stream_ready=True, user_answer=None)
625+
pm_module._video_handler_instance = vh
626+
627+
with patch("th_cli.test_run.prompt_manager._send_prompt_response", new_callable=AsyncMock) as mock_send:
628+
with patch("th_cli.test_run.prompt_manager._get_local_ip", return_value="10.0.0.1"):
629+
with patch("th_cli.test_run.prompt_manager.click.echo"):
630+
await handle_prompt(
631+
socket=mock_socket,
632+
request=prompt,
633+
message_type=MessageTypeEnum.STREAM_VERIFICATION_REQUEST,
634+
)
635+
636+
mock_send.assert_not_called()
637+
638+
@pytest.mark.asyncio
639+
async def test_missing_options_returns_early(self):
640+
"""Covers line 136 — options missing/empty."""
641+
prompt_no_opts = MagicMock(spec=StreamVerificationPromptRequest)
642+
prompt_no_opts.options = {}
643+
mock_socket = AsyncMock()
644+
645+
with patch("th_cli.test_run.prompt_manager._send_prompt_response", new_callable=AsyncMock) as mock_send:
646+
with patch("th_cli.test_run.prompt_manager.click.echo"):
647+
await handle_prompt(
648+
socket=mock_socket,
649+
request=prompt_no_opts,
650+
message_type=MessageTypeEnum.STREAM_VERIFICATION_REQUEST,
651+
)
652+
653+
mock_send.assert_not_called()

0 commit comments

Comments
 (0)