""" Tests for Issue #51: Add outline mode to schema generation This test module implements comprehensive tests for the new outline mode functionality that captures document structure with actual heading text and depth control. Following TDD8 methodology - these tests are written before implementation. """ import json import pytest from pathlib import Path from tempfile import NamedTemporaryFile from click.testing import CliRunner from markitect.cli import cli from markitect.schema_generator import SchemaGenerator from markitect.exceptions import InvalidDepthError class TestIssue51OutlineMode: """Test suite for outline mode schema generation functionality.""" def setup_method(self): """Set up test fixtures.""" self.schema_generator = SchemaGenerator() self.runner = CliRunner() def test_cli_accepts_mode_outline_option(self): """Test that CLI accepts --mode outline option.""" # Arrange markdown_content = """# Test Document ## Introduction This is a test document. ### Details Some details here. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) try: # Act result = self.runner.invoke(cli, [ 'schema-generate', '--mode', 'outline', str(temp_file) ]) # Assert assert result.exit_code == 0, f"CLI should accept --mode outline option, got: {result.output}" finally: temp_file.unlink() def test_cli_accepts_depth_parameter(self): """Test that CLI accepts --depth parameter with outline mode.""" # Arrange markdown_content = """# Test Document ## Introduction This is a test document. ### Details Some details here. #### Specifics Very specific information. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) try: # Act result = self.runner.invoke(cli, [ 'schema-generate', '--mode', 'outline', '--depth', '2', str(temp_file) ]) # Assert assert result.exit_code == 0, f"CLI should accept --depth parameter, got: {result.output}" finally: temp_file.unlink() def test_outline_mode_generates_schema_with_from_title(self): """Test that outline mode generates schema with 'from' in title instead of 'for'.""" # Arrange markdown_content = """# Test Document ## Introduction This is a test document. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) try: # Act result = self.runner.invoke(cli, [ 'schema-generate', '--mode', 'outline', str(temp_file) ]) # Assert assert result.exit_code == 0 schema = json.loads(result.output) expected_title = f"Schema from {temp_file.name}" assert schema["title"] == expected_title, f"Expected title 'Schema from {temp_file.name}', got '{schema.get('title')}'" finally: temp_file.unlink() def test_outline_mode_captures_actual_heading_text(self): """Test that outline mode captures actual heading text in schema.""" # Arrange markdown_content = """# Main Architecture Document ## System Overview High-level system description. ### Core Components Details about main components. ## Implementation Strategy Strategy for implementation. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) try: # Act result = self.runner.invoke(cli, [ 'schema-generate', '--mode', 'outline', str(temp_file) ]) # Assert assert result.exit_code == 0 schema = json.loads(result.output) # Check that headings properties exist and contain actual text assert "headings" in schema["properties"], "Schema should contain headings property" # Should have level_1, level_2, level_3 based on content headings = schema["properties"]["headings"]["properties"] assert "level_1" in headings, "Should have level_1 headings" assert "level_2" in headings, "Should have level_2 headings" assert "level_3" in headings, "Should have level_3 headings" # Check heading text is captured (this will need to be implemented) # For now, verify structure exists level_1_schema = headings["level_1"] assert level_1_schema["type"] == "array" assert "items" in level_1_schema finally: temp_file.unlink() def test_outline_mode_with_depth_limit_respects_depth(self): """Test that outline mode with --depth parameter respects depth limit.""" # Arrange markdown_content = """# Main Document ## Section A Content A. ### Subsection A1 Content A1. #### Deep Section A1.1 Very deep content. ## Section B Content B. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) try: # Act result = self.runner.invoke(cli, [ 'schema-generate', '--mode', 'outline', '--depth', '2', str(temp_file) ]) # Assert assert result.exit_code == 0 schema = json.loads(result.output) headings = schema["properties"]["headings"]["properties"] assert "level_1" in headings, "Should have level_1 headings" assert "level_2" in headings, "Should have level_2 headings" assert "level_3" not in headings, "Should not have level_3 headings with depth=2" assert "level_4" not in headings, "Should not have level_4 headings with depth=2" finally: temp_file.unlink() def test_outline_mode_integrates_with_metaschema_extensions(self): """Test that outline mode integrates with metaschema extensions from Issue #50.""" # Arrange markdown_content = """# Test Document ## Introduction This is a test document. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) try: # Act result = self.runner.invoke(cli, [ 'schema-generate', '--mode', 'outline', '--depth', '3', str(temp_file) ]) # Assert assert result.exit_code == 0 schema = json.loads(result.output) # Check for metaschema extensions assert "x-markitect-outline-mode" in schema, "Should have outline mode marker" assert schema["x-markitect-outline-mode"] is True, "Outline mode should be marked as true" assert "x-markitect-outline-depth" in schema, "Should have outline depth marker" assert schema["x-markitect-outline-depth"] == 3, "Should record the depth setting" finally: temp_file.unlink() def test_outline_mode_works_with_outfile_parameter(self): """Test that outline mode works with existing --outfile parameter.""" # Arrange markdown_content = """# Test Document ## Introduction This is a test document. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) with NamedTemporaryFile(mode='w', suffix='.json', delete=False) as outf: output_file = Path(outf.name) try: # Act result = self.runner.invoke(cli, [ 'schema-generate', '--mode', 'outline', '--outfile', str(output_file), str(temp_file) ]) # Assert assert result.exit_code == 0 assert output_file.exists(), "Output file should be created" schema_content = output_file.read_text() schema = json.loads(schema_content) expected_title = f"Schema from {temp_file.name}" assert schema["title"] == expected_title finally: temp_file.unlink() if output_file.exists(): output_file.unlink() def test_cli_maintains_backward_compatibility_with_max_depth(self): """Test that existing --max-depth option still works with default mode.""" # Arrange markdown_content = """# Test Document ## Introduction This is a test document. ### Details Some details here. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) try: # Act result = self.runner.invoke(cli, [ 'schema-generate', '--max-depth', '2', str(temp_file) ]) # Assert assert result.exit_code == 0, f"CLI should maintain backward compatibility with --max-depth, got: {result.output}" schema = json.loads(result.output) # Should use old title format for backward compatibility expected_title = f"Schema for {temp_file.name}" assert schema["title"] == expected_title, f"Default mode should use 'for' in title" finally: temp_file.unlink() def test_depth_parameter_validation(self): """Test that --depth parameter validates input correctly.""" # Arrange markdown_content = """# Test Document ## Introduction This is a test document. """ with NamedTemporaryFile(mode='w', suffix='.md', delete=False) as f: f.write(markdown_content) temp_file = Path(f.name) try: # Act - Test invalid depth result = self.runner.invoke(cli, [ 'schema-generate', '--mode', 'outline', '--depth', '0', str(temp_file) ]) # Assert assert result.exit_code != 0, "Should reject depth=0" assert "Invalid depth parameter" in result.output or "depth must be >= 1" in result.output finally: temp_file.unlink() def test_cli_help_includes_new_options(self): """Test that CLI help text includes documentation for new options.""" # Act result = self.runner.invoke(cli, ['schema-generate', '--help']) # Assert assert result.exit_code == 0 help_text = result.output assert "--mode" in help_text, "Help should document --mode option" assert "--depth" in help_text, "Help should document --depth option" assert "outline" in help_text, "Help should mention outline mode"