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
Completed Phase 3 of the schema-of-schemas implementation with a comprehensive metaschema that validates all MarkiTect schema files against conventions and standards. Metaschema Implementation (schema-schema-v1.0.md - 650+ lines): - Validates core JSON Schema fields ($schema, $id, title, description) - Validates MarkiTect version field (SemVer: major.minor.patch) - Validates $id URL format (HTTPS with version path) - Validates MarkiTect extensions: - x-markitect-sections: section classifications and content rules - x-markitect-content-control: pattern and quality validation - x-markitect-metadata: status, authors, tags - x-markitect-source: loader metadata (auto-added) - Section classification validation (required, recommended, optional, discouraged, improper) - Content control pattern validation - Comprehensive documentation with examples and usage guides CLI Command (markitect schema-validate): - Validates schema files against metaschema - Supports both markdown and JSON schema files - Detailed error reporting with schema paths - Structure validation recommendations - Exit codes for CI/CD integration Test Coverage (tests/test_schema_metaschema.py - 12 tests, 100% passing): - Metaschema self-validation - Manpage schema validation - Required fields enforcement - Version format validation (valid and invalid cases) - $id format validation (valid and invalid cases) - Section classification validation - Complete schema with all extensions Validation Results: - ✅ Metaschema validates itself successfully - ✅ Manpage schema (v1.0.md) validates successfully - ⚠️ Terminology schema needs migration (missing version, incorrect $id) Progress Tracking: - Updated TODO.md with Phase 3 completion - Updated CHANGELOG.md with implementation details - Next: Phase 4 - Schema Migration 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
301 lines
12 KiB
Python
301 lines
12 KiB
Python
"""
|
|
Unit tests for schema metaschema validation.
|
|
|
|
Tests that schemas validate correctly against the schema-for-schemas metaschema.
|
|
"""
|
|
|
|
import pytest
|
|
import json
|
|
from pathlib import Path
|
|
try:
|
|
from jsonschema import Draft7Validator
|
|
JSONSCHEMA_AVAILABLE = True
|
|
except ImportError:
|
|
JSONSCHEMA_AVAILABLE = False
|
|
|
|
from markitect.schema_loader import MarkdownSchemaLoader
|
|
|
|
|
|
@pytest.fixture
|
|
def loader():
|
|
"""Create a schema loader instance."""
|
|
return MarkdownSchemaLoader()
|
|
|
|
|
|
@pytest.fixture
|
|
def metaschema(loader):
|
|
"""Load the metaschema."""
|
|
metaschema_path = Path(__file__).parent.parent / 'markitect' / 'schemas' / 'schema-schema-v1.0.md'
|
|
metaschema_data = loader.load_schema(metaschema_path)
|
|
return metaschema_data['schema']
|
|
|
|
|
|
@pytest.mark.skipif(not JSONSCHEMA_AVAILABLE, reason="jsonschema not installed")
|
|
class TestMetaschemaValidation:
|
|
"""Tests for validating schemas against the metaschema."""
|
|
|
|
def test_metaschema_self_validation(self, loader, metaschema):
|
|
"""Test that metaschema validates itself."""
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(metaschema))
|
|
|
|
assert len(errors) == 0, f"Metaschema should validate itself, but got errors: {errors}"
|
|
|
|
def test_manpage_schema_validation(self, loader, metaschema):
|
|
"""Test that manpage schema validates against metaschema."""
|
|
manpage_path = Path(__file__).parent.parent / 'markitect' / 'schemas' / 'manpage-schema-v1.0.md'
|
|
manpage_data = loader.load_schema(manpage_path)
|
|
manpage_schema = manpage_data['schema']
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(manpage_schema))
|
|
|
|
assert len(errors) == 0, f"Manpage schema should be valid, but got errors: {[e.message for e in errors]}"
|
|
|
|
def test_required_fields_enforced(self, metaschema):
|
|
"""Test that metaschema enforces required fields."""
|
|
# Schema missing required fields
|
|
invalid_schema = {
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
# Missing: $id, title, description, version
|
|
}
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(invalid_schema))
|
|
|
|
# Should have errors for missing required fields
|
|
assert len(errors) > 0
|
|
error_messages = [e.message for e in errors]
|
|
|
|
# Check that required fields are mentioned
|
|
required_fields = ['$id', 'title', 'description', 'version']
|
|
for field in required_fields:
|
|
assert any(field in msg for msg in error_messages), \
|
|
f"Should report missing required field: {field}"
|
|
|
|
def test_version_format_validation(self, metaschema):
|
|
"""Test that metaschema validates version format."""
|
|
# Invalid version formats
|
|
invalid_versions = [
|
|
"1.0", # Missing patch
|
|
"v1.0.0", # Has v prefix
|
|
"1", # Only major
|
|
"1.0.0.0", # Too many parts
|
|
]
|
|
|
|
for invalid_version in invalid_versions:
|
|
schema = {
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": "https://markitect.dev/schemas/test/v1.0",
|
|
"title": "Test Schema",
|
|
"description": "Test schema for validation",
|
|
"version": invalid_version
|
|
}
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(schema))
|
|
|
|
assert len(errors) > 0, f"Should reject invalid version: {invalid_version}"
|
|
assert any('pattern' in str(e.schema_path) for e in errors), \
|
|
f"Should be a pattern error for version: {invalid_version}"
|
|
|
|
def test_valid_version_format(self, metaschema):
|
|
"""Test that valid version formats are accepted."""
|
|
valid_versions = [
|
|
"1.0.0",
|
|
"2.5.3",
|
|
"10.25.99",
|
|
"0.0.1",
|
|
]
|
|
|
|
for valid_version in valid_versions:
|
|
schema = {
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": "https://markitect.dev/schemas/test/v1.0",
|
|
"title": "Test Schema",
|
|
"description": "Test schema for validation",
|
|
"version": valid_version
|
|
}
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(schema))
|
|
|
|
# Filter out errors not related to version
|
|
version_errors = [e for e in errors if 'version' in str(e.path)]
|
|
assert len(version_errors) == 0, \
|
|
f"Should accept valid version: {valid_version}, but got errors: {version_errors}"
|
|
|
|
def test_id_format_validation(self, metaschema):
|
|
"""Test that metaschema validates $id format."""
|
|
invalid_ids = [
|
|
"http://example.com/schema", # Not HTTPS
|
|
"https://example.com/schema", # No version
|
|
"schema/v1.0", # Not a URL
|
|
"https://example.com/schemas/test/1.0", # No 'v' prefix
|
|
]
|
|
|
|
for invalid_id in invalid_ids:
|
|
schema = {
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": invalid_id,
|
|
"title": "Test Schema",
|
|
"description": "Test schema for validation",
|
|
"version": "1.0.0"
|
|
}
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(schema))
|
|
|
|
assert len(errors) > 0, f"Should reject invalid $id: {invalid_id}"
|
|
|
|
def test_valid_id_format(self, metaschema):
|
|
"""Test that valid $id formats are accepted."""
|
|
valid_ids = [
|
|
"https://markitect.dev/schemas/test/v1.0",
|
|
"https://example.com/schemas/my-schema/v2.5",
|
|
"https://api.example.com/schemas/domain/v10.25",
|
|
]
|
|
|
|
for valid_id in valid_ids:
|
|
schema = {
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": valid_id,
|
|
"title": "Test Schema",
|
|
"description": "Test schema for validation",
|
|
"version": "1.0.0"
|
|
}
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(schema))
|
|
|
|
# Filter out errors not related to $id
|
|
id_errors = [e for e in errors if '$id' in str(e.path)]
|
|
assert len(id_errors) == 0, \
|
|
f"Should accept valid $id: {valid_id}, but got errors: {id_errors}"
|
|
|
|
def test_section_classification_validation(self, metaschema):
|
|
"""Test that section classifications are validated."""
|
|
# Invalid classification
|
|
schema = {
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": "https://markitect.dev/schemas/test/v1.0",
|
|
"title": "Test Schema",
|
|
"description": "Test schema for validation",
|
|
"version": "1.0.0",
|
|
"x-markitect-sections": {
|
|
"SYNOPSIS": {
|
|
"classification": "mandatory", # Invalid, should be 'required'
|
|
"heading_level": 2
|
|
}
|
|
}
|
|
}
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(schema))
|
|
|
|
assert len(errors) > 0
|
|
# Should have error about invalid enum value
|
|
assert any('enum' in str(e.schema_path) or 'mandatory' in e.message for e in errors)
|
|
|
|
def test_valid_section_classifications(self, metaschema):
|
|
"""Test that valid section classifications are accepted."""
|
|
classifications = ['required', 'recommended', 'optional', 'discouraged', 'improper']
|
|
|
|
for classification in classifications:
|
|
schema = {
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": "https://markitect.dev/schemas/test/v1.0",
|
|
"title": "Test Schema",
|
|
"description": "Test schema for validation",
|
|
"version": "1.0.0",
|
|
"x-markitect-sections": {
|
|
"TEST_SECTION": {
|
|
"classification": classification,
|
|
"heading_level": 2
|
|
}
|
|
}
|
|
}
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(schema))
|
|
|
|
# Filter errors related to classification
|
|
classification_errors = [e for e in errors if 'classification' in str(e.path)]
|
|
assert len(classification_errors) == 0, \
|
|
f"Should accept valid classification: {classification}"
|
|
|
|
def test_schema_with_all_extensions(self, metaschema):
|
|
"""Test schema with all MarkiTect extensions."""
|
|
schema = {
|
|
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
"$id": "https://markitect.dev/schemas/test/v1.0",
|
|
"title": "Complete Test Schema",
|
|
"description": "Schema with all MarkiTect extensions",
|
|
"version": "1.0.0",
|
|
"type": "object",
|
|
"x-markitect-sections": {
|
|
"SYNOPSIS": {
|
|
"classification": "required",
|
|
"heading_level": 2,
|
|
"content_instruction": "Brief overview",
|
|
"min_paragraphs": 1,
|
|
"max_paragraphs": 3
|
|
}
|
|
},
|
|
"x-markitect-content-control": {
|
|
"synopsis": {
|
|
"required_patterns": ["\\*\\*.*\\*\\*"],
|
|
"content_quality": {
|
|
"min_words": 10,
|
|
"max_words": 100,
|
|
"readability_target": "technical"
|
|
}
|
|
}
|
|
},
|
|
"x-markitect-metadata": {
|
|
"status": "stable",
|
|
"authors": ["Test Author <test@example.com>"],
|
|
"tags": ["test", "example"]
|
|
}
|
|
}
|
|
|
|
validator = Draft7Validator(metaschema)
|
|
errors = list(validator.iter_errors(schema))
|
|
|
|
assert len(errors) == 0, f"Complete schema should be valid, but got errors: {[e.message for e in errors]}"
|
|
|
|
|
|
class TestSchemaLoaderIntegration:
|
|
"""Integration tests for schema loader with metaschema."""
|
|
|
|
def test_load_and_validate_manpage_schema(self, loader, metaschema):
|
|
"""Test loading and validating manpage schema."""
|
|
manpage_path = Path(__file__).parent.parent / 'markitect' / 'schemas' / 'manpage-schema-v1.0.md'
|
|
|
|
# Load schema
|
|
schema_data = loader.load_schema(manpage_path)
|
|
schema = schema_data['schema']
|
|
|
|
# Check metadata was merged
|
|
assert 'x-markitect-source' in schema
|
|
assert schema['x-markitect-source']['format'] == 'markdown'
|
|
assert schema['x-markitect-source']['filename'] == 'manpage-schema-v1.0.md'
|
|
|
|
# Validate structure
|
|
issues = loader.validate_schema_structure(schema)
|
|
# Should have no critical issues
|
|
assert all('Missing' not in issue or 'recommended' in issue.lower() for issue in issues)
|
|
|
|
def test_metaschema_structure_validation(self, loader):
|
|
"""Test metaschema structure with loader's validator."""
|
|
metaschema_path = Path(__file__).parent.parent / 'markitect' / 'schemas' / 'schema-schema-v1.0.md'
|
|
metaschema_data = loader.load_schema(metaschema_path)
|
|
metaschema = metaschema_data['schema']
|
|
|
|
# Validate structure
|
|
issues = loader.validate_schema_structure(metaschema)
|
|
|
|
# Metaschema should have minimal issues
|
|
critical_issues = [i for i in issues if 'Missing required field' in i]
|
|
assert len(critical_issues) == 0, f"Metaschema has critical issues: {critical_issues}"
|