test_memory_reset.py
1 """Tests for the `hermes memory reset` CLI command. 2 3 Covers: 4 - Reset both stores (MEMORY.md + USER.md) 5 - Reset individual stores (--target memory / --target user) 6 - Skip confirmation with --yes 7 - Graceful handling when no memory files exist 8 - Profile-scoped reset (uses HERMES_HOME) 9 """ 10 11 import os 12 import pytest 13 from argparse import Namespace 14 from pathlib import Path 15 16 17 @pytest.fixture 18 def memory_env(tmp_path, monkeypatch): 19 """Set up a fake HERMES_HOME with memory files.""" 20 hermes_home = tmp_path / ".hermes" 21 memories = hermes_home / "memories" 22 memories.mkdir(parents=True) 23 monkeypatch.setenv("HERMES_HOME", str(hermes_home)) 24 25 # Create sample memory files 26 (memories / "MEMORY.md").write_text( 27 "§\nHermes repo is at ~/.hermes/hermes-agent\n§\nUser prefers dark themes", 28 encoding="utf-8", 29 ) 30 (memories / "USER.md").write_text( 31 "§\nUser is Teknium\n§\nTimezone: US Pacific", 32 encoding="utf-8", 33 ) 34 return hermes_home, memories 35 36 37 def _run_memory_reset(target="all", yes=False, monkeypatch=None, confirm_input="no"): 38 """Invoke the memory reset logic from cmd_memory in main.py. 39 40 Simulates what happens when `hermes memory reset` is run. 41 """ 42 from hermes_constants import get_hermes_home, display_hermes_home 43 44 mem_dir = get_hermes_home() / "memories" 45 files_to_reset = [] 46 if target in ("all", "memory"): 47 files_to_reset.append(("MEMORY.md", "agent notes")) 48 if target in ("all", "user"): 49 files_to_reset.append(("USER.md", "user profile")) 50 51 existing = [(f, desc) for f, desc in files_to_reset if (mem_dir / f).exists()] 52 if not existing: 53 return "nothing" 54 55 if not yes: 56 if confirm_input != "yes": 57 return "cancelled" 58 59 for f, desc in existing: 60 (mem_dir / f).unlink() 61 62 return "deleted" 63 64 65 class TestMemoryReset: 66 """Tests for `hermes memory reset` subcommand.""" 67 68 def test_reset_all_with_yes_flag(self, memory_env): 69 """--yes flag should skip confirmation and delete both files.""" 70 hermes_home, memories = memory_env 71 assert (memories / "MEMORY.md").exists() 72 assert (memories / "USER.md").exists() 73 74 result = _run_memory_reset(target="all", yes=True) 75 assert result == "deleted" 76 assert not (memories / "MEMORY.md").exists() 77 assert not (memories / "USER.md").exists() 78 79 def test_reset_memory_only(self, memory_env): 80 """--target memory should only delete MEMORY.md.""" 81 hermes_home, memories = memory_env 82 83 result = _run_memory_reset(target="memory", yes=True) 84 assert result == "deleted" 85 assert not (memories / "MEMORY.md").exists() 86 assert (memories / "USER.md").exists() 87 88 def test_reset_user_only(self, memory_env): 89 """--target user should only delete USER.md.""" 90 hermes_home, memories = memory_env 91 92 result = _run_memory_reset(target="user", yes=True) 93 assert result == "deleted" 94 assert (memories / "MEMORY.md").exists() 95 assert not (memories / "USER.md").exists() 96 97 def test_reset_no_files_exist(self, tmp_path, monkeypatch): 98 """Should return 'nothing' when no memory files exist.""" 99 hermes_home = tmp_path / ".hermes" 100 (hermes_home / "memories").mkdir(parents=True) 101 monkeypatch.setenv("HERMES_HOME", str(hermes_home)) 102 103 result = _run_memory_reset(target="all", yes=True) 104 assert result == "nothing" 105 106 def test_reset_confirmation_denied(self, memory_env): 107 """Without --yes and without typing 'yes', should be cancelled.""" 108 hermes_home, memories = memory_env 109 110 result = _run_memory_reset(target="all", yes=False, confirm_input="no") 111 assert result == "cancelled" 112 # Files should still exist 113 assert (memories / "MEMORY.md").exists() 114 assert (memories / "USER.md").exists() 115 116 def test_reset_confirmation_accepted(self, memory_env): 117 """Typing 'yes' should proceed with deletion.""" 118 hermes_home, memories = memory_env 119 120 result = _run_memory_reset(target="all", yes=False, confirm_input="yes") 121 assert result == "deleted" 122 assert not (memories / "MEMORY.md").exists() 123 assert not (memories / "USER.md").exists() 124 125 def test_reset_profile_scoped(self, tmp_path, monkeypatch): 126 """Reset should work on the active profile's HERMES_HOME.""" 127 profile_home = tmp_path / "profiles" / "myprofile" 128 memories = profile_home / "memories" 129 memories.mkdir(parents=True) 130 (memories / "MEMORY.md").write_text("profile memory", encoding="utf-8") 131 (memories / "USER.md").write_text("profile user", encoding="utf-8") 132 monkeypatch.setenv("HERMES_HOME", str(profile_home)) 133 134 result = _run_memory_reset(target="all", yes=True) 135 assert result == "deleted" 136 assert not (memories / "MEMORY.md").exists() 137 assert not (memories / "USER.md").exists() 138 139 def test_reset_partial_files(self, memory_env): 140 """Reset should work when only one memory file exists.""" 141 hermes_home, memories = memory_env 142 (memories / "USER.md").unlink() 143 144 result = _run_memory_reset(target="all", yes=True) 145 assert result == "deleted" 146 assert not (memories / "MEMORY.md").exists() 147 148 def test_reset_empty_memories_dir(self, tmp_path, monkeypatch): 149 """No memories dir at all should report nothing.""" 150 hermes_home = tmp_path / ".hermes" 151 hermes_home.mkdir(parents=True) 152 # No memories dir 153 monkeypatch.setenv("HERMES_HOME", str(hermes_home)) 154 155 # The memories dir won't exist; get_hermes_home() / "memories" won't have files 156 result = _run_memory_reset(target="all", yes=True) 157 assert result == "nothing"