""" Test CLI integration functionality for Issue #138: Explode Markdown file to markdown directory. This test module covers the md-explode command integration with the existing CLI system and command-line interface functionality. """ import pytest import tempfile import shutil from pathlib import Path from click.testing import CliRunner from unittest.mock import Mock, patch # Import will fail initially (RED phase) until implementation exists try: from markitect.plugins.builtin.markdown_commands import ( md_explode_command, MarkdownCommandsPlugin ) from markitect.cli import cli except ImportError: # Expected during RED phase - tests should fail initially md_explode_command = None MarkdownCommandsPlugin = None cli = None class TestCLICommandExists: """Test that the md-explode command is properly registered and accessible.""" def test_md_explode_command_function_exists(self): """Test that md_explode_command function exists.""" # This should fail initially (RED phase) assert md_explode_command is not None assert callable(md_explode_command) def test_command_registered_in_plugin(self): """Test that md-explode is registered in the markdown commands plugin.""" # This should fail initially (RED phase) # Check if the plugin exposes the command plugin = MarkdownCommandsPlugin() commands = plugin.get_commands() assert 'md-explode' in commands assert commands['md-explode'] == md_explode_command def test_command_accessible_via_cli(self): """Test that md-explode command is accessible through main CLI.""" # This should fail initially (RED phase) runner = CliRunner() # Test command exists result = runner.invoke(cli, ['md-explode', '--help']) assert result.exit_code == 0 assert 'Explode' in result.output or 'explode' in result.output class TestCLICommandInterface: """Test the command-line interface of the md-explode command.""" def setup_method(self): """Set up test environment.""" self.runner = CliRunner() self.temp_dir = Path(tempfile.mkdtemp()) def teardown_method(self): """Clean up test environment.""" if self.temp_dir.exists(): shutil.rmtree(self.temp_dir) def test_command_requires_input_file(self): """Test that command requires an input file argument.""" # This should fail initially (RED phase) result = self.runner.invoke(cli, ['md-explode']) # Should fail without input file assert result.exit_code != 0 assert 'input' in result.output.lower() or 'file' in result.output.lower() def test_command_accepts_input_file_parameter(self): """Test that command accepts input file parameter.""" # This should fail initially (RED phase) # Create a test markdown file test_file = self.temp_dir / "test.md" test_file.write_text("# Test\nContent here.") # Command should accept the file result = self.runner.invoke(cli, ['md-explode', str(test_file)]) # Should not fail due to missing input file # (may fail for other reasons during RED phase) assert 'input' not in result.output.lower() or result.exit_code == 0 def test_command_supports_output_directory_option(self): """Test that command supports --output-dir option.""" # This should fail initially (RED phase) test_file = self.temp_dir / "test.md" test_file.write_text("# Test\nContent here.") output_dir = self.temp_dir / "output" result = self.runner.invoke(cli, [ 'md-explode', str(test_file), '--output-dir', str(output_dir) ]) # Should recognize the option (may fail for other reasons) assert 'output-dir' not in result.output or result.exit_code == 0 def test_command_help_text(self): """Test that command provides comprehensive help text.""" # This should fail initially (RED phase) result = self.runner.invoke(cli, ['md-explode', '--help']) assert result.exit_code == 0 help_text = result.output.lower() # Should mention key concepts assert any(word in help_text for word in ['explode', 'directory', 'markdown']) assert any(word in help_text for word in ['input', 'file']) assert any(word in help_text for word in ['output', 'directory']) def test_command_help_includes_examples(self): """Test that help text includes usage examples.""" # This should fail initially (RED phase) result = self.runner.invoke(cli, ['md-explode', '--help']) assert result.exit_code == 0 help_text = result.output.lower() # Should include examples assert 'example' in help_text or 'usage' in help_text class TestCLICommandExecution: """Test actual command execution and functionality.""" def setup_method(self): """Set up test environment.""" self.runner = CliRunner() self.temp_dir = Path(tempfile.mkdtemp()) def teardown_method(self): """Clean up test environment.""" if self.temp_dir.exists(): shutil.rmtree(self.temp_dir) def test_command_processes_simple_markdown_file(self): """Test command execution with a simple markdown file.""" # This should fail initially (RED phase) # Create test input test_content = """# Part 1: Introduction Introduction content. ## Chapter 1: Getting Started Chapter content. ## Chapter 2: Advanced Topics Advanced content. """ input_file = self.temp_dir / "test.md" input_file.write_text(test_content) output_dir = self.temp_dir / "exploded" # Execute command result = self.runner.invoke(cli, [ 'md-explode', str(input_file), '--output-dir', str(output_dir) ]) # Should succeed assert result.exit_code == 0 # Should create output structure assert output_dir.exists() md_files = list(output_dir.rglob("*.md")) assert len(md_files) > 0 def test_command_handles_file_not_found(self): """Test command behavior with non-existent input file.""" # This should fail initially (RED phase) non_existent_file = self.temp_dir / "nonexistent.md" result = self.runner.invoke(cli, [ 'md-explode', str(non_existent_file) ]) # Should fail gracefully with appropriate error message assert result.exit_code != 0 assert 'not found' in result.output.lower() or 'error' in result.output.lower() def test_command_handles_invalid_output_directory(self): """Test command behavior with invalid output directory.""" # This should fail initially (RED phase) input_file = self.temp_dir / "test.md" input_file.write_text("# Test\nContent") invalid_output = Path("/invalid/path/that/does/not/exist") result = self.runner.invoke(cli, [ 'md-explode', str(input_file), '--output-dir', str(invalid_output) ]) # Should handle error gracefully assert result.exit_code != 0 error_msg = result.output.lower() assert any(word in error_msg for word in ['error', 'permission', 'directory', 'path']) def test_command_verbose_output(self): """Test command execution with verbose flag.""" # This should fail initially (RED phase) input_file = self.temp_dir / "test.md" input_file.write_text("# Test\nContent") # Assume verbose flag exists (common pattern) result = self.runner.invoke(cli, [ 'md-explode', str(input_file), '--verbose' ]) # May fail during RED phase but should handle verbose flag # if it exists, should show more detailed output if result.exit_code == 0: # If verbose is supported, output should be more detailed assert len(result.output) > 50 # Some reasonable threshold def test_command_dry_run_option(self): """Test command execution with dry-run option.""" # This should fail initially (RED phase) input_file = self.temp_dir / "test.md" input_file.write_text("# Test\nContent") output_dir = self.temp_dir / "output" # Assume dry-run option exists (useful for this type of command) result = self.runner.invoke(cli, [ 'md-explode', str(input_file), '--output-dir', str(output_dir), '--dry-run' ]) # During dry run, should not create actual files if result.exit_code == 0: # Should show what would be done without doing it assert not output_dir.exists() or len(list(output_dir.iterdir())) == 0 class TestCLICommandOptions: """Test various command-line options and flags.""" def setup_method(self): """Set up test environment.""" self.runner = CliRunner() self.temp_dir = Path(tempfile.mkdtemp()) def teardown_method(self): """Clean up test environment.""" if self.temp_dir.exists(): shutil.rmtree(self.temp_dir) def test_command_supports_depth_limiting(self): """Test that command supports limiting the directory depth.""" # This should fail initially (RED phase) input_file = self.temp_dir / "test.md" input_file.write_text(""" # Level 1 ## Level 2 ### Level 3 #### Level 4 ##### Level 5 Content at level 5. """) result = self.runner.invoke(cli, [ 'md-explode', str(input_file), '--max-depth', '3' ]) # Should handle depth limiting option # Exact behavior depends on implementation if '--max-depth' in result.output: # Option not recognized assert False, "max-depth option not implemented" def test_command_supports_custom_file_extension(self): """Test that command supports custom file extensions.""" # This should fail initially (RED phase) input_file = self.temp_dir / "test.md" input_file.write_text("# Test\nContent") result = self.runner.invoke(cli, [ 'md-explode', str(input_file), '--extension', '.txt' ]) # Should handle custom extension option # May not be implemented initially if result.exit_code == 0: output_files = list(self.temp_dir.rglob("*.txt")) # If implemented, should create .txt files instead of .md