atlas-iac/testing/quality_hygiene.py

80 lines
2.9 KiB
Python

"""File-size and naming validation for the managed testing surface."""
from __future__ import annotations
import hashlib
import re
from collections.abc import Iterable
from pathlib import Path
from typing import Any
def _expand_globs(root: Path, patterns: Iterable[str]) -> list[Path]:
"""Expand a set of relative glob patterns to unique file paths."""
matched: set[Path] = set()
for pattern in patterns:
matched.update(path for path in root.glob(pattern) if path.is_file())
return sorted(matched)
def _is_unchanged_legacy(
config: dict[str, Any], root: Path, path: Path, line_count: int
) -> bool:
"""Allow an over-cap legacy file only while its exact bytes stay unchanged."""
relative = path.relative_to(root).as_posix()
record = config.get("legacy_line_exceptions", {}).get(relative)
if not isinstance(record, dict) or record.get("lines") != line_count:
return False
digest = hashlib.sha256(path.read_bytes()).hexdigest()
return record.get("sha256") == digest
def run_check(contract: dict[str, Any], root: Path) -> list[str]:
"""Return human-readable issues for naming and file-size rules."""
config = contract.get("hygiene", {})
max_lines = int(config.get("max_lines", 500))
issues: list[str] = []
for path in _expand_globs(root, config.get("line_limit_globs", [])):
line_count = sum(1 for _ in path.open("r", encoding="utf-8"))
if line_count > max_lines and not _is_unchanged_legacy(
config, root, path, line_count
):
issues.append(
f"file exceeds {max_lines} LOC: {path.relative_to(root)} ({line_count})"
)
for rule in config.get("naming_rules", []):
pattern = re.compile(rule["pattern"])
for path in _expand_globs(root, [rule["glob"]]):
if path.name == "conftest.py":
continue
if not pattern.match(path.name):
issues.append(
f"naming rule failed ({rule['description']}): {path.relative_to(root)}"
)
return issues
def count_files_over_line_limit(contract: dict[str, Any], root: Path) -> int:
"""Return the number of managed files that exceed the configured LOC cap."""
config = contract.get("hygiene", {})
max_lines = int(config.get("max_lines", 500))
count = 0
for path in _expand_globs(root, config.get("line_limit_globs", [])):
line_count = sum(1 for _ in path.open("r", encoding="utf-8"))
if line_count > max_lines and not _is_unchanged_legacy(
config, root, path, line_count
):
count += 1
return count
def count_files_with_line_limit(contract: dict[str, Any], root: Path) -> int:
"""Return the number of managed files included in the LOC cap."""
config = contract.get("hygiene", {})
return len(_expand_globs(root, config.get("line_limit_globs", [])))