Skip to content

Commit

Permalink
✨ test: add unit tests for git operations
Browse files Browse the repository at this point in the history
✨ test: add unit tests for git operations

✨ Features:
- Added unit tests for git commit operations in test_commits.py
- Implemented tests for file operations in test_files.py
- Created tests for basic git operations in test_operations.py

✅ Tests:
- Tested success and failure scenarios for commit creation
- Verified handling of warnings during git operations
- Ensured correct retrieval of staged files and handling of errors

Implemented comprehensive unit tests for git operations
  • Loading branch information
Test User committed Dec 9, 2024
1 parent 91f4a7b commit e803cef
Show file tree
Hide file tree
Showing 3 changed files with 286 additions and 0 deletions.
96 changes: 96 additions & 0 deletions tests/test_git/test_commits.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
"""Tests for git commit operations."""

import subprocess
from unittest.mock import MagicMock, patch

import pytest

from commitloom.core.git import GitError, GitOperations


@pytest.fixture
def git_operations():
"""Fixture for GitOperations instance."""
return GitOperations()


@patch("subprocess.run")
def test_create_commit_success(mock_run, git_operations):
"""Test successful commit creation."""
mock_run.side_effect = [
# git diff --cached --quiet
MagicMock(returncode=1), # Non-zero means there are staged changes
# git commit
MagicMock(returncode=0, stdout="", stderr=""),
]

result = git_operations.create_commit(
title="test: add new feature", message="Detailed commit message"
)

assert result is True
mock_run.assert_any_call(
[
"git",
"commit",
"-m",
"test: add new feature",
"-m",
"Detailed commit message",
],
capture_output=True,
text=True,
check=True,
)


@patch("subprocess.run")
def test_create_commit_failure(mock_run, git_operations):
"""Test handling of commit creation failure."""
mock_run.side_effect = [
# git diff --cached --quiet
MagicMock(returncode=1), # Non-zero means there are staged changes
# git commit
subprocess.CalledProcessError(1, "git", stderr=b"error"),
]

with pytest.raises(GitError) as exc_info:
git_operations.create_commit(title="test: add new feature", message="Detailed commit message")

assert "Failed to create commit" in str(exc_info.value)


@patch("subprocess.run")
@patch("commitloom.core.git.logger")
def test_create_commit_with_warning(mock_logger, mock_run, git_operations):
"""Test handling of git warnings during commit."""
mock_run.side_effect = [
# git diff --cached --quiet
MagicMock(returncode=1), # Non-zero means there are staged changes
# git commit
MagicMock(returncode=0, stderr="warning: CRLF will be replaced by LF", stdout=""),
]

result = git_operations.create_commit(title="test", message="message")

assert result is True
# Verify warning was logged
mock_logger.warning.assert_called_once_with(
"Git warning during commit: %s", "warning: CRLF will be replaced by LF"
)


@patch("subprocess.run")
@patch("commitloom.core.git.logger")
def test_create_commit_nothing_to_commit(mock_logger, mock_run, git_operations):
"""Test handling of 'nothing to commit' message."""
mock_run.return_value = MagicMock(
returncode=0, # Zero means no staged changes
stderr="",
stdout="nothing to commit, working tree clean",
)

result = git_operations.create_commit(title="test", message="message")

assert result is False
mock_logger.info.assert_called_once_with("Nothing to commit")
104 changes: 104 additions & 0 deletions tests/test_git/test_files.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
"""Tests for git file operations."""

import subprocess
from unittest.mock import MagicMock, patch

import pytest

from commitloom.core.git import GitError, GitOperations


@pytest.fixture
def git_operations():
"""Fixture for GitOperations instance."""
return GitOperations()


@patch("subprocess.run")
def test_get_diff_text_files(mock_run, git_operations, mock_git_file):
"""Test getting diff for text files."""
mock_diff = "diff --git a/file1.py b/file1.py\n+new line"
mock_run.return_value = MagicMock(
stdout=mock_diff,
stderr="",
returncode=0,
)

diff = git_operations.get_diff([mock_git_file("file1.py")])

assert diff == mock_diff


@patch("subprocess.run")
def test_get_diff_binary_files(mock_run, git_operations, mock_git_file):
"""Test getting diff for binary files."""
mock_run.return_value = MagicMock(
stdout="Binary files a/image.png and b/image.png differ",
stderr="",
returncode=0,
)

