contract_validator.py

Code Hygiene Score: 97

Issues 1

Zeile Typ Beschreibung
109 magic_number Magic Number gefunden: 100

Dependencies 6

Klassen 1

Funktionen 1

Code

"""Contract Validator - Orchestriert Contract-Validierung."""

import os
from typing import Optional, Any

from domain.contracts import ContractValidationResult
from validators.scope_resolver import ScopeResolver
from validators.rule_evaluator import RuleEvaluator


class ContractValidator:
    """Orchestriert Contract-Validierung mittels ScopeResolver und RuleEvaluator."""

    BASE_PATH = "/var/www/dev.campus.systemische-tools.de"
    SUPPORTED_EXTENSIONS = (".php", ".js", ".css", ".py")

    def __init__(
        self,
        contract_data: dict[str, Any],
        scope_resolver: Optional[ScopeResolver] = None,
        rule_evaluator: Optional[RuleEvaluator] = None,
    ):
        self.contract_data = contract_data
        self.contract_name = contract_data.get("contract", {}).get("name", "unknown")
        self.scope_resolver = scope_resolver or ScopeResolver(self.BASE_PATH)
        self.rule_evaluator = rule_evaluator or RuleEvaluator()

    def validate(self, target_path: Optional[str] = None) -> ContractValidationResult:
        """Fuehrt vollstaendige Validierung durch."""
        result = ContractValidationResult(
            contract=self.contract_name,
            outcome="passed",
            critical=0,
            major=0,
            minor=0,
            findings=[],
        )

        contract = self.contract_data.get("contract", {})
        scope = contract.get("scope", {})
        legacy_applicability = self.contract_data.get("applicability")

        check_paths = self.scope_resolver.resolve_paths(
            scope, target_path, legacy_applicability
        )

        if not check_paths:
            result.findings.append({"type": "info", "message": "No paths to validate"})
            return result

        for check_path in check_paths:
            if not os.path.exists(check_path):
                result.critical += 1
                result.findings.append({
                    "type": "critical",
                    "factor": "path_existence",
                    "message": f"Path does not exist: {check_path}",
                })
                continue

            if os.path.isdir(check_path):
                self._validate_directory(check_path, result)
            else:
                self._validate_file(check_path, result)

        result.outcome = self.rule_evaluator.determine_outcome(result)
        return result

    def _validate_directory(self, dir_path: str, result: ContractValidationResult) -> None:
        """Validiert alle Dateien in einem Verzeichnis."""
        for root, _, files in os.walk(dir_path):
            for f in files:
                if f.endswith(self.SUPPORTED_EXTENSIONS):
                    self._validate_file(os.path.join(root, f), result)

    def _validate_file(self, file_path: str, result: ContractValidationResult) -> None:
        """Validiert eine einzelne Datei gegen den Contract."""
        try:
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()

            contract = self.contract_data.get("contract", {})

            # Rules-basierte Validierung
            for rule in contract.get("rules", []):
                in_files = rule.get("in_files", "**/*")
                if self.scope_resolver.file_matches_pattern(file_path, in_files):
                    self.rule_evaluator.evaluate_rule(file_path, content, rule, result)

            # Legacy-Formate
            forbidden = contract.get("forbidden", [])
            if forbidden:
                self.rule_evaluator.evaluate_legacy_forbidden(
                    file_path, content, forbidden, result
                )

            required = contract.get("required", [])
            if required:
                self.rule_evaluator.evaluate_legacy_required(
                    file_path, content, required, result
                )

        except Exception as e:
            result.minor += 1
            result.findings.append({
                "type": "minor",
                "factor": "file_read_error",
                "file": file_path,
                "message": str(e)[:100],
            })


def run_contract_validation(
    contract_data: dict[str, Any],
    target_path: Optional[str] = None,
) -> ContractValidationResult:
    """Hilfsfunktion fuer Contract-Validierung."""
    validator = ContractValidator(contract_data)
    return validator.validate(target_path)
← Übersicht Graph