""" Unit tests for schema_analyzer module (Phase 2 schema refinement). """ import pytest import json from markitect.schema_analyzer import ( SchemaAnalyzer, IssueType, IssueSeverity, SchemaAnalysisResult ) class TestSchemaAnalyzer: """Tests for SchemaAnalyzer class.""" def test_analyze_flexible_schema(self): """Test analysis of a well-designed flexible schema.""" schema = { "type": "object", "x-markitect-sections": { "INTRO": { "classification": "required", "heading_level": 2 } }, "x-markitect-content-control": { "intro": { "content_quality": { "min_words": 50, "max_words": 500 } } }, "properties": { "headings": { "type": "object", "properties": { "level_2": { "type": "array", "minItems": 2, "maxItems": 10 } } } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) assert isinstance(result, SchemaAnalysisResult) assert result.has_classifications assert result.has_content_control assert result.rigidity_score < 50 assert not result.is_rigid def test_analyze_rigid_schema_exact_counts(self): """Test detection of exact count constraints.""" schema = { "type": "object", "properties": { "paragraphs": { "type": "array", "minItems": 5, "maxItems": 5 # Exact count } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) assert result.rigidity_score > 0 exact_count_issues = [i for i in result.issues if i.issue_type == IssueType.EXACT_COUNT] assert len(exact_count_issues) > 0 assert exact_count_issues[0].severity == IssueSeverity.WARNING def test_analyze_const_values(self): """Test detection of const constraints.""" schema = { "type": "object", "properties": { "level": { "type": "integer", "const": 1 } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) const_issues = [i for i in result.issues if i.issue_type == IssueType.EXACT_COUNT] assert len(const_issues) > 0 assert const_issues[0].current_value == 1 def test_analyze_overly_specific_numbers(self): """Test detection of overly specific numbers.""" schema = { "type": "object", "properties": { "items": { "type": "array", "minItems": 73 # Overly specific } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) specific_issues = [i for i in result.issues if i.issue_type == IssueType.OVERLY_SPECIFIC] assert len(specific_issues) > 0 assert specific_issues[0].current_value == 73 assert specific_issues[0].suggested_value == 70 # Should be rounded def test_analyze_narrow_range(self): """Test detection of narrow integer ranges.""" schema = { "type": "object", "properties": { "score": { "type": "integer", "minimum": 5, "maximum": 6 # Very narrow range } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) narrow_issues = [i for i in result.issues if i.issue_type == IssueType.NO_FLEXIBILITY] assert len(narrow_issues) > 0 def test_analyze_deprecated_extensions(self): """Test detection of deprecated extensions.""" schema = { "type": "object", "x-markitect-required-sections": ["INTRO", "CONCLUSION"] } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) assert result.uses_deprecated_extensions deprecated_issues = [i for i in result.issues if i.issue_type == IssueType.DEPRECATED_EXTENSIONS] assert len(deprecated_issues) > 0 assert deprecated_issues[0].severity == IssueSeverity.WARNING def test_analyze_missing_classifications(self): """Test detection of missing classification system.""" schema = { "type": "object", "properties": { "headings": { "type": "object" } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) assert not result.has_classifications classification_issues = [i for i in result.issues if i.issue_type == IssueType.MISSING_CLASSIFICATIONS] assert len(classification_issues) > 0 assert classification_issues[0].severity == IssueSeverity.INFO def test_analyze_missing_content_control(self): """Test detection of missing content control.""" schema = { "type": "object", "x-markitect-sections": { "INTRO": {"classification": "required"} } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) assert result.has_classifications assert not result.has_content_control content_issues = [i for i in result.issues if i.issue_type == IssueType.MISSING_CONTENT_INSTRUCTIONS] assert len(content_issues) > 0 def test_rigidity_score_calculation(self): """Test rigidity score calculation with multiple issues.""" schema = { "type": "object", "properties": { "array1": { "type": "array", "minItems": 5, "maxItems": 5 }, "array2": { "type": "array", "minItems": 73 }, "number": { "type": "integer", "const": 42 } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) # Should have moderate rigidity with multiple issues assert result.rigidity_score > 30 assert result.rigidity_score < 60 # Moderate range def test_issue_count_by_severity(self): """Test counting issues by severity.""" schema = { "type": "object", "properties": { "items": { "type": "array", "minItems": 1, "maxItems": 1 } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) counts = result.issue_count_by_severity assert IssueSeverity.WARNING in counts assert IssueSeverity.ERROR in counts assert IssueSeverity.INFO in counts def test_nested_properties_analysis(self): """Test analysis of nested property structures.""" schema = { "type": "object", "properties": { "outer": { "type": "object", "properties": { "inner": { "type": "array", "minItems": 3, "maxItems": 3 } } } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) # Should detect exact count in nested property exact_count_issues = [i for i in result.issues if i.issue_type == IssueType.EXACT_COUNT] assert len(exact_count_issues) > 0 assert "properties.outer.inner" in exact_count_issues[0].path def test_format_analysis_report(self): """Test report formatting.""" schema = { "type": "object", "properties": { "items": { "type": "array", "minItems": 1, "maxItems": 1 } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) report = analyzer.format_analysis_report(result, verbose=False) assert "Schema Analysis Report" in report assert "Rigidity Score" in report assert "Issues Found" in report def test_format_analysis_report_verbose(self): """Test verbose report formatting.""" schema = { "type": "object", "properties": { "items": { "type": "array", "minItems": 5, "maxItems": 5 } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) report = analyzer.format_analysis_report(result, verbose=True) assert "Current:" in report assert "Suggested:" in report def test_analyze_array_items_with_properties(self): """Test analysis of array items that have nested properties.""" schema = { "type": "object", "properties": { "headings": { "type": "array", "items": { "type": "object", "properties": { "level": { "type": "integer", "const": 1 } } } } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) # Should detect const in nested items const_issues = [i for i in result.issues if i.issue_type == IssueType.EXACT_COUNT] assert len(const_issues) > 0 assert "items" in const_issues[0].path def test_empty_schema(self): """Test analysis of minimal/empty schema.""" schema = { "type": "object" } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) # Should detect missing features but not crash assert not result.has_classifications assert not result.has_content_control assert result.rigidity_score < 50 # Not rigid, just minimal def test_no_issues_schema(self): """Test schema with perfect design (no issues).""" schema = { "type": "object", "x-markitect-sections": { "INTRO": { "classification": "required", "heading_level": 2, "content_instruction": "Introduction section" } }, "x-markitect-content-control": { "intro": { "content_quality": { "min_words": 50, "max_words": 500 } } }, "properties": { "paragraphs": { "type": "array", "minItems": 5, "maxItems": 50 # Good range } } } analyzer = SchemaAnalyzer() result = analyzer.analyze_schema(schema) report = analyzer.format_analysis_report(result) assert result.rigidity_score < 20 assert not result.is_rigid assert "No issues found" in report or result.issue_count_by_severity[IssueSeverity.WARNING] == 0