quality(bstein-home): publish real workspace coverage and loc totals
This commit is contained in:
parent
eedb77020e
commit
285b00183a
@ -221,6 +221,38 @@ def check_coverage(
|
|||||||
return issues
|
return issues
|
||||||
|
|
||||||
|
|
||||||
|
def _coverage_values_for_paths(
|
||||||
|
paths: Iterable[Path],
|
||||||
|
*,
|
||||||
|
backend_report: Path,
|
||||||
|
frontend_report: Path,
|
||||||
|
) -> list[float]:
|
||||||
|
"""Return per-file line coverage values for tracked backend/frontend files."""
|
||||||
|
|
||||||
|
backend_cov = _load_backend_coverage(backend_report) if backend_report.exists() else {}
|
||||||
|
frontend_cov = _load_frontend_coverage(frontend_report) if frontend_report.exists() else {}
|
||||||
|
values: list[float] = []
|
||||||
|
|
||||||
|
for path in paths:
|
||||||
|
if not path.exists():
|
||||||
|
continue
|
||||||
|
rel = path.relative_to(ROOT).as_posix() if path.is_absolute() else _normalize_key(str(path))
|
||||||
|
if rel.startswith("backend/"):
|
||||||
|
metrics = _coverage_lookup(backend_cov, rel)
|
||||||
|
if metrics is None:
|
||||||
|
continue
|
||||||
|
values.append(float(metrics.get("lines", 0.0)))
|
||||||
|
continue
|
||||||
|
if rel.startswith("frontend/"):
|
||||||
|
lookup = rel.split("frontend/", 1)[1]
|
||||||
|
metrics = _coverage_lookup(frontend_cov, lookup)
|
||||||
|
if metrics is None:
|
||||||
|
continue
|
||||||
|
pct = metrics.get("lines", {}).get("pct", 0.0)
|
||||||
|
values.append(float(pct))
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
def _build_parser() -> argparse.ArgumentParser:
|
def _build_parser() -> argparse.ArgumentParser:
|
||||||
parser = argparse.ArgumentParser(description="Run the repo's unified quality gate")
|
parser = argparse.ArgumentParser(description="Run the repo's unified quality gate")
|
||||||
parser.add_argument("--contract", default=str(DEFAULT_CONTRACT), help="Path to the JSON gate contract")
|
parser.add_argument("--contract", default=str(DEFAULT_CONTRACT), help="Path to the JSON gate contract")
|
||||||
@ -242,12 +274,21 @@ def run_gate(contract_path: Path, *, backend_coverage: Path, frontend_coverage:
|
|||||||
issues.extend(check_file_sizes(managed_files, max_lines=max_lines))
|
issues.extend(check_file_sizes(managed_files, max_lines=max_lines))
|
||||||
issues.extend(check_docstrings(docstring_files))
|
issues.extend(check_docstrings(docstring_files))
|
||||||
issues.extend(check_coverage(coverage_files, backend_report=backend_coverage, frontend_report=frontend_coverage, threshold=threshold))
|
issues.extend(check_coverage(coverage_files, backend_report=backend_coverage, frontend_report=frontend_coverage, threshold=threshold))
|
||||||
|
coverage_values = _coverage_values_for_paths(
|
||||||
|
coverage_files,
|
||||||
|
backend_report=backend_coverage,
|
||||||
|
frontend_report=frontend_coverage,
|
||||||
|
)
|
||||||
|
workspace_line_coverage_percent = round(sum(coverage_values) / len(coverage_values), 3) if coverage_values else 0.0
|
||||||
|
source_lines_over_500 = sum(1 for issue in issues if issue.check == "loc")
|
||||||
report = {
|
report = {
|
||||||
"managed_files": [str(path.relative_to(ROOT)) for path in managed_files],
|
"managed_files": [str(path.relative_to(ROOT)) for path in managed_files],
|
||||||
"docstring_files": [str(path.relative_to(ROOT)) for path in docstring_files],
|
"docstring_files": [str(path.relative_to(ROOT)) for path in docstring_files],
|
||||||
"coverage_files": [str(path.relative_to(ROOT)) for path in coverage_files],
|
"coverage_files": [str(path.relative_to(ROOT)) for path in coverage_files],
|
||||||
"max_lines": max_lines,
|
"max_lines": max_lines,
|
||||||
"coverage_threshold_pct": threshold,
|
"coverage_threshold_pct": threshold,
|
||||||
|
"workspace_line_coverage_percent": workspace_line_coverage_percent,
|
||||||
|
"source_lines_over_500": source_lines_over_500,
|
||||||
"issue_count": len(issues),
|
"issue_count": len(issues),
|
||||||
"issues": [issue.__dict__ for issue in issues],
|
"issues": [issue.__dict__ for issue in issues],
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,11 +3,13 @@ from __future__ import annotations
|
|||||||
import json
|
import json
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
import testing.ci.quality_gate as quality_gate_module
|
||||||
from testing.ci.quality_gate import (
|
from testing.ci.quality_gate import (
|
||||||
_js_node_issues,
|
_js_node_issues,
|
||||||
_python_node_issues,
|
_python_node_issues,
|
||||||
check_coverage,
|
check_coverage,
|
||||||
check_file_sizes,
|
check_file_sizes,
|
||||||
|
run_gate,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -79,3 +81,75 @@ def test_check_coverage_reads_backend_and_frontend_reports(tmp_path: Path) -> No
|
|||||||
)
|
)
|
||||||
|
|
||||||
assert issues == []
|
assert issues == []
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_gate_reports_workspace_coverage_and_loc_totals(tmp_path: Path) -> None:
|
||||||
|
root_before = quality_gate_module.ROOT
|
||||||
|
quality_gate_module.ROOT = tmp_path
|
||||||
|
try:
|
||||||
|
backend_dir = tmp_path / "backend" / "atlas_portal"
|
||||||
|
frontend_dir = tmp_path / "frontend" / "src"
|
||||||
|
backend_dir.mkdir(parents=True)
|
||||||
|
frontend_dir.mkdir(parents=True)
|
||||||
|
|
||||||
|
(backend_dir / "app_factory.py").write_text(
|
||||||
|
'"""Factory docs."""\n\n'
|
||||||
|
"def create_app():\n"
|
||||||
|
' """Create the app."""\n'
|
||||||
|
" return object()\n"
|
||||||
|
)
|
||||||
|
(backend_dir / "too_long.py").write_text("\n".join(f"line_{idx}" for idx in range(501)))
|
||||||
|
(frontend_dir / "auth.js").write_text(
|
||||||
|
"/**\n"
|
||||||
|
" * WHY: auth wrapper exists.\n"
|
||||||
|
" * @param {string} token - jwt.\n"
|
||||||
|
" * @returns {string} token.\n"
|
||||||
|
" */\n"
|
||||||
|
"function auth(token) {\n"
|
||||||
|
" return token;\n"
|
||||||
|
"}\n"
|
||||||
|
)
|
||||||
|
|
||||||
|
backend_report = tmp_path / "backend.xml"
|
||||||
|
backend_report.write_text(
|
||||||
|
'<coverage><packages><package><classes>'
|
||||||
|
'<class filename="backend/atlas_portal/app_factory.py" line-rate="1.0" branch-rate="1.0"/>'
|
||||||
|
'</classes></package></packages></coverage>'
|
||||||
|
)
|
||||||
|
frontend_report = tmp_path / "frontend.json"
|
||||||
|
frontend_report.write_text(
|
||||||
|
json.dumps(
|
||||||
|
{
|
||||||
|
"src/auth.js": {
|
||||||
|
"lines": {"pct": 100},
|
||||||
|
"statements": {"pct": 100},
|
||||||
|
"branches": {"pct": 100},
|
||||||
|
"functions": {"pct": 100},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
contract = {
|
||||||
|
"managed_files": [
|
||||||
|
"backend/atlas_portal/app_factory.py",
|
||||||
|
"backend/atlas_portal/too_long.py",
|
||||||
|
],
|
||||||
|
"docstring_files": ["backend/atlas_portal/app_factory.py"],
|
||||||
|
"coverage_files": [
|
||||||
|
"backend/atlas_portal/app_factory.py",
|
||||||
|
"frontend/src/auth.js",
|
||||||
|
],
|
||||||
|
"max_lines": 500,
|
||||||
|
"coverage_threshold_pct": 95,
|
||||||
|
}
|
||||||
|
contract_path = tmp_path / "contract.json"
|
||||||
|
contract_path.write_text(json.dumps(contract))
|
||||||
|
|
||||||
|
issues, report = run_gate(contract_path, backend_coverage=backend_report, frontend_coverage=frontend_report)
|
||||||
|
|
||||||
|
assert any(issue.check == "loc" for issue in issues)
|
||||||
|
assert report["workspace_line_coverage_percent"] == 100.0
|
||||||
|
assert report["source_lines_over_500"] == 1
|
||||||
|
finally:
|
||||||
|
quality_gate_module.ROOT = root_before
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user