From 338f63251402d7132c4f93e9644ad86ed17cce36 Mon Sep 17 00:00:00 2001 From: Jack Kingsman Date: Thu, 12 Mar 2026 17:33:53 -0700 Subject: [PATCH] Remove unused endpoint and fix stale slot retry problems --- app/radio.py | 9 +++++++++ app/services/message_send.py | 5 ++++- tests/test_send_messages.py | 26 ++++++++++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/app/radio.py b/app/radio.py index 89590c2..c467e3a 100644 --- a/app/radio.py +++ b/app/radio.py @@ -306,6 +306,15 @@ class RadioManager: self._channel_slot_by_key.move_to_end(normalized_key) self._channel_key_by_slot[slot] = normalized_key + def invalidate_cached_channel_slot(self, channel_key: str) -> None: + """Drop any cached slot assignment for a channel key.""" + normalized_key = channel_key.upper() + slot = self._channel_slot_by_key.pop(normalized_key, None) + if slot is None: + return + if self._channel_key_by_slot.get(slot) == normalized_key: + self._channel_key_by_slot.pop(slot, None) + def get_channel_send_cache_snapshot(self) -> list[tuple[str, int]]: """Return the current channel send cache contents in LRU order.""" return list(self._channel_slot_by_key.items()) diff --git a/app/services/message_send.py b/app/services/message_send.py index 2f5ae9f..5e90b23 100644 --- a/app/services/message_send.py +++ b/app/services/message_send.py @@ -112,7 +112,10 @@ async def send_channel_message_with_effective_scope( msg=text, timestamp=timestamp_bytes, ) - radio_manager.note_channel_slot_used(channel_key) + if send_result.type == EventType.ERROR: + radio_manager.invalidate_cached_channel_slot(channel_key) + else: + radio_manager.note_channel_slot_used(channel_key) return send_result finally: if override_scope and override_scope != baseline_scope: diff --git a/tests/test_send_messages.py b/tests/test_send_messages.py index 2aacd1e..2cd11d2 100644 --- a/tests/test_send_messages.py +++ b/tests/test_send_messages.py @@ -475,6 +475,32 @@ class TestOutgoingChannelBroadcast: assert mc.commands.set_channel.await_count == 2 assert radio_manager.get_cached_channel_slot(chan_key) is None + @pytest.mark.asyncio + async def test_send_channel_msg_error_invalidates_cached_slot(self, test_db): + mc = _make_mc(name="MyNode") + chan_key = "f1" * 16 + await ChannelRepository.upsert(key=chan_key, name="#stale") + radio_manager.max_channels = 4 + radio_manager._connection_info = "Serial: /dev/ttyUSB0" + + mc.commands.send_chan_msg = AsyncMock( + return_value=MagicMock(type=EventType.ERROR, payload="bad slot") + ) + + with ( + patch("app.routers.messages.require_connected", return_value=mc), + patch.object(radio_manager, "_meshcore", mc), + patch("app.decoder.calculate_channel_hash", return_value="abcd"), + patch("app.routers.messages.broadcast_event"), + pytest.raises(HTTPException) as exc_info, + ): + await send_channel_message( + SendChannelMessageRequest(channel_key=chan_key, text="this will fail") + ) + + assert exc_info.value.status_code == 500 + assert radio_manager.get_cached_channel_slot(chan_key) is None + class TestResendChannelMessage: """Test the user-triggered resend endpoint."""