From 18ab5c99d1f67c392b7dd14e6800ba38f16bfa43 Mon Sep 17 00:00:00 2001 From: sprmn24 Date: Sun, 12 Apr 2026 23:56:55 +0300 Subject: [PATCH] fix(backup): correct marker filenames in _validate_backup_zip MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The backup validation checked for 'hermes_state.db' and 'memory_store.db' as telltale markers of a valid Hermes backup zip. Neither name exists in a real Hermes installation — the actual database file is 'state.db' (hermes_state.py: DEFAULT_DB_PATH = get_hermes_home() / 'state.db'). A fresh Hermes installation produces: ~/.hermes/state.db (actual name) ~/.hermes/config.yaml ~/.hermes/.env Because the marker set never matched 'state.db', a backup zip containing only 'state.db' plus 'config.yaml' would fail validation with: 'zip does not appear to be a Hermes backup' and the import would exit with sys.exit(1), silently rejecting a valid backup. Fix: replace the wrong marker names with the correct filename. Adds TestValidateBackupZip with three cases: - state.db is accepted as a valid marker - old wrong names (hermes_state.db, memory_store.db) alone are rejected - config.yaml continues to pass (existing behaviour preserved) --- hermes_cli/backup.py | 2 +- tests/hermes_cli/test_backup.py | 38 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/hermes_cli/backup.py b/hermes_cli/backup.py index 9aca0f822..3380f494f 100644 --- a/hermes_cli/backup.py +++ b/hermes_cli/backup.py @@ -201,7 +201,7 @@ def _validate_backup_zip(zf: zipfile.ZipFile) -> tuple[bool, str]: return False, "zip archive is empty" # Look for telltale files that a hermes home would have - markers = {"config.yaml", ".env", "hermes_state.db", "memory_store.db"} + markers = {"config.yaml", ".env", "state.db"} found = set() for n in names: # Could be at the root or one level deep (if someone zipped the directory) diff --git a/tests/hermes_cli/test_backup.py b/tests/hermes_cli/test_backup.py index 8ef385896..a4dbae52a 100644 --- a/tests/hermes_cli/test_backup.py +++ b/tests/hermes_cli/test_backup.py @@ -232,6 +232,44 @@ class TestBackup: assert len(zips) == 1 +# --------------------------------------------------------------------------- +# _validate_backup_zip tests +# --------------------------------------------------------------------------- + +class TestValidateBackupZip: + def _make_zip(self, zip_path: Path, filenames: list[str]) -> None: + with zipfile.ZipFile(zip_path, "w") as zf: + for name in filenames: + zf.writestr(name, "dummy") + + def test_state_db_passes(self, tmp_path): + """A zip containing state.db is accepted as a valid Hermes backup.""" + from hermes_cli.backup import _validate_backup_zip + zip_path = tmp_path / "backup.zip" + self._make_zip(zip_path, ["state.db", "sessions/abc.json"]) + with zipfile.ZipFile(zip_path, "r") as zf: + ok, reason = _validate_backup_zip(zf) + assert ok, reason + + def test_old_wrong_db_name_fails(self, tmp_path): + """A zip with only hermes_state.db (old wrong name) is rejected.""" + from hermes_cli.backup import _validate_backup_zip + zip_path = tmp_path / "old.zip" + self._make_zip(zip_path, ["hermes_state.db", "memory_store.db"]) + with zipfile.ZipFile(zip_path, "r") as zf: + ok, reason = _validate_backup_zip(zf) + assert not ok + + def test_config_yaml_passes(self, tmp_path): + """A zip containing config.yaml is accepted (existing behaviour preserved).""" + from hermes_cli.backup import _validate_backup_zip + zip_path = tmp_path / "backup.zip" + self._make_zip(zip_path, ["config.yaml", "skills/x/SKILL.md"]) + with zipfile.ZipFile(zip_path, "r") as zf: + ok, reason = _validate_backup_zip(zf) + assert ok, reason + + # --------------------------------------------------------------------------- # Import tests # ---------------------------------------------------------------------------