"""Integration tests for the diff auditor.""" import os import tempfile from pathlib import Path import pytest from click.testing import CliRunner from git import Repo from cli_diff_auditor.cli import main from cli_diff_auditor.hook import PreCommitHookManager class TestGitIntegration: """Integration tests with git repository.""" def setup_method(self): """Set up test fixtures.""" self.runner = CliRunner() @pytest.fixture def temp_repo(self): """Create a temporary git repository.""" with tempfile.TemporaryDirectory() as tmpdir: repo = Repo.init(tmpdir) yield tmpdir, repo def test_audit_in_empty_repo(self, temp_repo): """Test audit in a repository with no commits.""" tmpdir, repo = temp_repo result = self.runner.invoke(main, ["audit"], catch_exceptions=False) assert result.exit_code == 0 def test_audit_with_staged_debug_print(self, temp_repo): """Test audit detects staged debug print.""" tmpdir, repo = temp_repo test_file = Path(tmpdir) / "test.py" test_file.write_text("print('hello')\n") repo.index.add(["test.py"]) repo.index.commit("Initial commit") test_file.write_text("print('world')\n") repo.index.add(["test.py"]) result = self.runner.invoke(main, ["audit"], catch_exceptions=False) assert result.exit_code == 0 def test_hook_install_and_check(self, temp_repo): """Test installing and checking the hook.""" tmpdir, repo = temp_repo manager = PreCommitHookManager() result = manager.install_hook(tmpdir) assert result.success is True installed = manager.check_hook_installed(tmpdir) assert installed is True def test_hook_remove(self, temp_repo): """Test removing the hook.""" tmpdir, repo = temp_repo manager = PreCommitHookManager() manager.install_hook(tmpdir) result = manager.remove_hook(tmpdir) assert result.success is True assert manager.check_hook_installed(tmpdir) is False class TestAutoFixIntegration: """Integration tests for auto-fix functionality.""" def setup_method(self): """Set up test fixtures.""" self.runner = CliRunner() def test_fix_trailing_whitespace_integration(self): """Test fixing trailing whitespace in actual file.""" with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write("def hello():\n return 'world' \n") temp_path = f.name try: result = self.runner.invoke(main, ["fix", temp_path]) assert result.exit_code == 0 content = Path(temp_path).read_text() assert content == "def hello():\n return 'world'\n" finally: os.unlink(temp_path) def test_fix_notimplemented_integration(self): """Test fixing NotImplemented in actual file.""" with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f: f.write("def foo():\n raise NotImplemented\n") temp_path = f.name try: from cli_diff_auditor.autofix import AutoFixer fixer = AutoFixer() result = fixer.fix_notimplemented_error(temp_path) assert result.success is True content = Path(temp_path).read_text() assert "raise NotImplementedError" in content finally: os.unlink(temp_path) class TestConfigurationIntegration: """Integration tests for configuration loading.""" def setup_method(self): """Set up test fixtures.""" self.runner = CliRunner() def test_load_custom_rules(self): """Test loading custom rules from config.""" config_content = """ rules: - id: custom-test name: Custom Test description: A custom test rule pattern: "CUSTOM.*" severity: warning category: custom """ with tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False) as f: f.write(config_content) config_path = f.name try: result = self.runner.invoke(main, ["--config", config_path, "rules"]) assert result.exit_code == 0 assert "custom-test" in result.output finally: os.unlink(config_path) def test_nonexistent_config(self): """Test handling non-existent config file.""" result = self.runner.invoke(main, ["--config", "/nonexistent/config.yaml", "rules"]) assert result.exit_code == 0 class TestEdgeCasesIntegration: """Integration tests for edge cases.""" def setup_method(self): """Set up test fixtures.""" self.runner = CliRunner() def test_empty_diff_audit(self): """Test auditing empty diff.""" result = self.runner.invoke(main, ["audit-diff", ""]) assert result.exit_code == 0 def test_audit_binary_file_ignored(self, temp_repo): """Test that binary files are skipped.""" tmpdir, repo = temp_repo test_file = Path(tmpdir) / "image.png" test_file.write_bytes(b"\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR") repo.index.add(["image.png"]) repo.index.commit("Add binary file") result = self.runner.invoke(main, ["audit"], catch_exceptions=False) assert result.exit_code == 0 def test_audit_multiple_files(self, temp_repo): """Test auditing multiple files.""" tmpdir, repo = temp_repo file1 = Path(tmpdir) / "file1.py" file1.write_text("print('hello')\n") file2 = Path(tmpdir) / "file2.py" file2.write_text("console.log('world')\n") repo.index.add(["file1.py", "file2.py"]) repo.index.commit("Add files") file1.write_text("print('updated')\n") file2.write_text("console.log('updated')\n") repo.index.add(["file1.py", "file2.py"]) result = self.runner.invoke(main, ["audit"], catch_exceptions=False) assert result.exit_code == 0 @pytest.fixture def temp_repo(self): """Create a temporary git repository.""" with tempfile.TemporaryDirectory() as tmpdir: repo = Repo.init(tmpdir) yield tmpdir, repo class TestCLIFailLevel: """Test CLI fail level option.""" def setup_method(self): """Set up test fixtures.""" self.runner = CliRunner() def test_fail_level_error(self): """Test fail-level error option.""" result = self.runner.invoke(main, ["check", "--fail-level", "error"]) assert result.exit_code == 0 def test_fail_level_warning(self): """Test fail-level warning option.""" result = self.runner.invoke(main, ["check", "--fail-level", "warning"]) assert result.exit_code == 0 def test_fail_level_info(self): """Test fail-level info option.""" result = self.runner.invoke(main, ["check", "--fail-level", "info"]) assert result.exit_code == 0