mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-08-06 00:33:23 +02:00
Add ability to pause radio connection (closes #51)
This commit is contained in:
@@ -56,6 +56,7 @@ class TestHealthFanoutStatus:
|
||||
assert data["status"] == "ok"
|
||||
assert data["radio_connected"] is True
|
||||
assert data["radio_initializing"] is False
|
||||
assert data["radio_state"] == "connected"
|
||||
assert data["connection_info"] == "Serial: /dev/ttyUSB0"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -69,6 +70,7 @@ class TestHealthFanoutStatus:
|
||||
assert data["status"] == "degraded"
|
||||
assert data["radio_connected"] is False
|
||||
assert data["radio_initializing"] is False
|
||||
assert data["radio_state"] == "disconnected"
|
||||
assert data["connection_info"] is None
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@@ -87,3 +89,40 @@ class TestHealthFanoutStatus:
|
||||
assert data["status"] == "degraded"
|
||||
assert data["radio_connected"] is True
|
||||
assert data["radio_initializing"] is True
|
||||
assert data["radio_state"] == "initializing"
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_state_paused_when_operator_disabled_connection(self, test_db):
|
||||
"""Health reports paused when the operator has disabled reconnect attempts."""
|
||||
with (
|
||||
patch(
|
||||
"app.routers.health.RawPacketRepository.get_oldest_undecrypted", return_value=None
|
||||
),
|
||||
patch("app.routers.health.radio_manager") as mock_rm,
|
||||
):
|
||||
mock_rm.is_setup_in_progress = False
|
||||
mock_rm.is_setup_complete = False
|
||||
mock_rm.connection_desired = False
|
||||
mock_rm.is_reconnecting = False
|
||||
data = await build_health_data(False, "BLE: AA:BB:CC:DD:EE:FF")
|
||||
|
||||
assert data["radio_state"] == "paused"
|
||||
assert data["radio_connected"] is False
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_health_state_connecting_while_reconnect_in_progress(self, test_db):
|
||||
"""Health reports connecting while retries are active but transport is not up yet."""
|
||||
with (
|
||||
patch(
|
||||
"app.routers.health.RawPacketRepository.get_oldest_undecrypted", return_value=None
|
||||
),
|
||||
patch("app.routers.health.radio_manager") as mock_rm,
|
||||
):
|
||||
mock_rm.is_setup_in_progress = False
|
||||
mock_rm.is_setup_complete = False
|
||||
mock_rm.connection_desired = True
|
||||
mock_rm.is_reconnecting = True
|
||||
data = await build_health_data(False, None)
|
||||
|
||||
assert data["radio_state"] == "connecting"
|
||||
assert data["radio_connected"] is False
|
||||
|
||||
@@ -300,6 +300,29 @@ class TestConnectionMonitor:
|
||||
|
||||
rm.post_connect_setup.assert_not_called()
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_monitor_does_not_reconnect_when_connection_is_paused(self):
|
||||
"""Operator-paused state suppresses reconnect attempts."""
|
||||
from app.radio import RadioManager
|
||||
|
||||
rm = RadioManager()
|
||||
rm._connection_desired = False
|
||||
rm.reconnect = AsyncMock()
|
||||
rm.post_connect_setup = AsyncMock()
|
||||
|
||||
async def _sleep(_seconds: float):
|
||||
raise asyncio.CancelledError()
|
||||
|
||||
with patch("app.radio.asyncio.sleep", side_effect=_sleep):
|
||||
await rm.start_connection_monitor()
|
||||
try:
|
||||
await rm._reconnect_task
|
||||
finally:
|
||||
await rm.stop_connection_monitor()
|
||||
|
||||
rm.reconnect.assert_not_called()
|
||||
rm.post_connect_setup.assert_not_called()
|
||||
|
||||
|
||||
class TestReconnectLock:
|
||||
"""Tests for reconnect() lock serialization — no duplicate reconnections."""
|
||||
@@ -408,6 +431,24 @@ class TestReconnectLock:
|
||||
assert result2 is True
|
||||
assert attempt == 2
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_reconnect_returns_false_when_connection_is_paused(self):
|
||||
"""Reconnect should no-op when the operator has paused connection attempts."""
|
||||
from app.radio import RadioManager
|
||||
|
||||
rm = RadioManager()
|
||||
rm._connection_desired = False
|
||||
rm.connect = AsyncMock()
|
||||
|
||||
with (
|
||||
patch("app.websocket.broadcast_health"),
|
||||
patch("app.websocket.broadcast_error"),
|
||||
):
|
||||
result = await rm.reconnect(broadcast_on_success=False)
|
||||
|
||||
assert result is False
|
||||
rm.connect.assert_not_called()
|
||||
|
||||
|
||||
class TestSerialDeviceProbe:
|
||||
"""Tests for test_serial_device() — verifies cleanup on all exit paths."""
|
||||
|
||||
@@ -15,6 +15,7 @@ from app.routers.radio import (
|
||||
RadioConfigResponse,
|
||||
RadioConfigUpdate,
|
||||
RadioSettings,
|
||||
disconnect_radio,
|
||||
get_radio_config,
|
||||
reboot_radio,
|
||||
reconnect_radio,
|
||||
@@ -394,3 +395,21 @@ class TestRebootAndReconnect:
|
||||
await reconnect_radio()
|
||||
|
||||
assert exc.value.status_code == 503
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_disconnect_pauses_connection_attempts_and_broadcasts_health(self):
|
||||
mock_rm = MagicMock()
|
||||
mock_rm.pause_connection = AsyncMock()
|
||||
mock_rm.connection_info = "BLE: AA:BB:CC:DD:EE:FF"
|
||||
|
||||
with (
|
||||
patch("app.routers.radio.radio_manager", _runtime(mock_rm)),
|
||||
patch("app.routers.radio.broadcast_health") as mock_broadcast,
|
||||
):
|
||||
result = await disconnect_radio()
|
||||
|
||||
assert result["status"] == "ok"
|
||||
assert result["connected"] is False
|
||||
assert result["paused"] is True
|
||||
mock_rm.pause_connection.assert_awaited_once()
|
||||
mock_broadcast.assert_called_once_with(False, "BLE: AA:BB:CC:DD:EE:FF")
|
||||
|
||||
Reference in New Issue
Block a user