Files
markitect-main/tests/test_issue_139_end_to_end.py
tegwick 81d3da5fe7
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
feat: comprehensive asset management system and testing improvements
Asset Management System (Issue #142):
- Add complete asset management framework with deduplication
- Implement AssetManager, AssetRegistry, and AssetDeduplicator classes
- Add AssetPackager for markdown document packaging
- Create comprehensive test suite for all asset management components
- Add asset constants and custom exceptions for robust error handling

Markdown Processing Enhancements:
- Update markdown_commands.py with improved functionality
- Enhanced parsing and content aggregation capabilities
- Improved filename encoding/decoding for special characters

Test Suite Improvements:
- Add comprehensive tests for Issue #138 markdown parsing
- Enhance Issue #139 content aggregation and end-to-end testing
- Complete test coverage for new asset management features

Examples and Documentation:
- Update BildungsKanonJon.md example with enhanced content
- Generate corresponding HTML output for documentation
- Add asset registry configuration

Development Tools:
- Add install script for simplified setup

This commit represents a major enhancement to MarkiTect's asset handling
capabilities with full test coverage and improved markdown processing.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-12 19:57:31 +02:00

624 lines
20 KiB
Python

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