| """Tests for cmd_update — branch fallback when remote branch doesn't exist.""" |
|
|
| import subprocess |
| from types import SimpleNamespace |
| from unittest.mock import patch |
|
|
| import pytest |
|
|
| from hermes_cli.main import cmd_update, PROJECT_ROOT |
|
|
|
|
| def _make_run_side_effect(branch="main", verify_ok=True, commit_count="0"): |
| """Build a side_effect function for subprocess.run that simulates git commands.""" |
|
|
| def side_effect(cmd, **kwargs): |
| joined = " ".join(str(c) for c in cmd) |
|
|
| |
| if "rev-parse" in joined and "--abbrev-ref" in joined: |
| return subprocess.CompletedProcess(cmd, 0, stdout=f"{branch}\n", stderr="") |
|
|
| |
| if "rev-parse" in joined and "--verify" in joined: |
| rc = 0 if verify_ok else 128 |
| return subprocess.CompletedProcess(cmd, rc, stdout="", stderr="") |
|
|
| |
| if "rev-list" in joined: |
| return subprocess.CompletedProcess(cmd, 0, stdout=f"{commit_count}\n", stderr="") |
|
|
| |
| return subprocess.CompletedProcess(cmd, 0, stdout="", stderr="") |
|
|
| return side_effect |
|
|
|
|
| @pytest.fixture |
| def mock_args(): |
| return SimpleNamespace() |
|
|
|
|
| class TestCmdUpdateBranchFallback: |
| """cmd_update falls back to main when current branch has no remote counterpart.""" |
|
|
| @patch("shutil.which", return_value=None) |
| @patch("subprocess.run") |
| def test_update_falls_back_to_main_when_branch_not_on_remote( |
| self, mock_run, _mock_which, mock_args, capsys |
| ): |
| mock_run.side_effect = _make_run_side_effect( |
| branch="fix/stoicneko", verify_ok=False, commit_count="3" |
| ) |
|
|
| cmd_update(mock_args) |
|
|
| commands = [" ".join(str(a) for a in c.args[0]) for c in mock_run.call_args_list] |
|
|
| |
| rev_list_cmds = [c for c in commands if "rev-list" in c] |
| assert len(rev_list_cmds) == 1 |
| assert "origin/main" in rev_list_cmds[0] |
| assert "origin/fix/stoicneko" not in rev_list_cmds[0] |
|
|
| |
| pull_cmds = [c for c in commands if "pull" in c] |
| assert len(pull_cmds) == 1 |
| assert "main" in pull_cmds[0] |
|
|
| @patch("shutil.which", return_value=None) |
| @patch("subprocess.run") |
| def test_update_uses_current_branch_when_on_remote( |
| self, mock_run, _mock_which, mock_args, capsys |
| ): |
| mock_run.side_effect = _make_run_side_effect( |
| branch="main", verify_ok=True, commit_count="2" |
| ) |
|
|
| cmd_update(mock_args) |
|
|
| commands = [" ".join(str(a) for a in c.args[0]) for c in mock_run.call_args_list] |
|
|
| rev_list_cmds = [c for c in commands if "rev-list" in c] |
| assert len(rev_list_cmds) == 1 |
| assert "origin/main" in rev_list_cmds[0] |
|
|
| pull_cmds = [c for c in commands if "pull" in c] |
| assert len(pull_cmds) == 1 |
| assert "main" in pull_cmds[0] |
|
|
| @patch("shutil.which", return_value=None) |
| @patch("subprocess.run") |
| def test_update_already_up_to_date( |
| self, mock_run, _mock_which, mock_args, capsys |
| ): |
| mock_run.side_effect = _make_run_side_effect( |
| branch="main", verify_ok=True, commit_count="0" |
| ) |
|
|
| cmd_update(mock_args) |
|
|
| captured = capsys.readouterr() |
| assert "Already up to date!" in captured.out |
|
|
| |
| commands = [" ".join(str(a) for a in c.args[0]) for c in mock_run.call_args_list] |
| pull_cmds = [c for c in commands if "pull" in c] |
| assert len(pull_cmds) == 0 |
|
|