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

82 lines
3.0 KiB
Python
Raw Normal View History

"""`_detect_stuck_tasks` must skip CEO-held sources (release_manager / x_post /
x_reply / video_post / PR-review / self-heal-pre-approve) before the age and
issues check. A held artifact sits PENDING by design until the CEO acts on it;
auto-blocking it on a "short description" wedges the held-artifact flow.
"""
from __future__ import annotations
from datetime import UTC, datetime, timedelta
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch
import pytest
from roboco.runtime.orchestrator import AgentOrchestrator
from roboco.services.task import VIDEO_POST_SOURCE
def _make_orch() -> AgentOrchestrator:
"""Bare orchestrator without running __init__ (no settings I/O)."""
return AgentOrchestrator.__new__(AgentOrchestrator)
def _old_task(tid: str, source: str) -> dict[str, Any]:
"""A PENDING task older than the 10-minute stuck threshold with a short
description — exactly the shape that triggers _auto_block_task today."""
created = (datetime.now(UTC) - timedelta(minutes=30)).isoformat()
return {
"id": tid,
"source": source,
"status": "pending",
"description": "short", # < _MIN_DESCRIPTION_LEN (10)
"branch_name": None,
"assigned_to": None,
"estimated_complexity": "low",
"parent_task_id": None,
"created_at": created,
}
@pytest.mark.asyncio
async def test_held_video_post_skipped_by_detect_stuck_tasks() -> None:
"""A PENDING video_post draft older than the threshold with a short
description must NOT be auto-blocked — it is a CEO-held artifact."""
orch = _make_orch()
held = _old_task("held-1", VIDEO_POST_SOURCE)
client: Any = MagicMock()
with (
patch.object(orch, "_fetch_tasks", new=AsyncMock(return_value=[held])),
patch.object(orch, "_check_dev_subtask_issue", new=AsyncMock(return_value=[])),
patch.object(orch, "_detect_sla_exceeded", new=AsyncMock()),
patch.object(orch, "_auto_block_task", new=AsyncMock()) as auto_block,
):
await orch._detect_stuck_tasks(client)
auto_block.assert_not_awaited()
@pytest.mark.asyncio
async def test_non_held_pending_task_still_auto_blocked() -> None:
"""A non-held PENDING task of the same age + short description is still
auto-blocked — the held-source skip must not over-widen."""
orch = _make_orch()
normal = _old_task("normal-1", "manual")
client: Any = MagicMock()
with (
patch.object(orch, "_fetch_tasks", new=AsyncMock(return_value=[normal])),
patch.object(orch, "_check_dev_subtask_issue", new=AsyncMock(return_value=[])),
patch.object(orch, "_detect_sla_exceeded", new=AsyncMock()),
patch.object(orch, "_auto_block_task", new=AsyncMock()) as auto_block,
):
await orch._detect_stuck_tasks(client)
auto_block.assert_awaited_once()
assert auto_block.await_args is not None
assert auto_block.await_args.args[1] == "normal-1"
if __name__ == "__main__":
pytest.main([__file__, "-q"])