Files
roboco/tests/unit/runtime/test_board_program_loop.py
T

62 lines
2.1 KiB
Python
Raw Normal View History

"""The generic Board Program orchestrator loop — replaces
``_roadmap_engine_loop`` / ``_x_feature_spotlight_loop`` (see
tests/unit/services/test_board_program_engine.py for the engine's own
trigger/dedup/LEARN coverage; this file covers the orchestrator-loop shell
only: interval computation + the sleep/tick/heartbeat wiring).
Unlike the two collapsed loops, ``_board_program_loop`` has no single static
disablement gate — each program's enablement is checked per-tick inside
``BoardProgramEngine``, DB-backed — so there is no "returns immediately when
disabled" behavior to test here; that guarantee now lives in
test_board_program_engine.py's disabled-program coverage.
"""
from __future__ import annotations
from typing import Any
from unittest.mock import AsyncMock, patch
import pytest
from roboco.runtime.orchestrator import AgentOrchestrator
TWO_TICKS = 2
ONE_HOUR_SECONDS = 3600
def _orch() -> Any:
"""Bypass __init__ — the loop helper under test needs only the
heartbeats dict and ``_running``."""
o = AgentOrchestrator.__new__(AgentOrchestrator)
o._loop_heartbeats = {}
o._running = True
return o
def test_board_program_interval_is_shortest_registered_cadence_capped() -> None:
orch = _orch()
interval = orch._board_program_interval_seconds()
# x_feature's 1-day default is the shortest of the two registered
# programs, well above the 300s floor — capped at the 3600s ceiling.
assert interval == ONE_HOUR_SECONDS
@pytest.mark.asyncio
async def test_board_program_loop_one_tick_exception_does_not_crash_loop() -> None:
"""A raising cycle is logged and the loop keeps ticking — mirrors every
other engine loop's ``except Exception: logger.exception(...)`` shape."""
orch = _orch()
calls = {"n": 0}
async def _cycle() -> None:
calls["n"] += 1
if calls["n"] >= TWO_TICKS:
orch._running = False
raise RuntimeError("boom")
orch._run_board_program_cycle = AsyncMock(side_effect=_cycle)
with patch("asyncio.sleep", new=AsyncMock()):
await orch._board_program_loop()
assert calls["n"] == TWO_TICKS