95 lines
2.7 KiB
Python
95 lines
2.7 KiB
Python
"""Gateway slash commands must do their blocking work inside the routed profile.
|
|
|
|
The multiplexed inbound handler wraps the whole message in
|
|
``_profile_runtime_scope``, which installs the routed profile's ``HERMES_HOME``
|
|
override and its secret scope as **contextvars**. A bare
|
|
``loop.run_in_executor(None, ...)`` starts the worker with an EMPTY context, so
|
|
``SessionDB()`` / ``get_hermes_home()`` inside the worker resolve the LAUNCH
|
|
home — /insights reported the default profile's conversations from another
|
|
profile's chat. ``/compress`` already routes through
|
|
``_run_in_executor_with_context``; every other hop in the mixin must too.
|
|
|
|
Drives the real mixin methods and the real ``_profile_runtime_scope``: the
|
|
contextvar loss is a property of the hop, so mocking the hop away would test
|
|
nothing.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
@pytest.fixture
|
|
def profile_home(tmp_path, monkeypatch):
|
|
root = tmp_path / ".hermes"
|
|
home = root / "profiles" / "coder"
|
|
home.mkdir(parents=True)
|
|
monkeypatch.setattr(Path, "home", lambda: tmp_path)
|
|
monkeypatch.setenv("HERMES_HOME", str(root))
|
|
return home
|
|
|
|
|
|
@pytest.fixture
|
|
def runner():
|
|
"""Minimal host exposing the mixin plus the runner's executor helpers."""
|
|
from gateway.run import GatewayRunner
|
|
from gateway.slash_commands import GatewaySlashCommandsMixin
|
|
|
|
class _Runner(GatewaySlashCommandsMixin):
|
|
_run_in_executor_with_context = GatewayRunner._run_in_executor_with_context
|
|
_get_executor = GatewayRunner._get_executor
|
|
|
|
r = _Runner()
|
|
r.adapters = {}
|
|
r._pending_skills_reload_notes = {}
|
|
return r
|
|
|
|
|
|
class _Event:
|
|
def __init__(self, args: str = ""):
|
|
self._args = args
|
|
self.source = None
|
|
|
|
def get_command_args(self) -> str:
|
|
return self._args
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_insights_opens_session_db_under_the_routed_home(
|
|
runner, profile_home, monkeypatch
|
|
):
|
|
import agent.insights as insights_mod
|
|
import hermes_state
|
|
from gateway.run import _profile_runtime_scope
|
|
from hermes_constants import get_hermes_home
|
|
|
|
seen: dict = {}
|
|
|
|
class _RecordingDB:
|
|
def __init__(self, *a, **kw):
|
|
seen["home"] = str(get_hermes_home())
|
|
|
|
def close(self):
|
|
pass
|
|
|
|
class _Engine:
|
|
def __init__(self, db):
|
|
pass
|
|
|
|
def generate(self, **kw):
|
|
return {}
|
|
|
|
def format_gateway(self, report):
|
|
return "ok"
|
|
|
|
monkeypatch.setattr(hermes_state, "SessionDB", _RecordingDB)
|
|
monkeypatch.setattr(insights_mod, "InsightsEngine", _Engine)
|
|
|
|
with _profile_runtime_scope(profile_home):
|
|
result = await runner._handle_insights_command(_Event(""))
|
|
|
|
assert result == "ok"
|
|
assert seen["home"] == str(profile_home)
|