Files
gaokao-volunteer-system/tests/test_t5_e2e_workflows.py

328 lines
9.4 KiB
Python
Raw Normal View History

2026-06-13 14:49:58 +08:00
"""T5.1 端到端业务场景测试。
覆盖 5 条主链路
1. 咨询 -> 方案生成
2. 审核 -> 报告
3. 订单 -> 交付
4. 升级流程
5. 数据溯源展示
"""
from __future__ import annotations
import importlib
import importlib.util
import json
import os
import subprocess
import sys
from pathlib import Path
from typing import Any, cast
import pytest
PROJECT_ROOT = Path(__file__).resolve().parents[1]
if str(PROJECT_ROOT) not in sys.path:
sys.path.insert(0, str(PROJECT_ROOT))
_AUDIT_CLI = importlib.import_module("skills.gaokao-audit.scripts.audit_cli")
_REPORT_GENERATOR = importlib.import_module(
"skills.gaokao-audit.scripts.report_generator"
)
_ReportGeneratorBase = cast(type[Any], _REPORT_GENERATOR.ReportGenerator)
_TRACE_CLI = importlib.import_module("data.crowd_db.cli")
SAMPLE_PLAN = (
PROJECT_ROOT
/ "skills"
/ "gaokao-audit"
/ "tests"
/ "fixtures"
/ "sample_xianyu.txt"
)
QUICK_SCRIPT = PROJECT_ROOT / "scripts" / "gaokao-quick-3min.py"
ORDER_SCRIPT = PROJECT_ROOT / "scripts" / "gaokao-order-manager"
TRACE_SCRIPT = PROJECT_ROOT / "scripts" / "gaokao-data-trace"
os.environ.setdefault("GAOKAO_ORDERS_FERNET_KEY", "test-secret-for-t5-e2e")
class _CaptureReportGenerator(_ReportGeneratorBase): # type: ignore[valid-type, misc]
last_html: str = ""
def __init__(self, **kwargs: object) -> None:
super().__init__(
now_text=lambda: "2026-06-13 10:00",
report_id_factory=lambda: "AUDIT-T5-E2E-001",
**kwargs,
)
def generate_pdf(self, result, output_path: str, **kwargs: object) -> str:
html = self.render_html(result, **kwargs)
type(self).last_html = html
target = Path(output_path)
target.parent.mkdir(parents=True, exist_ok=True)
target.write_bytes(b"%PDF-1.4\nt5 fake pdf\n")
return str(target)
@pytest.fixture(scope="module")
def quick_module():
spec = importlib.util.spec_from_file_location("gaokao_quick_3min", QUICK_SCRIPT)
assert spec is not None and spec.loader is not None
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
@pytest.fixture
def tmp_orders_db(tmp_path: Path) -> Path:
return tmp_path / "orders.db"
@pytest.fixture
def order_env() -> dict[str, str]:
return {"GAOKAO_ORDERS_FERNET_KEY": os.environ["GAOKAO_ORDERS_FERNET_KEY"]}
def _run_script(
script: Path, *args: str, env: dict[str, str] | None = None
) -> subprocess.CompletedProcess[str]:
merged_env = os.environ.copy()
if env:
merged_env.update(env)
return subprocess.run(
[sys.executable, str(script), *args],
cwd=PROJECT_ROOT,
capture_output=True,
text=True,
env=merged_env,
)
def _extract_json(stdout: str) -> dict:
lines = stdout.splitlines()
for index, line in enumerate(lines):
if line.startswith("{"):
return json.loads("\n".join(lines[index:]))
raise AssertionError(f"stdout 中未找到 JSON 输出: {stdout}")
def test_consultation_to_plan_generation_flow(quick_module) -> None:
reply = """1. 李明
2. 浙江
3. 612
4. 15230
5. R
6. 物理数学
7. C
8.
9.
10.
"""
info = quick_module.parse_quick_response(reply)
summary = quick_module.generate_quick_summary(info)
recommendation = quick_module.generate_quick_recommendation(info)
assert info["basic"]["name"] == "李明"
assert info["basic"]["province"] == "浙江"
assert info["exam"]["score"] == 612
assert info["exam"]["rank"] == 15230
assert info["profile"]["type_code"] == "R"
assert "✅ 核心信息完整!可以开始推荐" in summary
assert "📊 高考612分" in summary
assert "📊 位次15230名" in summary
assert "计算机科学与技术" in recommendation
assert "物理数学强 → 计算机、电子信息、自动化" in recommendation
def test_audit_to_report_flow(
tmp_path: Path,
monkeypatch: pytest.MonkeyPatch,
capsys: pytest.CaptureFixture[str],
) -> None:
output_path = tmp_path / "audit-report.pdf"
monkeypatch.setattr(_AUDIT_CLI, "ReportGenerator", _CaptureReportGenerator)
exit_code = _AUDIT_CLI.main([
str(SAMPLE_PLAN),
"--output",
str(output_path),
"--json",
])
captured = capsys.readouterr()
payload = _extract_json(captured.out)
assert exit_code == 0
assert output_path.exists()
assert output_path.read_bytes().startswith(b"%PDF-1.4")
assert payload["province"] == "湖南"
assert payload["candidate_score"] == 578
assert payload["crowd_risks"]
assert "AUDIT-T5-E2E-001" in _CaptureReportGenerator.last_html
assert "免责声明" in _CaptureReportGenerator.last_html
def test_order_to_delivery_flow_records_artifacts(
tmp_orders_db: Path,
tmp_path: Path,
order_env: dict[str, str],
) -> None:
plan_path = tmp_path / "plan.md"
report_path = tmp_path / "audit.json"
pdf_path = tmp_path / "report.pdf"
plan_path.write_text("咨询后生成的志愿方案", encoding="utf-8")
report_path.write_text('{"overall_score": 88}', encoding="utf-8")
pdf_path.write_bytes(b"%PDF-1.4\nreport\n")
created = _run_script(
ORDER_SCRIPT,
"--db",
str(tmp_orders_db),
"create",
"--source",
"xianyu",
"--service-version",
"audit",
"--amount-cents",
"4900",
"--customer-name",
"王家长",
"--customer-phone",
"13800001234",
"--candidate-name",
"李明",
"--candidate-province",
"湖南",
"--candidate-score",
"578",
"--candidate-rank",
"26800",
env=order_env,
)
assert created.returncode == 0, created.stderr
order_id = json.loads(created.stdout)["order"]["id"]
updated = _run_script(
ORDER_SCRIPT,
"--db",
str(tmp_orders_db),
"update",
order_id,
"--assigned-consultant",
"long-teacher",
"--plan-file",
str(plan_path),
"--audit-report",
str(report_path),
"--pdf-path",
str(pdf_path),
"--note",
"方案与审核报告已归档",
env=order_env,
)
assert updated.returncode == 0, updated.stderr
updated_payload = json.loads(updated.stdout)
assert updated_payload["order"]["plan_file"] == str(plan_path)
assert updated_payload["order"]["audit_report"] == str(report_path)
assert updated_payload["order"]["pdf_path"] == str(pdf_path)
paid = _run_script(
ORDER_SCRIPT,
"--db",
str(tmp_orders_db),
"pay",
order_id,
"--reason",
"xianyu-paid",
env=order_env,
)
assert paid.returncode == 0, paid.stderr
delivered = _run_script(
ORDER_SCRIPT,
"--db",
str(tmp_orders_db),
"deliver",
order_id,
"--reason",
"pdf-delivered",
env=order_env,
)
assert delivered.returncode == 0, delivered.stderr
delivered_payload = json.loads(delivered.stdout)
assert delivered_payload["order"]["status"] == "delivered"
assert delivered_payload["order"]["delivered_at"] is not None
assert delivered_payload["order"]["plan_file"] == str(plan_path)
assert delivered_payload["order"]["pdf_path"] == str(pdf_path)
def test_upgrade_flow_creates_delta_order(
tmp_orders_db: Path,
order_env: dict[str, str],
) -> None:
created = _run_script(
ORDER_SCRIPT,
"--db",
str(tmp_orders_db),
"create",
"--source",
"wechat",
"--service-version",
"audit",
"--amount-cents",
"4900",
"--customer-name",
"王家长",
"--customer-phone",
"13900001234",
env=order_env,
)
assert created.returncode == 0, created.stderr
source_order_id = json.loads(created.stdout)["order"]["id"]
upgraded = _run_script(
ORDER_SCRIPT,
"--db",
str(tmp_orders_db),
"upgrade",
source_order_id,
"--service-version",
"standard",
"--target-amount-cents",
"9900",
"--reason",
"upgrade_to_standard",
env=order_env,
)
assert upgraded.returncode == 0, upgraded.stderr
payload = json.loads(upgraded.stdout)
assert payload["order"]["upgrade_from"] == source_order_id
assert payload["order"]["service_version"] == "standard"
assert payload["order"]["amount_cents"] == 5000
assert payload["source_order"]["id"] == source_order_id
assert "upgraded" in payload["source_order"]["tags"]
def test_traceability_display_flow(capsys: pytest.CaptureFixture[str]) -> None:
exit_code = _TRACE_CLI.main(["--human", "长沙理工大学"])
captured = capsys.readouterr()
assert exit_code == 0
assert "query: 长沙理工大学" in captured.out
assert "湖南 / 2025年数据 / 长沙理工大学 / 会计学" in captured.out
assert "source_type: report (⚠️报告)" in captured.out
assert "source_url: https://" in captured.out
assert "confidence: 0.85" in captured.out
def test_traceability_json_entrypoint_matches_cli_contract() -> None:
result = _run_script(TRACE_SCRIPT, "长沙理工大学")
assert result.returncode == 0, result.stderr
payload = json.loads(result.stdout)
assert payload["query"] == "长沙理工大学"
assert payload["match_count"] >= 1
assert any(match["province"] == "湖南" for match in payload["matches"])