""" Test suite for Issue #148 - Core Infrastructure for Explode-Implode Variants Tests the foundational infrastructure components that support multiple explode-implode variants with manifest-based reversibility. """ import pytest import tempfile import yaml from pathlib import Path from datetime import datetime from markitect.explode_variants import ( ExplodeVariant, ExplodeMode, ManifestVersion, DetectionConfidence, BaseVariant, ExplodeOptions, ImplodeOptions, ExplodeResult, ImplodeResult, ManifestManager, ManifestData, StructureEntry, VariantDetector, DetectionResult ) class TestExplodeVariantEnum: """Test the ExplodeVariant enum and related enums.""" def test_explode_variant_values(self): """Test that all expected variants are available.""" assert ExplodeVariant.FLAT.value == "flat" assert ExplodeVariant.HIERARCHICAL.value == "hierarchical" assert ExplodeVariant.SEMANTIC.value == "semantic" def test_explode_mode_values(self): """Test ExplodeMode enum values.""" assert ExplodeMode.STANDARD.value == "standard" assert ExplodeMode.LEGACY.value == "legacy" assert ExplodeMode.PREVIEW.value == "preview" def test_detection_confidence_values(self): """Test DetectionConfidence enum values.""" assert DetectionConfidence.HIGH.value == "high" assert DetectionConfidence.MEDIUM.value == "medium" assert DetectionConfidence.LOW.value == "low" assert DetectionConfidence.UNKNOWN.value == "unknown" class TestStructureEntry: """Test the StructureEntry dataclass.""" def test_structure_entry_creation(self): """Test creating a StructureEntry.""" entry = StructureEntry( type="h1", title="Chapter 1", path="chapter_1/index.md", order=1, parent=None, level=1, original_line=5 ) assert entry.type == "h1" assert entry.title == "Chapter 1" assert entry.path == "chapter_1/index.md" assert entry.order == 1 assert entry.parent is None assert entry.level == 1 assert entry.original_line == 5 def test_structure_entry_defaults(self): """Test StructureEntry with default values.""" entry = StructureEntry( type="h2", title="Section", path="section.md", order=2 ) assert entry.parent is None assert entry.level == 1 assert entry.original_line is None class TestManifestData: """Test the ManifestData dataclass.""" def test_manifest_data_creation(self): """Test creating ManifestData.""" manifest = ManifestData( explosion_type="flat", original_file="book.md", created="2025-10-12T19:30:00Z", markitect_version="0.1.0" ) assert manifest.explosion_type == "flat" assert manifest.original_file == "book.md" assert manifest.created == "2025-10-12T19:30:00Z" assert manifest.markitect_version == "0.1.0" assert manifest.manifest_version == ManifestVersion.V1_0.value class TestManifestManager: """Test the ManifestManager class.""" def test_manifest_manager_initialization(self): """Test ManifestManager initialization.""" manager = ManifestManager("0.1.0") assert manager.markitect_version == "0.1.0" assert manager.MANIFEST_FILENAME == "manifest.md" def test_create_manifest(self): """Test creating a manifest file.""" with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) manager = ManifestManager("0.1.0") # Create test structure structure = [ StructureEntry( type="h1", title="Book Title", path="book_title/index.md", order=1 ), StructureEntry( type="h2", title="Chapter 1", path="book_title/chapter_1.md", order=2, parent="Book Title" ) ] manifest_path = manager.create_manifest( output_dir=temp_path, original_file=Path("book.md"), variant=ExplodeVariant.FLAT, structure=structure, preservation_options={ "front_matter": True, "section_order": True, "heading_levels": True } ) assert manifest_path.exists() assert manifest_path.name == "manifest.md" # Verify content content = manifest_path.read_text(encoding='utf-8') assert "explosion_type: flat" in content assert "original_file: book.md" in content assert "Book Title" in content assert "Chapter 1" in content def test_read_manifest(self): """Test reading a manifest file.""" with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) manager = ManifestManager("0.1.0") # Create manifest structure = [ StructureEntry( type="h1", title="Test Title", path="test_title/index.md", order=1 ) ] manifest_path = manager.create_manifest( output_dir=temp_path, original_file=Path("test.md"), variant=ExplodeVariant.HIERARCHICAL, structure=structure ) # Read manifest back manifest_data = manager.read_manifest(temp_path) assert manifest_data is not None assert manifest_data.explosion_type == "hierarchical" assert manifest_data.original_file == "test.md" assert manifest_data.markitect_version == "0.1.0" assert len(manifest_data.structure) == 1 assert manifest_data.structure[0].title == "Test Title" def test_read_nonexistent_manifest(self): """Test reading manifest from directory without one.""" with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) manager = ManifestManager("0.1.0") manifest_data = manager.read_manifest(temp_path) assert manifest_data is None def test_validate_manifest(self): """Test manifest validation.""" manager = ManifestManager("0.1.0") # Valid manifest valid_manifest = ManifestData( explosion_type="flat", original_file="test.md", created="2025-10-12T19:30:00Z", markitect_version="0.1.0" ) errors = manager.validate_manifest(valid_manifest) assert len(errors) == 0 # Invalid manifest invalid_manifest = ManifestData( explosion_type="invalid_variant", original_file="", created="", markitect_version="0.1.0" ) errors = manager.validate_manifest(invalid_manifest) assert len(errors) > 0 assert any("Invalid explosion_type" in error for error in errors) assert any("Missing original_file" in error for error in errors) class TestVariantDetector: """Test the VariantDetector class.""" def test_variant_detector_initialization(self): """Test VariantDetector initialization.""" detector = VariantDetector() assert detector.manifest_manager is not None def test_detect_variant_nonexistent_directory(self): """Test variant detection on nonexistent directory.""" detector = VariantDetector() result = detector.detect_variant(Path("/nonexistent/path")) assert result.variant is None assert result.confidence == DetectionConfidence.UNKNOWN assert result.score == 0.0 assert not result.manifest_found assert "does not exist" in result.evidence[0] def test_detect_variant_with_manifest(self): """Test variant detection when manifest is present.""" with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) # Create a manifest manager = ManifestManager("0.1.0") manager.create_manifest( output_dir=temp_path, original_file=Path("test.md"), variant=ExplodeVariant.HIERARCHICAL, structure=[] ) detector = VariantDetector() result = detector.detect_variant(temp_path) assert result.variant == ExplodeVariant.HIERARCHICAL assert result.confidence == DetectionConfidence.HIGH assert result.score == 1.0 assert result.manifest_found assert result.manifest_data is not None def test_detect_variant_hierarchical_pattern(self): """Test variant detection based on hierarchical naming patterns.""" with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) # Create directories with numbered prefixes (temp_path / "01_chapter_one").mkdir() (temp_path / "02_chapter_two").mkdir() (temp_path / "03_chapter_three").mkdir() detector = VariantDetector() result = detector.detect_variant(temp_path) assert result.variant in [ExplodeVariant.HIERARCHICAL, ExplodeVariant.FLAT] assert result.confidence in [DetectionConfidence.HIGH, DetectionConfidence.MEDIUM] assert not result.manifest_found def test_detect_variant_semantic_pattern(self): """Test variant detection based on semantic directory names.""" with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) # Create semantic directories (temp_path / "parts").mkdir() (temp_path / "chapters").mkdir() (temp_path / "appendices").mkdir() detector = VariantDetector() result = detector.detect_variant(temp_path) # Should detect semantic or fall back to flat assert result.variant in [ExplodeVariant.SEMANTIC, ExplodeVariant.FLAT] assert not result.manifest_found def test_is_exploded_directory(self): """Test detection of exploded directory structures.""" detector = VariantDetector() with tempfile.TemporaryDirectory() as temp_dir: temp_path = Path(temp_dir) # Empty directory should not be detected as exploded assert not detector.is_exploded_directory(temp_path) # Directory with manifest should be detected (temp_path / "manifest.md").write_text("test manifest") assert detector.is_exploded_directory(temp_path) # Clean up and test other patterns (temp_path / "manifest.md").unlink() # Directory with numbered subdirs and markdown should be detected subdir = temp_path / "01_chapter" subdir.mkdir() (subdir / "index.md").write_text("test content") assert detector.is_exploded_directory(temp_path) class TestExplodeImplodeOptions: """Test the options dataclasses.""" def test_explode_options_defaults(self): """Test ExplodeOptions with defaults.""" options = ExplodeOptions(variant=ExplodeVariant.FLAT) assert options.variant == ExplodeVariant.FLAT assert options.mode == ExplodeMode.STANDARD assert options.output_dir is None assert options.max_depth is None assert options.preserve_front_matter is True assert options.section_spacing == 2 assert options.dry_run is False assert options.verbose is False assert options.create_manifest is True def test_implode_options_defaults(self): """Test ImplodeOptions with defaults.""" options = ImplodeOptions() assert options.output_file is None assert options.force_variant is None assert options.preserve_front_matter is True assert options.section_spacing == 2 assert options.dry_run is False assert options.verbose is False assert options.overwrite is False class TestResults: """Test the result dataclasses.""" def test_explode_result_creation(self): """Test creating an ExplodeResult.""" result = ExplodeResult( success=True, output_directory=Path("/test/output"), files_created=[Path("file1.md"), Path("file2.md")], manifest_path=Path("/test/output/manifest.md"), warnings=["Warning 1"], errors=[], variant_used=ExplodeVariant.FLAT ) assert result.success is True assert result.output_directory == Path("/test/output") assert len(result.files_created) == 2 assert result.manifest_path == Path("/test/output/manifest.md") assert len(result.warnings) == 1 assert len(result.errors) == 0 assert result.variant_used == ExplodeVariant.FLAT def test_implode_result_creation(self): """Test creating an ImplodeResult.""" result = ImplodeResult( success=True, output_file=Path("/test/output.md"), files_processed=[Path("file1.md"), Path("file2.md")], variant_detected=ExplodeVariant.HIERARCHICAL, warnings=[], errors=[] ) assert result.success is True assert result.output_file == Path("/test/output.md") assert len(result.files_processed) == 2 assert result.variant_detected == ExplodeVariant.HIERARCHICAL assert len(result.warnings) == 0 assert len(result.errors) == 0 if __name__ == '__main__': pytest.main([__file__, "-v"])