AgentEvalTool/tests/integration/test_stats_api.py
sinohqb 739d586aec feat(backend): v0.4 triggered_by tracking, login gate, compare guard, dashboard stats
- EvalRun.triggered_by 全链路(manual/ai_assistant/cli)+ 迁移 b7d4e6f81c22
- 标准 agenteval-run SKILL.md 纳入版本管理,deploy 脚本同步 + API Key 注入
- 简单登录:AGENTEVAL_ADMIN_PASSWORD + HMAC 会话 token,require_auth 双凭据
- 对比报告限同场景(400)+ 空 results 误判修复
- /api/stats/dashboard 扩展聚合;/api/runs 返回场景/对象名
- 测试 218 → 232
2026-07-28 17:40:54 +08:00

107 lines
3.7 KiB
Python

"""Integration tests for /api/stats/dashboard aggregation."""
import pytest
from fastapi.testclient import TestClient
from sqlmodel import Session, SQLModel, create_engine
from agenteval.models import (
Case, CaseType, ChannelType, EvalRun, EvalTarget, PlatformType,
RunStatus, RunTrigger, Scenario, TargetStatus,
)
from agenteval.storage.repository import RunRepository, ScenarioRepository, TargetRepository
from agenteval.web.app import app
from agenteval.web.deps import get_db
@pytest.fixture()
def client_with_db(tmp_path):
from agenteval.storage.db import ( # noqa: F401
EvalResultDB, EvalRunDB, EvalTargetDB, ModelConfigDB, ScenarioDB, TurnDB,
)
engine = create_engine(
f"sqlite:///{tmp_path / 'stats_api.db'}",
connect_args={"check_same_thread": False},
)
SQLModel.metadata.create_all(engine)
session = Session(engine)
def override_get_db():
try:
yield session
finally:
pass
app.dependency_overrides[get_db] = override_get_db
client = TestClient(app)
yield client, session
app.dependency_overrides.clear()
session.close()
def _seed(session: Session) -> None:
target = TargetRepository(session).create(EvalTarget(
name="对象A", platform=PlatformType.AI_DIGITAL_EMPLOYEE,
channel_type=ChannelType.TUTU_API, channel_config={}, status=TargetStatus.ACTIVE,
))
scenario = ScenarioRepository(session).create(Scenario(
name="场景A", cases=[Case(id="c1", type=CaseType.SINGLE, messages=["hi"])],
))
repo = RunRepository(session)
for pass_rate, trigger in [(1.0, RunTrigger.MANUAL), (0.5, RunTrigger.AI_ASSISTANT)]:
run = repo.create(EvalRun(
target_id=target.id, scenario_id=scenario.id,
status=RunStatus.COMPLETED, triggered_by=trigger,
))
run.summary = {"total_cases": 2, "passed_cases": 1, "failed_cases": 1,
"total_rules": 2, "passed_rules": 1, "pass_rate": pass_rate}
repo.update(run)
repo.create(EvalRun(
target_id=target.id, scenario_id=scenario.id,
status=RunStatus.RUNNING, triggered_by=RunTrigger.MANUAL,
))
def test_dashboard_aggregates(client_with_db):
client, session = client_with_db
_seed(session)
data = client.get("/api/stats/dashboard").json()
assert data["targets_count"] == 1
assert data["scenarios_count"] == 1
assert data["runs_count"] == 3
assert data["model_configs_count"] == 0
assert data["running_count"] == 1
assert data["today_runs"] == 3
assert data["overall_pass_rate"] == pytest.approx(0.75)
assert data["trigger_breakdown"] == {"manual": 2, "ai_assistant": 1}
assert len(data["scenario_stats"]) == 1
stat = data["scenario_stats"][0]
assert stat["scenario_name"] == "场景A"
assert stat["run_count"] == 2
assert stat["avg_pass_rate"] == pytest.approx(0.75)
assert stat["last_run_at"] is not None
assert len(data["recent_runs"]) == 3
assert data["recent_runs"][0]["scenario_name"] == "场景A"
assert data["recent_runs"][0]["target_name"] == "对象A"
assert "triggered_by" in data["recent_runs"][0]
def test_dashboard_empty_db(client_with_db):
client, _ = client_with_db
data = client.get("/api/stats/dashboard").json()
assert data["runs_count"] == 0
assert data["overall_pass_rate"] is None
assert data["scenario_stats"] == []
assert data["recent_runs"] == []
def test_trend_returns_daily_points(client_with_db):
client, session = client_with_db
_seed(session)
points = client.get("/api/stats/trend").json()
assert len(points) == 1
assert points[0]["run_count"] == 2
assert points[0]["pass_rate"] == pytest.approx(75.0)