""" Test end-to-end scenarios for Issue #139: Implode directory to a markdown file. This test module covers comprehensive end-to-end testing including round-trip testing with md-explode, processing of complex structures, and validation scenarios. """ import pytest import tempfile import shutil import subprocess from pathlib import Path from unittest.mock import Mock, patch # Import will fail initially (RED phase) until implementation exists try: from markitect.plugins.builtin.markdown_commands import ( explode_markdown_file, implode_directory, cli_implode_directory ) from markitect.cli import cli except ImportError: # Expected during RED phase - tests should fail initially explode_markdown_file = None implode_directory = None cli_implode_directory = None cli = None # Note: cli_explode_markdown doesn't exist, we use explode_markdown_file directly def cli_explode_markdown(input_file, output_dir): """Wrapper for explode_markdown_file for testing.""" class MockResult: def __init__(self, success, output_dir=None): self.success = success self.output_dir = output_dir try: result_dir = explode_markdown_file(input_file, output_dir) return MockResult(True, result_dir) except Exception: return MockResult(False) class TestEndToEndRoundTripTesting: """Test complete round-trip scenarios: original → explode → implode → compare.""" def setup_method(self): """Set up temporary directory for each test.""" self.temp_dir = Path(tempfile.mkdtemp()) def teardown_method(self): """Clean up temporary directory after each test.""" if self.temp_dir.exists(): shutil.rmtree(self.temp_dir) def test_simple_document_round_trip(self): """Test round-trip with simple hierarchical document.""" # This should fail initially (RED phase) original_content = """# Introduction Welcome to the document. ## Chapter 1: Getting Started This is the first chapter. ### Section 1.1: Installation Install instructions here. ### Section 1.2: Configuration Configuration details. ## Chapter 2: Advanced Topics Advanced material here. # Conclusion Final thoughts. """ # Create original file original_file = self.temp_dir / "original.md" original_file.write_text(original_content) # Step 1: Explode the document exploded_dir = self.temp_dir / "exploded" explode_result = cli_explode_markdown( input_file=original_file, output_dir=exploded_dir ) assert explode_result.success == True # Step 2: Implode back to markdown imploded_file = self.temp_dir / "imploded.md" implode_result = cli_implode_directory( input_dir=exploded_dir, output_file=imploded_file ) assert implode_result.success == True # Step 3: Compare results imploded_content = imploded_file.read_text() # Should preserve all major structural elements assert "# Introduction" in imploded_content assert "## Chapter 1: Getting Started" in imploded_content assert "### Section 1.1: Installation" in imploded_content assert "### Section 1.2: Configuration" in imploded_content assert "## Chapter 2: Advanced Topics" in imploded_content assert "# Conclusion" in imploded_content # Should preserve content assert "Welcome to the document." in imploded_content assert "Install instructions here." in imploded_content assert "Final thoughts." in imploded_content def test_document_with_front_matter_round_trip(self): """Test round-trip preservation of YAML front matter.""" # This should fail initially (RED phase) original_content = """--- title: "Test Document" author: "Test Author" date: "2023-01-01" tags: ["documentation", "test"] --- # Main Content Document content here. ## Section 1 Section content. """ original_file = self.temp_dir / "with_frontmatter.md" original_file.write_text(original_content) # Explode → Implode exploded_dir = self.temp_dir / "exploded_fm" explode_result = cli_explode_markdown(original_file, exploded_dir) assert explode_result.success == True imploded_file = self.temp_dir / "imploded_fm.md" implode_result = cli_implode_directory(exploded_dir, imploded_file) assert implode_result.success == True imploded_content = imploded_file.read_text() # Should preserve front matter assert imploded_content.startswith("---") assert "title: \"Test Document\"" in imploded_content assert "author: \"Test Author\"" in imploded_content assert "tags:" in imploded_content # Should preserve content structure assert "# Main Content" in imploded_content assert "## Section 1" in imploded_content def test_complex_nested_structure_round_trip(self): """Test round-trip with deeply nested document structure.""" # This should fail initially (RED phase) complex_content = """# Part 1: Fundamentals Introduction to part 1. ## Chapter 1: Basics Basic concepts. ### Section 1.1: Overview Overview content. #### Subsection 1.1.1: Details Detailed information. #### Subsection 1.1.2: Examples Example content. ### Section 1.2: Implementation Implementation details. ## Chapter 2: Intermediate Intermediate concepts. # Part 2: Advanced Topics Advanced material. ## Chapter 3: Expert Level Expert content here. """ original_file = self.temp_dir / "complex.md" original_file.write_text(complex_content) # Round-trip process exploded_dir = self.temp_dir / "complex_exploded" explode_result = cli_explode_markdown(original_file, exploded_dir) assert explode_result.success == True imploded_file = self.temp_dir / "complex_imploded.md" implode_result = cli_implode_directory(exploded_dir, imploded_file) assert implode_result.success == True imploded_content = imploded_file.read_text() # Should preserve all heading levels assert "# Part 1: Fundamentals" in imploded_content assert "## Chapter 1: Basics" in imploded_content assert "### Section 1.1: Overview" in imploded_content assert "#### Subsection 1.1.1: Details" in imploded_content assert "#### Subsection 1.1.2: Examples" in imploded_content assert "# Part 2: Advanced Topics" in imploded_content def test_round_trip_preserves_markdown_formatting(self): """Test that round-trip preserves all markdown formatting elements.""" # This should fail initially (RED phase) formatted_content = """# Document with Formatting ## Text Formatting This has **bold text** and *italic text* and `inline code`. ## Code Blocks Here's a code block: ```python def example(): return "formatted code" ``` ## Lists and Tables - Item 1 - Item 2 - Nested item | Header 1 | Header 2 | |----------|----------| | Cell 1 | Cell 2 | ## Links and Images [Link text](http://example.com) ![Alt text](image.png) > This is a blockquote --- Horizontal rule above. """ original_file = self.temp_dir / "formatted.md" original_file.write_text(formatted_content) # Round-trip exploded_dir = self.temp_dir / "formatted_exploded" explode_result = cli_explode_markdown(original_file, exploded_dir) assert explode_result.success == True imploded_file = self.temp_dir / "formatted_imploded.md" implode_result = cli_implode_directory(exploded_dir, imploded_file) assert implode_result.success == True imploded_content = imploded_file.read_text() # Should preserve all formatting assert "**bold text**" in imploded_content assert "*italic text*" in imploded_content assert "`inline code`" in imploded_content assert "```python" in imploded_content assert "- Item 1" in imploded_content assert "| Header 1 | Header 2 |" in imploded_content assert "[Link text]" in imploded_content assert "![Alt text]" in imploded_content assert "> This is a blockquote" in imploded_content assert "---" in imploded_content class TestBookLikeStructureProcessing: """Test processing book-like structures with parts, chapters, and sections.""" def setup_method(self): """Set up temporary directory for each test.""" self.temp_dir = Path(tempfile.mkdtemp()) def teardown_method(self): """Clean up temporary directory after each test.""" if self.temp_dir.exists(): shutil.rmtree(self.temp_dir) def test_process_book_structure_from_explode_output(self): """Test processing a book-like directory structure created by md-explode.""" # This should fail initially (RED phase) # Simulate structure created by md-explode for a book self._create_book_structure() # Implode the structure imploded_file = self.temp_dir / "reconstructed_book.md" result = cli_implode_directory( input_dir=self.temp_dir, output_file=imploded_file ) assert result.success == True content = imploded_file.read_text() # Should reconstruct proper book hierarchy assert "# Part 1: Introduction" in content assert "## Chapter 1: Getting Started" in content assert "### Section 1.1: Installation" in content assert "### Section 1.2: Setup" in content assert "## Chapter 2: Basic Concepts" in content assert "# Part 2: Advanced Topics" in content assert "## Chapter 3: Expert Techniques" in content def test_handle_book_with_mixed_content_types(self): """Test handling books with various content types (code, tables, images).""" # This should fail initially (RED phase) # Create structure with mixed content self._create_mixed_content_book_structure() imploded_file = self.temp_dir / "mixed_content_book.md" result = cli_implode_directory(self.temp_dir, imploded_file) assert result.success == True content = imploded_file.read_text() # Should preserve all content types assert "```python" in content assert "| Feature | Description |" in content assert "![Architecture](diagram.png)" in content assert "1. First step" in content def _create_book_structure(self): """Create a realistic book directory structure.""" # Part 1 part1_dir = self.temp_dir / "part_1_introduction" part1_dir.mkdir() (part1_dir / "index.md").write_text("# Part 1: Introduction\nIntroduction to the book.") # Chapter 1 ch1_dir = part1_dir / "chapter_1_getting_started" ch1_dir.mkdir() (ch1_dir / "index.md").write_text("## Chapter 1: Getting Started\nGetting started content.") (ch1_dir / "section_11_installation.md").write_text("### Section 1.1: Installation\nInstallation instructions.") (ch1_dir / "section_12_setup.md").write_text("### Section 1.2: Setup\nSetup procedures.") # Chapter 2 ch2_dir = part1_dir / "chapter_2_basic_concepts" ch2_dir.mkdir() (ch2_dir / "index.md").write_text("## Chapter 2: Basic Concepts\nBasic concepts explanation.") # Part 2 part2_dir = self.temp_dir / "part_2_advanced_topics" part2_dir.mkdir() (part2_dir / "index.md").write_text("# Part 2: Advanced Topics\nAdvanced topics introduction.") (part2_dir / "chapter_3_expert_techniques.md").write_text("## Chapter 3: Expert Techniques\nExpert level content.") def _create_mixed_content_book_structure(self): """Create book structure with mixed content types.""" tech_dir = self.temp_dir / "technical_guide" tech_dir.mkdir() (tech_dir / "index.md").write_text("# Technical Guide\nGuide introduction.") # Code examples chapter code_dir = tech_dir / "chapter_1_code_examples" code_dir.mkdir() code_content = """## Chapter 1: Code Examples Example implementation: ```python def process_data(data): return data.strip().lower() ``` And configuration: ```yaml settings: debug: true port: 8080 ``` """ (code_dir / "index.md").write_text(code_content) # Tables and data chapter data_dir = tech_dir / "chapter_2_data_reference" data_dir.mkdir() data_content = """## Chapter 2: Data Reference | Feature | Description | Available | |---------|-------------|-----------| | API | REST API | Yes | | CLI | Command Line| Yes | | Web UI | Web Interface| No | ### Steps to follow: 1. First step 2. Second step - Sub-step A - Sub-step B """ (data_dir / "index.md").write_text(data_content) # Images and media chapter media_content = """## Chapter 3: Architecture System overview: ![Architecture](diagram.png) > Note: The diagram shows the complete system architecture. For more details, see [documentation](https://example.com). """ (tech_dir / "chapter_3_architecture.md").write_text(media_content) class TestTechnicalDocumentationProcessing: """Test processing technical documentation with deep nesting.""" def setup_method(self): """Set up temporary directory for each test.""" self.temp_dir = Path(tempfile.mkdtemp()) def teardown_method(self): """Clean up temporary directory after each test.""" if self.temp_dir.exists(): shutil.rmtree(self.temp_dir) def test_process_api_documentation_structure(self): """Test processing API documentation with deep hierarchical structure.""" # This should fail initially (RED phase) self._create_api_documentation_structure() imploded_file = self.temp_dir / "api_docs.md" result = cli_implode_directory(self.temp_dir, imploded_file) assert result.success == True content = imploded_file.read_text() # Should maintain API documentation structure assert "# API Documentation" in content assert "## Authentication" in content assert "### OAuth2 Flow" in content assert "#### Token Validation" in content assert "## Endpoints" in content assert "### Users API" in content def test_handle_very_deep_nesting(self): """Test handling documentation with very deep nesting levels.""" # This should fail initially (RED phase) self._create_deep_nested_structure() imploded_file = self.temp_dir / "deep_nested.md" result = cli_implode_directory(self.temp_dir, imploded_file) assert result.success == True content = imploded_file.read_text() # Should handle deep nesting appropriately assert "# Level 1" in content assert "## Level 2" in content assert "### Level 3" in content assert "#### Level 4" in content assert "##### Level 5" in content def _create_api_documentation_structure(self): """Create realistic API documentation structure.""" api_dir = self.temp_dir / "api_documentation" api_dir.mkdir() (api_dir / "index.md").write_text("# API Documentation\nComplete API reference.") # Authentication section auth_dir = api_dir / "authentication" auth_dir.mkdir() (auth_dir / "index.md").write_text("## Authentication\nAuthentication overview.") oauth_dir = auth_dir / "oauth2_flow" oauth_dir.mkdir() (oauth_dir / "index.md").write_text("### OAuth2 Flow\nOAuth2 implementation details.") (oauth_dir / "token_validation.md").write_text("#### Token Validation\nHow to validate tokens.") # Endpoints section endpoints_dir = api_dir / "endpoints" endpoints_dir.mkdir() (endpoints_dir / "index.md").write_text("## Endpoints\nAPI endpoints reference.") (endpoints_dir / "users_api.md").write_text("### Users API\nUser management endpoints.") def _create_deep_nested_structure(self): """Create structure with very deep nesting.""" current_dir = self.temp_dir content_parts = [] for level in range(1, 6): dir_name = f"level_{level}" current_dir = current_dir / dir_name current_dir.mkdir(parents=True, exist_ok=True) heading = "#" * level content = f"{heading} Level {level}\nContent for level {level}." (current_dir / "index.md").write_text(content) class TestValidationAndErrorScenarios: """Test validation scenarios and error handling in end-to-end workflows.""" def setup_method(self): """Set up temporary directory for each test.""" self.temp_dir = Path(tempfile.mkdtemp()) def teardown_method(self): """Clean up temporary directory after each test.""" if self.temp_dir.exists(): shutil.rmtree(self.temp_dir) def test_validate_against_md_explode_output(self): """Test that implode works correctly with actual md-explode output.""" # This should fail initially (RED phase) # Create original document original_content = """# User Guide ## Getting Started Start here. ### Installation Install steps. ## Advanced Usage Advanced topics. """ original_file = self.temp_dir / "user_guide.md" original_file.write_text(original_content) # Use actual md-explode command exploded_dir = self.temp_dir / "user_guide_exploded" explode_result = cli_explode_markdown(original_file, exploded_dir) assert explode_result.success == True # Verify exploded structure exists assert exploded_dir.exists() assert (exploded_dir / "user_guide" / "getting_started").exists() # Now implode it back imploded_file = self.temp_dir / "reconstructed.md" implode_result = cli_implode_directory(exploded_dir, imploded_file) assert implode_result.success == True # Validate result reconstructed = imploded_file.read_text() assert "# User Guide" in reconstructed assert "## Getting Started" in reconstructed assert "### Installation" in reconstructed def test_handle_malformed_directory_structures(self): """Test handling malformed or incomplete directory structures.""" # This should fail initially (RED phase) # Create malformed structure (missing index files, irregular naming) malformed_dir = self.temp_dir / "malformed" malformed_dir.mkdir() # Regular file at root (malformed_dir / "introduction.md").write_text("# Introduction\nIntro content") # Directory with no index file orphan_dir = malformed_dir / "orphan_section" orphan_dir.mkdir() (orphan_dir / "content.md").write_text("Content without proper heading structure") # Directory with mixed conventions mixed_dir = malformed_dir / "mixed_conventions" mixed_dir.mkdir() (mixed_dir / "index.md").write_text("## Mixed Section\nSection content") (mixed_dir / "irregular_file_name.md").write_text("Some content") # Should handle gracefully imploded_file = self.temp_dir / "malformed_result.md" result = cli_implode_directory(malformed_dir, imploded_file) # Should either succeed with best-effort result or fail gracefully if result.success: content = imploded_file.read_text() assert len(content) > 0 else: assert result.error_message is not None def test_handle_empty_and_edge_case_directories(self): """Test handling empty directories and edge cases.""" # This should fail initially (RED phase) # Completely empty directory empty_dir = self.temp_dir / "empty" empty_dir.mkdir() result = cli_implode_directory(empty_dir, self.temp_dir / "empty_result.md") # Should handle empty directory appropriately assert result.success == False or (result.success == True and result.warning is not None) # Directory with only non-markdown files non_md_dir = self.temp_dir / "non_markdown" non_md_dir.mkdir() (non_md_dir / "readme.txt").write_text("Not markdown") (non_md_dir / "data.json").write_text("{}") result = cli_implode_directory(non_md_dir, self.temp_dir / "non_md_result.md") # Should handle appropriately assert result.success == False or result.warning is not None