Files
markitect-main/tests/test_issue_149_explode_implode_variants.py
tegwick c17efc112d feat: complete Issue #149 - Phase 2: Implement Explode-Implode Variants
Implement all three explode-implode variants with full CLI integration:

🔧 Variant Implementations:
- FlatVariant: Encapsulates existing flat structure behavior
- HierarchicalVariant: Numbered directory structures (01_, 02_, 03_)
- SemanticVariant: Content-based organization (intro, chapters, appendices)

🏭 Factory System:
- VariantFactory: Centralized variant creation and management
- Auto-detection algorithms with confidence scoring
- Content analysis for variant recommendation

🖥️ CLI Integration:
- Enhanced md-explode command with --variant parameter
- Enhanced md-implode command with auto-detection
- Improved error handling and user feedback

🧪 Comprehensive Testing:
- 22 unit tests covering all variant functionality
- Roundtrip validation ensuring perfect reversibility
- Performance testing with large documents
- Error handling and edge case coverage

📊 Key Features:
- Three distinct organization strategies
- Automatic variant detection from directory structures
- Full backward compatibility with existing behavior
- Extensible architecture for future variants
- Manifest-based reversibility

Files Added:
- markitect/explode_variants/flat_variant.py
- markitect/explode_variants/hierarchical_variant.py
- markitect/explode_variants/semantic_variant.py
- markitect/explode_variants/variant_factory.py
- tests/test_issue_149_explode_implode_variants.py
- tests/test_issue_149_roundtrip_validation.py
- cost_notes/issue_149_cost_2025-10-12.md

Files Modified:
- markitect/explode_variants/__init__.py (updated exports)
- markitect/plugins/builtin/markdown_commands.py (CLI integration)

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-12 22:30:06 +02:00

452 lines
15 KiB
Python

