97 lines
3.2 KiB
Python
97 lines
3.2 KiB
Python
"""Startup cleanup of orphan runs (interrupted by server restart)."""
|
||
|
||
from agenteval.models import Campaign, CampaignPlanEntry, CampaignStatus, EvalRun, RunStatus
|
||
from agenteval.storage.repository import CampaignRepository, RunRepository
|
||
|
||
|
||
def _make_run(session, status: RunStatus) -> str:
|
||
run = RunRepository(session).create(EvalRun(
|
||
target_id="t-1", scenario_id="s-1", status=status,
|
||
))
|
||
return run.id
|
||
|
||
|
||
def test_mark_orphans_failed(db_session):
|
||
repo = RunRepository(db_session)
|
||
running_id = _make_run(db_session, RunStatus.RUNNING)
|
||
pending_id = _make_run(db_session, RunStatus.PENDING)
|
||
completed_id = _make_run(db_session, RunStatus.COMPLETED)
|
||
failed_id = _make_run(db_session, RunStatus.FAILED)
|
||
|
||
count = repo.mark_orphans_failed()
|
||
|
||
assert count == 2
|
||
for rid in (running_id, pending_id):
|
||
run = repo.get(rid)
|
||
assert run.status == RunStatus.FAILED
|
||
assert run.summary.error.code == "interrupted"
|
||
assert run.completed_at is not None
|
||
# 已完结的运行不受影响
|
||
assert repo.get(completed_id).status == RunStatus.COMPLETED
|
||
assert repo.get(failed_id).status == RunStatus.FAILED
|
||
assert repo.get(completed_id).summary is None
|
||
|
||
|
||
def test_mark_orphans_failed_noop_when_clean(db_session):
|
||
repo = RunRepository(db_session)
|
||
_make_run(db_session, RunStatus.COMPLETED)
|
||
assert repo.mark_orphans_failed() == 0
|
||
|
||
|
||
def test_mark_orphans_preserves_only_recoverable_campaign_pending(db_session):
|
||
campaign = CampaignRepository(db_session).create(
|
||
Campaign(
|
||
id="campaign-1",
|
||
name="running",
|
||
target_id="t-1",
|
||
status=CampaignStatus.RUNNING,
|
||
window_seconds=60,
|
||
plan=[CampaignPlanEntry(scenario_id="s-1", offset_seconds=0, count=2)],
|
||
)
|
||
)
|
||
repo = RunRepository(db_session)
|
||
pending = repo.create(
|
||
EvalRun(
|
||
target_id="t-1",
|
||
scenario_id="s-1",
|
||
campaign_id=campaign.id,
|
||
campaign_plan_index=0,
|
||
campaign_occurrence_index=0,
|
||
status=RunStatus.PENDING,
|
||
)
|
||
)
|
||
running = repo.create(
|
||
EvalRun(
|
||
target_id="t-1",
|
||
scenario_id="s-1",
|
||
campaign_id=campaign.id,
|
||
campaign_plan_index=0,
|
||
campaign_occurrence_index=1,
|
||
status=RunStatus.RUNNING,
|
||
)
|
||
)
|
||
|
||
count = repo.mark_orphans_failed()
|
||
|
||
assert count == 1
|
||
assert repo.get(pending.id).status is RunStatus.PENDING
|
||
assert repo.get(running.id).status is RunStatus.FAILED
|
||
|
||
|
||
def test_update_preserves_scenario_version_and_triggered_by(db_session):
|
||
"""update() 不得丢字段:scenario_version / triggered_by 必须回写(漂移回归)。"""
|
||
from agenteval.models import RunTrigger
|
||
|
||
repo = RunRepository(db_session)
|
||
run = repo.create(EvalRun(
|
||
target_id="t-1", scenario_id="s-1", status=RunStatus.RUNNING,
|
||
scenario_version=4, triggered_by=RunTrigger.AI_ASSISTANT,
|
||
))
|
||
run.scenario_version = 5
|
||
run.triggered_by = RunTrigger.CLI
|
||
run.status = RunStatus.COMPLETED
|
||
updated = repo.update(run)
|
||
|
||
assert updated.scenario_version == 5
|
||
assert updated.triggered_by == RunTrigger.CLI
|