mirror of
https://github.com/NickCrew/Claude-Cortex.git
synced 2026-09-14 20:17:11 +08:00
111 lines
3.3 KiB
Python
111 lines
3.3 KiB
Python
"""Unit tests for claude_ctx_py.git.run."""
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from claude_ctx_py.git.run import (
|
|
check_dirty_tree,
|
|
get_current_branch,
|
|
get_tracking_remote,
|
|
is_protected_branch,
|
|
resolve_repo_root,
|
|
run_git,
|
|
)
|
|
|
|
_MOD = "claude_ctx_py.git.run"
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestRunGit:
|
|
@patch(f"{_MOD}.subprocess.run")
|
|
def test_success(self, mock_sub):
|
|
mock_sub.return_value.returncode = 0
|
|
mock_sub.return_value.stdout = "ok\n"
|
|
mock_sub.return_value.stderr = ""
|
|
code, out, err = run_git(["status"], Path("/repo"))
|
|
assert code == 0
|
|
assert out == "ok\n"
|
|
|
|
@patch(f"{_MOD}.subprocess.run", side_effect=FileNotFoundError)
|
|
def test_git_not_found(self, _):
|
|
code, out, err = run_git(["status"], Path("/repo"))
|
|
assert code == 127
|
|
assert "not found" in err
|
|
|
|
@patch(f"{_MOD}.subprocess.run", side_effect=OSError("disk error"))
|
|
def test_os_error(self, _):
|
|
code, out, err = run_git(["status"], Path("/repo"))
|
|
assert code == 1
|
|
assert "disk error" in err
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestResolveRepoRoot:
|
|
@patch(f"{_MOD}.run_git", return_value=(0, "/home/user/repo\n", ""))
|
|
def test_success(self, _):
|
|
root, err = resolve_repo_root(Path("/home/user/repo/sub"))
|
|
assert root == Path("/home/user/repo")
|
|
assert err is None
|
|
|
|
@patch(f"{_MOD}.run_git", return_value=(128, "", "fatal: not a git repo"))
|
|
def test_not_a_repo(self, _):
|
|
root, err = resolve_repo_root(Path("/tmp"))
|
|
assert root is None
|
|
assert "not a git repo" in err.lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestIsProtectedBranch:
|
|
def test_main_protected(self):
|
|
assert is_protected_branch("main") is True
|
|
|
|
def test_master_protected(self):
|
|
assert is_protected_branch("master") is True
|
|
|
|
def test_feature_not_protected(self):
|
|
assert is_protected_branch("feature/foo") is False
|
|
|
|
def test_custom_set(self):
|
|
assert is_protected_branch("prod", frozenset({"prod", "staging"})) is True
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestCheckDirtyTree:
|
|
@patch(f"{_MOD}.run_git", return_value=(0, " M file.py\n?? new.py\n", ""))
|
|
def test_dirty(self, _):
|
|
dirty, files = check_dirty_tree(Path("/repo"))
|
|
assert dirty is True
|
|
assert "file.py" in files
|
|
assert "new.py" in files
|
|
|
|
@patch(f"{_MOD}.run_git", return_value=(0, "", ""))
|
|
def test_clean(self, _):
|
|
dirty, files = check_dirty_tree(Path("/repo"))
|
|
assert dirty is False
|
|
assert files == []
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestGetCurrentBranch:
|
|
@patch(f"{_MOD}.run_git", return_value=(0, "main\n", ""))
|
|
def test_on_branch(self, _):
|
|
assert get_current_branch(Path("/repo")) == "main"
|
|
|
|
@patch(f"{_MOD}.run_git", return_value=(0, "\n", ""))
|
|
def test_detached(self, _):
|
|
assert get_current_branch(Path("/repo")) is None
|
|
|
|
|
|
@pytest.mark.unit
|
|
class TestGetTrackingRemote:
|
|
@patch(f"{_MOD}.run_git", return_value=(0, "origin\n", ""))
|
|
def test_has_remote(self, _):
|
|
assert get_tracking_remote("main", Path("/repo")) == "origin"
|
|
|
|
@patch(f"{_MOD}.run_git", return_value=(1, "", ""))
|
|
def test_no_remote(self, _):
|
|
assert get_tracking_remote("local-only", Path("/repo")) is None
|