markitect-main/tests/test_issue_6_stub_generation.py

260 lines
10 KiB
Python
Raw Normal View History

2025-09-30 03:31:48 +02:00
"""
Tests for Issue #6: Generate a Markdown Stub from a Schema.
This module tests the functionality to create markdown template files
from JSON schemas with appropriate placeholder content and structure.
"""
import json
import pytest
from pathlib import Path
from tempfile import NamedTemporaryFile, TemporaryDirectory
from markitect.stub_generator import StubGenerator
from markitect.schema_generator import SchemaGenerator
class TestIssue6StubGeneration:
"""Test suite for markdown stub generation from schemas."""
@pytest.fixture
def sample_schema(self):
"""Sample JSON schema for testing."""
return {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Blog Post Schema",
"description": "Schema for blog post structure",
"properties": {
"headings": {
"type": "object",
"properties": {
"level_1": {
"type": "array",
"items": {
"type": "object",
"properties": {
"content": {"type": "string"},
"level": {"type": "integer"}
}
},
"minItems": 1,
"maxItems": 1
},
"level_2": {
"type": "array",
"items": {
"type": "object",
"properties": {
"content": {"type": "string"},
"level": {"type": "integer"}
}
},
"minItems": 3,
"maxItems": 3
}
}
}
}
}
@pytest.fixture
def stub_generator(self):
"""Create a StubGenerator instance."""
return StubGenerator()
def test_stub_generator_can_be_created(self, stub_generator):
"""StubGenerator class should be importable and instantiable."""
assert stub_generator is not None
assert isinstance(stub_generator, StubGenerator)
def test_generate_stub_from_schema_dict(self, stub_generator, sample_schema):
"""Should generate markdown stub from schema dictionary."""
result = stub_generator.generate_stub_from_schema(sample_schema)
assert result is not None
assert isinstance(result, str)
# Should contain appropriate heading structure
lines = result.strip().split('\n')
assert any(line.startswith('# ') for line in lines) # H1 heading
assert any(line.startswith('## ') for line in lines) # H2 headings
# Should have placeholder content
assert 'TODO' in result or 'placeholder' in result.lower()
def test_generate_stub_creates_proper_heading_hierarchy(self, stub_generator, sample_schema):
"""Generated stub should have correct heading levels and count."""
result = stub_generator.generate_stub_from_schema(sample_schema)
lines = result.strip().split('\n')
h1_count = len([line for line in lines if line.startswith('# ') and not line.startswith('## ')])
h2_count = len([line for line in lines if line.startswith('## ')])
# Based on schema: 1 H1, 3 H2
assert h1_count == 1
assert h2_count == 3
def test_generate_stub_from_file_path(self, stub_generator, sample_schema):
"""Should generate stub from schema file path."""
with NamedTemporaryFile(mode='w', suffix='.json', delete=False) as temp_file:
json.dump(sample_schema, temp_file)
temp_file.flush()
try:
result = stub_generator.generate_stub_from_file(Path(temp_file.name))
assert result is not None
assert isinstance(result, str)
assert '# ' in result # Should contain headings
finally:
Path(temp_file.name).unlink()
def test_generate_stub_with_output_file(self, stub_generator, sample_schema):
"""Should save generated stub to output file."""
with TemporaryDirectory() as temp_dir:
output_file = Path(temp_dir) / "generated_stub.md"
stub_generator.generate_stub_to_file(sample_schema, output_file)
assert output_file.exists()
content = output_file.read_text()
assert '# ' in content
assert len(content.strip()) > 0
def test_generate_stub_with_custom_placeholders(self, stub_generator):
"""Should support custom placeholder text generation."""
schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"title": "Custom Schema",
"properties": {
"headings": {
"type": "object",
"properties": {
"level_1": {
"type": "array",
"minItems": 1,
"maxItems": 1
}
}
}
}
}
result = stub_generator.generate_stub_from_schema(schema, placeholder_style="custom")
assert result is not None
# Should contain some form of placeholder content
assert any(keyword in result.lower() for keyword in ['todo', 'placeholder', 'content', 'section'])
def test_generate_stub_handles_empty_schema(self, stub_generator):
"""Should handle empty or minimal schemas gracefully."""
empty_schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object"
}
result = stub_generator.generate_stub_from_schema(empty_schema)
assert result is not None
assert isinstance(result, str)
# Should at least create a basic document structure
def test_generate_stub_handles_complex_hierarchy(self, stub_generator):
"""Should handle complex heading hierarchies correctly."""
complex_schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"headings": {
"type": "object",
"properties": {
"level_1": {"type": "array", "minItems": 1, "maxItems": 1},
"level_2": {"type": "array", "minItems": 2, "maxItems": 2},
"level_3": {"type": "array", "minItems": 4, "maxItems": 4},
"level_4": {"type": "array", "minItems": 1, "maxItems": 1}
}
}
}
}
result = stub_generator.generate_stub_from_schema(complex_schema)
lines = result.strip().split('\n')
h1_count = len([l for l in lines if l.startswith('# ') and not l.startswith('## ')])
h2_count = len([l for l in lines if l.startswith('## ') and not l.startswith('### ')])
h3_count = len([l for l in lines if l.startswith('### ') and not l.startswith('#### ')])
h4_count = len([l for l in lines if l.startswith('#### ') and not l.startswith('##### ')])
assert h1_count == 1
assert h2_count == 2
assert h3_count == 4
assert h4_count == 1
def test_round_trip_validation(self, stub_generator):
"""Generated stub should validate against original schema."""
# First create a schema from a sample document
schema_generator = SchemaGenerator()
sample_doc = Path("sample_blog.md") # Using existing sample
if sample_doc.exists():
schema = schema_generator.generate_schema_from_file(sample_doc)
stub = stub_generator.generate_stub_from_schema(schema)
# Create temporary file with generated stub
with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as temp_file:
temp_file.write(stub)
temp_file.flush()
try:
# Generate schema from the stub and compare basic structure
stub_schema = schema_generator.generate_schema_from_file(Path(temp_file.name))
# Should have similar heading structure
original_headings = schema.get('properties', {}).get('headings', {}).get('properties', {})
stub_headings = stub_schema.get('properties', {}).get('headings', {}).get('properties', {})
# Should have same heading levels
assert set(original_headings.keys()) == set(stub_headings.keys())
finally:
Path(temp_file.name).unlink()
class TestStubGeneratorErrorHandling:
"""Test error handling for stub generation."""
def test_handles_invalid_schema_file(self):
"""Should handle invalid schema file gracefully."""
generator = StubGenerator()
with pytest.raises(FileNotFoundError):
generator.generate_stub_from_file(Path("nonexistent_schema.json"))
def test_handles_invalid_json_schema(self):
"""Should handle malformed JSON schema files."""
generator = StubGenerator()
with NamedTemporaryFile(mode='w', suffix='.json', delete=False) as temp_file:
temp_file.write("invalid json content")
temp_file.flush()
try:
with pytest.raises(json.JSONDecodeError):
generator.generate_stub_from_file(Path(temp_file.name))
finally:
Path(temp_file.name).unlink()
def test_handles_schema_without_headings(self):
"""Should handle schemas that don't define heading structure."""
generator = StubGenerator()
schema = {
"$schema": "http://json-schema.org/draft-07/schema#",
"type": "object",
"properties": {
"other_stuff": {"type": "string"}
}
}
result = generator.generate_stub_from_schema(schema)
assert result is not None
assert isinstance(result, str)
# Should create a minimal document even without heading structure