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
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>
191 lines
6.5 KiB
Python
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 |