- 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
107 lines
3.7 KiB
Python
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)
|