AgentEvalTool/tests/unit/test_llm_task_lifecycle.py
sinohqb f3a528611e refactor(tasks): route LLM background tasks through TaskRegistry
架构保养第二轮候选 1:分析 / 周期对比 / judge 复核三条 LLM 任务链
收进各自的模块级 TaskRegistry(强引用防 GC、按 id 幂等、shutdown
统一收敛),删除 judge 的 _BACKGROUND_TASKS 私货,start_* 不再返回
无人消费的 Task。启动清理块补两笔 orphan 清扫:滞留的 generating
分析与对比行标记为 failed,与僵尸运行清扫同构。新增 7 个单测。
2026-08-04 09:58:20 +08:00

112 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""LLM 后台任务生命周期直测(架构保养第二轮候选 1
分析 / 对比 / judge 复核三条 LLM 任务链收进各自的 TaskRegistry
强引用防 GC、按 id 幂等、shutdown 统一收敛;启动清扫把滞留的
generating 行标记为 failed。
"""
import asyncio
import pytest
from agenteval.evaluation import analysis, comparison
from agenteval.exploration import judge
from agenteval.storage.repository import (
CampaignAnalysisRepository,
CampaignPeriodComparisonRepository,
)
from sqlmodel import Session, SQLModel, create_engine
@pytest.fixture()
def db_session(tmp_path):
from agenteval.storage.db import ( # noqa: F401
CampaignAnalysisDB,
CampaignDB,
CampaignPeriodComparisonDB,
)
engine = create_engine(
f"sqlite:///{tmp_path / 'llmtasks.db'}",
connect_args={"check_same_thread": False},
)
SQLModel.metadata.create_all(engine)
session = Session(engine)
try:
yield session
finally:
session.close()
engine.dispose()
async def test_start_analysis_registers_in_registry(monkeypatch):
monkeypatch.setattr(analysis, "execute_campaign_analysis", lambda *a, **kw: asyncio.sleep(0))
analysis.start_campaign_analysis("c-1", triggered_by="manual")
assert analysis.analysis_registry.is_running("c-1")
await analysis.analysis_registry.shutdown_all()
assert not analysis.analysis_registry.is_running("c-1")
async def test_start_comparison_registers_in_registry(monkeypatch):
monkeypatch.setattr(comparison, "execute_campaign_comparison", lambda *a, **kw: asyncio.sleep(0))
comparison.start_campaign_comparison("c-2", triggered_by="manual")
assert comparison.comparison_registry.is_running("c-2")
await comparison.comparison_registry.shutdown_all()
async def test_start_judge_registers_in_registry(monkeypatch):
monkeypatch.setattr(judge, "execute_judge_review", lambda *a, **kw: asyncio.sleep(0))
judge.start_judge_review("s-1")
assert judge.judge_registry.is_running("s-1")
await judge.judge_registry.shutdown_all()
async def test_launch_is_idempotent_for_live_id(monkeypatch):
gate = asyncio.Event()
async def hang(*args, **kwargs):
await gate.wait()
monkeypatch.setattr(analysis, "execute_campaign_analysis", hang)
analysis.start_campaign_analysis("c-dup", triggered_by="manual")
analysis.start_campaign_analysis("c-dup", triggered_by="manual")
assert len(analysis.analysis_registry._tasks) == 1
gate.set()
await analysis.analysis_registry.shutdown_all()
async def test_shutdown_all_cancels_hanging_task(monkeypatch):
async def hang(*args, **kwargs):
await asyncio.Event().wait()
monkeypatch.setattr(comparison, "execute_campaign_comparison", hang)
comparison.start_campaign_comparison("c-hang", triggered_by="manual")
assert comparison.comparison_registry.is_running("c-hang")
await comparison.comparison_registry.shutdown_all()
assert not comparison.comparison_registry.is_running("c-hang")
def test_mark_orphans_failed_flips_generating_analysis(db_session):
repo = CampaignAnalysisRepository(db_session)
repo.upsert("c-gen", status="generating", triggered_by="auto")
repo.upsert("c-done", status="completed", result={"ok": True}, triggered_by="auto")
count = repo.mark_orphans_failed()
assert count == 1
assert repo.get_by_campaign("c-gen").status == "failed"
assert repo.get_by_campaign("c-gen").error
assert repo.get_by_campaign("c-done").status == "completed"
def test_mark_orphans_failed_flips_generating_comparison(db_session):
repo = CampaignPeriodComparisonRepository(db_session)
repo.upsert("c-gen", status="generating", baseline_campaign_id="b-1", triggered_by="auto")
repo.upsert("c-done", status="completed", baseline_campaign_id="b-2", result={"ok": True}, triggered_by="auto")
count = repo.mark_orphans_failed()
assert count == 1
assert repo.get_by_campaign("c-gen").status == "failed"
assert repo.get_by_campaign("c-gen").error
assert repo.get_by_campaign("c-done").status == "completed"