"""File-size and naming validation for the managed testing surface.""" from __future__ import annotations 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 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: 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: count += 1 return count