"""
Test suite for Issue #149 - Phase 2: Implement Explode-Implode Variants
Tests all three variant implementations (flat, hierarchical, semantic) with
comprehensive explode-implode operations, roundtrip validation, and CLI integration.
"""
import pytest
import tempfile
from pathlib import Path
from markitect.explode_variants import (
ExplodeVariant, ExplodeOptions, ImplodeOptions,
FlatVariant, HierarchicalVariant, SemanticVariant,
VariantFactory, get_variant_factory, create_variant
)
class TestFlatVariant:
"""Test the FlatVariant implementation."""
def test_flat_variant_initialization(self):
"""Test FlatVariant initialization."""
variant = FlatVariant()
assert variant.variant_type == ExplodeVariant.FLAT
assert variant.name == "Flat Structure"
assert "directories based on h1 headings" in variant.description
def test_flat_variant_explode_basic(self):
"""Test basic explosion with flat variant."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test markdown file
test_content = """# Introduction
This is the introduction.
## Overview
Some overview content.
# Chapter 1
First chapter content.
## Section 1.1
Section content here.
# Conclusion
Final thoughts.
"""
input_file = temp_path / "test.md"
input_file.write_text(test_content, encoding='utf-8')
variant = FlatVariant()
options = ExplodeOptions(
variant=ExplodeVariant.FLAT,
create_manifest=True
)
result = variant.explode(input_file, options)
assert result.success
assert result.variant_used == ExplodeVariant.FLAT
assert result.output_directory.exists()
assert result.manifest_path is not None
assert result.manifest_path.exists()
assert len(result.files_created) > 0
def test_flat_variant_can_handle_directory(self):
"""Test flat variant directory detection."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create flat structure
(temp_path / "introduction").mkdir()
(temp_path / "introduction" / "index.md").write_text("# Introduction")
(temp_path / "chapter_1").mkdir()
(temp_path / "chapter_1" / "index.md").write_text("# Chapter 1")
variant = FlatVariant()
assert variant.can_handle_directory(temp_path)
def test_flat_variant_detection_patterns(self):
"""Test flat variant detection patterns."""
variant = FlatVariant()
patterns = variant.get_detection_patterns()
assert patterns["manifest_type"] == "flat"
assert "numbered_directory_ratio" in patterns
assert "fallback_score" in patterns
class TestHierarchicalVariant:
"""Test the HierarchicalVariant implementation."""
def test_hierarchical_variant_initialization(self):
"""Test HierarchicalVariant initialization."""
variant = HierarchicalVariant()
assert variant.variant_type == ExplodeVariant.HIERARCHICAL
assert variant.name == "Hierarchical Structure"
assert "numbered directory structures" in variant.description
def test_hierarchical_variant_explode_basic(self):
"""Test basic explosion with hierarchical variant."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test markdown file
test_content = """# Getting Started
Introduction to the system.
## Installation
How to install.
## Configuration
How to configure.
# Advanced Topics
Advanced material.
## Performance
Performance considerations.
# Conclusion
Final notes.
"""
input_file = temp_path / "guide.md"
input_file.write_text(test_content, encoding='utf-8')
variant = HierarchicalVariant()
options = ExplodeOptions(
variant=ExplodeVariant.HIERARCHICAL,
create_manifest=True
)
result = variant.explode(input_file, options)
assert result.success
assert result.variant_used == ExplodeVariant.HIERARCHICAL
assert result.output_directory.exists()
assert result.manifest_path is not None
# Check for numbered directories
subdirs = [d for d in result.output_directory.iterdir() if d.is_dir()]
numbered_dirs = [d for d in subdirs if d.name.startswith(('01_', '02_', '03_'))]
assert len(numbered_dirs) > 0
def test_hierarchical_variant_can_handle_directory(self):
"""Test hierarchical variant directory detection."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create hierarchical structure
(temp_path / "01_introduction").mkdir()
(temp_path / "01_introduction" / "index.md").write_text("# Introduction")
(temp_path / "02_chapter_one").mkdir()
(temp_path / "02_chapter_one" / "index.md").write_text("# Chapter One")
variant = HierarchicalVariant()
assert variant.can_handle_directory(temp_path)
def test_hierarchical_variant_detection_patterns(self):
"""Test hierarchical variant detection patterns."""
variant = HierarchicalVariant()
patterns = variant.get_detection_patterns()
assert patterns["manifest_type"] == "hierarchical"
assert "numbered_directory_ratio" in patterns
assert patterns["numbered_directory_ratio"]["min"] == 0.6
class TestSemanticVariant:
"""Test the SemanticVariant implementation."""
def test_semantic_variant_initialization(self):
"""Test SemanticVariant initialization."""
variant = SemanticVariant()
assert variant.variant_type == ExplodeVariant.SEMANTIC
assert variant.name == "Semantic Structure"
assert "content-based directory groupings" in variant.description
def test_semantic_variant_explode_basic(self):
"""Test basic explosion with semantic variant."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test markdown file with semantic content
test_content = """# Introduction
Welcome to this comprehensive guide.
# Tutorial: Getting Started
This tutorial will walk you through the basics.
## Step 1: Installation
Install the software.
## Step 2: Configuration
Configure your environment.
# Reference: API Documentation
Complete API reference.
## Function Listing
List of all functions.
# Appendix A: Troubleshooting
Common issues and solutions.
# Conclusion
Final thoughts and summary.
"""
input_file = temp_path / "manual.md"
input_file.write_text(test_content, encoding='utf-8')
variant = SemanticVariant()
options = ExplodeOptions(
variant=ExplodeVariant.SEMANTIC,
create_manifest=True
)
result = variant.explode(input_file, options)
assert result.success
assert result.variant_used == ExplodeVariant.SEMANTIC
assert result.output_directory.exists()
assert result.manifest_path is not None
# Check for semantic directories
subdirs = [d.name for d in result.output_directory.iterdir() if d.is_dir()]
semantic_dirs = [d for d in subdirs if d in ['introduction', 'tutorials', 'reference', 'appendices', 'conclusion']]
assert len(semantic_dirs) > 0
def test_semantic_variant_can_handle_directory(self):
"""Test semantic variant directory detection."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create semantic structure
(temp_path / "introduction").mkdir()
(temp_path / "introduction" / "overview.md").write_text("# Overview")
(temp_path / "chapters").mkdir()
(temp_path / "chapters" / "basics.md").write_text("# Basics")
(temp_path / "appendices").mkdir()
(temp_path / "appendices" / "glossary.md").write_text("# Glossary")
variant = SemanticVariant()
assert variant.can_handle_directory(temp_path)
def test_semantic_variant_detection_patterns(self):
"""Test semantic variant detection patterns."""
variant = SemanticVariant()
patterns = variant.get_detection_patterns()
assert patterns["manifest_type"] == "semantic"
assert "semantic_directory_ratio" in patterns
assert patterns["semantic_directory_ratio"]["min"] == 0.4
class TestVariantFactory:
"""Test the VariantFactory functionality."""
def test_variant_factory_initialization(self):
"""Test VariantFactory initialization."""
factory = VariantFactory()
assert factory is not None
# Test that all built-in variants are registered
stats = factory.get_variant_statistics()
assert stats['total_variants'] >= 3
assert ExplodeVariant.FLAT in stats['variant_types']
assert ExplodeVariant.HIERARCHICAL in stats['variant_types']
assert ExplodeVariant.SEMANTIC in stats['variant_types']
def test_variant_factory_create_variant(self):
"""Test creating variants through factory."""
factory = VariantFactory()
flat_variant = factory.create_variant(ExplodeVariant.FLAT)
assert isinstance(flat_variant, FlatVariant)
hierarchical_variant = factory.create_variant(ExplodeVariant.HIERARCHICAL)
assert isinstance(hierarchical_variant, HierarchicalVariant)
semantic_variant = factory.create_variant(ExplodeVariant.SEMANTIC)
assert isinstance(semantic_variant, SemanticVariant)
def test_variant_factory_detect_variant(self):
"""Test variant detection through factory."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create a numbered directory structure
(temp_path / "01_intro").mkdir()
(temp_path / "02_main").mkdir()
(temp_path / "03_end").mkdir()
factory = VariantFactory()
result = factory.detect_variant(temp_path)
assert result.variant is not None
# Should detect hierarchical due to numbered directories
assert result.variant in [ExplodeVariant.HIERARCHICAL, ExplodeVariant.FLAT]
def test_variant_factory_convenience_functions(self):
"""Test convenience functions."""
# Test global factory
factory = get_variant_factory()
assert isinstance(factory, VariantFactory)
# Test create_variant convenience function
variant = create_variant(ExplodeVariant.FLAT)
assert isinstance(variant, FlatVariant)
def test_variant_factory_list_available_variants(self):
"""Test listing available variants."""
factory = VariantFactory()
variants_info = factory.list_available_variants()
assert len(variants_info) >= 3
# Check that required fields are present
for info in variants_info:
assert 'type' in info
assert 'name' in info
assert 'description' in info
assert 'detection_patterns' in info
def test_variant_factory_get_best_variant_for_content(self):
"""Test content-based variant recommendation."""
factory = VariantFactory()
# Content with numbered sections (should suggest hierarchical)
numbered_content = """# 1. Introduction
# 2. Main Content
# 3. Conclusion"""
result = factory.get_best_variant_for_content(numbered_content)
assert result in [ExplodeVariant.HIERARCHICAL, ExplodeVariant.FLAT]
# Content with semantic keywords (should suggest semantic)
semantic_content = """# Introduction
# Tutorial: Getting Started
# Reference Manual
# Appendix A"""
result = factory.get_best_variant_for_content(semantic_content)
assert result in [ExplodeVariant.SEMANTIC, ExplodeVariant.FLAT]
class TestVariantIntegration:
"""Test integration between variants and CLI commands."""
def test_explode_options_validation(self):
"""Test ExplodeOptions validation."""
# Valid options
options = ExplodeOptions(variant=ExplodeVariant.FLAT)
assert options.variant == ExplodeVariant.FLAT
assert options.create_manifest is True # default
# Custom options
custom_options = ExplodeOptions(
variant=ExplodeVariant.HIERARCHICAL,
max_depth=5,
create_manifest=False,
dry_run=True
)
assert custom_options.max_depth == 5
assert custom_options.create_manifest is False
assert custom_options.dry_run is True
def test_implode_options_validation(self):
"""Test ImplodeOptions validation."""
# Default options
options = ImplodeOptions()
assert options.preserve_front_matter is True # default
assert options.section_spacing == 2 # default
# Custom options
custom_options = ImplodeOptions(
output_file=Path("/tmp/output.md"),
section_spacing=3,
dry_run=True
)
assert custom_options.output_file == Path("/tmp/output.md")
assert custom_options.section_spacing == 3
assert custom_options.dry_run is True
def test_error_handling(self):
"""Test error handling in variants."""
variant = FlatVariant()
# Test with non-existent file
options = ExplodeOptions(variant=ExplodeVariant.FLAT)
result = variant.explode(Path("/nonexistent/file.md"), options)
assert not result.success
assert len(result.errors) > 0
assert "does not exist" in result.errors[0].lower()
def test_manifest_integration(self):
"""Test manifest creation and reading integration."""
with tempfile.TemporaryDirectory() as temp_dir:
temp_path = Path(temp_dir)
# Create test file
test_content = "# Test\n\nContent here."
input_file = temp_path / "test.md"
input_file.write_text(test_content, encoding='utf-8')
# Test each variant creates a manifest
for variant_type in [ExplodeVariant.FLAT, ExplodeVariant.HIERARCHICAL, ExplodeVariant.SEMANTIC]:
variant = create_variant(variant_type)
options = ExplodeOptions(
variant=variant_type,
output_dir=temp_path / f"test_{variant_type.value}",
create_manifest=True
)
result = variant.explode(input_file, options)
assert result.success
assert result.manifest_path is not None
assert result.manifest_path.exists()
# Verify manifest contains correct variant type
manifest_content = result.manifest_path.read_text(encoding='utf-8')
assert f"explosion_type: {variant_type.value}" in manifest_content
if __name__ == '__main__':
pytest.main([__file__, "-v"])