Files
meshcore-bot/tests/unit/test_scheduler_submit.py
agessaman b2a04ed8ca refactor(scheduler): one _submit_to_main_loop for the polling thread's fire-and-forget calls
run_scheduler scheduled feed polling, channel, radio and config operations and the feed message queue on the main loop with five copies of run_coroutine_threadsafe plus a done-callback that logged failures. _submit_to_main_loop(coro, message, log_traceback=True) does both; feed polling keeps logging without a traceback, the others with one, and the messages are unchanged. The temporary-event-loop fallbacks are left as they are until their reachability is settled.
2026-10-01 07:14:09 -07:00

72 lines
2.3 KiB
Python

"""MessageScheduler._submit_to_main_loop: fire-and-forget scheduling with failure logging."""
import asyncio
import threading
from unittest.mock import MagicMock
from modules.scheduler import MessageScheduler
def _scheduler_with_loop():
loop = asyncio.new_event_loop()
thread = threading.Thread(target=loop.run_forever, daemon=True)
thread.start()
sched = object.__new__(MessageScheduler)
sched.bot = MagicMock(main_event_loop=loop)
sched.logger = MagicMock()
return sched, loop, thread
async def _boom():
raise RuntimeError("nope")
async def _ok():
return 5
def test_failure_is_logged_with_traceback_by_default():
sched, loop, thread = _scheduler_with_loop()
try:
fut = sched._submit_to_main_loop(_boom(), "Error processing radio operations")
try:
fut.result(timeout=5)
except RuntimeError:
pass
loop.call_soon_threadsafe(lambda: None)
# done callbacks run in the loop thread right after completion
for _ in range(50):
if sched.logger.exception.called:
break
threading.Event().wait(0.01)
args = sched.logger.exception.call_args[0]
assert args[0] == "Error processing radio operations: %s"
assert str(args[1]) == "nope"
sched.logger.error.assert_not_called()
finally:
loop.call_soon_threadsafe(loop.stop)
thread.join(2)
def test_plain_error_when_traceback_off_and_silence_on_success():
sched, loop, thread = _scheduler_with_loop()
try:
fut = sched._submit_to_main_loop(_boom(), "Error in feed polling cycle", log_traceback=False)
try:
fut.result(timeout=5)
except RuntimeError:
pass
for _ in range(50):
if sched.logger.error.called:
break
threading.Event().wait(0.01)
assert sched.logger.error.call_args[0][0] == "Error in feed polling cycle: %s"
sched.logger.reset_mock()
assert sched._submit_to_main_loop(_ok(), "x").result(timeout=5) == 5
threading.Event().wait(0.05)
sched.logger.error.assert_not_called()
sched.logger.exception.assert_not_called()
finally:
loop.call_soon_threadsafe(loop.stop)
thread.join(2)