Files
hermes-agent/tests/plugins/lsp/test_backend_gate.py
alt-glitch 6d80aa80eb refactor(lsp): simplify __init__.py per /simplify review
- Remove dead _post_tool_call (body was only comments)
- Remove _on_session_start (redundant — _ensure_service lazy-inits)
- Remove _atexit_cleanup (duplicate of _on_session_end)
- Switch _baselines from dict to set (presence sentinel only)
- Remove redundant enabled_for recheck in transform_tool_result
- Remove V4A guard (path-empty check already covers it)
- Use modern type syntax (X | None, dict[], set[])
- Reduce from 322 → 217 lines, same behavior

77/77 tests pass.
2026-05-12 15:01:44 +00:00

155 lines
5.0 KiB
Python

"""Integration test: LSP plugin skips non-local paths.
The host-side LSP server can't see files inside Docker/Modal/SSH
sandboxes. The plugin's ``_pre_tool_call`` uses ``os.path.exists``
on the parent directory as a heuristic local-only gate. These tests
verify the plugin hooks skip when the path clearly doesn't exist on
the host filesystem.
"""
from __future__ import annotations
import os
import sys
from unittest.mock import patch
import pytest
@pytest.fixture(autouse=True)
def _isolate_plugin_state():
"""Reset plugin module state between tests."""
# Import the plugin and clear any service state
from plugins.lsp import _baselines
_baselines.clear()
yield
_baselines.clear()
def test_pre_tool_call_skips_nonexistent_parent_dir():
"""pre_tool_call returns early when the path's parent dir doesn't exist (Docker/SSH heuristic)."""
from plugins import lsp as lsp_plugin
# Simulate a path that doesn't exist on host (e.g., inside Docker)
fake_path = "/nonexistent-docker-container-fs/app/main.py"
# Mock _ensure_service to return a mock service
mock_service = type("MockService", (), {
"is_active": lambda self: True,
"enabled_for": lambda self, p: True,
"snapshot_baseline": lambda self, p: None,
})()
with patch.object(lsp_plugin, "_service", mock_service):
lsp_plugin._pre_tool_call(
tool_name="write_file",
args={"path": fake_path},
session_id="test-session",
tool_call_id="call-1",
)
# Baseline should NOT be captured because parent dir doesn't exist
assert ("test-session", os.path.normpath(fake_path)) not in lsp_plugin._baselines
def test_pre_tool_call_proceeds_for_local_path(tmp_path):
"""pre_tool_call captures baseline when path exists locally."""
from plugins import lsp as lsp_plugin
# Create a real file so the parent-dir check passes
test_file = tmp_path / "test.py"
test_file.write_text("x = 1\n")
mock_service = type("MockService", (), {
"is_active": lambda self: True,
"enabled_for": lambda self, p: True,
"snapshot_baseline": lambda self, p: None,
})()
with patch.object(lsp_plugin, "_service", mock_service):
lsp_plugin._pre_tool_call(
tool_name="write_file",
args={"path": str(test_file)},
session_id="test-session",
tool_call_id="call-2",
)
# Baseline SHOULD be captured because the local path exists
assert ("test-session", str(test_file)) in lsp_plugin._baselines
def test_pre_tool_call_skips_non_write_tools():
"""pre_tool_call is a no-op for tools other than write_file/patch."""
from plugins import lsp as lsp_plugin
lsp_plugin._pre_tool_call(
tool_name="terminal",
args={"command": "ls"},
session_id="test-session",
tool_call_id="call-3",
)
assert len(lsp_plugin._baselines) == 0
def test_pre_tool_call_skips_v4a_patch():
"""pre_tool_call skips V4A multi-file patches (has 'patch' key, no 'path' key)."""
from plugins import lsp as lsp_plugin
mock_service = type("MockService", (), {
"is_active": lambda self: True,
"enabled_for": lambda self, p: True,
"snapshot_baseline": lambda self, p: None,
})()
with patch.object(lsp_plugin, "_service", mock_service):
lsp_plugin._pre_tool_call(
tool_name="patch",
args={"patch": "*** Begin Patch\n*** Update File: foo.py\n..."},
session_id="test-session",
tool_call_id="call-4",
)
assert len(lsp_plugin._baselines) == 0
def test_transform_tool_result_injects_diagnostics(tmp_path):
"""transform_tool_result appends lsp_diagnostics field to JSON result."""
from plugins import lsp as lsp_plugin
test_file = tmp_path / "test.py"
abs_path = str(test_file)
# Pre-populate a baseline entry (simulating pre_tool_call ran)
lsp_plugin._baselines.add(("test-session", abs_path))
# Mock service that returns a diagnostic
mock_service = type("MockService", (), {
"is_active": lambda self: True,
"enabled_for": lambda self, p: True,
"get_diagnostics_sync": lambda self, p, delta=True, timeout=3.0: [
{
"severity": 1,
"range": {"start": {"line": 1, "character": 4}},
"message": "Type error: str is not int",
"code": "reportReturnType",
"source": "Pyright",
}
],
})()
with patch.object(lsp_plugin, "_service", mock_service):
result = lsp_plugin._transform_tool_result(
tool_name="write_file",
args={"path": abs_path},
result='{"bytes_written": 42, "dirs_created": false}',
session_id="test-session",
tool_call_id="call-5",
)
assert result is not None
import json
data = json.loads(result)
assert "lsp_diagnostics" in data
assert "reportReturnType" in data["lsp_diagnostics"]
assert "bytes_written" in data # Original fields preserved