""" 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