fix: guard /api/status active count against missing state.db

Review finding: SessionDB(read_only=True) requires the DB file to exist
(its documented contract says callers guard on db_path.exists()); on a
fresh install every /api/status poll paid an OperationalError until the
first session was written. Short-circuit to 0 when state.db is absent.
Tests: fresh-install guard + existing read_only test adjusted.
This commit is contained in:
kshitijk4poor 2026-07-08 17:43:31 +05:30 committed by kshitij
parent 1e2ad17afd
commit 664878b0ba
2 changed files with 29 additions and 2 deletions

View file

@ -1183,7 +1183,13 @@ def _count_status_active_sessions() -> int:
connection so /api/status never tries to initialise or migrate state.db
while another Hermes process is writing to it.
"""
from hermes_state import SessionDB
from hermes_state import DEFAULT_DB_PATH, SessionDB
# read_only opens require the DB to already exist (see SessionDB.__init__
# read_only contract) — on a fresh install every /api/status poll would
# otherwise pay an OperationalError until the first session is written.
if not DEFAULT_DB_PATH.exists():
return 0
db = SessionDB(read_only=True)
try:

View file

@ -267,8 +267,15 @@ class TestWebServerEndpoints:
assert "active_sessions" in data
assert data["can_update_hermes"] is True
def test_status_active_session_count_uses_read_only_db(self, monkeypatch):
def test_status_active_session_count_uses_read_only_db(self, monkeypatch, tmp_path):
import hermes_cli.web_server as web_server
import hermes_state
# Satisfy the fresh-install guard: read_only opens require the DB
# file to already exist.
fake_db_path = tmp_path / "state.db"
fake_db_path.touch()
monkeypatch.setattr(hermes_state, "DEFAULT_DB_PATH", fake_db_path)
captured = {}
@ -293,6 +300,20 @@ class TestWebServerEndpoints:
assert web_server._count_status_active_sessions() == 1
assert captured == {"read_only": True, "limit": 50, "closed": True}
def test_status_active_session_count_fresh_install_returns_zero(self, monkeypatch, tmp_path):
"""No state.db yet (fresh install): return 0 without attempting a
read-only open, which would raise OperationalError on every poll."""
import hermes_cli.web_server as web_server
import hermes_state
monkeypatch.setattr(hermes_state, "DEFAULT_DB_PATH", tmp_path / "absent.db")
def _boom(*a, **k):
raise AssertionError("SessionDB must not be constructed when db file is absent")
monkeypatch.setattr("hermes_state.SessionDB", _boom)
assert web_server._count_status_active_sessions() == 0
def test_get_status_degrades_when_active_session_count_fails(self, monkeypatch):
import hermes_cli.web_server as web_server