From 387e104359a388ea568f557191d5366075fa2594 Mon Sep 17 00:00:00 2001 From: jenkins Date: Mon, 20 Apr 2026 21:33:46 -0300 Subject: [PATCH] test(titan-iac): widen tracked quality coverage --- testing/quality_contract.json | 14 ++- .../test_publish_test_metrics_quality.py | 105 ++++++++++++++++++ .../tests/test_quality_coverage_helpers.py | 76 +++++++++++++ testing/tests/test_quality_hygiene_helpers.py | 26 +++++ 4 files changed, 219 insertions(+), 2 deletions(-) create mode 100644 testing/tests/test_publish_test_metrics_quality.py create mode 100644 testing/tests/test_quality_coverage_helpers.py create mode 100644 testing/tests/test_quality_hygiene_helpers.py diff --git a/testing/quality_contract.json b/testing/quality_contract.json index 06741e18..371e8e1e 100644 --- a/testing/quality_contract.json +++ b/testing/quality_contract.json @@ -16,20 +16,27 @@ ], "managed_modules": [ "ci/scripts/publish_test_metrics.py", - "services/mailu/scripts/mailu_sync.py", + "ci/scripts/publish_test_metrics_quality.py", "testing/__init__.py", "testing/quality_contract.py", "testing/quality_docs.py", "testing/quality_hygiene.py", "testing/quality_coverage.py", - "testing/quality_gate.py" + "testing/quality_gate.py", + "ci/tests/glue/test_ariadne_schedules.py", + "ci/tests/glue/test_glue_metrics.py", + "testing/tests/test_publish_test_metrics.py", + "testing/tests/test_quality_contract.py", + "testing/tests/test_quality_gate.py" ], "lint_paths": [ "ci/scripts/publish_test_metrics.py", + "ci/scripts/publish_test_metrics_quality.py", "ci/tests/glue", "scripts/tests", "services/comms/scripts/tests", "services/mailu/scripts/mailu_sync.py", + "testing/tests", "testing" ], "pytest_suites": { @@ -70,6 +77,8 @@ "hygiene", "unit", "coverage", + "sonarqube", + "ironbank", "glue" ] }, @@ -151,6 +160,7 @@ "minimum_percent": 95.0, "tracked_files": [ "ci/scripts/publish_test_metrics.py", + "ci/scripts/publish_test_metrics_quality.py", "testing/quality_contract.py", "testing/quality_docs.py", "testing/quality_hygiene.py", diff --git a/testing/tests/test_publish_test_metrics_quality.py b/testing/tests/test_publish_test_metrics_quality.py new file mode 100644 index 00000000..2b6d2cb7 --- /dev/null +++ b/testing/tests/test_publish_test_metrics_quality.py @@ -0,0 +1,105 @@ +"""Focused tests for publish_test_metrics quality helper fallbacks.""" + +from __future__ import annotations + +from pathlib import Path + +from ci.scripts import publish_test_metrics_quality as quality_helpers + + +def test_infer_workspace_coverage_percent_handles_candidate_paths_and_parse_failures(tmp_path: Path) -> None: + """Coverage inference should honor explicit XML hints and fail closed on bad XML.""" + + build_dir = tmp_path / "build" + build_dir.mkdir() + + coverage_xml = build_dir / "custom-coverage.xml" + coverage_xml.write_text('', encoding="utf-8") + summary = {"results": [None, {"name": "coverage", "coverage_xml": str(coverage_xml)}]} + assert quality_helpers._infer_workspace_coverage_percent(summary, "build/default.xml") == 97.5 + + missing_xml = build_dir / "missing.xml" + assert quality_helpers._infer_workspace_coverage_percent({}, str(missing_xml)) == 0.0 + + no_rate_xml = build_dir / "no-rate.xml" + no_rate_xml.write_text("", encoding="utf-8") + assert quality_helpers._infer_workspace_coverage_percent({}, str(no_rate_xml)) == 0.0 + + bad_xml = build_dir / "bad.xml" + bad_xml.write_text(" None: + """LOC inference should ignore non-lists and only count explicit over-limit strings.""" + + summary = { + "results": [ + None, + {"name": "docs", "issues": ["ignore me"]}, + {"name": "loc", "issues": "not-a-list"}, + { + "name": "hygiene", + "issues": [ + "file exceeds 500 LOC: alpha.py (501)", + 42, + "naming rule failed: beta.py", + "file exceeds 500 LOC: gamma.py (650)", + ], + }, + ] + } + + assert quality_helpers._infer_source_lines_over_500(summary) == 2 + + +def test_build_check_statuses_uses_fallbacks_for_tests_docs_and_gate_glue() -> None: + """Fallback status synthesis should derive missing checks from related signals.""" + + statuses = quality_helpers._build_check_statuses( + summary={ + "results": [ + {"name": "docs", "status": "ok"}, + {"name": "smell", "status": "ok"}, + {"name": "gate", "status": "warning"}, + ] + }, + tests={"tests": 3, "failures": 1, "errors": 0, "skipped": 0}, + workspace_line_coverage_percent=0.0, + source_lines_over_500=0, + sonarqube_report={}, + supply_chain_report={}, + supply_chain_required=False, + ) + + assert statuses["tests"] == "failed" + assert statuses["coverage"] == "not_applicable" + assert statuses["loc"] == "ok" + assert statuses["docs_naming"] == "ok" + assert statuses["gate_glue"] == "failed" + + +def test_build_check_statuses_preserves_explicit_loc_docs_and_glue_results() -> None: + """Explicit canonical statuses should win over fallback inference.""" + + statuses = quality_helpers._build_check_statuses( + summary={ + "results": [ + {"name": "loc", "status": "ok"}, + {"name": "docs_naming", "status": "ok"}, + {"name": "gate_glue", "status": "ok"}, + ] + }, + tests={"tests": 0, "failures": 0, "errors": 0, "skipped": 0}, + workspace_line_coverage_percent=96.0, + source_lines_over_500=9, + sonarqube_report={}, + supply_chain_report={}, + supply_chain_required=False, + ) + + assert statuses["tests"] == "not_applicable" + assert statuses["coverage"] == "ok" + assert statuses["loc"] == "ok" + assert statuses["docs_naming"] == "ok" + assert statuses["gate_glue"] == "ok" diff --git a/testing/tests/test_quality_coverage_helpers.py b/testing/tests/test_quality_coverage_helpers.py new file mode 100644 index 00000000..5eaa624e --- /dev/null +++ b/testing/tests/test_quality_coverage_helpers.py @@ -0,0 +1,76 @@ +"""Focused coverage-helper tests for titan-iac quality modules.""" + +from __future__ import annotations + +import textwrap +from pathlib import Path + +from testing.quality_coverage import compute_workspace_line_coverage, run_check + + +def test_compute_workspace_line_coverage_handles_missing_xml(tmp_path: Path) -> None: + """Missing coverage XML should produce a zero workspace coverage score.""" + + contract = {"coverage": {"tracked_files": ["managed.py"]}} + assert compute_workspace_line_coverage(contract, tmp_path, tmp_path / "missing.xml") == 0.0 + + +def test_compute_workspace_line_coverage_averages_present_tracked_files(tmp_path: Path) -> None: + """Workspace coverage should average only tracked files that appear in the report.""" + + coverage_xml = tmp_path / "coverage.xml" + coverage_xml.write_text( + textwrap.dedent( + """\ + + + + + + + + + + + """ + ), + encoding="utf-8", + ) + + contract = {"coverage": {"tracked_files": ["alpha.py", "beta.py", "missing.py"]}} + assert compute_workspace_line_coverage(contract, tmp_path, coverage_xml) == 75.0 + + +def test_run_check_keeps_relative_names_when_source_roots_do_not_match(tmp_path: Path) -> None: + """Relative filenames should remain relative when no declared source root contains them.""" + + coverage_xml = tmp_path / "coverage.xml" + unmatched_root = tmp_path / "other-root" + unmatched_root.mkdir() + coverage_xml.write_text( + textwrap.dedent( + f"""\ + + + {unmatched_root} + + + + + + + + + + """ + ), + encoding="utf-8", + ) + + issues = run_check( + {"coverage": {"minimum_percent": 95.0, "tracked_files": ["relative.py"]}}, + tmp_path, + coverage_xml, + ) + + assert issues == ["coverage below 95.0%: relative.py (80.0%)"] diff --git a/testing/tests/test_quality_hygiene_helpers.py b/testing/tests/test_quality_hygiene_helpers.py new file mode 100644 index 00000000..a303c650 --- /dev/null +++ b/testing/tests/test_quality_hygiene_helpers.py @@ -0,0 +1,26 @@ +"""Focused hygiene-helper tests for titan-iac quality modules.""" + +from __future__ import annotations + +from pathlib import Path + +from testing.quality_hygiene import count_files_over_line_limit + + +def test_count_files_over_line_limit_counts_only_long_matches(tmp_path: Path) -> None: + """Only files beyond the configured limit should contribute to the LOC total.""" + + tests_dir = tmp_path / "tests" + tests_dir.mkdir() + (tests_dir / "test_short.py").write_text("line\n", encoding="utf-8") + (tests_dir / "test_long.py").write_text("line\n" * 4, encoding="utf-8") + (tests_dir / "notes.txt").write_text("line\n" * 8, encoding="utf-8") + + contract = { + "hygiene": { + "max_lines": 3, + "line_limit_globs": ["tests/*.py"], + } + } + + assert count_files_over_line_limit(contract, tmp_path) == 1