- Add metrics.py with pool utilization, task backlog, stuck rate, avg processing time, eval completion rate - Add alerts.py with alert rules (pool utilization > 90%, task backlog > 50, stuck rate > 10%) - Implement alert history and webhook notifications - Add metrics and alerts APIs - Add database migration for alert history table - Add 11 unit tests for metrics, 10 unit tests for alerts, 8 integration tests - Update migration tests to include new alert history table All 853 tests passing.
214 lines
6.0 KiB
Python
214 lines
6.0 KiB
Python
"""Unit tests for metrics calculation."""
|
|
|
|
from datetime import timedelta
|
|
|
|
import pytest
|
|
from sqlmodel import Session
|
|
|
|
from agenteval.intelligent_eval import metrics
|
|
from agenteval.intelligent_eval.models import IntelligentEvalStatus
|
|
from agenteval.storage.db import (
|
|
IntelligentEvalDB,
|
|
IntelligentEvalTaskQueueDB,
|
|
OpenClawCronPoolDB,
|
|
utc_now,
|
|
)
|
|
|
|
|
|
def test_calculate_pool_utilization_empty(db_session: Session):
|
|
"""Test pool utilization when no crons exist."""
|
|
utilization = metrics.calculate_pool_utilization(db_session)
|
|
assert utilization == 0.0
|
|
|
|
|
|
def test_calculate_pool_utilization(db_session: Session):
|
|
"""Test pool utilization calculation."""
|
|
# Create 10 crons: 6 busy, 4 idle
|
|
for i in range(6):
|
|
cron = OpenClawCronPoolDB(
|
|
openclaw_cron_id=f"busy-{i}",
|
|
status="busy",
|
|
last_active_at=utc_now(),
|
|
)
|
|
db_session.add(cron)
|
|
|
|
for i in range(4):
|
|
cron = OpenClawCronPoolDB(
|
|
openclaw_cron_id=f"idle-{i}",
|
|
status="idle",
|
|
last_active_at=utc_now(),
|
|
)
|
|
db_session.add(cron)
|
|
|
|
db_session.commit()
|
|
|
|
utilization = metrics.calculate_pool_utilization(db_session)
|
|
assert utilization == 0.6
|
|
|
|
|
|
def test_calculate_task_backlog_empty(db_session: Session):
|
|
"""Test task backlog when no tasks exist."""
|
|
backlog = metrics.calculate_task_backlog(db_session)
|
|
assert backlog == 0
|
|
|
|
|
|
def test_calculate_task_backlog(db_session: Session):
|
|
"""Test task backlog calculation."""
|
|
# Create 5 pending tasks
|
|
for i in range(5):
|
|
task = IntelligentEvalTaskQueueDB(
|
|
eval_id=f"eval-{i}",
|
|
status="pending",
|
|
priority=1,
|
|
reason="slot_due",
|
|
)
|
|
db_session.add(task)
|
|
|
|
# Create 3 assigned tasks (not counted)
|
|
for i in range(3):
|
|
task = IntelligentEvalTaskQueueDB(
|
|
eval_id=f"eval-assigned-{i}",
|
|
status="assigned",
|
|
priority=1,
|
|
reason="slot_due",
|
|
)
|
|
db_session.add(task)
|
|
|
|
db_session.commit()
|
|
|
|
backlog = metrics.calculate_task_backlog(db_session)
|
|
assert backlog == 5
|
|
|
|
|
|
def test_calculate_stuck_rate_empty(db_session: Session):
|
|
"""Test stuck rate when no crons exist."""
|
|
rate = metrics.calculate_stuck_rate(db_session)
|
|
assert rate == 0.0
|
|
|
|
|
|
def test_calculate_stuck_rate(db_session: Session):
|
|
"""Test stuck rate calculation."""
|
|
# Create 10 crons: 2 stuck, 8 active
|
|
for i in range(2):
|
|
cron = OpenClawCronPoolDB(
|
|
openclaw_cron_id=f"stuck-{i}",
|
|
status="stuck",
|
|
last_active_at=utc_now(),
|
|
)
|
|
db_session.add(cron)
|
|
|
|
for i in range(8):
|
|
cron = OpenClawCronPoolDB(
|
|
openclaw_cron_id=f"active-{i}",
|
|
status="busy",
|
|
last_active_at=utc_now(),
|
|
)
|
|
db_session.add(cron)
|
|
|
|
db_session.commit()
|
|
|
|
rate = metrics.calculate_stuck_rate(db_session)
|
|
assert rate == 0.2
|
|
|
|
|
|
def test_calculate_avg_processing_time_empty(db_session: Session):
|
|
"""Test average processing time when no completed tasks."""
|
|
avg_time = metrics.calculate_avg_processing_time(db_session)
|
|
assert avg_time is None
|
|
|
|
|
|
def test_calculate_avg_processing_time(db_session: Session):
|
|
"""Test average processing time calculation."""
|
|
now = utc_now()
|
|
|
|
# Create 3 completed tasks with different processing times
|
|
task1 = IntelligentEvalTaskQueueDB(
|
|
eval_id="eval-1",
|
|
status="completed",
|
|
priority=1,
|
|
reason="slot_due",
|
|
assigned_at=now - timedelta(minutes=10),
|
|
completed_at=now - timedelta(minutes=5),
|
|
)
|
|
task2 = IntelligentEvalTaskQueueDB(
|
|
eval_id="eval-2",
|
|
status="completed",
|
|
priority=1,
|
|
reason="slot_due",
|
|
assigned_at=now - timedelta(minutes=20),
|
|
completed_at=now - timedelta(minutes=10),
|
|
)
|
|
task3 = IntelligentEvalTaskQueueDB(
|
|
eval_id="eval-3",
|
|
status="completed",
|
|
priority=1,
|
|
reason="slot_due",
|
|
assigned_at=now - timedelta(minutes=30),
|
|
completed_at=now - timedelta(minutes=15),
|
|
)
|
|
|
|
db_session.add_all([task1, task2, task3])
|
|
db_session.commit()
|
|
|
|
avg_time = metrics.calculate_avg_processing_time(db_session)
|
|
# Average: (5 + 10 + 15) / 3 = 10 minutes = 600 seconds
|
|
assert avg_time == 600.0
|
|
|
|
|
|
def test_calculate_eval_completion_rate_empty(db_session: Session):
|
|
"""Test eval completion rate when no evals exist."""
|
|
rate = metrics.calculate_eval_completion_rate(db_session)
|
|
assert rate == 0.0
|
|
|
|
|
|
def test_calculate_eval_completion_rate(db_session: Session):
|
|
"""Test eval completion rate calculation."""
|
|
# Create 10 evals: 7 completed, 3 executing
|
|
for i in range(7):
|
|
eval_db = IntelligentEvalDB(
|
|
name=f"eval-completed-{i}",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.COMPLETED.value,
|
|
)
|
|
db_session.add(eval_db)
|
|
|
|
for i in range(3):
|
|
eval_db = IntelligentEvalDB(
|
|
name=f"eval-executing-{i}",
|
|
target_id="target1",
|
|
status=IntelligentEvalStatus.EXECUTING.value,
|
|
)
|
|
db_session.add(eval_db)
|
|
|
|
db_session.commit()
|
|
|
|
rate = metrics.calculate_eval_completion_rate(db_session)
|
|
assert rate == 0.7
|
|
|
|
|
|
def test_get_all_metrics(db_session: Session):
|
|
"""Test getting all metrics."""
|
|
# Create some test data
|
|
for i in range(5):
|
|
cron = OpenClawCronPoolDB(
|
|
openclaw_cron_id=f"cron-{i}",
|
|
status="busy" if i < 3 else "idle",
|
|
last_active_at=utc_now(),
|
|
)
|
|
db_session.add(cron)
|
|
|
|
db_session.commit()
|
|
|
|
all_metrics = metrics.get_all_metrics(db_session)
|
|
|
|
assert "pool_utilization" in all_metrics
|
|
assert "task_backlog" in all_metrics
|
|
assert "stuck_rate" in all_metrics
|
|
assert "avg_processing_time_seconds" in all_metrics
|
|
assert "eval_completion_rate" in all_metrics
|
|
assert "timestamp" in all_metrics
|
|
|
|
assert all_metrics["pool_utilization"] == 0.6
|
|
assert all_metrics["task_backlog"] == 0
|
|
assert all_metrics["stuck_rate"] == 0.0
|