"""Tests for cli.py.""" import tempfile import os from pathlib import Path from unittest.mock import patch, MagicMock import pytest from click.testing import CliRunner from gitignore_generator.cli import main, generate, detect, list, show, cache, clear_cache class TestCLI: """Tests for CLI commands.""" @pytest.fixture def runner(self): """Create CLI runner.""" return CliRunner() @pytest.fixture def temp_dir(self): """Create temporary directory.""" with tempfile.TemporaryDirectory() as tmpdir: yield Path(tmpdir) def test_main_help(self, runner): """Test main help command.""" result = runner.invoke(main, ['--help']) assert result.exit_code == 0 assert 'generate' in result.output assert 'detect' in result.output assert 'list' in result.output def test_generate_help(self, runner): """Test generate help.""" result = runner.invoke(generate, ['--help']) assert result.exit_code == 0 assert 'TECH' in result.output assert '--output' in result.output assert '--preview' in result.output def test_generate_single_tech(self, runner): """Test generating gitignore for single tech.""" with patch('gitignore_generator.api.get_patterns') as mock: mock.return_value = '__pycache__/\n*.pyc\n' result = runner.invoke(generate, ['python', '--preview']) assert result.exit_code == 0 assert '__pycache__' in result.output def test_generate_multiple_techs(self, runner): """Test generating gitignore for multiple techs.""" with patch('gitignore_generator.api.get_patterns') as mock: def side_effect(tech, force=False): if tech == 'node': return 'node_modules/\n' elif tech == 'python': return '__pycache__/\n' return '' mock.side_effect = side_effect result = runner.invoke(generate, ['node', 'python', '--preview']) assert result.exit_code == 0 assert 'node_modules' in result.output assert '__pycache__' in result.output def test_generate_to_file(self, runner, temp_dir): """Test generating gitignore to file.""" output_path = temp_dir / '.gitignore' with patch('gitignore_generator.api.get_patterns') as mock: mock.return_value = '__pycache__/\n' result = runner.invoke(generate, ['python', '-o', str(output_path)]) assert result.exit_code == 0 assert output_path.exists() content = output_path.read_text() assert '__pycache__' in content def test_generate_with_add_pattern(self, runner): """Test generating with additional pattern.""" with patch('gitignore_generator.api.get_patterns') as mock: mock.return_value = '__pycache__/\n' result = runner.invoke(generate, ['python', '--preview', '--add-pattern', '*.custom']) assert result.exit_code == 0 assert '*.custom' in result.output def test_generate_preview_and_output_conflict(self, runner, temp_dir): """Test that --preview and -o cannot be used together.""" output_path = temp_dir / '.gitignore' with patch('gitignore_generator.api.get_patterns') as mock: mock.return_value = '__pycache__/\n' result = runner.invoke(generate, ['python', '--preview', '-o', str(output_path)]) assert result.exit_code != 0 assert 'cannot be used together' in result.output def test_detect_help(self, runner): """Test detect help.""" result = runner.invoke(detect, ['--help']) assert result.exit_code == 0 assert '--preview' in result.output assert '--output' in result.output def test_detect_no_project(self, runner, temp_dir): """Test detect when no project is found.""" with patch('gitignore_generator.detector.ProjectDetector') as mock_detector: mock_instance = MagicMock() mock_instance.detect.return_value = [] mock_instance.suggest_gitignore.return_value = [] mock_instance.get_detection_details.return_value = [] mock_detector.return_value = mock_instance with runner.isolated_filesystem(): result = runner.invoke(detect, []) assert result.exit_code == 0 assert 'No project type detected' in result.output def test_detect_with_project(self, runner, temp_dir): """Test detect when project is found.""" with patch('gitignore_generator.detector.ProjectDetector') as mock_detector: mock_instance = MagicMock() mock_instance.detect.return_value = ['python'] mock_instance.suggest_gitignore.return_value = ['python'] mock_instance.get_detection_details.return_value = [ {'technology': 'python', 'matched_files': ['requirements.txt'], 'confidence': 1} ] mock_detector.return_value = mock_instance with patch('gitignore_generator.api.get_patterns') as mock_patterns: mock_patterns.return_value = '__pycache__/\n' with runner.isolated_filesystem(): result = runner.invoke(detect, ['--preview']) assert result.exit_code == 0 assert 'python' in result.output def test_list_help(self, runner): """Test list help.""" result = runner.invoke(list, ['--help']) assert result.exit_code == 0 assert '--force-refresh' in result.output def test_list_all(self, runner): """Test listing all technologies.""" with patch('gitignore_generator.cli.get_list') as mock: mock.return_value = ['node', 'python', 'django'] result = runner.invoke(list, []) assert result.exit_code == 0 assert 'node' in result.output assert 'python' in result.output def test_list_filtered(self, runner): """Test listing filtered technologies.""" with patch('gitignore_generator.cli.get_list') as mock: mock.return_value = ['node', 'python', 'nodemon'] result = runner.invoke(list, ['node']) assert result.exit_code == 0 assert 'Matching' in result.output def test_show_help(self, runner): """Test show help.""" result = runner.invoke(show, ['--help']) assert result.exit_code == 0 assert '--force-refresh' in result.output def test_show_tech(self, runner): """Test showing patterns for a tech.""" with patch('gitignore_generator.cli.get_patterns') as mock: mock.return_value = 'node_modules/\n*.log\n' result = runner.invoke(show, ['node']) assert result.exit_code == 0 assert 'node_modules' in result.output def test_cache_help(self, runner): """Test cache help.""" result = runner.invoke(cache, ['--help']) assert result.exit_code == 0 def test_cache_show_stats(self, runner): """Test showing cache stats.""" with patch('gitignore_generator.cli.CacheManager') as mock: mock_instance = MagicMock() mock_instance.get_stats.return_value = { 'total_items': 10, 'valid_items': 8, 'expired_items': 2, 'cache_dir': '/tmp/cache' } mock.return_value = mock_instance result = runner.invoke(cache, []) assert result.exit_code == 0 assert '10' in result.output assert '8' in result.output def test_clear_cache(self, runner): """Test clearing cache.""" with patch('gitignore_generator.cli.CacheManager') as mock: mock_instance = MagicMock() mock_instance.clear.return_value = 5 mock.return_value = mock_instance result = runner.invoke(clear_cache, []) assert result.exit_code == 0 assert '5' in result.output def test_cli_error_handling(self, runner): """Test CLI error handling.""" with patch('gitignore_generator.cli.get_patterns') as mock: from gitignore_generator.api import GitignoreIOError mock.side_effect = GitignoreIOError("Test error") result = runner.invoke(show, ['nonexistent']) assert result.exit_code != 0 assert 'Error' in result.output