"""
Integration Tests for Repository Targeting
Tests the ability to target different repositories for git operations.
"""
import pytest
import os
from pathlib import Path
from git import Repo
from commit_helper_mcp.services.repository_manager import RepositoryManager
from commit_helper_mcp.services.commitizen_core import CommitzenCore
from commit_helper_mcp.services.gitpython_core import GitPythonCore
@pytest.mark.integration
class TestRepositoryTargeting:
"""Test cases for repository targeting functionality."""
def test_explicit_repository_path(self, temp_dir):
"""Test targeting a repository with explicit path parameter."""
# Setup test repository
repo_path = temp_dir / "test_repo"
repo_path.mkdir()
repo = Repo.init(repo_path)
# Configure git user
with repo.config_writer() as config:
config.set_value("user", "name", "Test User")
config.set_value("user", "email", "test@example.com")
# Create test file
test_file = repo_path / "test.py"
test_file.write_text("print('Hello, World!')\n")
repo.git.add(str(test_file))
# Initialize repository manager
manager = RepositoryManager()
# Get repository info with explicit path
repo_info = manager.switch_repository(str(repo_path))
# Verify repository info
assert repo_info["success"] is True
assert repo_info["repository_path"] == str(repo_path)
assert repo_info["has_git"] is True
# Get git status to verify staged files
_, git_service = manager.get_repository_services(str(repo_path))
status = git_service.get_repository_status()
assert len(status["staged_files"]) == 1
assert "test.py" in status["staged_files"][0]
def test_multiple_repositories(self, temp_dir):
"""Test targeting multiple repositories in the same session."""
# Setup two test repositories
repo1_path = temp_dir / "repo1"
repo2_path = temp_dir / "repo2"
repo1_path.mkdir()
repo2_path.mkdir()
repo1 = Repo.init(repo1_path)
repo2 = Repo.init(repo2_path)
# Configure git users
with repo1.config_writer() as config:
config.set_value("user", "name", "Test User 1")
config.set_value("user", "email", "test1@example.com")
with repo2.config_writer() as config:
config.set_value("user", "name", "Test User 2")
config.set_value("user", "email", "test2@example.com")
# Create test files
test_file1 = repo1_path / "test1.py"
test_file1.write_text("print('Repo 1')\n")
repo1.git.add(str(test_file1))
test_file2 = repo2_path / "test2.py"
test_file2.write_text("print('Repo 2')\n")
repo2.git.add(str(test_file2))
# Initialize services for both repositories
git_service1 = GitPythonCore(repo_path=str(repo1_path))
git_service2 = GitPythonCore(repo_path=str(repo2_path))
# Get status from both repositories
status1 = git_service1.get_repository_status()
status2 = git_service2.get_repository_status()
# Verify repository statuses
assert status1["repository_path"] == str(repo1_path)
assert "test1.py" in status1["staged_files"][0]
assert status2["repository_path"] == str(repo2_path)
assert "test2.py" in status2["staged_files"][0]
def test_environment_variable_targeting(self, temp_dir, monkeypatch):
"""Test targeting a repository with environment variable."""
# Setup test repository
repo_path = temp_dir / "env_repo"
repo_path.mkdir()
repo = Repo.init(repo_path)
# Configure git user
with repo.config_writer() as config:
config.set_value("user", "name", "Test User")
config.set_value("user", "email", "test@example.com")
# Create test file
test_file = repo_path / "env_test.py"
test_file.write_text("print('Environment Variable Test')\n")
repo.git.add(str(test_file))
# Set environment variable for repository path
monkeypatch.setenv("COMMIT_HELPER_REPO_PATH", str(repo_path))
# Initialize repository manager without explicit path
manager = RepositoryManager()
# Get repository info (should use environment variable)
repo_info = manager.switch_repository(str(repo_path))
# Verify repository info
assert repo_info["success"] is True
assert repo_info["repository_path"] == str(repo_path)
assert repo_info["has_git"] is True
# Get git status to verify staged files
_, git_service = manager.get_repository_services(str(repo_path))
status = git_service.get_repository_status()
assert len(status["staged_files"]) == 1
assert "env_test.py" in status["staged_files"][0]
def test_fallback_to_current_directory(self, temp_dir, monkeypatch):
"""Test fallback to current directory when no path specified."""
# Setup test repository
repo_path = temp_dir / "current_dir_repo"
repo_path.mkdir()
repo = Repo.init(repo_path)
# Configure git user
with repo.config_writer() as config:
config.set_value("user", "name", "Test User")
config.set_value("user", "email", "test@example.com")
# Create test file
test_file = repo_path / "current_dir_test.py"
test_file.write_text("print('Current Directory Test')\n")
repo.git.add(str(test_file))
# Change current directory to the test repository
original_dir = os.getcwd()
os.chdir(repo_path)
try:
# Clear any environment variable
monkeypatch.delenv("COMMIT_HELPER_REPO_PATH", raising=False)
# Initialize repository manager without explicit path
manager = RepositoryManager()
# Get repository info (should use current directory)
repo_info = manager.switch_repository(str(repo_path))
# Verify repository info
assert repo_info["success"] is True
assert Path(repo_info["repository_path"]).samefile(repo_path)
assert repo_info["has_git"] is True
# Get git status to verify staged files
_, git_service = manager.get_repository_services()
status = git_service.get_repository_status()
assert len(status["staged_files"]) == 1
assert "current_dir_test.py" in status["staged_files"][0]
finally:
# Restore original directory
os.chdir(original_dir)
def test_priority_order(self, temp_dir, monkeypatch):
"""Test priority order of repository targeting methods."""
# Setup three test repositories
explicit_repo_path = temp_dir / "explicit_repo"
env_repo_path = temp_dir / "env_repo"
current_repo_path = temp_dir / "current_repo"
for path in [explicit_repo_path, env_repo_path, current_repo_path]:
path.mkdir()
repo = Repo.init(path)
# Configure git user
with repo.config_writer() as config:
config.set_value("user", "name", "Test User")
config.set_value("user", "email", "test@example.com")
# Create test file with repository-specific name
test_file = path / f"{path.name}_test.py"
test_file.write_text(f"print('{path.name} Test')\n")
repo.git.add(str(test_file))
# Set environment variable
monkeypatch.setenv("COMMIT_HELPER_REPO_PATH", str(env_repo_path))
# Change current directory
original_dir = os.getcwd()
os.chdir(current_repo_path)
try:
# Initialize repository manager
manager = RepositoryManager()
# Test 1: Explicit path should take precedence
repo_info = manager.switch_repository(str(explicit_repo_path))
assert Path(repo_info["repository_path"]).samefile(explicit_repo_path)
assert repo_info["has_git"] is True
# Get git status to verify staged files
_, git_service = manager.get_repository_services(str(explicit_repo_path))
status = git_service.get_repository_status()
assert "explicit_repo_test.py" in status["staged_files"][0]
# Test 2: Environment variable should take precedence over current directory
repo_info = manager.switch_repository(str(env_repo_path))
assert Path(repo_info["repository_path"]).samefile(env_repo_path)
assert repo_info["has_git"] is True
# Get git status to verify staged files
_, git_service = manager.get_repository_services(str(env_repo_path))
status = git_service.get_repository_status()
assert "env_repo_test.py" in status["staged_files"][0]
# Test 3: Clear environment variable to test current directory
monkeypatch.delenv("COMMIT_HELPER_REPO_PATH")
repo_info = manager.switch_repository(str(current_repo_path))
assert Path(repo_info["repository_path"]).samefile(current_repo_path)
assert repo_info["has_git"] is True
# Get git status to verify staged files
_, git_service = manager.get_repository_services(str(current_repo_path))
status = git_service.get_repository_status()
assert "current_repo_test.py" in status["staged_files"][0]
finally:
# Restore original directory
os.chdir(original_dir)
@pytest.mark.integration
class TestRepositoryValidation:
"""Test cases for repository validation."""
def test_invalid_repository_path(self, temp_dir):
"""Test handling of invalid repository path."""
# Non-existent path
non_existent_path = temp_dir / "non_existent"
# Initialize repository manager
manager = RepositoryManager()
# Get repository info for non-existent path
repo_info = manager.switch_repository(str(non_existent_path))
# Verify error handling
assert repo_info["success"] is False
assert "error" in repo_info
assert "invalid or inaccessible repository" in repo_info["error"].lower()
def test_non_git_directory(self, temp_dir):
"""Test handling of directory that is not a git repository."""
# Create directory but don't initialize git
non_git_path = temp_dir / "non_git_dir"
non_git_path.mkdir()
# Create a file
test_file = non_git_path / "test.py"
test_file.write_text("print('Not a git repo')\n")
# Initialize repository manager
manager = RepositoryManager()
# Get repository info for non-git directory
repo_info = manager.switch_repository(str(non_git_path))
# Verify error handling
# Non-git directories can still be valid for Commitizen
# So we need to check if git is enabled
assert "has_git" in repo_info
assert repo_info["has_git"] is False
def test_path_traversal_prevention(self, temp_dir):
"""Test prevention of path traversal attacks."""
# Setup legitimate repository
repo_path = temp_dir / "legitimate_repo"
repo_path.mkdir()
repo = Repo.init(repo_path)
# Configure git user
with repo.config_writer() as config:
config.set_value("user", "name", "Test User")
config.set_value("user", "email", "test@example.com")
# Create test file
test_file = repo_path / "test.py"
test_file.write_text("print('Legitimate Repo')\n")
repo.git.add(str(test_file))
# Initialize repository manager
manager = RepositoryManager()
# Attempt path traversal
traversal_path = f"{repo_path}/../../../etc"
# Get repository info with traversal attempt
repo_info = manager.switch_repository(traversal_path)
# Verify error handling
assert repo_info["success"] is False
assert "error" in repo_info