|
| 1 | +"""Tests for the AC_remote_* executor commands and registry singleton.""" |
| 2 | +import time |
| 3 | + |
| 4 | +import pytest |
| 5 | + |
| 6 | +from je_auto_control.utils.executor.action_executor import executor |
| 7 | +from je_auto_control.utils.remote_desktop.registry import registry |
| 8 | + |
| 9 | + |
| 10 | +@pytest.fixture(autouse=True) |
| 11 | +def reset_registry(): |
| 12 | + """Tear down any leftover host/viewer before and after each test.""" |
| 13 | + registry.disconnect_viewer() |
| 14 | + registry.stop_host() |
| 15 | + yield |
| 16 | + registry.disconnect_viewer() |
| 17 | + registry.stop_host() |
| 18 | + |
| 19 | + |
| 20 | +def _wait_until(predicate, timeout: float = 2.0, |
| 21 | + interval: float = 0.02) -> bool: |
| 22 | + deadline = time.monotonic() + timeout |
| 23 | + while time.monotonic() < deadline: |
| 24 | + if predicate(): |
| 25 | + return True |
| 26 | + time.sleep(interval) |
| 27 | + return predicate() |
| 28 | + |
| 29 | + |
| 30 | +def test_known_commands_include_remote_desktop(): |
| 31 | + assert "AC_start_remote_host" in executor.known_commands() |
| 32 | + assert "AC_stop_remote_host" in executor.known_commands() |
| 33 | + assert "AC_remote_host_status" in executor.known_commands() |
| 34 | + assert "AC_remote_connect" in executor.known_commands() |
| 35 | + assert "AC_remote_disconnect" in executor.known_commands() |
| 36 | + assert "AC_remote_viewer_status" in executor.known_commands() |
| 37 | + assert "AC_remote_send_input" in executor.known_commands() |
| 38 | + |
| 39 | + |
| 40 | +def test_start_host_then_status_via_executor(): |
| 41 | + captured = [] |
| 42 | + |
| 43 | + def stub_provider() -> bytes: |
| 44 | + return b"test-frame" |
| 45 | + |
| 46 | + # Reach into the registry to install a stub provider so this test |
| 47 | + # never touches PIL.ImageGrab; mirrors what GUI would do for a fake. |
| 48 | + from je_auto_control.utils.remote_desktop.host import RemoteDesktopHost |
| 49 | + |
| 50 | + host = RemoteDesktopHost( |
| 51 | + token="t", bind="127.0.0.1", port=0, fps=50.0, quality=70, |
| 52 | + frame_provider=stub_provider, input_dispatcher=captured.append, |
| 53 | + ) |
| 54 | + host.start() |
| 55 | + registry._host = host # noqa: SLF001 # test-only injection |
| 56 | + |
| 57 | + record = executor.execute_action([["AC_remote_host_status"]]) |
| 58 | + status_value = next(iter(record.values())) |
| 59 | + assert status_value["running"] is True |
| 60 | + assert status_value["port"] > 0 |
| 61 | + |
| 62 | + |
| 63 | +def test_start_host_with_blank_token_records_error(): |
| 64 | + record = executor.execute_action([ |
| 65 | + ["AC_start_remote_host", {"token": ""}], |
| 66 | + ]) |
| 67 | + assert any("ValueError" in repr(v) for v in record.values()) |
| 68 | + |
| 69 | + |
| 70 | +def test_send_input_without_viewer_records_connection_error(): |
| 71 | + record = executor.execute_action([ |
| 72 | + ["AC_remote_send_input", {"action": {"action": "ping"}}], |
| 73 | + ]) |
| 74 | + assert any("ConnectionError" in repr(v) for v in record.values()) |
| 75 | + |
| 76 | + |
| 77 | +def test_remote_round_trip_through_executor(): |
| 78 | + """Start host + connect viewer + send input via executor commands.""" |
| 79 | + record = executor.execute_action([ |
| 80 | + ["AC_start_remote_host", { |
| 81 | + "token": "tok", "bind": "127.0.0.1", "port": 0, |
| 82 | + "fps": 50, "quality": 70, |
| 83 | + }], |
| 84 | + ]) |
| 85 | + start_status = next(iter(record.values())) |
| 86 | + assert start_status["running"] is True |
| 87 | + port = start_status["port"] |
| 88 | + assert port > 0 |
| 89 | + |
| 90 | + # Replace the default frame provider (PIL.ImageGrab) with a stub so |
| 91 | + # the test does not depend on a real screen being available. |
| 92 | + registry._host._frame_provider = lambda: b"executor-frame" # noqa: SLF001 |
| 93 | + captured = [] |
| 94 | + registry._host._dispatch = captured.append # noqa: SLF001 |
| 95 | + |
| 96 | + executor.execute_action([ |
| 97 | + ["AC_remote_connect", { |
| 98 | + "host": "127.0.0.1", "port": port, "token": "tok", |
| 99 | + }], |
| 100 | + ]) |
| 101 | + viewer_status = registry.viewer_status() |
| 102 | + assert viewer_status["connected"] is True |
| 103 | + assert _wait_until(lambda: registry.host.connected_clients == 1) |
| 104 | + |
| 105 | + executor.execute_action([ |
| 106 | + ["AC_remote_send_input", { |
| 107 | + "action": {"action": "mouse_move", "x": 5, "y": 7}, |
| 108 | + }], |
| 109 | + ]) |
| 110 | + assert _wait_until(lambda: captured == [ |
| 111 | + {"action": "mouse_move", "x": 5, "y": 7} |
| 112 | + ]) |
| 113 | + |
| 114 | + executor.execute_action([["AC_remote_disconnect"]]) |
| 115 | + assert registry.viewer_status()["connected"] is False |
| 116 | + executor.execute_action([["AC_stop_remote_host"]]) |
| 117 | + assert registry.host_status()["running"] is False |
0 commit comments