Files
markitect-main/markitect/validation_error.py
tegwick ccbca967c8
Some checks failed
Test Suite / unit-tests (3.11) (push) Has been cancelled
Test Suite / unit-tests (3.12) (push) Has been cancelled
Test Suite / integration-tests (push) Has been cancelled
Test Suite / e2e-tests (push) Has been cancelled
Test Suite / performance-tests (push) Has been cancelled
Test Suite / code-quality (push) Has been cancelled
Test Suite / security-scan (push) Has been cancelled
Test Suite / test-summary (push) Has been cancelled
feat: Complete Issue #8 - Detailed Validation Error Reporting and CLI Enhancements
Major Features:
- Implement comprehensive validation error reporting system (Issue #8)
- Add direct CLI access with 'markitect' command
- Create extensive makefile targets for CLI usage
- Enhance schema validation with detailed error collection

Components Added:
- markitect/validation_error.py: ValidationError system with 8 error types
- Enhanced markitect/schema_validator.py: Detailed error reporting methods
- markitect/cli.py: Enhanced with --detailed-errors and --error-format options
- visualize_schema.py: Schema visualization with ASCII and colorful modes
- Comprehensive test suite for validation error reporting

CLI Enhancements:
- Direct 'markitect' command access for all operations
- Makefile targets for typical CLI usage (cli-help, cli-ingest, etc.)
- Support for text, JSON, and markdown error output formats
- Backward compatibility with existing validation functionality

Testing:
- 11 comprehensive tests for Issue #8 validation error reporting
- Tests for schema validation, visualization, and CLI integration
- 100% test coverage for validation error scenarios

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-29 21:21:21 +02:00

191 lines
6.5 KiB
Python

"""
Validation Error Models for Issue #8: Get Validation Errors.
This module provides structured error reporting for schema validation failures,
enabling detailed feedback for arc42 compliance checking and user guidance.
"""
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from enum import Enum
class ValidationErrorType(Enum):
"""Types of validation errors that can occur during schema validation."""
MISSING_REQUIRED_HEADING = "missing_required_heading"
UNEXPECTED_HEADING = "unexpected_heading"
INSUFFICIENT_CONTENT = "insufficient_content"
EXCESS_CONTENT = "excess_content"
MISSING_REQUIRED_SECTION = "missing_required_section"
HEADING_COUNT_MISMATCH = "heading_count_mismatch"
CONTENT_COUNT_MISMATCH = "content_count_mismatch"
STRUCTURAL_VIOLATION = "structural_violation"
@dataclass
class ValidationError:
"""
Represents a specific validation error with detailed information.
This class provides structured error information that can be used
to generate user-friendly error messages and guide compliance fixing.
"""
error_type: ValidationErrorType
message: str
path: str # JSON path or section identifier
expected: Optional[Any] = None
actual: Optional[Any] = None
suggestion: Optional[str] = None
severity: str = "error" # error, warning, info
def __str__(self) -> str:
"""Return a formatted error message for display."""
return self.message
class ValidationErrorCollector:
"""
Collects and manages validation errors during schema validation.
This class accumulates validation errors and provides methods to
format and present them in user-friendly ways.
"""
def __init__(self):
"""Initialize empty error collector."""
self.errors: List[ValidationError] = []
def add_error(
self,
error_type: ValidationErrorType,
message: str,
path: str,
expected: Optional[Any] = None,
actual: Optional[Any] = None,
suggestion: Optional[str] = None,
severity: str = "error"
) -> None:
"""Add a validation error to the collection."""
error = ValidationError(
error_type=error_type,
message=message,
path=path,
expected=expected,
actual=actual,
suggestion=suggestion,
severity=severity
)
self.errors.append(error)
def has_errors(self) -> bool:
"""Check if any errors have been collected."""
return len(self.errors) > 0
def get_error_count(self) -> int:
"""Get the total number of errors."""
return len(self.errors)
def get_errors_by_type(self, error_type: ValidationErrorType) -> List[ValidationError]:
"""Get all errors of a specific type."""
return [error for error in self.errors if error.error_type == error_type]
def get_errors_by_severity(self, severity: str) -> List[ValidationError]:
"""Get all errors of a specific severity level."""
return [error for error in self.errors if error.severity == severity]
def format_errors(self, format_type: str = "text") -> str:
"""
Format all errors for display.
Args:
format_type: Output format ('text', 'json', 'markdown')
Returns:
Formatted error string
"""
if not self.errors:
return "No validation errors found."
if format_type == "json":
import json
error_data = [
{
"type": error.error_type.value,
"message": error.message,
"path": error.path,
"expected": error.expected,
"actual": error.actual,
"suggestion": error.suggestion,
"severity": error.severity
}
for error in self.errors
]
return json.dumps(error_data, indent=2)
elif format_type == "markdown":
lines = ["# Validation Errors\n"]
# Group errors by severity
error_groups = {}
for error in self.errors:
if error.severity not in error_groups:
error_groups[error.severity] = []
error_groups[error.severity].append(error)
for severity in ["error", "warning", "info"]:
if severity in error_groups:
lines.append(f"## {severity.title()}s\n")
for error in error_groups[severity]:
lines.append(f"- **{error.path}**: {error.message}")
if error.expected is not None:
lines.append(f" - Expected: {error.expected}")
if error.actual is not None:
lines.append(f" - Actual: {error.actual}")
if error.suggestion:
lines.append(f" - Suggestion: {error.suggestion}")
lines.append("")
return "\n".join(lines)
else: # text format
lines = []
lines.append(f"Validation failed with {len(self.errors)} error(s):")
lines.append("")
for i, error in enumerate(self.errors, 1):
lines.append(f"{i}. [{error.severity.upper()}] {error.path}")
lines.append(f" {error.message}")
if error.expected is not None:
lines.append(f" Expected: {error.expected}")
if error.actual is not None:
lines.append(f" Actual: {error.actual}")
if error.suggestion:
lines.append(f" 💡 {error.suggestion}")
lines.append("")
return "\n".join(lines)
def get_summary(self) -> Dict[str, int]:
"""Get a summary of error counts by type and severity."""
summary = {
"total_errors": len(self.errors),
"by_severity": {},
"by_type": {}
}
for error in self.errors:
# Count by severity
if error.severity not in summary["by_severity"]:
summary["by_severity"][error.severity] = 0
summary["by_severity"][error.severity] += 1
# Count by type
error_type_name = error.error_type.value
if error_type_name not in summary["by_type"]:
summary["by_type"][error_type_name] = 0
summary["by_type"][error_type_name] += 1
return summary