chore: restore original directory structure (project under code-review-graph-main/)

This commit is contained in:
AuraK Developer
2026-08-31 13:08:20 +08:00
parent ecc55158c1
commit ecfd03a21c
404 changed files with 0 additions and 0 deletions
+332
View File
@@ -0,0 +1,332 @@
"""Tests for the unified-review HTML/Markdown report tool."""
from __future__ import annotations
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).parent.parent))
from code_review_graph.scoring import ( # noqa: E402
build_report_data,
render_markdown_report,
)
from code_review_graph.tools.scoring_tools import ( # noqa: E402
_load_report_template,
generate_report_func,
)
def _review_data() -> dict:
return {
"scope": "change-level",
"tier": "standard",
"timestamp": "2026-08-05T00:00:00Z",
"files": "app.py, main.py",
"baseline": "generic",
"verdict": "\u2705 PASS",
"quality_score": 7.0,
"counts": {"critical": 1, "informational": 2},
"metrics": {
"sql_risk": {"value": 0, "grade": "good", "note": "heuristic"},
"exception_coverage": {"value": 33.33, "grade": "warn", "note": "heuristic"},
},
"findings": [
{
"severity": "MAJOR",
"category": "Data",
"message": "Possible N+1 query",
"path": "src/app.py",
"line": 42,
"confidence": 7,
"fix": "Add eager loading",
},
],
"manual_review": ["Payment callback idempotency"],
"llm_judged": ["requirement_coverage", "logic_alignment"],
}
def _mkroot(tmp_path: Path) -> Path:
Path(tmp_path, ".code-review-graph").mkdir(exist_ok=True)
return tmp_path
class TestBuildReportData:
def test_normalises_review_data(self):
data = build_report_data(_review_data())
assert data["verdict"] == "\u2705 PASS"
assert data["tier"] == "standard"
assert data["scope"] == "change-level"
assert data["metrics"]["sql_risk"]["grade"] == "good"
assert len(data["issues"]) == 1
assert data["issues"][0]["location"] == "src/app.py:42"
assert "requirement_coverage" in data["llm_judged"]
def test_quality_score_and_counts_carried(self):
data = build_report_data(_review_data())
assert data["quality_score"] == 7.0
assert data["counts"] == {"critical": 1, "informational": 2}
def test_empty_findings(self):
rd = _review_data()
rd["findings"] = []
data = build_report_data(rd)
assert data["issues"] == []
def test_defaults(self):
data = build_report_data({})
assert data["scope"] == "change-level"
assert data["tier"] == "standard"
assert data["verdict"] == "\u274c FAIL"
assert data["issues"] == []
def test_reviewed_files_carried(self):
rd = _review_data()
rd["reviewed_files"] = ["src/a.py", "src/b.py"]
data = build_report_data(rd)
assert data["reviewed_files"] == ["src/a.py", "src/b.py"]
def test_reviewed_files_default_empty(self):
data = build_report_data(_review_data())
assert data["reviewed_files"] == []
def test_files_list_normalised_to_string(self):
rd = _review_data()
rd["files"] = ["src/a.py", "src/b.py"]
data = build_report_data(rd)
assert data["files"] == "src/a.py, src/b.py"
def test_reviewed_files_fallback_from_files_list(self):
rd = _review_data()
rd["files"] = ["src/a.py", "src/b.py"]
data = build_report_data(rd)
assert data["reviewed_files"] == ["src/a.py", "src/b.py"]
# explicit reviewed_files wins over the files-list fallback
rd["reviewed_files"] = ["src/a.py"]
assert build_report_data(rd)["reviewed_files"] == ["src/a.py"]
def test_reviewed_files_string_split(self):
"""A comma-separated STRING reviewed_files must be split into an array
(agents pass both formats). Guards against iterating the string
char-by-char (which rendered 's','e','r'... as fake files)."""
rd = _review_data()
rd["reviewed_files"] = "src/a.rs, src/b.rs, "
data = build_report_data(rd)
assert data["reviewed_files"] == ["src/a.rs", "src/b.rs"]
md = render_markdown_report(rd)
assert "src/a.rs" in md
assert "src/b.rs" in md
# no single-char file entries
assert "- `s`" not in md
assert "265" not in md
def test_metrics_filters_non_objective_keys(self):
rd = _review_data()
rd["metrics"]["blast_radius"] = {
"direct_changed_nodes": 101,
"impacted_nodes": 13,
}
rd["metrics"]["objective_grade"] = "fail"
data = build_report_data(rd)
assert "blast_radius" not in data["metrics"]
assert "objective_grade" not in data["metrics"]
assert set(data["metrics"]) == {
"sql_risk",
"exception_coverage",
}
class TestLoadTemplate:
def test_loads_package_asset(self):
template = _load_report_template()
assert "{{REPORT_DATA}}" in template
assert template.startswith("<!DOCTYPE html>")
class TestRenderMarkdownReport:
def test_renders_chinese_structure(self):
md = render_markdown_report(_review_data())
assert "# 代码审查报告" in md
assert "结论" in md
assert "PR 质量分" in md and "7.0/10" in md
assert "客观指标" in md
assert "SQL 注入风险" in md
assert "异常分支覆盖" in md
assert "问题清单" in md
assert "🟡 主要" in md
assert "置信度 7/10" in md
assert "修复建议" in md
assert "需要人工审查" in md
assert "Payment callback idempotency" in md
assert "需 LLM 判断的指标" in md
def test_grade_localisation(self):
rd = _review_data()
rd["metrics"] = {
"sql_risk": {"value": 0, "grade": "good"},
"vulnerability_risk": {"value": 2, "grade": "fail"},
"high_risk_density": {"value": None, "grade": "na"},
}
md = render_markdown_report(rd)
assert "良好" in md
assert "不合格" in md
assert "不适用" in md
def test_empty_report(self):
md = render_markdown_report({})
assert "# 代码审查报告" in md
assert "未发现问题" in md
def test_line_unit_coverage_skips_file_count_rows(self):
"""gate=\"line+unit\" (feature) must NOT render the 全库/高风险 rows."""
rd = {
"scope": "feature",
"tier": "standard",
"reviewed_files": ["src/a.py", "src/b.py"],
"coverage": {
"coverage_pct": None,
"high_risk_coverage_pct": None,
"grade": "good",
"target_reached": True,
"gate": "line+unit",
"line_coverage_pct": 100.0,
"unit_coverage_pct": 100.0,
"line_gap_files": [],
"unit_gap_files": [],
},
}
md = render_markdown_report(rd)
assert "## 覆盖度" in md
assert "**状态**" in md
assert "100.0%" in md
assert "覆盖度(全库)" not in md
assert "覆盖度(高风险)" not in md
def test_reviewed_files_collapsible(self):
rd = {
"scope": "feature",
"tier": "standard",
"reviewed_files": ["src/a.py", "src/b.py"],
}
md = render_markdown_report(rd)
assert "审查文件(2 个)" in md
assert "<details>" in md
assert "点击展开 / 收起" in md
assert "src/a.py" in md
# flat "文件" meta line is skipped when reviewed_files is present
assert "- **文件**" not in md
class TestGenerateReport:
def test_both_writes_html_and_md(self, tmp_path):
root = _mkroot(tmp_path)
result = generate_report_func(
_review_data(),
output_path="sub/report",
repo_root=str(root),
)
assert result["status"] == "ok"
assert isinstance(result["output_path"], list)
assert len(result["files"]) == 2
html = root / "sub" / "report.html"
md = root / "sub" / "report.md"
assert html.is_file()
assert md.is_file()
html_text = html.read_text(encoding="utf-8")
md_text = md.read_text(encoding="utf-8")
assert "{{REPORT_DATA}}" not in html_text
assert "代码审查报告" in html_text
assert "\u2705 PASS" in html_text
assert "N+1" in html_text
assert "# 代码审查报告" in md_text
def test_script_closing_sequence_is_escaped(self, tmp_path):
"""Literal "</script>" in finding text must not break the HTML.
A literal ``</script>`` inside the injected JSON data would close the
surrounding <script> tag early, corrupting the report. The JSON payload
must escape every "<" as "\\u003c" so the browser parses it as a plain
string while the JS template re-renders it as "<".
"""
root = _mkroot(tmp_path)
rd = _review_data()
rd["findings"] = [
{
"severity": "major",
"category": "security",
"message": 'literal </script> opener',
"path": "src/app.py",
"line": 42,
"confidence": 8,
"fix": "escape </script> before embedding",
},
]
result = generate_report_func(
rd,
output_path="script-safe",
repo_root=str(root),
)
assert result["status"] == "ok"
html_text = (root / "script-safe.html").read_text(encoding="utf-8")
# The injected JSON payload must not contain a bare closing tag.
data_block = html_text.split("const data = ", 1)[1].split(";", 1)[0]
assert "</script" not in data_block
# It must keep the JSON escape so the value round-trips as "<".
assert "\\u003c/script" in data_block
# The real template closing tag must still be present exactly once.
assert html_text.count("</script>") == 1
# Finding text is intact after JSON decoding.
import json as _json
decoded = _json.loads(data_block)
assert "literal </script> opener" in decoded["issues"][0]["message"]
def test_markdown_only(self, tmp_path):
root = _mkroot(tmp_path)
result = generate_report_func(
_review_data(),
output_path="md-only",
repo_root=str(root),
format="markdown",
)
assert result["status"] == "ok"
assert len(result["files"]) == 1
assert str(result["output_path"]).endswith("md-only.md")
assert (root / "md-only.md").is_file()
assert not (root / "md-only.html").exists()
def test_html_only(self, tmp_path):
root = _mkroot(tmp_path)
result = generate_report_func(
_review_data(),
output_path="html-only",
repo_root=str(root),
format="html",
)
assert result["status"] == "ok"
assert len(result["files"]) == 1
assert str(result["output_path"]).endswith("html-only.html")
assert (root / "html-only.html").is_file()
assert not (root / "html-only.md").exists()
def test_default_output_path_is_repo_root(self, tmp_path):
root = _mkroot(tmp_path)
result = generate_report_func(_review_data(), repo_root=str(root))
assert result["status"] == "ok"
assert (root / "code-review-report.html").is_file()
assert (root / "code-review-report.md").is_file()
def test_invalid_format_errors(self, tmp_path):
root = _mkroot(tmp_path)
result = generate_report_func(
_review_data(),
repo_root=str(root),
format="xml",
)
assert result["status"] == "error"
assert "xml" in result["error"]
def test_handles_missing_repo_root(self, tmp_path):
with pytest.raises(ValueError):
generate_report_func(_review_data(), repo_root=str(tmp_path / "missing"))