Files
ecm_sync_system/scripts/run_auto_test_all_domains.py
strepsiades 4a9c444b10 first commit
2026-03-09 16:31:42 +08:00

253 lines
8.2 KiB
Python

from __future__ import annotations
import argparse
import json
import re
import subprocess
import sys
from pathlib import Path
from auto_test_lib import load_yaml_config, now_slug
ALL_DOMAINS = [
"company",
"supplier",
"project",
"contract",
"contract_change",
"construction_task",
"construction_task_detail",
"material",
"material_detail",
"contract_settlement",
"contract_settlement_detail",
"personnel",
"personnel_detail",
"preparation",
"production",
"lar",
"lar_change",
"project_detail_data",
"units",
]
def _list_report_dirs(report_base: Path) -> set[Path]:
if not report_base.exists():
return set()
return {path for path in report_base.iterdir() if path.is_dir()}
def _extract_report_dir(stdout: str, stderr: str) -> Path | None:
combined = "\n".join(part for part in [stdout, stderr] if part)
match = re.search(r"报告已生成:\s*(.+?/report\.md)", combined)
if not match:
return None
return Path(match.group(1).strip()).parent
def _build_test_summary(domain_reports: list[dict]) -> dict:
cases: list[dict] = []
passed_domains = 0
failed_domains = 0
for domain in domain_reports:
if domain.get("passed"):
passed_domains += 1
else:
failed_domains += 1
for round_report in domain.get("rounds", []):
cases.append(
{
"nodeid": f"{domain['domain']}::{round_report['name']}",
"domain": domain["domain"],
"case": round_report["name"],
"purpose": round_report.get("purpose", ""),
"passed": bool(round_report.get("passed")),
"result": "PASSED" if round_report.get("passed") else "FAILED",
"returncode": round_report.get("returncode"),
"issue_count": len(round_report.get("issues", [])),
"issues": round_report.get("issues", []),
}
)
passed_cases = sum(1 for case in cases if case["passed"])
return {
"total_domains": len(domain_reports),
"passed_domains": passed_domains,
"failed_domains": failed_domains,
"total_cases": len(cases),
"passed_cases": passed_cases,
"failed_cases": len(cases) - passed_cases,
"cases": cases,
}
def _build_markdown_report(run_id: str, summary: dict, domain_reports: list[dict], run_records: list[dict]) -> str:
lines = [
f"# 自动测试聚合报告 {run_id}",
"",
"## 运行方式",
"",
"- 本报告由 `scripts/run_auto_test_all_domains.py` 聚合生成",
"- 每个 domain 独立初始化并单独执行,避免跨 domain 状态污染",
"",
"## Pytest 风格汇总",
"",
f"collected {summary['total_cases']} cases across {summary['total_domains']} domains",
"",
]
for case in summary["cases"]:
lines.append(f"- {case['nodeid']} {case['result']}")
lines.extend(
[
"",
f"== {summary['passed_cases']} passed, {summary['failed_cases']} failed, {summary['passed_domains']} passed domains, {summary['failed_domains']} failed domains ==",
"",
"## Domain 汇总",
"",
"| Domain | 结果 | 单域报告 |",
"| --- | --- | --- |",
]
)
run_record_by_domain = {item["domain"]: item for item in run_records}
for domain in domain_reports:
run_record = run_record_by_domain.get(domain["domain"], {})
lines.append(
f"| {domain['domain']} | {'通过' if domain.get('passed') else '失败'} | {run_record.get('report_dir', '')} |"
)
lines.append("")
for domain in domain_reports:
lines.append(f"## Domain: {domain['domain']}")
if domain.get("description"):
lines.extend(["", domain["description"]])
lines.append("")
lines.append(f"- 结果: {'通过' if domain.get('passed') else '失败'}")
record = run_record_by_domain.get(domain["domain"], {})
if record.get("report_dir"):
lines.append(f"- 单域报告目录: {record['report_dir']}")
if record.get("returncode") not in (None, 0):
lines.append(f"- 单域脚本返回码: {record['returncode']}")
if domain.get("issues"):
lines.append("- 问题:")
for issue in domain["issues"]:
lines.append(f" - {issue}")
lines.append("")
return "\n".join(lines)
def main() -> int:
parser = argparse.ArgumentParser(description="一轮执行所有已纳入 auto-test 的 domain")
parser.add_argument(
"--config",
default="tests/fixtures/auto_sync/auto_test_config.yaml",
help="自动测试配置文件路径",
)
parser.add_argument(
"--skip-init",
action="store_true",
help="透传给 run_auto_test.py",
)
parser.add_argument(
"--clear-only",
action="store_true",
help="透传给 run_auto_test.py",
)
args = parser.parse_args()
runner = Path(__file__).with_name("run_auto_test.py")
config = load_yaml_config(args.config)
report_base = Path(config["paths"]["report_dir"])
aggregate_run_id = now_slug()
aggregate_report_dir = report_base / aggregate_run_id
aggregate_report_dir.mkdir(parents=True, exist_ok=True)
domain_reports: list[dict] = []
run_records: list[dict] = []
overall_returncode = 0
for domain in ALL_DOMAINS:
command = [sys.executable, str(runner), "--config", args.config, "--domain", domain]
if args.skip_init:
command.append("--skip-init")
if args.clear_only:
command.append("--clear-only")
before_dirs = _list_report_dirs(report_base)
result = subprocess.run(command, check=False, text=True, capture_output=True)
after_dirs = _list_report_dirs(report_base)
new_dirs = sorted(after_dirs - before_dirs)
report_dir = _extract_report_dir(result.stdout, result.stderr)
if report_dir is None and new_dirs:
report_dir = new_dirs[-1]
run_record = {
"domain": domain,
"returncode": result.returncode,
"report_dir": str(report_dir) if report_dir else None,
"stdout_tail": (result.stdout or "")[-4000:],
"stderr_tail": (result.stderr or "")[-4000:],
}
run_records.append(run_record)
if result.returncode != 0:
overall_returncode = result.returncode
if report_dir is None:
domain_reports.append(
{
"domain": domain,
"description": "",
"passed": False,
"issues": ["未找到单域报告目录"],
"rounds": [],
}
)
continue
domain_report_path = report_dir / f"{domain}.json"
if not domain_report_path.exists():
domain_reports.append(
{
"domain": domain,
"description": "",
"passed": False,
"issues": [f"缺少单域报告文件: {domain_report_path}"],
"rounds": [],
}
)
overall_returncode = overall_returncode or 1
continue
domain_reports.append(json.loads(domain_report_path.read_text(encoding="utf-8")))
summary = _build_test_summary(domain_reports)
detail = {
"mode": "isolated-per-domain",
"aggregate_run_id": aggregate_run_id,
"summary": summary,
"runs": run_records,
"domains": domain_reports,
}
(aggregate_report_dir / "summary.json").write_text(
json.dumps(detail, ensure_ascii=False, indent=2),
encoding="utf-8",
)
(aggregate_report_dir / "report.md").write_text(
_build_markdown_report(aggregate_run_id, summary, domain_reports, run_records),
encoding="utf-8",
)
print(f"聚合报告已生成: {aggregate_report_dir / 'report.md'}")
if summary["failed_domains"]:
return overall_returncode or 1
return 0
if __name__ == "__main__":
raise SystemExit(main())