- Add decision.py with worker decision logic (execute_session/wait/start_analysis) - Implement time slot parsing and current slot detection - Implement session deficit calculation per time slot - Implement high severity issue detection - Implement eval completion detection - Add 12 unit tests for decision logic - Add 2 end-to-end tests for complete lifecycle All 798 tests passing.
287 lines
9.2 KiB
Python
287 lines
9.2 KiB
Python
"""Unit tests for worker decision logic."""
|
|
|
|
from datetime import timedelta
|
|
|
|
import pytest
|
|
from sqlmodel import Session
|
|
|
|
from agenteval.intelligent_eval.decision import DecisionType, is_eval_completed, make_decision
|
|
from agenteval.intelligent_eval.models import IntelligentEvalStatus
|
|
from agenteval.storage.db import IntelligentEvalDB, IntelligentEvalSessionDB, utc_now
|
|
|
|
|
|
def test_decision_wait_not_executing(db_session: Session):
|
|
"""Test decision is WAIT when eval is not executing."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.COMPLETED.value,
|
|
)
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
decision = make_decision(eval_db, db_session)
|
|
assert decision.decision_type == DecisionType.WAIT
|
|
assert "不在执行中" in decision.reason
|
|
|
|
|
|
def test_decision_wait_no_plan(db_session: Session):
|
|
"""Test decision is WAIT when eval has no plan."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
started_at=utc_now(),
|
|
)
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
decision = make_decision(eval_db, db_session)
|
|
assert decision.decision_type == DecisionType.WAIT
|
|
assert "缺少计划" in decision.reason
|
|
|
|
|
|
def test_decision_start_analysis_all_completed(db_session: Session):
|
|
"""Test decision is START_ANALYSIS when all sessions completed."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
started_at=utc_now() - timedelta(hours=10),
|
|
)
|
|
eval_db.set_plan({
|
|
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
|
|
"estimated_sessions": 2,
|
|
})
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
# Add 2 completed sessions
|
|
for _ in range(2):
|
|
session_db = IntelligentEvalSessionDB(
|
|
eval_id=eval_db.id,
|
|
target_id="target1",
|
|
status="completed",
|
|
)
|
|
db_session.add(session_db)
|
|
db_session.commit()
|
|
|
|
decision = make_decision(eval_db, db_session)
|
|
assert decision.decision_type == DecisionType.START_ANALYSIS
|
|
assert "所有 2 个会话已完成" in decision.reason
|
|
|
|
|
|
def test_decision_execute_session_slot_deficit(db_session: Session):
|
|
"""Test decision is EXECUTE_SESSION when current slot has deficit."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
started_at=utc_now() - timedelta(hours=8, minutes=30), # 8.5 hours ago
|
|
)
|
|
eval_db.set_plan({
|
|
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
|
|
"estimated_sessions": 2,
|
|
})
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
# Add 1 session created within the 8-10h slot (deficit = 1)
|
|
# Slot starts at started_at + 8h = 0.5h ago
|
|
session_db = IntelligentEvalSessionDB(
|
|
eval_id=eval_db.id,
|
|
target_id="target1",
|
|
status="running",
|
|
created_at=utc_now() - timedelta(minutes=20), # 20 minutes ago, within slot
|
|
)
|
|
db_session.add(session_db)
|
|
db_session.commit()
|
|
|
|
decision = make_decision(eval_db, db_session)
|
|
assert decision.decision_type == DecisionType.EXECUTE_SESSION
|
|
assert "欠账 1 个会话" in decision.reason
|
|
assert decision.context["current_slot"] == "8-10h"
|
|
assert decision.context["deficit"] == 1
|
|
|
|
|
|
def test_decision_execute_session_high_severity(db_session: Session):
|
|
"""Test decision is EXECUTE_SESSION when high severity issue found."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
started_at=utc_now() - timedelta(hours=8, minutes=30),
|
|
)
|
|
eval_db.set_plan({
|
|
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
|
|
"estimated_sessions": 3, # Not all completed yet
|
|
})
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
# Add 2 completed sessions within the slot (no deficit)
|
|
for _ in range(2):
|
|
session_db = IntelligentEvalSessionDB(
|
|
eval_id=eval_db.id,
|
|
target_id="target1",
|
|
status="completed",
|
|
created_at=utc_now() - timedelta(minutes=20), # Within slot
|
|
)
|
|
# One with high severity
|
|
session_db.set_verdict({"severity": "high", "issues": ["critical bug"]})
|
|
db_session.add(session_db)
|
|
db_session.commit()
|
|
|
|
decision = make_decision(eval_db, db_session)
|
|
assert decision.decision_type == DecisionType.EXECUTE_SESSION
|
|
assert "高严重度问题" in decision.reason
|
|
|
|
|
|
def test_decision_wait_no_deficit(db_session: Session):
|
|
"""Test decision is WAIT when no deficit in current slot."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
started_at=utc_now() - timedelta(hours=8, minutes=30),
|
|
)
|
|
eval_db.set_plan({
|
|
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
|
|
"estimated_sessions": 3, # Not all completed yet
|
|
})
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
# Add 2 sessions within the slot (no deficit)
|
|
for _ in range(2):
|
|
session_db = IntelligentEvalSessionDB(
|
|
eval_id=eval_db.id,
|
|
target_id="target1",
|
|
status="running",
|
|
created_at=utc_now() - timedelta(minutes=20), # Within slot
|
|
)
|
|
db_session.add(session_db)
|
|
db_session.commit()
|
|
|
|
decision = make_decision(eval_db, db_session)
|
|
assert decision.decision_type == DecisionType.WAIT
|
|
assert "无欠账" in decision.reason
|
|
|
|
|
|
def test_decision_wait_outside_slots(db_session: Session):
|
|
"""Test decision is WAIT when current time is outside all slots."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
started_at=utc_now() - timedelta(hours=5), # 5 hours ago, outside 8-10h
|
|
)
|
|
eval_db.set_plan({
|
|
"time_distribution": [{"time_slot": "8-10h", "sessions": 2}],
|
|
"estimated_sessions": 2,
|
|
})
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
decision = make_decision(eval_db, db_session)
|
|
assert decision.decision_type == DecisionType.WAIT
|
|
assert "不在任何时段内" in decision.reason
|
|
|
|
|
|
def test_is_eval_completed_not_executing(db_session: Session):
|
|
"""Test is_eval_completed returns False when eval is not executing."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.COMPLETED.value,
|
|
)
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
assert is_eval_completed(eval_db, db_session) is False
|
|
|
|
|
|
def test_is_eval_completed_no_plan(db_session: Session):
|
|
"""Test is_eval_completed returns False when eval has no plan."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
)
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
assert is_eval_completed(eval_db, db_session) is False
|
|
|
|
|
|
def test_is_eval_completed_sessions_not_finished(db_session: Session):
|
|
"""Test is_eval_completed returns False when not all sessions completed."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
)
|
|
eval_db.set_plan({"estimated_sessions": 2})
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
# Add only 1 completed session
|
|
session_db = IntelligentEvalSessionDB(
|
|
eval_id=eval_db.id,
|
|
target_id="target1",
|
|
status="completed",
|
|
)
|
|
db_session.add(session_db)
|
|
db_session.commit()
|
|
|
|
assert is_eval_completed(eval_db, db_session) is False
|
|
|
|
|
|
def test_is_eval_completed_no_report(db_session: Session):
|
|
"""Test is_eval_completed returns False when report not submitted."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
)
|
|
eval_db.set_plan({"estimated_sessions": 2})
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
# Add 2 completed sessions
|
|
for _ in range(2):
|
|
session_db = IntelligentEvalSessionDB(
|
|
eval_id=eval_db.id,
|
|
target_id="target1",
|
|
status="completed",
|
|
)
|
|
db_session.add(session_db)
|
|
db_session.commit()
|
|
|
|
assert is_eval_completed(eval_db, db_session) is False
|
|
|
|
|
|
def test_is_eval_completed_true(db_session: Session):
|
|
"""Test is_eval_completed returns True when all conditions met."""
|
|
eval_db = IntelligentEvalDB(
|
|
name="test",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
)
|
|
eval_db.set_plan({"estimated_sessions": 2})
|
|
eval_db.set_report({"summary": "test report"})
|
|
db_session.add(eval_db)
|
|
db_session.commit()
|
|
|
|
# Add 2 completed sessions
|
|
for _ in range(2):
|
|
session_db = IntelligentEvalSessionDB(
|
|
eval_id=eval_db.id,
|
|
target_id="target1",
|
|
status="completed",
|
|
)
|
|
db_session.add(session_db)
|
|
db_session.commit()
|
|
|
|
assert is_eval_completed(eval_db, db_session) is True
|