12d9f2f647
CI / Lint, Typecheck & Test (push) Successful in 1m56s
将 gitt a/i 命令改用 fn job 包装(git_add_commit/git_init_add_commit), 内部检查 has_files() 和 not_has_git_repo() 条件,避免无更改时 git commit 报错。修正 has_files() 实现为检查 git status --porcelain 而非目录文件。
129 lines
4.9 KiB
Python
129 lines
4.9 KiB
Python
"""Tests for cli.gittool module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
import pyflowx as px
|
|
from pyflowx.cli import gittool
|
|
from pyflowx.cli._ops import dev
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# not_has_git_repo
|
|
# ---------------------------------------------------------------------- #
|
|
class TestNotHasGitRepo:
|
|
"""Test not_has_git_repo function."""
|
|
|
|
def test_not_has_git_repo_true(self, tmp_path: Path) -> None:
|
|
"""Should return True when no .git directory."""
|
|
with patch.object(Path, "cwd", return_value=tmp_path):
|
|
result = dev.not_has_git_repo()
|
|
assert result is True
|
|
|
|
def test_not_has_git_repo_false(self, tmp_path: Path) -> None:
|
|
"""Should return False when .git directory exists."""
|
|
git_dir = tmp_path / ".git"
|
|
git_dir.mkdir()
|
|
|
|
with patch.object(Path, "cwd", return_value=tmp_path):
|
|
result = dev.not_has_git_repo()
|
|
assert result is False
|
|
|
|
def test_not_has_git_repo_cwd_not_exists(self, tmp_path: Path) -> None:
|
|
"""Should return True when cwd doesn't exist."""
|
|
nonexistent = tmp_path / "nonexistent"
|
|
|
|
with patch.object(Path, "cwd", return_value=nonexistent):
|
|
result = dev.not_has_git_repo()
|
|
assert result is True
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# has_files
|
|
# ---------------------------------------------------------------------- #
|
|
class TestHasFiles:
|
|
"""Test has_files function."""
|
|
|
|
def test_has_files_true(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Should return True when there are uncommitted changes."""
|
|
|
|
class _FakeResult:
|
|
stdout = " M test.txt\n"
|
|
|
|
monkeypatch.setattr("subprocess.run", lambda *_, **__: _FakeResult())
|
|
result = dev.has_files()
|
|
assert result is True
|
|
|
|
def test_has_files_false(self, tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
|
|
"""Should return False when no uncommitted changes."""
|
|
|
|
class _FakeResult:
|
|
stdout = ""
|
|
|
|
monkeypatch.setattr("subprocess.run", lambda *_, **__: _FakeResult())
|
|
result = dev.has_files()
|
|
assert result is False
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# init_sub_dirs
|
|
# ---------------------------------------------------------------------- #
|
|
class TestInitSubDirs:
|
|
"""Test init_sub_dirs function."""
|
|
|
|
def test_init_sub_dirs_with_subdirectories(self, tmp_path: Path) -> None:
|
|
"""Should initialize git in subdirectories."""
|
|
subdir1 = tmp_path / "subdir1"
|
|
subdir1.mkdir()
|
|
subdir2 = tmp_path / "subdir2"
|
|
subdir2.mkdir()
|
|
|
|
with patch.object(Path, "cwd", return_value=tmp_path), patch.object(px, "run") as mock_run:
|
|
dev.init_sub_dirs()
|
|
# Should call px.run for each subdirectory
|
|
assert mock_run.call_count == 2
|
|
|
|
def test_init_sub_dirs_no_subdirectories(self, tmp_path: Path) -> None:
|
|
"""Should handle no subdirectories."""
|
|
with patch.object(Path, "cwd", return_value=tmp_path), patch.object(px, "run") as mock_run:
|
|
dev.init_sub_dirs()
|
|
# Should not call px.run
|
|
assert mock_run.call_count == 0
|
|
|
|
|
|
# ---------------------------------------------------------------------- #
|
|
# main function
|
|
# ---------------------------------------------------------------------- #
|
|
class TestMain:
|
|
"""Test main function."""
|
|
|
|
def test_main_calls_run_yaml(self) -> None:
|
|
"""main() should parse args and call px.run_yaml()."""
|
|
# 使用有效命令 p (推送), mock run_yaml 避免实际执行 git 命令
|
|
with patch("sys.argv", ["gittool", "p"]), patch.object(px, "run_yaml") as mock_run_yaml:
|
|
gittool.main()
|
|
assert mock_run_yaml.called
|
|
# 验证 jobs 参数为 "p" (YAML 中的 job 名)
|
|
assert mock_run_yaml.call_args.kwargs["jobs"] == "p"
|
|
|
|
def test_main_with_valid_command(self) -> None:
|
|
"""main() should handle valid commands."""
|
|
# 测试各个有效命令均能调用 run_yaml
|
|
for cmd in ("a", "c", "i", "isub", "p", "pl"):
|
|
with patch("sys.argv", ["gittool", cmd]), patch.object(px, "run_yaml") as mock_run_yaml:
|
|
gittool.main()
|
|
assert mock_run_yaml.called
|
|
assert mock_run_yaml.call_args.kwargs["jobs"] == cmd
|
|
|
|
def test_main_with_no_args_shows_help(self, capsys: pytest.CaptureFixture[str]) -> None:
|
|
"""main() with no args should show help and return."""
|
|
# 新的 main() 在无有效命令时打印 help 并 return, 不再 sys.exit
|
|
with patch("sys.argv", ["gittool"]):
|
|
gittool.main()
|
|
captured = capsys.readouterr()
|
|
assert "Gittool" in captured.out or "usage" in captured.out
|