AgentEvalTool/tests/unit/test_go_no_go.py
sinohqb 2314ebe3bc
All checks were successful
CI / test (pull_request) Successful in 4m7s
feat(report): 上线验收报告 go/no-go 结论
新增纯函数模块 evaluation/go_no_go.py,基于 RunSummary 指标自动生成
go/no-go/conditional 三级结论。

- AcceptanceCriteria 模型:judged_pass_rate_min、pass_rate_min、avg_latency_max_ms
- GoNoGoVerdict 模型:decision、summary、criteria_results
- evaluate_go_no_go() 纯函数:接收 summary dict + criteria -> 返回 verdict
- 集成到 generate_report(),报告 dict 自动附带 go_no_go 字段
- 三级结论:go(全部达标)、no_go(核心未达标)、conditional(有风险)
- 新增 9 项单元测试(642 tests passed)

Closes #20
2026-08-25 14:28:55 +08:00

139 lines
4.2 KiB
Python

"""Tests for go/no-go acceptance verdict."""
import pytest
from agenteval.evaluation.go_no_go import (
AcceptanceCriteria,
CriterionResult,
GoNoGoVerdict,
evaluate_go_no_go,
)
def test_go_verdict_all_pass():
"""All criteria met -> go."""
summary = {
"judged_pass_rate": 0.96,
"pass_rate": 0.95,
"avg_latency_ms": 2000,
}
criteria = AcceptanceCriteria(
judged_pass_rate_min=0.95,
pass_rate_min=0.90,
avg_latency_max_ms=5000,
)
verdict = evaluate_go_no_go(summary, criteria)
assert verdict.decision == "go"
assert "达标" in verdict.summary
assert all(r.passed for r in verdict.criteria_results)
def test_no_go_verdict_core_failed():
"""Core metric (pass rate) failed -> no_go."""
summary = {
"judged_pass_rate": 0.80,
"pass_rate": 0.75,
}
criteria = AcceptanceCriteria(
judged_pass_rate_min=0.95,
pass_rate_min=0.90,
)
verdict = evaluate_go_no_go(summary, criteria)
assert verdict.decision == "no_go"
assert "不建议上线" in verdict.summary
assert any(not r.passed for r in verdict.criteria_results)
def test_conditional_verdict_non_core_risk():
"""Core passed but non-core (latency) failed -> conditional."""
summary = {
"judged_pass_rate": 0.96,
"pass_rate": 0.95,
"avg_latency_ms": 8000,
}
criteria = AcceptanceCriteria(
judged_pass_rate_min=0.95,
pass_rate_min=0.90,
avg_latency_max_ms=5000,
)
verdict = evaluate_go_no_go(summary, criteria)
assert verdict.decision == "conditional"
assert "风险" in verdict.summary
def test_default_criteria():
"""Default criteria should be applied when None."""
summary = {
"judged_pass_rate": 0.96,
"pass_rate": 0.95,
}
verdict = evaluate_go_no_go(summary, None)
assert verdict.decision == "go"
assert len(verdict.criteria_results) >= 1
def test_empty_summary():
"""Empty summary -> conditional with no results."""
verdict = evaluate_go_no_go({}, None)
assert verdict.decision == "conditional"
assert "无可用指标" in verdict.summary
assert len(verdict.criteria_results) == 0
def test_criterion_result_model():
"""CriterionResult model should work correctly."""
result = CriterionResult(
criterion="pass_rate",
threshold=0.95,
actual=0.96,
passed=True,
detail="通过率 96% >= 95%",
)
assert result.criterion == "pass_rate"
assert result.passed is True
def test_verdict_model():
"""GoNoGoVerdict model should work correctly."""
verdict = GoNoGoVerdict(
decision="go",
summary="测试通过",
criteria_results=[],
)
assert verdict.decision == "go"
assert verdict.generated_at is not None
def test_latency_only_when_configured():
"""Latency should only be checked when avg_latency_max_ms is set."""
summary = {
"judged_pass_rate": 0.96,
"avg_latency_ms": 10000, # High latency
}
# Without latency threshold
criteria_no_latency = AcceptanceCriteria(judged_pass_rate_min=0.95)
verdict1 = evaluate_go_no_go(summary, criteria_no_latency)
assert verdict1.decision == "go"
assert all(r.criterion != "avg_latency_ms" for r in verdict1.criteria_results)
# With latency threshold
criteria_with_latency = AcceptanceCriteria(
judged_pass_rate_min=0.95,
avg_latency_max_ms=5000,
)
verdict2 = evaluate_go_no_go(summary, criteria_with_latency)
assert verdict2.decision == "conditional"
assert any(r.criterion == "avg_latency_ms" for r in verdict2.criteria_results)
def test_judged_pass_rate_fallback_to_pass_rate():
"""If judged_pass_rate is missing, fall back to pass_rate."""
summary = {"pass_rate": 0.96}
criteria = AcceptanceCriteria(judged_pass_rate_min=0.95)
verdict = evaluate_go_no_go(summary, criteria)
assert verdict.decision == "go"
# Should have one result using pass_rate as judged_pass_rate
assert len(verdict.criteria_results) == 1
assert verdict.criteria_results[0].criterion == "judged_pass_rate"
assert verdict.criteria_results[0].actual == 0.96