mirror of
https://github.com/jkingsman/Remote-Terminal-for-MeshCore.git
synced 2026-03-28 17:43:05 +01:00
117 lines
3.7 KiB
Python
117 lines
3.7 KiB
Python
from contextlib import asynccontextmanager
|
|
from unittest.mock import AsyncMock
|
|
|
|
import pytest
|
|
from fastapi import HTTPException
|
|
|
|
from app.services.radio_runtime import RadioRuntime
|
|
|
|
|
|
class _Manager:
|
|
def __init__(
|
|
self,
|
|
*,
|
|
meshcore=None,
|
|
is_connected=False,
|
|
is_reconnecting=False,
|
|
is_setup_in_progress=False,
|
|
is_setup_complete=False,
|
|
connection_info=None,
|
|
path_hash_mode=0,
|
|
path_hash_mode_supported=False,
|
|
):
|
|
self.meshcore = meshcore
|
|
self.is_connected = is_connected
|
|
self.is_reconnecting = is_reconnecting
|
|
self.is_setup_in_progress = is_setup_in_progress
|
|
self.is_setup_complete = is_setup_complete
|
|
self.connection_info = connection_info
|
|
self.path_hash_mode = path_hash_mode
|
|
self.path_hash_mode_supported = path_hash_mode_supported
|
|
self.calls: list[tuple[str, dict]] = []
|
|
|
|
@asynccontextmanager
|
|
async def radio_operation(self, name: str, **kwargs):
|
|
self.calls.append((name, kwargs))
|
|
yield self.meshcore
|
|
|
|
|
|
def test_uses_latest_manager_from_getter():
|
|
first = _Manager(meshcore="mc1", is_connected=True, connection_info="first")
|
|
second = _Manager(meshcore="mc2", is_connected=True, connection_info="second")
|
|
current = {"manager": first}
|
|
runtime = RadioRuntime(lambda: current["manager"])
|
|
|
|
assert runtime.connection_info == "first"
|
|
assert runtime.require_connected() == "mc1"
|
|
|
|
current["manager"] = second
|
|
|
|
assert runtime.connection_info == "second"
|
|
assert runtime.require_connected() == "mc2"
|
|
|
|
|
|
def test_require_connected_preserves_http_semantics():
|
|
runtime = RadioRuntime(
|
|
_Manager(meshcore=None, is_connected=True, is_setup_in_progress=True),
|
|
)
|
|
with pytest.raises(HTTPException, match="Radio is initializing") as exc:
|
|
runtime.require_connected()
|
|
assert exc.value.status_code == 503
|
|
|
|
runtime = RadioRuntime(_Manager(meshcore=None, is_connected=False, is_setup_in_progress=False))
|
|
with pytest.raises(HTTPException, match="Radio not connected") as exc:
|
|
runtime.require_connected()
|
|
assert exc.value.status_code == 503
|
|
|
|
|
|
def test_require_connected_returns_fresh_meshcore_after_connectivity_check():
|
|
old_meshcore = object()
|
|
new_meshcore = object()
|
|
|
|
class _SwappingManager:
|
|
def __init__(self):
|
|
self._meshcore = old_meshcore
|
|
self.is_setup_in_progress = False
|
|
|
|
@property
|
|
def is_connected(self):
|
|
self._meshcore = new_meshcore
|
|
return True
|
|
|
|
@property
|
|
def meshcore(self):
|
|
return self._meshcore
|
|
|
|
runtime = RadioRuntime(_SwappingManager())
|
|
|
|
assert runtime.require_connected() is new_meshcore
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_radio_operation_delegates_to_current_manager():
|
|
manager = _Manager(meshcore="meshcore", is_connected=True)
|
|
runtime = RadioRuntime(manager)
|
|
|
|
async with runtime.radio_operation("sync_contacts", pause_polling=True) as mc:
|
|
assert mc == "meshcore"
|
|
|
|
assert manager.calls == [("sync_contacts", {"pause_polling": True})]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_lifecycle_passthrough_methods_delegate_to_current_manager():
|
|
manager = _Manager(meshcore="meshcore", is_connected=True)
|
|
manager.start_connection_monitor = AsyncMock()
|
|
manager.stop_connection_monitor = AsyncMock()
|
|
manager.disconnect = AsyncMock()
|
|
runtime = RadioRuntime(manager)
|
|
|
|
await runtime.start_connection_monitor()
|
|
await runtime.stop_connection_monitor()
|
|
await runtime.disconnect()
|
|
|
|
manager.start_connection_monitor.assert_awaited_once()
|
|
manager.stop_connection_monitor.assert_awaited_once()
|
|
manager.disconnect.assert_awaited_once()
|