105 lines
3.8 KiB
Python
105 lines
3.8 KiB
Python
"""Orphaned update-autostash surfacing (#63717 problem 6).
|
|
|
|
``hermes update`` can legitimately leave an autostash behind (--keep-stash
|
|
parks it; a conflicted restore preserves it), but nothing ever mentioned those
|
|
entries again — they persisted invisibly for weeks. ``hermes update`` now
|
|
warns about ``hermes-update-autostash-*`` entries older than the threshold.
|
|
Behavioral tests use real git repos; no production mocking of the code under
|
|
test.
|
|
"""
|
|
|
|
import subprocess
|
|
from datetime import datetime, timedelta, timezone
|
|
|
|
import pytest
|
|
|
|
from hermes_cli import update_cmd
|
|
|
|
|
|
def _git(cwd, *args, check=True):
|
|
return subprocess.run(
|
|
["git", *args], cwd=cwd, capture_output=True, text=True, check=check
|
|
)
|
|
|
|
|
|
def _make_repo_with_autostash(tmp_path, age_days: float):
|
|
"""Real repo with one hermes-update-autostash entry aged ``age_days``."""
|
|
import shutil
|
|
|
|
if shutil.which("git") is None:
|
|
pytest.skip("git not available")
|
|
_git(tmp_path, "init", "-q", "-b", "main")
|
|
_git(tmp_path, "config", "user.email", "t@example.com")
|
|
_git(tmp_path, "config", "user.name", "t")
|
|
(tmp_path / "tracked.txt").write_text("v1\n")
|
|
_git(tmp_path, "add", "-A")
|
|
_git(tmp_path, "commit", "-qm", "init")
|
|
|
|
(tmp_path / "tracked.txt").write_text("local change\n")
|
|
stamp = (
|
|
datetime.now(timezone.utc) - timedelta(days=age_days)
|
|
).strftime("%Y%m%d-%H%M%S")
|
|
name = f"hermes-update-autostash-{stamp}"
|
|
_git(tmp_path, "stash", "push", "--include-untracked", "-m", name)
|
|
return name
|
|
|
|
|
|
def test_old_autostash_is_surfaced(tmp_path, capsys):
|
|
name = _make_repo_with_autostash(tmp_path, age_days=9)
|
|
count = update_cmd._warn_orphaned_update_autostashes(["git"], tmp_path)
|
|
out = capsys.readouterr().out
|
|
assert count == 1
|
|
assert "leftover update autostash" in out
|
|
assert name in out
|
|
assert "git stash apply" in out
|
|
# Never a GC: the entry must still exist.
|
|
listed = _git(tmp_path, "stash", "list").stdout
|
|
assert name in listed
|
|
|
|
|
|
def test_fresh_autostash_is_not_flagged(tmp_path, capsys):
|
|
_make_repo_with_autostash(tmp_path, age_days=1)
|
|
count = update_cmd._warn_orphaned_update_autostashes(["git"], tmp_path)
|
|
assert count == 0
|
|
assert "leftover update autostash" not in capsys.readouterr().out
|
|
|
|
|
|
def test_non_hermes_stash_is_ignored(tmp_path, capsys):
|
|
import shutil
|
|
|
|
if shutil.which("git") is None:
|
|
pytest.skip("git not available")
|
|
_git(tmp_path, "init", "-q", "-b", "main")
|
|
_git(tmp_path, "config", "user.email", "t@example.com")
|
|
_git(tmp_path, "config", "user.name", "t")
|
|
(tmp_path / "tracked.txt").write_text("v1\n")
|
|
_git(tmp_path, "add", "-A")
|
|
_git(tmp_path, "commit", "-qm", "init")
|
|
(tmp_path / "tracked.txt").write_text("user's own WIP\n")
|
|
_git(tmp_path, "stash", "push", "-m", "my own stash from 20200101-000000")
|
|
count = update_cmd._warn_orphaned_update_autostashes(["git"], tmp_path)
|
|
assert count == 0
|
|
assert "leftover update autostash" not in capsys.readouterr().out
|
|
|
|
|
|
def test_unparseable_autostash_timestamp_left_alone(tmp_path, capsys):
|
|
import shutil
|
|
|
|
if shutil.which("git") is None:
|
|
pytest.skip("git not available")
|
|
_git(tmp_path, "init", "-q", "-b", "main")
|
|
_git(tmp_path, "config", "user.email", "t@example.com")
|
|
_git(tmp_path, "config", "user.name", "t")
|
|
(tmp_path / "tracked.txt").write_text("v1\n")
|
|
_git(tmp_path, "add", "-A")
|
|
_git(tmp_path, "commit", "-qm", "init")
|
|
(tmp_path / "tracked.txt").write_text("change\n")
|
|
_git(tmp_path, "stash", "push", "-m", "hermes-update-autostash-notadate")
|
|
count = update_cmd._warn_orphaned_update_autostashes(["git"], tmp_path)
|
|
assert count == 0
|
|
|
|
|
|
def test_git_failure_is_nonfatal(tmp_path):
|
|
# Not a git repo at all — must return 0, not raise.
|
|
assert update_cmd._warn_orphaned_update_autostashes(["git"], tmp_path) == 0
|