mirror of
https://github.com/agessaman/meshcore-bot.git
synced 2026-11-04 03:52:34 +00:00
fix(core): replace a stale radio-offline flag at startup
bot.radio_offline is written only when the breaker changes state. A run that went offline and was then restarted left 'true' in bot_metadata, and the new process, which starts with no send failures, never transitioned, so nothing overwrote it: the web viewer showed Radio Offline indefinitely while the bot sent normally. The viewer-clear check couldn't help either, since it only runs while the bot itself thinks it's offline. start() now publishes the fresh offline state right after setting bot.initializing, before the early web viewer starts, so the stored flag always belongs to the current run. The zombie flag didn't need this; a successful connect already writes it false.
This commit is contained in:
1 parent
4fb63d91bf
commit
73e7b95140
3 files changed
+33
No files matched your search
@@ -48,6 +48,7 @@ semantic versioning.
|
||||
|
||||
### Fixed
|
||||
|
||||
- The web viewer no longer shows Radio Offline for good after a restart. The bot wrote the offline flag only when it changed, so a `true` left by an earlier run stayed in the database while the new run sent normally, and the banner never cleared. The bot now records its own state at startup, before the viewer starts.
|
||||
- Web viewer keyboard and screen-reader access: contact columns sort from the keyboard and announce their sort order, plugin cards and Realtime packet details open with Enter or Space, and every button and form field on every page has an accessible name (about 300 Plugins settings fields were announced without one).
|
||||
- Web viewer text contrast: status colors used as text (amber, cyan, green, red), muted text, outline buttons, code, log levels and badges now pass WCAG AA in both themes. The worst cases were the dashboard's Gone Quiet count and Contacts' 7-day counts at 1.6:1 and 2.0:1.
|
||||
- Web viewer text no longer drops below 0.7rem (Realtime timestamps were 9.3px), monospace text uses one font stack, and `/config` no longer scrolls sideways on a phone.
|
||||
|
||||
@@ -891,6 +891,12 @@ class MeshCoreBot(ServiceSupervisorMixin, RadioLinkMixin, RadioOfflineBreaker, D
|
||||
except Exception as e:
|
||||
self.logger.debug("Could not set bot.initializing metadata: %s", e)
|
||||
|
||||
# The offline flag in bot_metadata is only written on transitions, so a
|
||||
# 'true' left by an earlier run would keep the viewer's Radio Offline
|
||||
# banner up forever: this process starts with no send failures and never
|
||||
# transitions. Publish the fresh state so the stored flag is this run's.
|
||||
self._publish_offline_state()
|
||||
|
||||
# Start web viewer early (before radio connect) so operators can see the
|
||||
# initializing banner and monitor startup progress
|
||||
if self.web_viewer_integration and self.web_viewer_integration.enabled:
|
||||
|
||||
@@ -928,6 +928,32 @@ class TestRadioOfflineState:
|
||||
bot = self._make_bot(tmp_path)
|
||||
assert bot.is_radio_offline is False
|
||||
|
||||
def test_start_replaces_a_stale_offline_flag_from_an_earlier_run(self, tmp_path):
|
||||
bot = self._make_bot(tmp_path)
|
||||
bot.db_manager.set_metadata('bot.radio_offline', 'true')
|
||||
bot.db_manager.set_metadata('bot.radio_offline_since', '2026-10-15T14:42:58+00:00')
|
||||
|
||||
with patch.object(bot, "connect", return_value=False):
|
||||
asyncio.run(bot.start())
|
||||
|
||||
assert bot.db_manager.get_metadata('bot.radio_offline') == 'false'
|
||||
assert bot.db_manager.get_metadata('bot.radio_offline_since') == ''
|
||||
|
||||
def test_start_publishes_before_the_web_viewer_starts(self, tmp_path):
|
||||
"""The early viewer must not render the stale banner from the first request."""
|
||||
bot = self._make_bot(tmp_path)
|
||||
bot.db_manager.set_metadata('bot.radio_offline', 'true')
|
||||
seen = []
|
||||
viewer = MagicMock(enabled=True)
|
||||
viewer.start_viewer.side_effect = lambda: seen.append(
|
||||
bot.db_manager.get_metadata('bot.radio_offline'))
|
||||
bot.web_viewer_integration = viewer
|
||||
|
||||
with patch.object(bot, "connect", return_value=False):
|
||||
asyncio.run(bot.start())
|
||||
|
||||
assert seen == ['false']
|
||||
|
||||
def test_record_send_failure_increments_counter(self, tmp_path):
|
||||
bot = self._make_bot(tmp_path)
|
||||
bot._record_send_failure()
|
||||
|
||||
Reference in new issue
Block a user