chore: sync local changes, add Chinese docs and opencode config

This commit is contained in:
AuraK Developer
2026-08-31 11:37:03 +08:00
parent 307d2fd471
commit ecc55158c1
81 changed files with 7645 additions and 144 deletions
+421
View File
@@ -0,0 +1,421 @@
"""Tests for coverage computation (compute_coverage / check_community_health)."""
from __future__ import annotations
import json
import sys
from pathlib import Path
import pytest
sys.path.insert(0, str(Path(__file__).parent.parent))
from code_review_graph.parser import normalize_file_path
from code_review_graph.scoring import ( # noqa: E402
_is_source_file,
build_report_data,
check_community_health,
compute_coverage,
deep_read_plan,
render_markdown_report,
)
class _Node:
def __init__(self, qualified_name, file_path, is_test=False, kind="Function"):
self.qualified_name = qualified_name
self.file_path = file_path
self.is_test = is_test
self.kind = kind
class _Edge:
def __init__(self, kind, source_qualified, target_qualified):
self.kind = kind
self.source_qualified = source_qualified
self.target_qualified = target_qualified
class _Store:
"""Minimal fake GraphStore for coverage unit tests."""
def __init__(self, files, nodes, edges):
self._files = list(files)
self._nodes = nodes
self._edges = edges
def get_all_files(self):
return list(self._files)
def get_nodes_by_file(self, file_path):
return [n for n in self._nodes if n.file_path == file_path]
def get_edges_by_target(self, qualified_name):
return [e for e in self._edges if e.target_qualified == qualified_name]
@pytest.fixture
def tiny_repo(tmp_path: Path) -> Path:
"""A tiny repo with two source files + one non-source file."""
Path(tmp_path, "src").mkdir(exist_ok=True)
Path(tmp_path, "src", "a.py").write_text(
"def a():\n"
" try:\n"
" return 1\n"
" except Exception as e:\n"
" raise ValueError(str(e))\n",
encoding="utf-8",
)
Path(tmp_path, "src", "b.py").write_text(
"def b():\n return 2\n",
encoding="utf-8",
)
Path(tmp_path, "docs").mkdir(exist_ok=True)
Path(tmp_path, "docs", "README.md").write_text(
"# docs\n",
encoding="utf-8",
)
return tmp_path
def test_is_source_file_excludes_non_source():
assert _is_source_file("src/app.py") is True
assert _is_source_file("docs/README.md") is False
assert _is_source_file("tests/test_x.py") is False
assert _is_source_file("scripts/build.py") is False
assert _is_source_file("src/app.py.bak") is False
def test_compute_coverage_returns_expected_shape(tiny_repo):
files = [
"src/a.py",
"src/b.py",
]
nodes = [
_Node("D:/repo/src/a.py::a", "src/a.py"),
_Node("D:/repo/src/b.py::b", "src/b.py"),
]
store = _Store(files, nodes, [])
result = compute_coverage(
store,
tiny_repo,
deep_read_files=["src/a.py"],
include_churn=False,
)
assert result["status"] == "ok"
assert result["total_files"] == 2
assert result["deep_read_count"] == 1
assert 0.0 <= result["coverage_pct"] <= 100.0
assert 0.0 <= result["high_risk_coverage_pct"] <= 100.0
assert result["target"] == 95.0
assert result["overall_target"] == 85.0
assert result["high_risk_target"] == 95.0
assert result["target_reached"] is False # 1/2 = 50% < 95%
assert result["uncovered_files"] == ["src/b.py"]
# b.py is clean (no sql/vuln/redundancy signals, no topology, no churn)
# => w1 = good(1) < 2.0 and no signals => it IS a silent file.
assert result["silent_files"] == ["src/b.py"]
def test_compute_coverage_full_deep_read_reaches_target(tiny_repo):
files = ["src/a.py", "src/b.py"]
store = _Store(files, [], [])
result = compute_coverage(
store,
tiny_repo,
deep_read_files=["src/a.py", "src/b.py"],
include_churn=False,
)
assert result["coverage_pct"] == 100.0
# tiny_repo files carry no sql/vuln/redundancy signal => no high-risk
# files => gate falls back to overall coverage (100%) => reached.
assert result["target_reached"] is True
assert result["grade"] == "good"
def test_compute_coverage_excludes_docs_from_denominator(tiny_repo):
files = ["src/a.py", "src/b.py", "docs/README.md"]
store = _Store(files, [], [])
result = compute_coverage(
store,
tiny_repo,
deep_read_files=["src/a.py"],
include_churn=False,
)
assert result["total_files"] == 2 # docs excluded
assert "docs/README.md" not in result["uncovered_files"]
def test_check_community_health_fake_store():
class _Conn:
def execute(self, _sql):
return _Cursor()
class _Cursor:
def fetchone(self):
return (4,)
store = type("S", (), {"_conn": _Conn()})()
# Our fake always returns 4 for every count; just ensure it runs.
result = check_community_health(store)
assert "status" in result
def test_report_renders_coverage_section():
rd = {
"verdict": "PASS",
"scope": "whole-project",
"tier": "standard",
"timestamp": "2026-08-11T00:00:00Z",
"files": "src/a.py",
"metrics": {"sql_risk": {"value": 0, "grade": "good", "note": "ok"}},
"findings": [],
"coverage": {
"coverage_pct": 7.1,
"high_risk_coverage_pct": 62.0,
"grade": "good",
"deep_read_count": 24,
"total_files": 508,
"high_risk_total_files": 205,
"high_risk_deep_count": 14,
"target_reached": True,
"target": 95.0,
"uncovered_files": ["src/c.py"],
"silent_files": ["src/d.py"],
},
}
md = render_markdown_report(rd)
assert "## 覆盖度" in md
assert "7.1%" in md
assert "62.0%" in md
assert "✅ 达标" in md
data = build_report_data(rd)
assert data["coverage"]["coverage_pct"] == 7.1
assert data["coverage"]["high_risk_coverage_pct"] == 62.0
def test_report_marks_insufficient_coverage():
rd = {
"verdict": "FAIL",
"scope": "whole-project",
"tier": "standard",
"timestamp": "2026-08-11T00:00:00Z",
"coverage": {
"coverage_pct": 3.0,
"high_risk_coverage_pct": 30.0,
"grade": "fail",
"deep_read_count": 5,
"total_files": 508,
"high_risk_total_files": 205,
"high_risk_deep_count": 3,
"target_reached": False,
"target": 95.0,
"uncovered_files": [],
"silent_files": [],
},
}
md = render_markdown_report(rd)
assert "🔴 覆盖不足" in md
assert "30.0%" in md
@pytest.fixture
def risk_repo(tmp_path: Path) -> Path:
"""a.py carries an SQL-risk signal (fail w1), the rest are clean."""
Path(tmp_path, "src").mkdir(exist_ok=True)
Path(tmp_path, "src", "a.py").write_text(
"def a():\n"
" sql = 'SELECT * FROM users WHERE id=' + str(uid)\n"
" return sql\n",
encoding="utf-8",
)
for name in ("b", "c", "d"):
Path(tmp_path, "src", f"{name}.py").write_text(
f"def {name}():\n return 2\n",
encoding="utf-8",
)
return tmp_path
def test_coverage_gate_overall_vs_high_risk_vs_both(risk_repo):
files = ["src/a.py", "src/b.py", "src/c.py", "src/d.py"]
store = _Store(files, [], [])
# Deep-read only the risky file: overall file-count 25%, high-risk 100%.
base = dict(store=store, repo_root=risk_repo,
deep_read_files=["src/a.py"], include_churn=False)
overall = compute_coverage(gate="overall", **base)
assert overall["target_reached"] is False # 25% < 85%
high_risk = compute_coverage(gate="high_risk", **base)
assert high_risk["target_reached"] is True # 100% >= 95%
both = compute_coverage(gate="both", **base)
assert both["target_reached"] is False # overall fails
assert both["gate"] == "both"
def test_coverage_returns_gap_fields(risk_repo):
files = ["src/a.py", "src/b.py", "src/c.py", "src/d.py"]
store = _Store(files, [], [])
result = compute_coverage(
store, risk_repo,
deep_read_files=["src/a.py"],
include_churn=False,
gate="both",
)
# File-count gap: 4 files, 1 deep-read, overall target 85% => 4*0.85-1 = 2.4.
assert result["total_files"] == 4
assert result["remaining_files_to_target"] == 2.4
# Compatible risk-weight gap: a.py w1=2 (warn, one SQL hit), others
# w1=1 => total=5, overall target 85% => 4.25 - 2.0 = 2.25.
assert result["total_weight"] == 5.0
assert result["remaining_weight_to_target"] == 2.25
prio = result["priority_deep_read_files"]
assert [p["path"] for p in prio] == [
normalize_file_path(str(Path(risk_repo, "src", f"{n}.py")))
for n in ("b", "c", "d")
]
assert all(p["weight"] == 1.0 for p in prio)
def test_deep_read_plan_greedy_and_groups(risk_repo):
files = ["src/a.py", "src/b.py", "src/c.py", "src/d.py"]
store = _Store(files, [], [])
plan = deep_read_plan(
store, risk_repo,
deep_read_files=["src/a.py"],
target_coverage=85.0,
batch_size=40,
include_churn=False,
)
# File-count: total=4, current=1, target=3.4, remaining=2.4 -> pick
# the 3 uncovered files b, c, d (weights equal, any order is fine but
# the greedy loop fills up to the remaining count).
assert plan["current_files"] == 1
assert plan["target_files"] == 3.4
assert plan["remaining_files"] == 2.4
assert len(plan["planned_files"]) == 3
planned = [normalize_file_path(str(Path(risk_repo, "src", f"{n}.py")))
for n in ("b", "c", "d")]
assert plan["planned_files"] == planned
assert plan["estimated_batches"] == 1
assert plan["groups"][0]["name"] == "src"
assert len(plan["groups"][0]["files"]) == 3
def test_deep_read_plan_excludes_prior_covered(risk_repo):
files = ["src/a.py", "src/b.py", "src/c.py", "src/d.py"]
store = _Store(files, [], [])
prior = {normalize_file_path(str(Path(risk_repo, "src", "b.py")))}
plan = deep_read_plan(
store, risk_repo,
deep_read_files=["src/a.py"],
target_coverage=85.0,
include_churn=False,
prior_covered=prior,
)
# b already covered by a prior round => current=2 files, remaining =
# 4*0.85 - 2 = 1.4 -> pick c and d (2 files).
assert plan["current_files"] == 2
assert plan["remaining_files"] == 1.4
assert len(plan["planned_files"]) == 2
planned = [normalize_file_path(str(Path(risk_repo, "src", f"{n}.py")))
for n in ("c", "d")]
assert plan["planned_files"] == planned
def test_coverage_dual_target_overall_ok_high_risk_not(risk_repo):
"""Overall 85% reached but high-risk 95% not reached => gate=both fails."""
repo = risk_repo.parent / "dual_target_repo"
repo.mkdir(exist_ok=True)
src = repo / "src"
src.mkdir(exist_ok=True)
(src / "risky.py").write_text(
"def r():\n sql = 'SELECT * FROM users WHERE id=' + str(uid)\n return sql\n",
encoding="utf-8",
)
clean = [f"f{i}.py" for i in range(9)]
for name in clean:
(src / name).write_text(f"def {name}():\n return 1\n", encoding="utf-8")
files = ["src/risky.py"] + [f"src/{n}" for n in clean]
store = _Store(files, [], [])
deep = [f"src/{n}" for n in clean] # 9 clean files, skip risky
res = compute_coverage(
store, repo,
deep_read_files=deep,
include_churn=False,
gate="both",
)
assert res["total_files"] == 10
assert res["deep_read_count"] == 9
assert res["coverage_pct"] == 90.0 # >= 85% overall target
assert res["high_risk_coverage_pct"] == 0.0 # < 95% high-risk target
assert res["overall_target"] == 85.0
assert res["high_risk_target"] == 95.0
assert res["target_reached"] is False # both gate: high-risk fails
assert res["grade"] == "fail"
res_overall = compute_coverage(
store, repo,
deep_read_files=deep,
include_churn=False,
gate="overall",
)
assert res_overall["target_reached"] is True
def _write_coverage_index(repo: Path, entries: dict[str, str]) -> Path:
"""Write a coverage-index.json mapping rel path -> SHA."""
payload = {"version": 1, "last_updated": "2026-08-14T00:00:00", "entries": {
rel: {"sha": sha} for rel, sha in entries.items()
}}
index = repo / ".code-review-graph" / "coverage-index.json"
index.parent.mkdir(parents=True, exist_ok=True)
index.write_text(json.dumps(payload), encoding="utf-8")
return index
def _blob_sha(repo: Path, rel: str) -> str:
import subprocess
out = subprocess.run(
["git", "hash-object", rel],
capture_output=True, text=True, stdin=subprocess.DEVNULL,
cwd=str(repo), timeout=15,
)
assert out.returncode == 0, out.stderr
return out.stdout.strip()
def test_load_coverage_index_batch(tmp_path: Path) -> None:
"""Batch git hash-object path returns only SHA-stable files."""
repo = tmp_path
(repo / ".git").mkdir(parents=True)
# A git index is required for hash-object of tracked/untracked files to
# work in the same way; an empty repo is enough (hash-object works on any
# existing file path in the working tree).
(repo / "a.txt").write_text("alpha", encoding="utf-8")
(repo / "b.txt").write_text("beta", encoding="utf-8")
sha_a = _blob_sha(repo, "a.txt")
_write_coverage_index(repo, {"a.txt": sha_a, "b.txt": "0000000000000000000000000000000000000000"})
from code_review_graph.tools.scoring_tools import _load_coverage_index
covered = _load_coverage_index(repo)
assert covered == ["a.txt"] # only a.txt's SHA still matches
def test_load_coverage_index_skips_missing_file(tmp_path: Path) -> None:
"""A deleted indexed file must not truncate the batch or be returned."""
repo = tmp_path
(repo / ".git").mkdir(parents=True)
(repo / "a.txt").write_text("alpha", encoding="utf-8")
sha_a = _blob_sha(repo, "a.txt")
_write_coverage_index(repo, {"a.txt": sha_a, "gone.txt": sha_a})
from code_review_graph.tools.scoring_tools import _load_coverage_index
covered = _load_coverage_index(repo)
assert covered == ["a.txt"]