codeflash/tests/test_git_utils.py
Mohamed Ashraf d268e5d0ff Add tests for handling detached HEAD state in Git operations
Enhance the test suite for Git utilities by adding scenarios to check behavior when the HEAD is detached. This includes mocking the repository state to ensure proper handling in both attached and detached scenarios during branch push operations.
2025-12-18 14:05:04 +02:00

119 lines
5.2 KiB
Python

import unittest
from unittest.mock import patch
import git
from codeflash.code_utils.git_utils import check_and_push_branch, check_running_in_git_repo, get_repo_owner_and_name
class TestGitUtils(unittest.TestCase):
@patch("codeflash.code_utils.git_utils.get_remote_url")
def test_test_get_repo_owner_and_name(self, mock_get_remote_url):
# Test with a standard GitHub HTTPS URL
mock_get_remote_url.return_value = "https://github.com/owner/repo.git"
get_repo_owner_and_name.cache_clear()
owner, repo_name = get_repo_owner_and_name()
assert owner == "owner"
assert repo_name == "repo"
# Test with a GitHub SSH URL
mock_get_remote_url.return_value = "git@github.com:owner/repo.git"
get_repo_owner_and_name.cache_clear()
owner, repo_name = get_repo_owner_and_name()
assert owner == "owner"
assert repo_name == "repo"
# Test with another GitHub SSH URL
mock_get_remote_url.return_value = "git@github.com:codeflash-ai/posthog.git"
get_repo_owner_and_name.cache_clear()
owner, repo_name = get_repo_owner_and_name()
assert owner == "codeflash-ai"
assert repo_name == "posthog"
# Test with a URL without the .git suffix
mock_get_remote_url.return_value = "https://github.com/owner/repo"
get_repo_owner_and_name.cache_clear()
owner, repo_name = get_repo_owner_and_name()
assert owner == "owner"
assert repo_name == "repo"
# Test with another GitHub SSH URL
mock_get_remote_url.return_value = "git@github.com:codeflash-ai/posthog/"
get_repo_owner_and_name.cache_clear()
owner, repo_name = get_repo_owner_and_name()
assert owner == "codeflash-ai"
assert repo_name == "posthog"
@patch("codeflash.code_utils.git_utils.git.Repo")
def test_check_running_in_git_repo_in_git_repo(self, mock_repo):
mock_repo.return_value.git_dir = "/path/to/repo/.git"
assert check_running_in_git_repo("/path/to/repo")
@patch("codeflash.code_utils.git_utils.git.Repo")
@patch("codeflash.code_utils.git_utils.sys.__stdin__.isatty", return_value=True)
@patch("codeflash.code_utils.git_utils.confirm_proceeding_with_no_git_repo", return_value=True)
def test_check_running_in_git_repo_not_in_git_repo_interactive(self, mock_confirm, mock_isatty, mock_repo):
mock_repo.side_effect = git.InvalidGitRepositoryError # type: ignore
assert check_running_in_git_repo("/path/to/non-repo") == False
@patch("codeflash.code_utils.git_utils.git.Repo")
@patch("codeflash.code_utils.git_utils.sys.__stdin__.isatty", return_value=False)
def test_check_running_in_git_repo_not_in_git_repo_non_interactive(self, mock_isatty, mock_repo):
mock_repo.side_effect = git.exc.InvalidGitRepositoryError # type: ignore
assert check_running_in_git_repo("/path/to/non-repo") is False
@patch("codeflash.code_utils.git_utils.git.Repo")
@patch("codeflash.code_utils.git_utils.sys.__stdin__.isatty", return_value=True)
@patch("codeflash.code_utils.git_utils.Confirm.ask", return_value=True)
def test_check_and_push_branch(self, mock_confirm, mock_isatty, mock_repo):
mock_repo_instance = mock_repo.return_value
# Mock HEAD not being detached
mock_repo_instance.head.is_detached = False
mock_repo_instance.active_branch.name = "test-branch"
mock_repo_instance.refs = []
mock_origin = mock_repo_instance.remote.return_value
mock_origin.push.return_value = None
assert check_and_push_branch(mock_repo_instance)
mock_origin.push.assert_called_once_with(mock_repo_instance.active_branch)
mock_origin.push.reset_mock()
# Test when branch is already pushed
mock_repo_instance.refs = [f"origin/{mock_repo_instance.active_branch.name}"]
assert check_and_push_branch(mock_repo_instance)
mock_origin.push.assert_not_called()
mock_origin.push.reset_mock()
@patch("codeflash.code_utils.git_utils.git.Repo")
@patch("codeflash.code_utils.git_utils.sys.__stdin__.isatty", return_value=False)
def test_check_and_push_branch_non_tty(self, mock_isatty, mock_repo):
mock_repo_instance = mock_repo.return_value
# Mock HEAD not being detached
mock_repo_instance.head.is_detached = False
mock_repo_instance.active_branch.name = "test-branch"
mock_repo_instance.refs = []
mock_origin = mock_repo_instance.remote.return_value
mock_origin.push.return_value = None
assert not check_and_push_branch(mock_repo_instance)
mock_origin.push.assert_not_called()
mock_origin.push.reset_mock()
@patch("codeflash.code_utils.git_utils.git.Repo")
def test_check_and_push_branch_detached_head(self, mock_repo):
mock_repo_instance = mock_repo.return_value
# Mock HEAD being detached
mock_repo_instance.head.is_detached = True
mock_origin = mock_repo_instance.remote.return_value
mock_origin.push.return_value = None
# Should return False when HEAD is detached
assert not check_and_push_branch(mock_repo_instance)
mock_origin.push.assert_not_called()
if __name__ == "__main__":
unittest.main()