#!/usr/bin/env python3 """ Test suite for Issue #135: Instant Markdown base and publication directory This test suite validates the publication directory functionality for the md-render command, including directory processing, environment variable handling, and CLI flags. TDD8 Workflow: ISSUE→TEST→RED→GREEN→REFACTOR→DOCUMENT→REFINE→PUBLISH State: RED (Tests should fail initially) """ import pytest import tempfile import os import shutil from pathlib import Path from unittest.mock import patch, MagicMock import subprocess class TestPublicationDirectoryManagement: """Test publication directory configuration and management.""" def test_default_publication_directory_is_notes_home(self): """Test that default publication directory is ~/Notes/.""" from markitect.plugins.builtin.markdown_commands import get_publication_directory with patch.dict(os.environ, {}, clear=True): # Remove any existing MARKITECT_PUBLICATION_DIR env var if 'MARKITECT_PUBLICATION_DIR' in os.environ: del os.environ['MARKITECT_PUBLICATION_DIR'] pub_dir = get_publication_directory() expected = Path.home() / "Notes" assert pub_dir == expected def test_environment_variable_overrides_default_publication_directory(self): """Test that MARKITECT_PUBLICATION_DIR environment variable overrides default.""" from markitect.plugins.builtin.markdown_commands import get_publication_directory custom_dir = "/tmp/custom_publication" with patch.dict(os.environ, {'MARKITECT_PUBLICATION_DIR': custom_dir}): pub_dir = get_publication_directory() assert pub_dir == Path(custom_dir) def test_publication_directory_creation_when_nonexistent(self): """Test that publication directory is created when it doesn't exist.""" from markitect.plugins.builtin.markdown_commands import ensure_publication_directory with tempfile.TemporaryDirectory() as tmpdir: pub_dir = Path(tmpdir) / "nonexistent" / "publication" assert not pub_dir.exists() ensure_publication_directory(pub_dir) assert pub_dir.exists() assert pub_dir.is_dir() def test_publication_directory_path_normalization(self): """Test that publication directory paths are properly normalized.""" from markitect.plugins.builtin.markdown_commands import normalize_publication_path # Test tilde expansion path_with_tilde = "~/Documents/Notes" normalized = normalize_publication_path(path_with_tilde) assert str(normalized).startswith(str(Path.home())) # Test relative path resolution relative_path = "./publication" normalized = normalize_publication_path(relative_path) assert normalized.is_absolute() class TestSingleFileProcessing: """Test single markdown file processing behavior.""" def setup_method(self): """Set up test environment with temporary files.""" self.temp_dir = tempfile.mkdtemp() self.test_md_file = Path(self.temp_dir) / "test.md" self.test_md_file.write_text("# Test Document\n\nThis is a test.") # Set up publication directory self.pub_dir = Path(self.temp_dir) / "publication" self.pub_dir.mkdir(exist_ok=True) def teardown_method(self): """Clean up test environment.""" shutil.rmtree(self.temp_dir) def test_single_file_default_html_next_to_md(self): """Test that by default, single file HTML is generated next to MD file.""" from markitect.plugins.builtin.markdown_commands import process_single_file result = process_single_file( input_file=self.test_md_file, use_publication_dir=False, publication_dir=self.pub_dir ) expected_output = self.test_md_file.with_suffix('.html') assert result == expected_output # This will fail until implementation is added assert expected_output.exists() def test_single_file_with_use_publication_dir_flag(self): """Test single file with --use-publication-dir places HTML in publication directory.""" from markitect.plugins.builtin.markdown_commands import process_single_file result = process_single_file( input_file=self.test_md_file, use_publication_dir=True, publication_dir=self.pub_dir ) expected_output = self.pub_dir / "test.html" assert result == expected_output # This will fail until implementation is added assert expected_output.exists() def test_single_file_output_naming_conventions(self): """Test that output file naming follows conventions.""" from markitect.plugins.builtin.markdown_commands import get_output_filename # Test basic naming input_file = Path("document.md") output_name = get_output_filename(input_file) assert output_name == "document.html" # Test with complex filename input_file = Path("my-document_v2.md") output_name = get_output_filename(input_file) assert output_name == "my-document_v2.html" class TestDirectoryProcessing: """Test directory tree processing functionality.""" def setup_method(self): """Set up test directory structure.""" self.temp_dir = tempfile.mkdtemp() self.base_dir = Path(self.temp_dir) / "source" self.pub_dir = Path(self.temp_dir) / "publication" # Create test directory structure self.base_dir.mkdir() self.pub_dir.mkdir() # Create test markdown files (self.base_dir / "root.md").write_text("# Root Document") (self.base_dir / "subdir").mkdir() (self.base_dir / "subdir" / "sub.md").write_text("# Sub Document") (self.base_dir / "subdir" / "deep").mkdir() (self.base_dir / "subdir" / "deep" / "deep.md").write_text("# Deep Document") # Create non-markdown files (should be ignored) (self.base_dir / "readme.txt").write_text("Not markdown") (self.base_dir / "image.png").write_bytes(b"fake image data") def teardown_method(self): """Clean up test environment.""" shutil.rmtree(self.temp_dir) def test_directory_default_html_in_publication_dir_with_structure(self): """Test that directory processing puts HTML in publication directory with preserved structure.""" from markitect.plugins.builtin.markdown_commands import process_directory results = process_directory( input_dir=self.base_dir, use_publication_dir=True, publication_dir=self.pub_dir ) # Check that all expected HTML files are in publication directory expected_files = [ self.pub_dir / "root.html", self.pub_dir / "subdir" / "sub.html", self.pub_dir / "subdir" / "deep" / "deep.html" ] assert len(results) == 3 for expected_file in expected_files: assert expected_file in results # This will fail until implementation is added assert expected_file.exists() def test_directory_with_dont_use_publication_dir_html_next_to_md(self): """Test directory processing with --dont-use-publication-dir places HTML next to MD files.""" from markitect.plugins.builtin.markdown_commands import process_directory results = process_directory( input_dir=self.base_dir, use_publication_dir=False, publication_dir=self.pub_dir ) # Check that HTML files are next to their corresponding MD files expected_files = [ self.base_dir / "root.html", self.base_dir / "subdir" / "sub.html", self.base_dir / "subdir" / "deep" / "deep.html" ] assert len(results) == 3 for expected_file in expected_files: assert expected_file in results # This will fail until implementation is added assert expected_file.exists() def test_directory_recursive_traversal(self): """Test that directory processing recursively finds all markdown files.""" from markitect.plugins.builtin.markdown_commands import find_markdown_files md_files = find_markdown_files(self.base_dir) expected_files = [ self.base_dir / "root.md", self.base_dir / "subdir" / "sub.md", self.base_dir / "subdir" / "deep" / "deep.md" ] assert len(md_files) == 3 for expected_file in expected_files: assert expected_file in md_files def test_directory_structure_preservation(self): """Test that directory structure is preserved in publication directory.""" from markitect.plugins.builtin.markdown_commands import get_relative_output_path # Test relative path calculation source_file = self.base_dir / "subdir" / "deep" / "deep.md" relative_path = get_relative_output_path(source_file, self.base_dir, self.pub_dir) expected_path = self.pub_dir / "subdir" / "deep" / "deep.html" assert relative_path == expected_path class TestCLIIntegration: """Test CLI integration with new flags.""" def setup_method(self): """Set up test environment.""" self.temp_dir = tempfile.mkdtemp() self.test_file = Path(self.temp_dir) / "test.md" self.test_file.write_text("# Test Document") def teardown_method(self): """Clean up test environment.""" shutil.rmtree(self.temp_dir) def test_md_render_command_has_use_publication_dir_flag(self): """Test that md-render command has --use-publication-dir flag.""" result = subprocess.run( ["markitect", "md-render", "--help"], capture_output=True, text=True ) assert "--use-publication-dir" in result.stdout assert "publication directory" in result.stdout.lower() def test_md_render_command_has_dont_use_publication_dir_flag(self): """Test that md-render command has --dont-use-publication-dir flag.""" result = subprocess.run( ["markitect", "md-render", "--help"], capture_output=True, text=True ) assert "--dont-use-publication-dir" in result.stdout def test_md_render_with_directory_input(self): """Test that md-render command accepts directory as input.""" # Create a test directory with markdown files test_dir = Path(self.temp_dir) / "test_dir" test_dir.mkdir() (test_dir / "test.md").write_text("# Test") # This should not fail with directory input result = subprocess.run( ["markitect", "md-render", str(test_dir)], capture_output=True, text=True, timeout=30 ) # Should not error on directory input assert result.returncode == 0 or "directory" in result.stderr def test_environment_variable_integration(self): """Test that MARKITECT_PUBLICATION_DIR environment variable is respected.""" pub_dir = Path(self.temp_dir) / "custom_pub" pub_dir.mkdir() env = os.environ.copy() env['MARKITECT_PUBLICATION_DIR'] = str(pub_dir) result = subprocess.run( ["markitect", "md-render", str(self.test_file), "--use-publication-dir"], capture_output=True, text=True, env=env, timeout=30 ) # Should succeed or at least recognize the flag # This will fail until implementation is complete assert result.returncode == 0 class TestEdgeCases: """Test edge cases and error conditions.""" def setup_method(self): """Set up test environment.""" self.temp_dir = tempfile.mkdtemp() def teardown_method(self): """Clean up test environment.""" shutil.rmtree(self.temp_dir) def test_empty_directory_processing(self): """Test processing of empty directory.""" from markitect.plugins.builtin.markdown_commands import process_directory empty_dir = Path(self.temp_dir) / "empty" empty_dir.mkdir() pub_dir = Path(self.temp_dir) / "pub" pub_dir.mkdir() results = process_directory( input_dir=empty_dir, use_publication_dir=True, publication_dir=pub_dir ) assert results == [] def test_mixed_content_directory_processing(self): """Test directory with mixed content (markdown and other files).""" from markitect.plugins.builtin.markdown_commands import find_markdown_files mixed_dir = Path(self.temp_dir) / "mixed" mixed_dir.mkdir() # Create various file types (mixed_dir / "document.md").write_text("# Document") (mixed_dir / "readme.txt").write_text("Text file") (mixed_dir / "image.jpg").write_bytes(b"fake image") (mixed_dir / "script.py").write_text("print('hello')") (mixed_dir / "another.md").write_text("# Another") md_files = find_markdown_files(mixed_dir) # Should only find markdown files assert len(md_files) == 2 assert all(f.suffix == '.md' for f in md_files) def test_nonexistent_input_error_handling(self): """Test error handling for nonexistent input files/directories.""" from markitect.plugins.builtin.markdown_commands import process_single_file nonexistent_file = Path(self.temp_dir) / "nonexistent.md" pub_dir = Path(self.temp_dir) / "pub" with pytest.raises(FileNotFoundError): process_single_file( input_file=nonexistent_file, use_publication_dir=False, publication_dir=pub_dir ) if __name__ == '__main__': pytest.main([__file__, "-v"])