Add node GPS enablement + sourcing. Closes #53.

This commit is contained in:
Jack Kingsman
2026-03-11 22:42:41 -07:00
parent f8e88b3737
commit 6466a5c355
7 changed files with 159 additions and 0 deletions
+34
View File
@@ -32,6 +32,7 @@ def _mock_meshcore_with_info():
mc.commands.set_tx_power = AsyncMock()
mc.commands.set_radio = AsyncMock()
mc.commands.set_path_hash_mode = AsyncMock(return_value=_radio_result())
mc.commands.set_advert_loc_policy = AsyncMock(return_value=_radio_result())
mc.commands.send_appstart = AsyncMock()
mc.commands.import_private_key = AsyncMock(return_value=_radio_result())
return mc
@@ -68,6 +69,39 @@ class TestApplyRadioConfigUpdate:
sync_radio_time_fn.assert_awaited_once_with(mc)
mc.commands.send_appstart.assert_awaited_once()
@pytest.mark.asyncio
async def test_updates_advert_location_source(self):
mc = _mock_meshcore_with_info()
await apply_radio_config_update(
mc,
RadioConfigUpdate(advert_location_source="node_gps"),
path_hash_mode_supported=True,
set_path_hash_mode=MagicMock(),
sync_radio_time_fn=AsyncMock(),
)
mc.commands.set_advert_loc_policy.assert_awaited_once_with(1)
mc.commands.send_appstart.assert_awaited_once()
@pytest.mark.asyncio
async def test_raises_when_radio_rejects_advert_location_source(self):
mc = _mock_meshcore_with_info()
mc.commands.set_advert_loc_policy = AsyncMock(
return_value=_radio_result(EventType.ERROR, {"error": "nope"})
)
with pytest.raises(RadioCommandRejectedError):
await apply_radio_config_update(
mc,
RadioConfigUpdate(advert_location_source="off"),
path_hash_mode_supported=True,
set_path_hash_mode=MagicMock(),
sync_radio_time_fn=AsyncMock(),
)
mc.commands.send_appstart.assert_not_awaited()
@pytest.mark.asyncio
async def test_rejects_unsupported_path_hash_mode(self):
mc = _mock_meshcore_with_info()
+42
View File
@@ -70,12 +70,14 @@ def _mock_meshcore_with_info():
"radio_bw": 62.5,
"radio_sf": 7,
"radio_cr": 5,
"adv_loc_policy": 2,
}
mc.commands = MagicMock()
mc.commands.set_name = AsyncMock()
mc.commands.set_coords = AsyncMock()
mc.commands.set_tx_power = AsyncMock()
mc.commands.set_radio = AsyncMock()
mc.commands.set_advert_loc_policy = AsyncMock(return_value=_radio_result())
mc.commands.send_appstart = AsyncMock()
mc.commands.import_private_key = AsyncMock(return_value=_radio_result())
return mc
@@ -94,6 +96,17 @@ class TestGetRadioConfig:
assert response.lon == 20.0
assert response.radio.freq == 910.525
assert response.radio.cr == 5
assert response.advert_location_source == "saved_coords"
@pytest.mark.asyncio
async def test_maps_node_gps_advert_location_source(self):
mc = _mock_meshcore_with_info()
mc.self_info["adv_loc_policy"] = 1
with patch("app.routers.radio.require_connected", return_value=mc):
response = await get_radio_config()
assert response.advert_location_source == "node_gps"
@pytest.mark.asyncio
async def test_returns_503_when_self_info_missing(self):
@@ -138,6 +151,35 @@ class TestUpdateRadioConfig:
mock_sync_time.assert_awaited_once()
assert result == expected
@pytest.mark.asyncio
async def test_updates_advert_location_source(self):
mc = _mock_meshcore_with_info()
expected = RadioConfigResponse(
public_key="aa" * 32,
name="NodeA",
lat=10.0,
lon=20.0,
tx_power=17,
max_tx_power=22,
radio=RadioSettings(freq=910.525, bw=62.5, sf=7, cr=5),
path_hash_mode=0,
path_hash_mode_supported=False,
advert_location_source="node_gps",
)
with (
patch("app.routers.radio.require_connected", return_value=mc),
patch.object(radio_manager, "_meshcore", mc),
patch("app.routers.radio.sync_radio_time", new_callable=AsyncMock),
patch(
"app.routers.radio.get_radio_config", new_callable=AsyncMock, return_value=expected
),
):
result = await update_radio_config(RadioConfigUpdate(advert_location_source="node_gps"))
mc.commands.set_advert_loc_policy.assert_awaited_once_with(1)
assert result == expected
def test_model_rejects_negative_path_hash_mode(self):
with pytest.raises(ValidationError):
RadioConfigUpdate(path_hash_mode=-1)