diff --git a/tests/agent/lsp/test_client_e2e.py b/tests/agent/lsp/test_client_e2e.py index f24e276b99..a42cb1f4bb 100644 --- a/tests/agent/lsp/test_client_e2e.py +++ b/tests/agent/lsp/test_client_e2e.py @@ -154,15 +154,26 @@ async def test_docs_cache_is_lru_bounded_and_reopens_evicted(tmp_path: Path, mon monkeypatch.setattr(client_mod, "MAX_TRACKED_FILES", 3) files = [tmp_path / f"f{i}.py" for i in range(5)] for f in files: - f.write_text("print('hi')\n") + f.write_text("print('hi')\n", encoding="utf-8") client = _client(tmp_path, "errors") + real_send = client._send_notification + sent: list = [] + + async def _spy(method, params): + sent.append((method, params)) + await real_send(method, params) + + monkeypatch.setattr(client, "_send_notification", _spy) await client.start() try: for f in files: await client.open_file(str(f), language_id="python") assert len(client._docs) == 3 assert str(files[0]) not in client._docs # least recently touched went first + # The server releases its mirror too: every evicted doc got a didClose on the wire. + closed = [p["textDocument"]["uri"] for m, p in sent if m == "textDocument/didClose"] + assert closed == [client_mod.file_uri(str(files[0])), client_mod.file_uri(str(files[1]))] version = await client.open_file(str(files[0]), language_id="python") assert version == 0 # fresh didOpen, not a didChange against dropped state assert len(client._docs) == 3 diff --git a/tests/gateway/test_tool_log_mode.py b/tests/gateway/test_tool_log_mode.py index 03c6dc1a5f..0b6bcf96ec 100644 --- a/tests/gateway/test_tool_log_mode.py +++ b/tests/gateway/test_tool_log_mode.py @@ -39,55 +39,6 @@ class TestLogBranchSemantics: assert q.empty() -@pytest.mark.asyncio -async def test_write_tool_log_writes_and_rotates_handler(tmp_path, monkeypatch): - """The writer coroutine drains the queue into logs/tool_calls.log.""" - import gateway.run as gateway_run - - monkeypatch.setattr(gateway_run, "_hermes_home", tmp_path) - - log_queue: queue.Queue = queue.Queue() - log_queue.put("2026-07-02 10:00:00 terminal: \"echo hi\"") - log_queue.put("2026-07-02 10:00:01 read_file: \"foo.py\"") - - # Minimal inline copy of write_tool_log wiring (the real coroutine is a - # closure inside _run_agent); exercise the same handler configuration. - import logging - from logging.handlers import RotatingFileHandler - - from agent.redact import RedactingFormatter - - log_dir = tmp_path / "logs" - log_dir.mkdir(parents=True, exist_ok=True) - handler = RotatingFileHandler( - log_dir / "tool_calls.log", maxBytes=5 * 1024 * 1024, backupCount=3, - encoding="utf-8", - ) - handler.setFormatter(RedactingFormatter("%(message)s")) - tool_logger = logging.getLogger(f"hermes.tool_calls.test.{id(log_queue)}") - tool_logger.setLevel(logging.INFO) - tool_logger.propagate = False - tool_logger.addHandler(handler) - try: - while True: - try: - tool_logger.info("%s", log_queue.get_nowait()) - except queue.Empty: - break - finally: - tool_logger.removeHandler(handler) - handler.flush() - handler.close() - - content = (log_dir / "tool_calls.log").read_text(encoding="utf-8") - assert "terminal" in content - assert "read_file" in content - assert content.count("\n") == 2 - await asyncio.sleep(0) # keep the asyncio marker honest - - - - @pytest.mark.asyncio async def test_write_tool_log_shares_one_logger_across_turns(tmp_path, monkeypatch): """Two turns with distinct queues register no new Logger (loggerDict is process-lifetime) and