AgentEvalTool/tests/unit/test_report_render.py
sinohqb f285738f6d refactor(report): split report generation from pure rendering
report.py mixed DB-reading generation with string formatting: the four
render_*_report(run_id, session) functions each re-fetched via
generate_report, so the HTML/Markdown/JSON formatting was welded to storage
and could not be unit-tested from a plain dict. Extract the formatting into a
new pure report_render module whose renderers take the already-built report
dict (no session, no storage import). Migrate every caller to generate-then-
render, delete the old coupled renderers with no back-compat shim, and drop
the _aggregate_runs middle-man alias in favour of metrics.aggregate_runs.
2026-07-31 10:19:04 +08:00

188 lines
6.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Unit tests for the pure renderers: dict in, HTML/Markdown/JSON out (no DB)."""
import json
from agenteval.evaluation.report_render import (
render_campaign_markdown,
render_html,
render_json,
render_markdown,
)
def _run_report(**overrides) -> dict:
"""A hand-built report dict matching generate_report's shape."""
report = {
"run_id": "run-1",
"target_id": "t-1",
"target_name": "客服机器人",
"scenario_id": "s-1",
"scenario_name": "售后场景",
"scenario_version": 2,
"triggered_by": "manual",
"status": "completed",
"started_at": "2026-07-30T10:00:00+00:00",
"completed_at": "2026-07-30T10:05:00+00:00",
"summary": {
"total_cases": 2,
"passed_cases": 1,
"failed_cases": 1,
"total_rules": 3,
"passed_rules": 2,
"pass_rate": 0.5,
"connectivity_cases": 1,
"judged_pass_rate": 0.0,
},
"cases": [
{
"case_id": "case-a",
"passed": True,
"connectivity": True,
"turns": [
{
"round": 0,
"sent_text": "你好",
"reply_text": "您好,请问有什么可以帮您?",
"latency_ms": 120,
"question_msg_id": "m-1",
}
],
"results": [],
},
{
"case_id": "case-b",
"passed": False,
"connectivity": False,
"turns": [
{
"round": 0,
"sent_text": "退货流程",
"reply_text": None,
"latency_ms": None,
"question_msg_id": "m-2",
}
],
"results": [
{"rule_type": "keyword_match", "passed": False, "score": 0.0, "reason": "缺少关键词"},
],
},
],
}
report.update(overrides)
return report
def _campaign_report() -> dict:
"""A hand-built dict matching generate_campaign_report's shape."""
return {
"campaign_id": "c-1",
"name": "夜间巡检",
"target_id": "t-1",
"status": "completed",
"window_seconds": 7200,
"time_scale": 1.0,
"started_at": "2026-07-30T00:00:00+00:00",
"completed_at": "2026-07-30T02:00:00+00:00",
"summary": {
"total_runs": 2,
"completed_runs": 2,
"overall_pass_rate": 0.75,
"overall_availability": 1.0,
"avg_latency_ms": 150.0,
},
"time_trend": [
{
"bucket_index": 0,
"start_seconds": 0.0,
"end_seconds": 3600.0,
"run_count": 2,
"pass_rate": 0.75,
"availability": 1.0,
"avg_latency_ms": 150.0,
},
{
"bucket_index": 1,
"start_seconds": 3600.0,
"end_seconds": 7200.0,
"run_count": 0,
"pass_rate": None,
"availability": None,
"avg_latency_ms": None,
},
],
"capability_summary": [
{
"scenario_id": "s-1",
"scenario_name": "售后场景",
"run_count": 2,
"pass_rate": 0.75,
"availability": 1.0,
"avg_latency_ms": 150.0,
},
],
}
# ── render_html ─────────────────────────────────────────────────────────────
def test_render_html_contains_names_and_summary():
html = render_html(_run_report())
assert "客服机器人" in html
assert "售后场景" in html
assert "50.00%" in html # pass_rate 0.5
def test_render_html_contains_turns_and_rule_badges():
html = render_html(_run_report())
assert "退货流程" in html
assert "keyword_match" in html
assert "失败" in html
# ── render_markdown ─────────────────────────────────────────────────────────
def test_render_markdown_summary_table():
md = render_markdown(_run_report())
assert "| 总用例数 | 2 |" in md
assert "| 通过率 | 50.0% |" in md
assert "| 连通用例 | 1 |" in md
def test_render_markdown_connectivity_badge_and_no_reply():
md = render_markdown(_run_report())
assert "🔗" in md # connectivity case badge
assert "(连通用例,未配置判定标准)" in md
assert "(无回复)" in md
def test_render_markdown_judged_pass_rate_dash_when_none():
report = _run_report()
report["summary"]["judged_pass_rate"] = None
md = render_markdown(report)
assert "| 判定型通过率 | — |" in md
# ── render_json ─────────────────────────────────────────────────────────────
def test_render_json_roundtrips():
report = _run_report()
parsed = json.loads(render_json(report))
assert parsed == report
# ── render_campaign_markdown ────────────────────────────────────────────────
def test_render_campaign_markdown_summary_and_axes():
md = render_campaign_markdown(_campaign_report())
assert "# 活动周期报告 — 夜间巡检" in md
assert "| 整窗通过率 | 75.0% |" in md
assert "## 时间趋势" in md
assert "## 能力汇总" in md
assert "| 售后场景 | 2 | 75.0% | 100.0% | 150ms |" in md
def test_render_campaign_markdown_empty_bucket_dashes():
md = render_campaign_markdown(_campaign_report())
# bucket 1 has no runs: pass_rate/availability/latency all render as —
assert "| 36007200 | 0 | — | — | — |" in md