"""
Integration tests for Git MCP tools.
Tests the git-related MCP tools that interact with the git repository.
"""
import pytest
import os
import tempfile
import shutil
from pathlib import Path
from unittest.mock import patch, MagicMock
from commit_helper_mcp.server.git_tools import (
get_git_status,
preview_git_commit,
execute_git_commit,
generate_and_commit,
validate_commit_readiness,
stage_files_and_commit,
get_git_implementation_info,
)
class TestGitMCPTools:
"""Integration tests for Git MCP tools."""
@pytest.fixture
def temp_git_repo(self):
"""Create a temporary git repository for testing."""
temp_dir = tempfile.mkdtemp()
try:
# Initialize git repository
os.chdir(temp_dir)
os.system("git init")
os.system("git config user.name 'Test User'")
os.system("git config user.email 'test@example.com'")
# Create a test file
test_file = Path(temp_dir) / "test.txt"
test_file.write_text("test content")
os.system("git add test.txt")
yield temp_dir
finally:
os.chdir(self.original_cwd)
shutil.rmtree(temp_dir)
def setup_method(self):
"""Set up test environment."""
self.original_cwd = os.getcwd()
def teardown_method(self):
"""Clean up test environment."""
os.chdir(self.original_cwd)
def test_get_git_status(self, temp_git_repo):
"""Test get_git_status tool."""
status = get_git_status(temp_git_repo)
# Should return a dict with git_enabled field
assert isinstance(status, dict)
assert "git_enabled" in status
assert status["git_enabled"] is True
# Should have repository info
assert "repository_path" in status
assert "staged_files" in status
assert "staged_count" in status
assert isinstance(status["staged_files"], list)
assert isinstance(status["staged_count"], int)
assert status["staged_count"] > 0
assert "test.txt" in status["staged_files"]
def test_validate_commit_readiness(self, temp_git_repo):
"""Test validate_commit_readiness tool."""
readiness = validate_commit_readiness(temp_git_repo)
# Should return a dict with required fields
assert isinstance(readiness, dict)
assert "ready_to_commit" in readiness
assert "git_enabled" in readiness
assert isinstance(readiness["ready_to_commit"], bool)
assert isinstance(readiness["git_enabled"], bool)
assert readiness["git_enabled"] is True
# Should have checks and recommendations
assert "checks" in readiness
assert "recommendations" in readiness
assert isinstance(readiness["checks"], dict)
assert isinstance(readiness["recommendations"], list)
def test_preview_git_commit(self, temp_git_repo):
"""Test preview_git_commit tool."""
test_message = "feat: add git MCP tools for repository operations"
preview = preview_git_commit(test_message, temp_git_repo)
# Should return a dict with required fields
assert isinstance(preview, dict)
assert "git_enabled" in preview
assert "message" in preview
assert "dry_run" in preview
assert preview["message"] == test_message
assert preview["dry_run"] is True # Preview is always dry run
assert preview["git_enabled"] is True
# Should have validation and file info
assert "is_valid" in preview
assert "files_to_commit" in preview
assert "staged_files_count" in preview
assert isinstance(preview["is_valid"], bool)
assert isinstance(preview["files_to_commit"], list)
assert isinstance(preview["staged_files_count"], int)
assert preview["staged_files_count"] > 0
assert "test.txt" in preview["files_to_commit"]
def test_execute_git_commit_safety(self, temp_git_repo):
"""Test execute_git_commit safety mechanisms."""
test_message = "test: verify safety mechanisms work"
# Test without force flag - should not execute
result = execute_git_commit(test_message, temp_git_repo, force_execute=False)
assert isinstance(result, dict)
assert "executed" in result
assert "dry_run" in result
assert result["executed"] is False
assert result["dry_run"] is True
assert "error" in result
assert "force_execute=True required" in result["error"]
def test_generate_and_commit_preview(self, temp_git_repo):
"""Test generate_and_commit in preview mode."""
result = generate_and_commit(
type="feat",
subject="add comprehensive git integration",
repo_path=temp_git_repo,
body="Implements git status, preview, and commit functionality with safety checks",
preview_only=True,
)
# Should return a dict with required fields
assert isinstance(result, dict)
assert "preview_only" in result
assert result["preview_only"] is True
# Should have message generation results
assert "message" in result
assert "is_valid" in result
assert isinstance(result["message"], str)
assert len(result["message"]) > 0
assert isinstance(result["is_valid"], bool)
assert result["is_valid"] is True
# Should have git preview
assert "git_preview" in result
assert isinstance(result["git_preview"], dict)
assert "files_to_commit" in result["git_preview"]
assert "test.txt" in result["git_preview"]["files_to_commit"]
def test_get_git_implementation_info(self):
"""Test get_git_implementation_info tool."""
result = get_git_implementation_info()
# Should return implementation info
assert "git_enabled" in result
assert "implementation" in result
assert "enhanced_features" in result
# If git is enabled, should have GitPython implementation
if result["git_enabled"]:
assert result["implementation"] == "GitPython"
assert result["enhanced_features"] is True
assert "features" in result
assert isinstance(result["features"], dict)
class TestGitToolsErrorHandling:
"""Test error handling in Git MCP tools."""
def test_invalid_repository_path(self):
"""Test tools with invalid repository path."""
invalid_path = "/definitely/does/not/exist"
# Test each git tool
tools_to_test = [
get_git_status,
lambda path: preview_git_commit("test message", path),
]
for tool in tools_to_test:
result = tool(invalid_path)
# Should fail gracefully
assert "git_enabled" in result
assert result["git_enabled"] is False
assert "error" in result
# Test validate_commit_readiness separately as it uses the new error handling system
result = validate_commit_readiness(invalid_path)
# The error is caught by the handle_errors decorator and returned as a dict
assert isinstance(result, dict)
assert "error" in result
assert "success" in result
assert result["success"] is False
assert "error_type" in result
assert "RepositoryError" in result["error_type"]