- Fixed undefined 'tool' variable in display_history function - Changed '[tool]' markup tag usage to proper Rich syntax - All tests now pass (38/38 unit tests) - Type checking passes with mypy --strict
235 lines
6.9 KiB
Python
235 lines
6.9 KiB
Python
"""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
|