diff = git_operations.get_diff(
[mock_git_file("image.png", size=1024, hash_="abc123")]
)

assert "Binary files" in diff


@patch("subprocess.run")
def test_reset_staged_changes_success(mock_run, git_operations):
"""Test successful reset of staged changes."""
mock_run.return_value = MagicMock(returncode=0)

git_operations.reset_staged_changes()

mock_run.assert_called_with(["git", "reset"], capture_output=True, text=True, check=True)


@patch("subprocess.run")
def test_reset_staged_changes_failure(mock_run, git_operations):
"""Test handling of reset failure."""
mock_run.side_effect = subprocess.CalledProcessError(1, "git", stderr=b"error")

with pytest.raises(GitError) as exc_info:
git_operations.reset_staged_changes()

assert "Failed to reset staged changes" in str(exc_info.value)


@patch("subprocess.run")
@patch("commitloom.core.git.logger")
def test_stage_files_with_warning(mock_logger, mock_run, git_operations):
"""Test handling of git warnings during staging."""
mock_run.return_value = MagicMock(
returncode=0,
stderr="warning: LF will be replaced by CRLF in file1.py",
stdout="",
)

git_operations.stage_files(["file1.py"])

# Verify warning was logged
mock_logger.warning.assert_called_once_with(
"Git warning while staging %s: %s",
"file1.py",
"warning: LF will be replaced by CRLF in file1.py",
)


@patch("subprocess.run")
@patch("commitloom.core.git.logger")
def test_stage_files_with_info(mock_logger, mock_run, git_operations):
"""Test handling of git info messages during staging."""
mock_run.return_value = MagicMock(
returncode=0,
stderr="Updating index",
stdout="",
)

git_operations.stage_files(["file1.py"])

# Verify info was logged
mock_logger.info.assert_called_once_with(
"Git message while staging %s: %s", "file1.py", "Updating index"
)
86 changes: 86 additions & 0 deletions tests/test_git/test_operations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
"""Tests for basic git operations."""

import subprocess
from unittest.mock import MagicMock, patch

import pytest

from commitloom.core.git import GitError, GitOperations


@pytest.fixture
def git_operations():
"""Fixture for GitOperations instance."""
return GitOperations()


@patch("subprocess.run")
def test_get_staged_files_success(mock_run, git_operations, mock_git_file):
"""Test successful retrieval of staged files."""
mock_run.return_value = MagicMock(
stdout=" M file1.py\nM file2.py\n",
stderr="",
returncode=0,
)

files = git_operations.get_staged_files()

assert len(files) == 2
assert files[0].path == "file1.py"
assert files[0].status == "M"
assert files[1].path == "file2.py"
assert files[1].status == "M"


@patch("subprocess.run")
def test_get_staged_files_empty(mock_run, git_operations):
"""Test when no files are staged."""
mock_run.return_value = MagicMock(stdout="", stderr="", returncode=0)

files = git_operations.get_staged_files()

assert len(files) == 0


@patch("subprocess.run")
def test_get_staged_files_error(mock_run, git_operations):
"""Test error handling in get_staged_files."""
mock_run.side_effect = subprocess.CalledProcessError(1, "git", stderr=b"error")

with pytest.raises(GitError) as exc_info:
git_operations.get_staged_files()

assert "Failed to get staged files" in str(exc_info.value)


@patch("subprocess.run")
def test_get_staged_files_with_renames(mock_run, git_operations):
"""Test handling of renamed files."""
mock_run.return_value = MagicMock(
stdout='R "old file.py" -> "new file.py"\n',
stderr="",
returncode=0,
)

files = git_operations.get_staged_files()

assert len(files) == 1
assert files[0].path == "new file.py"
assert files[0].status == "R"
assert files[0].old_path == "old file.py"


@patch("subprocess.run")
def test_get_staged_files_ignores_untracked(mock_run, git_operations):
"""Test that untracked files are ignored."""
mock_run.return_value = MagicMock(
stdout="?? new.py\n M tracked.py\n",
stderr="",
returncode=0,
)

files = git_operations.get_staged_files()

assert len(files) == 1
assert files[0].path == "tracked.py"
assert files[0].status == "M"

0 comments on commit e803cef

Please sign in to comment.