Files
hermes-agent/tests/state/test_compression_lease_refresh_before_publish.py
T
Teknium 53db597201 simplify(compat): hermes_state — drop 81 re-exports + 3 registry aliases + 3 shims, repoint 45 callers + 60 test files
hermes_state.py: delete every '# noqa: F401 (re-exported...)' import block (hermes_state_common/errors/guard/
readpool/sessions/fts/dbfile/wal/repair/registry + agent.context_compressor _DB_PERSISTED_MARKER_KEY); keep
only the names hermes_state.py itself uses, without noqa.
hermes_state_registry.py: drop get_shared_session_db/release_shared_session_db/close_shared_session_dbs
aliases; every caller (gateway/, tools/, tui_gateway/, cron/, mcp_serve, run_agent, tests) now imports
acquire/release/close_all/release_or_close from hermes_state_registry.
hermes_state_titles.py: drop set_auto_title_if_empty shim (title_generator keeps its getattr fallback).
Re-remove shim-only names restored by 34abf954bd: latest_user_message_row_id (tests call
latest_message_row_id(key, role='user'); role-targeting assertions kept) and get_session_activity (tests
build the snapshot via agent.session_activity.build_activity_snapshot over db.get_session(sid)).
hermes_state_wal._log_once resolves its dedupe sets as module globals instead of via hermes_state;
hermes_state_repair helpers call module globals directly (tests patch hermes_state_repair.<name>).
Frozen updater surface untouched (update_cmd_maint imports only SessionDB from hermes_state).
2026-09-03 13:46:50 -07:00

176 lines
6.8 KiB
Python

"""Tests for RC2: pre-publication lease refresh in publish_compression_child.
When the lease refresher stopped due to transient DB failures, the final
pre-publication refresh inside the same transaction gives one last chance
to extend the lease before the expiry check.
"""
import sqlite3
import threading
import time
from unittest.mock import patch
import pytest
from hermes_state import SessionDB
from hermes_state_errors import CompressionSessionBusyError
def _setup_db(tmp_path):
db = SessionDB(tmp_path / "state.db")
return db
def _seed_lock(conn, session_id, holder, expired=False):
now = time.time()
conn.execute(
"INSERT INTO compression_locks (session_id, holder, acquired_at, expires_at) VALUES (?, ?, ?, ?)",
(session_id, holder, now, (now - 10.0) if expired else (now + 300.0)),
)
class TestLeaseRefreshBeforePublish:
def test_refresher_stopped_final_refresh_succeeds(self, tmp_path):
db = _setup_db(tmp_path)
db.create_session("parent-1", source="test")
_seed_lock(db._conn, "parent-1", "holder-1", expired=True)
with patch.object(db, "_execute_write", side_effect=lambda fn: fn(db._conn)):
db.publish_compression_child(
parent_session_id="parent-1",
child_session_id="child-1",
source="test",
messages=[{"role": "user", "content": "hello"}],
compression_lock_holder="holder-1",
require_compression_lease=True,
require_lease_refresh=True,
lease_ttl_seconds=300.0,
)
lock = db._conn.execute(
"SELECT expires_at FROM compression_locks WHERE session_id = ?",
("parent-1",),
).fetchone()
assert lock is not None
assert lock[0] > time.time()
parent = db._conn.execute(
"SELECT ended_at FROM sessions WHERE id = ?",
("parent-1",),
).fetchone()
assert parent is not None
assert parent[0] is not None
def test_refresher_stopped_final_refresh_fails_wrong_holder(self, tmp_path):
db = _setup_db(tmp_path)
_seed_lock(db._conn, "parent-1", "other-holder", expired=True)
with patch.object(db, "_execute_write", side_effect=lambda fn: fn(db._conn)):
with pytest.raises(CompressionSessionBusyError, match="lease lost"):
db.publish_compression_child(
parent_session_id="parent-1",
child_session_id="child-1",
source="test",
messages=[{"role": "user", "content": "hello"}],
compression_lock_holder="holder-1",
require_compression_lease=True,
require_lease_refresh=True,
lease_ttl_seconds=300.0,
)
def test_refresher_healthy_no_duplicate_behavior(self, tmp_path):
db = _setup_db(tmp_path)
db.create_session("parent-1", source="test")
now = time.time()
future = now + 300.0
conn = db._conn
conn.execute(
"INSERT INTO compression_locks (session_id, holder, acquired_at, expires_at) VALUES (?, ?, ?, ?)",
("parent-1", "holder-1", now, future),
)
with patch.object(db, "_execute_write", side_effect=lambda fn: fn(db._conn)):
db.publish_compression_child(
parent_session_id="parent-1",
child_session_id="child-1",
source="test",
messages=[{"role": "user", "content": "hello"}],
compression_lock_holder="holder-1",
require_compression_lease=True,
require_lease_refresh=True,
lease_ttl_seconds=300.0,
)
lock = conn.execute(
"SELECT expires_at FROM compression_locks WHERE session_id = ?",
("parent-1",),
).fetchone()
assert lock is not None
assert lock[0] >= future
def test_stale_holder_cannot_refresh_and_publish(self, tmp_path):
db = _setup_db(tmp_path)
_seed_lock(db._conn, "parent-1", "new-holder", expired=False)
with patch.object(db, "_execute_write", side_effect=lambda fn: fn(db._conn)):
with pytest.raises(CompressionSessionBusyError, match="lease lost"):
db.publish_compression_child(
parent_session_id="parent-1",
child_session_id="child-1",
source="test",
messages=[{"role": "user", "content": "hello"}],
compression_lock_holder="old-holder",
require_compression_lease=True,
require_lease_refresh=True,
lease_ttl_seconds=300.0,
)
def test_no_refresh_when_require_lease_refresh_false(self, tmp_path):
db = _setup_db(tmp_path)
_seed_lock(db._conn, "parent-1", "holder-1", expired=True)
with patch.object(db, "_execute_write", side_effect=lambda fn: fn(db._conn)):
with pytest.raises(CompressionSessionBusyError, match="lease lost"):
db.publish_compression_child(
parent_session_id="parent-1",
child_session_id="child-1",
source="test",
messages=[{"role": "user", "content": "hello"}],
compression_lock_holder="holder-1",
require_compression_lease=True,
require_lease_refresh=False,
lease_ttl_seconds=300.0,
)
def test_refresh_and_lease_check_are_atomic(self, tmp_path):
db = _setup_db(tmp_path)
db.create_session("parent-1", source="test")
_seed_lock(db._conn, "parent-1", "holder-1", expired=True)
real_execute_write = SessionDB._execute_write
def intercepted_execute_write(self, fn, patience_s=None):
original_fn = fn
def wrapper(conn):
result = original_fn(conn)
lock = conn.execute(
"SELECT expires_at FROM compression_locks WHERE session_id = ?",
("parent-1",),
).fetchone()
assert lock is not None
assert lock[0] > time.time()
return result
return real_execute_write(self, wrapper, patience_s)
with patch.object(SessionDB, "_execute_write", intercepted_execute_write):
db.publish_compression_child(
parent_session_id="parent-1",
child_session_id="child-1",
source="test",
messages=[{"role": "user", "content": "hello"}],
compression_lock_holder="holder-1",
require_compression_lease=True,
require_lease_refresh=True,
lease_ttl_seconds=300.0,
)