from pathlib import Path from subprocess import CalledProcessError from types import SimpleNamespace import pytest from hermes_cli import main as hermes_main def test_stash_local_changes_if_needed_returns_none_when_tree_clean(monkeypatch, tmp_path): calls = [] def fake_run(cmd, **kwargs): calls.append((cmd, kwargs)) if cmd[-2:] == ["status", "--porcelain"]: return SimpleNamespace(stdout="", returncode=0) raise AssertionError(f"unexpected command: {cmd}") monkeypatch.setattr(hermes_main.subprocess, "run", fake_run) stash_ref = hermes_main._stash_local_changes_if_needed(["git"], tmp_path) assert stash_ref is None assert [cmd[-2:] for cmd, _ in calls] == [["status", "--porcelain"]] def test_stash_local_changes_if_needed_returns_specific_stash_commit(monkeypatch, tmp_path): calls = [] def fake_run(cmd, **kwargs): calls.append((cmd, kwargs)) if cmd[-2:] == ["status", "--porcelain"]: return SimpleNamespace(stdout=" M hermes_cli/main.py\n?? notes.txt\n", returncode=0) if cmd[1:4] == ["stash", "push", "--include-untracked"]: return SimpleNamespace(stdout="Saved working directory\n", returncode=0) if cmd[-3:] == ["rev-parse", "--verify", "refs/stash"]: return SimpleNamespace(stdout="abc123\n", returncode=0) raise AssertionError(f"unexpected command: {cmd}") monkeypatch.setattr(hermes_main.subprocess, "run", fake_run) stash_ref = hermes_main._stash_local_changes_if_needed(["git"], tmp_path) assert stash_ref == "abc123" assert calls[1][0][1:4] == ["stash", "push", "--include-untracked"] assert calls[2][0][-3:] == ["rev-parse", "--verify", "refs/stash"] def test_restore_stashed_changes_applies_specific_stash_and_drops_it(monkeypatch, tmp_path, capsys): calls = [] def fake_run(cmd, **kwargs): calls.append((cmd, kwargs)) if cmd[1:3] == ["stash", "apply"]: return SimpleNamespace(stdout="applied\n", stderr="", returncode=0) if cmd[1:3] == ["stash", "drop"]: return SimpleNamespace(stdout="dropped\n", stderr="", returncode=0) raise AssertionError(f"unexpected command: {cmd}") monkeypatch.setattr(hermes_main.subprocess, "run", fake_run) hermes_main._restore_stashed_changes(["git"], tmp_path, "abc123") assert calls[0][0] == ["git", "stash", "apply", "abc123"] assert calls[1][0] == ["git", "stash", "drop", "abc123"] assert "Restoring local changes" in capsys.readouterr().out def test_restore_stashed_changes_exits_cleanly_when_apply_fails(monkeypatch, tmp_path, capsys): calls = [] def fake_run(cmd, **kwargs): calls.append((cmd, kwargs)) if cmd[1:3] == ["stash", "apply"]: return SimpleNamespace(stdout="conflict output\n", stderr="conflict stderr\n", returncode=1) raise AssertionError(f"unexpected command: {cmd}") monkeypatch.setattr(hermes_main.subprocess, "run", fake_run) with pytest.raises(SystemExit, match="1"): hermes_main._restore_stashed_changes(["git"], tmp_path, "abc123") out = capsys.readouterr().out assert "Your changes are still preserved in git stash." in out assert "git stash apply abc123" in out assert calls == [(["git", "stash", "apply", "abc123"], {"cwd": tmp_path, "capture_output": True, "text": True})] def test_stash_local_changes_if_needed_raises_when_stash_ref_missing(monkeypatch, tmp_path): def fake_run(cmd, **kwargs): if cmd[-2:] == ["status", "--porcelain"]: return SimpleNamespace(stdout=" M hermes_cli/main.py\n", returncode=0) if cmd[1:4] == ["stash", "push", "--include-untracked"]: return SimpleNamespace(stdout="Saved working directory\n", returncode=0) if cmd[-3:] == ["rev-parse", "--verify", "refs/stash"]: raise CalledProcessError(returncode=128, cmd=cmd) raise AssertionError(f"unexpected command: {cmd}") monkeypatch.setattr(hermes_main.subprocess, "run", fake_run) with pytest.raises(CalledProcessError): hermes_main._stash_local_changes_if_needed(["git"], Path(tmp_path))