Files
hermes-agent/tests/agent/test_setup_logging_profile_scope.py
teknium1 8affd9aeaa fix(agent): trim the #126005 salvage to the one-line active-home fix (#125974)
setup_logging(hermes_home=get_hermes_home()) is the whole fix; the FileNotFoundError fallback chain guarded a home that is being served (it exists), and the two extra tests re-tested it. Keep the A->B routing invariant and the no-bare-handler guard.
2026-09-28 12:23:25 -07:00

110 lines
4.2 KiB
Python

"""#125974 — ``agent.agent_init._setup_logging`` must honor the active profile scope.
A Desktop serve backend (tui_gateway/compute_host.py) builds agents for several profiles
inside ``set_hermes_home_override(profile_home)``. ``_setup_logging`` used to pass the
import-time ``run_agent._hermes_home`` freeze, so the second profile's ``setup_logging``
call saw a home it already served, never adopted it, and every profile's records kept
landing in the launch profile's agent.log.
"""
import logging
from pathlib import Path
from types import SimpleNamespace
import pytest
import hermes_logging
from hermes_constants import reset_hermes_home_override, set_hermes_home_override
from hermes_logging import RotatingFileHandler, _ProfileRoutingFileHandler
@pytest.fixture(autouse=True)
def _reset_logging_state():
"""Reset hermes_logging module globals so each test starts unconfigured."""
hermes_logging._logging_initialized = False
hermes_logging._reset_queued_handlers()
yield
hermes_logging._reset_queued_handlers()
hermes_logging._logging_initialized = False
hermes_logging.clear_session_context()
def _agent_stub() -> SimpleNamespace:
# _setup_logging only reads verbose_logging; quiet keeps console clean.
return SimpleNamespace(verbose_logging=False, quiet_mode=True)
def _witness(name: str, message: str) -> None:
logging.getLogger(name).info(message)
class TestSetupLoggingProfileScope:
def test_routes_agent_records_to_profile_scope(self, tmp_path: Path) -> None:
"""L1: an agent built under override B logs into B's agent.log, not A's."""
from agent.agent_init import _setup_logging
home_a = tmp_path / "home-a"
home_b = tmp_path / "home-b"
home_a.mkdir()
home_b.mkdir()
token = set_hermes_home_override(str(home_a))
try:
_setup_logging(_agent_stub())
finally:
reset_hermes_home_override(token)
token = set_hermes_home_override(str(home_b))
try:
_setup_logging(_agent_stub())
_witness("agent.test_profile_scope_125974", "scoped-witness-b")
finally:
reset_hermes_home_override(token)
hermes_logging.flush_log_queue()
log_b = home_b / "logs" / "agent.log"
assert log_b.is_file(), "the profile-scoped agent's records must reach its own logs/"
assert "scoped-witness-b" in log_b.read_text(encoding="utf-8-sig")
log_a = home_a / "logs" / "agent.log"
text_a = log_a.read_text(encoding="utf-8-sig") if log_a.is_file() else ""
assert "scoped-witness-b" not in text_a, "record must not leak into the launch home"
def test_under_routing_adds_no_bare_handler(self, tmp_path: Path) -> None:
"""L3 guard: with routing already live, a scoped _setup_logging adds no bare handler.
A bare RotatingFileHandler beside the profile routers would take every home's
records — exactly the leak this fix closes. Passing on both sides of the fix is
the point: it pins the blast radius.
"""
from agent.agent_init import _setup_logging
home_a = tmp_path / "home-a"
home_b = tmp_path / "home-b"
home_a.mkdir()
home_b.mkdir()
hermes_logging.setup_logging(hermes_home=home_a)
assert hermes_logging.enable_profile_log_routing([home_a, home_b]) is True
token = set_hermes_home_override(str(home_b))
try:
_setup_logging(_agent_stub())
_witness("agent.test_routing_guard_125974", "routing-guard-witness-b")
finally:
reset_hermes_home_override(token)
hermes_logging.flush_log_queue()
bare = [
h for h in hermes_logging._queued_file_handlers
if isinstance(h, RotatingFileHandler)
]
assert not bare, f"bare file handlers must not ride beside the routers: {bare}"
assert "routing-guard-witness-b" in (
home_b / "logs" / "agent.log"
).read_text(encoding="utf-8-sig")
# And the routers themselves must still be routing, not replaced.
assert any(
isinstance(h, _ProfileRoutingFileHandler)
for h in hermes_logging._queued_file_handlers
)