import os import tempfile import shutil import pytest from pathlib import Path from typing import Generator from repo_to_text.main import ( get_tree_structure, load_ignore_specs, should_ignore_file, is_ignored_path, remove_empty_dirs, save_repo_to_text ) @pytest.fixture def temp_dir() -> Generator[str, None, None]: """Create a temporary directory for testing.""" temp_path = tempfile.mkdtemp() yield temp_path shutil.rmtree(temp_path) @pytest.fixture def sample_repo(temp_dir: str) -> str: """Create a sample repository structure for testing.""" # Create directories os.makedirs(os.path.join(temp_dir, "src")) os.makedirs(os.path.join(temp_dir, "tests")) # Create sample files files = { "README.md": "# Test Project", ".gitignore": """ *.pyc __pycache__/ .git/ """, "src/main.py": "print('Hello World')", "tests/test_main.py": "def test_sample(): pass", ".repo-to-text-settings.yaml": """ gitignore-import-and-ignore: True ignore-tree-and-content: - ".git/" - ".repo-to-text-settings.yaml" ignore-content: - "README.md" """ } for file_path, content in files.items(): full_path = os.path.join(temp_dir, file_path) os.makedirs(os.path.dirname(full_path), exist_ok=True) with open(full_path, "w") as f: f.write(content) return temp_dir def test_is_ignored_path() -> None: """Test the is_ignored_path function.""" assert is_ignored_path(".git/config") is True assert is_ignored_path("repo-to-text_output.txt") is True assert is_ignored_path("src/main.py") is False assert is_ignored_path("normal_file.txt") is False def test_load_ignore_specs(sample_repo: str) -> None: """Test loading ignore specifications from files.""" gitignore_spec, content_ignore_spec, tree_and_content_ignore_spec = load_ignore_specs(sample_repo) assert gitignore_spec is not None assert content_ignore_spec is not None assert tree_and_content_ignore_spec is not None # Test gitignore patterns assert gitignore_spec.match_file("test.pyc") is True assert gitignore_spec.match_file("__pycache__/cache.py") is True assert gitignore_spec.match_file(".git/config") is True # Test content ignore patterns assert content_ignore_spec.match_file("README.md") is True # Test tree and content ignore patterns assert tree_and_content_ignore_spec.match_file(".git/config") is True def test_should_ignore_file(sample_repo: str) -> None: """Test file ignoring logic.""" gitignore_spec, content_ignore_spec, tree_and_content_ignore_spec = load_ignore_specs(sample_repo) # Test various file paths assert should_ignore_file( ".git/config", ".git/config", gitignore_spec, content_ignore_spec, tree_and_content_ignore_spec ) is True assert should_ignore_file( "src/main.py", "src/main.py", gitignore_spec, content_ignore_spec, tree_and_content_ignore_spec ) is False def test_get_tree_structure(sample_repo: str) -> None: """Test tree structure generation.""" gitignore_spec, _, tree_and_content_ignore_spec = load_ignore_specs(sample_repo) tree_output = get_tree_structure(sample_repo, gitignore_spec, tree_and_content_ignore_spec) # Basic structure checks assert "src" in tree_output assert "tests" in tree_output assert "main.py" in tree_output assert "test_main.py" in tree_output assert ".git" not in tree_output def test_remove_empty_dirs(temp_dir: str) -> None: """Test removal of empty directories from tree output.""" # Create test directory structure os.makedirs(os.path.join(temp_dir, "src")) os.makedirs(os.path.join(temp_dir, "empty_dir")) os.makedirs(os.path.join(temp_dir, "tests")) # Create some files with open(os.path.join(temp_dir, "src/main.py"), "w") as f: f.write("print('test')") with open(os.path.join(temp_dir, "tests/test_main.py"), "w") as f: f.write("def test(): pass") # Create a mock tree output that matches the actual tree command format tree_output = ( f"{temp_dir}\n" f"├── {os.path.join(temp_dir, 'src')}\n" f"│ └── {os.path.join(temp_dir, 'src/main.py')}\n" f"├── {os.path.join(temp_dir, 'empty_dir')}\n" f"└── {os.path.join(temp_dir, 'tests')}\n" f" └── {os.path.join(temp_dir, 'tests/test_main.py')}\n" ) filtered_output = remove_empty_dirs(tree_output, temp_dir) # Check that empty_dir is removed but other directories remain assert "empty_dir" not in filtered_output assert os.path.join(temp_dir, "src") in filtered_output assert os.path.join(temp_dir, "tests") in filtered_output assert os.path.join(temp_dir, "src/main.py") in filtered_output assert os.path.join(temp_dir, "tests/test_main.py") in filtered_output def test_save_repo_to_text(sample_repo: str) -> None: """Test the main save_repo_to_text function.""" # Create output directory output_dir = os.path.join(sample_repo, "output") os.makedirs(output_dir, exist_ok=True) # Create .git directory to ensure it's properly ignored os.makedirs(os.path.join(sample_repo, ".git")) with open(os.path.join(sample_repo, ".git/config"), "w") as f: f.write("[core]\n\trepositoryformatversion = 0\n") # Test file output output_file = save_repo_to_text(sample_repo, output_dir=output_dir) assert os.path.exists(output_file) assert os.path.dirname(output_file) == output_dir # Check file contents with open(output_file, 'r') as f: content = f.read() # Basic content checks assert "Directory Structure:" in content # Check for expected files assert "src/main.py" in content assert "tests/test_main.py" in content # Check for file contents assert "print('Hello World')" in content assert "def test_sample(): pass" in content # Ensure ignored patterns are not in output assert ".git/config" not in content # Check specific file assert "repo-to-text_" not in content assert ".repo-to-text-settings.yaml" not in content # Check that .gitignore content is not included assert "*.pyc" not in content assert "__pycache__" not in content if __name__ == "__main__": pytest.main([__file__])