"""Stale-assigned requeue tests (worker crash recovery). 方案③ worker 由平台触发 openclaw agent(cron=manual-run,非真实 cron), fault_tolerance 的 stuck 检测不适用;任务可能永久卡 assigned。requeue 把「assigned 超时且评估仍 executing」的任务重置为 pending。 """ from datetime import timedelta from agenteval.intelligent_eval.models import IntelligentEvalStatus from agenteval.intelligent_eval.task_queue import ( STALE_ASSIGNED_MINUTES, requeue_stale_assigned_tasks, ) from agenteval.storage.db import ( IntelligentEvalDB, IntelligentEvalTaskQueueDB, utc_now, ) from sqlmodel import Session def _make_executing_eval(db_session: Session) -> IntelligentEvalDB: ev = IntelligentEvalDB( name="req-eval", target_id="t1", status=IntelligentEvalStatus.EXECUTING.value, started_at=utc_now(), ) db_session.add(ev) db_session.commit() return ev def test_requeue_stale_assigned_task(db_session: Session): ev = _make_executing_eval(db_session) task = IntelligentEvalTaskQueueDB( eval_id=ev.id, status="assigned", priority=1, reason="slot_due", assigned_cron_id="manual-run-x", assigned_at=utc_now() - timedelta(minutes=STALE_ASSIGNED_MINUTES + 5), ) db_session.add(task) db_session.commit() assert requeue_stale_assigned_tasks(db_session) == 1 db_session.refresh(task) assert task.status == "pending" assert task.assigned_cron_id is None assert task.assigned_at is None def test_fresh_assigned_not_requeued(db_session: Session): ev = _make_executing_eval(db_session) task = IntelligentEvalTaskQueueDB( eval_id=ev.id, status="assigned", priority=1, reason="slot_due", assigned_cron_id="manual-run-y", assigned_at=utc_now(), # fresh ) db_session.add(task) db_session.commit() assert requeue_stale_assigned_tasks(db_session) == 0 db_session.refresh(task) assert task.status == "assigned" def test_stale_assigned_non_executing_not_requeued(db_session: Session): ev = IntelligentEvalDB( name="cancelled-eval", target_id="t1", status=IntelligentEvalStatus.CANCELLED.value, ) db_session.add(ev) db_session.commit() task = IntelligentEvalTaskQueueDB( eval_id=ev.id, status="assigned", priority=1, reason="slot_due", assigned_cron_id="manual-run-z", assigned_at=utc_now() - timedelta(minutes=STALE_ASSIGNED_MINUTES + 5), ) db_session.add(task) db_session.commit() assert requeue_stale_assigned_tasks(db_session) == 0 db_session.refresh(task) assert task.status == "assigned" def test_stale_assigned_abandoned_at_attempts_cap(db_session: Session): """ADR-0011:attempts 达到上限后任务置 failed(不再重入队)并补录决策日志。""" from agenteval.intelligent_eval.task_queue import MAX_TASK_ATTEMPTS from agenteval.storage.db import IntelligentEvalDecisionLogDB from sqlmodel import select ev = _make_executing_eval(db_session) task = IntelligentEvalTaskQueueDB( eval_id=ev.id, status="assigned", priority=1, reason="slot_due", assigned_cron_id="manual-run-cap", assigned_at=utc_now() - timedelta(minutes=STALE_ASSIGNED_MINUTES + 5), attempts=MAX_TASK_ATTEMPTS - 1, ) db_session.add(task) db_session.commit() assert requeue_stale_assigned_tasks(db_session) == 0 db_session.refresh(task) assert task.status == "failed" assert task.attempts == MAX_TASK_ATTEMPTS assert task.completed_at is not None assert "放弃" in task.error logs = db_session.exec( select(IntelligentEvalDecisionLogDB).where( IntelligentEvalDecisionLogDB.eval_id == ev.id, IntelligentEvalDecisionLogDB.decision_type == "task_abandoned", ) ).all() assert len(logs) == 1