Files
hermes-agent/tests/hermes_state/test_session_import_keep_flags.py
kshitijk4poor d440c5b59b refactor(state): trim the flag-import comment and MB-passing test lines
Gate finding (BN07 quality #3): the comment above _IMPORT_FLAG_COLS restated the
test module docstring (and called the sweep's auto_archived a user decision); cut
it to one WHY line. The donor-content and retry-adoption assertions exercise
adopt_session_lineage_from behaviour that already holds on main (verified: they
pass against the merge-base prod file), so they add no teeth for this fix.
2026-10-03 16:41:37 +05:30

84 lines
4.1 KiB
Python

"""A restored or adopted session keeps the user's durable flags.
``import_sessions`` (the dashboard import of a ``hermes sessions export`` backup, and stranded-session
adoption) restored ``archived`` but not ``pinned`` or ``hidden``. Pinned is the "keep" flag the startup
prune and the stale-archive sweep both exempt, so an old pinned session came back unpinned and the next
startup deleted it; an adopted Bot Mode chat came back visible and no longer canonical; and without
auto_archived a chat the idle sweep archived came back as a manual archive that resume never undoes.
"""
from __future__ import annotations
import json
import time
import pytest
from hermes_state import SessionDB
SESSION_ID = "20260301_120000_abc123"
def test_restored_sessions_keep_the_pin_and_the_sweeps_archive_provenance(tmp_path):
"""A pinned session survives the startup prune; a chat the idle sweep archived still comes
back on resume, which only a sweep archive (not the user's own) does."""
source = SessionDB(db_path=tmp_path / "source.db")
target = SessionDB(db_path=tmp_path / "target.db")
swept = "20260301_130000_def456"
try:
for sid, age_days in ((SESSION_ID, 200), (swept, 30)):
source.create_session(sid, source="cli")
source.append_message(sid, "user", "keep this one")
source.end_session(sid, "user_exit")
old = time.time() - age_days * 86400
source._conn.execute("UPDATE sessions SET started_at = ?, ended_at = ? WHERE id = ?", (old, old, sid))
source._conn.execute("UPDATE messages SET timestamp = ? WHERE session_id = ?", (old, sid))
source._conn.commit()
source.set_session_pinned(SESSION_ID, True)
assert source.archive_stale_sessions(7) == 1
payload = json.loads(json.dumps(source.export_all(include_inactive=True)))
# An export that predates the durable flags still imports as an ordinary, visible session.
payload.append({"id": "legacy", "source": "cli", "messages": [{"role": "user", "content": "old"}]})
# A hand-edited export's string "0" flags are coerced like the int columns, not by truthiness,
# and a live row is never marked as the sweep's archive.
flags = ("archived", "auto_archived", "pinned", "hidden")
payload.append({"id": "edited", "source": "cli", **{flag: "0" for flag in flags}, "auto_archived": 1})
assert target.import_sessions(payload)["imported"] == 4
target.maybe_auto_prune_and_vacuum(retention_days=90, vacuum=False)
restored = target.get_session(SESSION_ID)
assert restored is not None, "the startup prune deleted a session the user pinned to keep"
assert restored["pinned"]
assert target.get_session(swept)["archived"]
target.reopen_session(swept)
assert not target.get_session(swept)["archived"], "a restored sweep archive became a manual one"
for sid in ("legacy", "edited"):
assert all(target.get_session(sid)[flag] == 0 for flag in flags), sid
finally:
source.close()
target.close()
def test_adopted_bot_chat_stays_the_hidden_canonical_chat(tmp_path):
"""Stranded-session adoption imports the donor's Bot Mode chat into the profile store; Bot Mode
chats are hidden, and hidden is what keeps the canonical one out of listings and the stale sweep."""
donor = SessionDB(db_path=tmp_path / "default.db")
profile = SessionDB(db_path=tmp_path / "profile.db")
try:
donor.create_session(SESSION_ID, source="tui")
donor.set_session_title(SESSION_ID, SessionDB.CANONICAL_BOT_CHAT_TITLE)
donor.set_session_hidden(SESSION_ID, True)
donor.append_message(SESSION_ID, "user", "hi bot")
assert profile.adopt_session_lineage_from(donor, SESSION_ID)["adopted"]
assert profile.get_session(SESSION_ID)["hidden"]
assert SESSION_ID not in {s["id"] for s in profile.list_sessions_rich(limit=50)}
assert profile.archive_stale_sessions(idle_days=0) == 0
with pytest.raises(ValueError, match="canonical Bot Chat"):
profile.set_session_title(SESSION_ID, "renamed")
finally:
donor.close()
profile.close()