4ff075e0da
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 18s
CI / quality (pull_request) Successful in 32s
CI / typecheck (pull_request) Successful in 56s
CI / security (pull_request) Successful in 57s
CI / lint (pull_request) Successful in 3m19s
CI / integration_tests (pull_request) Successful in 3m44s
CI / unit_tests (pull_request) Successful in 6m39s
CI / docker (pull_request) Successful in 1m9s
CI / e2e_tests (pull_request) Successful in 9m38s
CI / coverage (pull_request) Successful in 11m19s
CI / status-check (pull_request) Successful in 1s
CI / benchmark-regression (pull_request) Failing after 1h13m38s
Replace local-mode stubs with real LSP protocol support: - StdioTransport: subprocess management with JSON-RPC framing - LspClient: LSP protocol (initialize/shutdown/diagnostics/completions) - LspLifecycleManager: reference-counted instances, health checks, crash restart - LspRuntime: registry-based server lookup, auto-restart on crash - LspToolAdapter: runtime-delegating handlers with local-mode fallback - LanguageDiscovery: 4-layer detection (extension, shebang, UKO, project) - activate_bindings/deactivate_bindings: actor compiler LSP binding wiring Tests: 27 Behave scenarios, 6 Robot integration tests, 250 existing pass. ISSUES CLOSED: #826
145 lines
4.2 KiB
Python
145 lines
4.2 KiB
Python
"""Robot Framework helper for LSP functional runtime integration tests."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import importlib
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Ensure local source tree is importable
|
|
_SRC = str(Path(__file__).resolve().parents[1] / "src")
|
|
if _SRC not in sys.path:
|
|
sys.path.insert(0, _SRC)
|
|
|
|
import cleveragents # noqa: E402
|
|
|
|
importlib.reload(cleveragents)
|
|
|
|
from cleveragents.lsp.discovery import LanguageDiscovery # noqa: E402
|
|
from cleveragents.lsp.errors import LspServerNotFoundError # noqa: E402
|
|
from cleveragents.lsp.lifecycle import LspLifecycleManager # noqa: E402
|
|
from cleveragents.lsp.models import LspCapability, LspServerConfig # noqa: E402
|
|
from cleveragents.lsp.runtime import LspRuntime # noqa: E402
|
|
from cleveragents.lsp.tool_adapter import LspToolAdapter # noqa: E402
|
|
from cleveragents.lsp.transport import StdioTransport # noqa: E402
|
|
|
|
|
|
def _run_transport_lifecycle() -> None:
|
|
"""Test transport create/alive check."""
|
|
t = StdioTransport(command="cat")
|
|
assert not t.is_alive
|
|
t.start()
|
|
assert t.is_alive
|
|
t.stop()
|
|
assert not t.is_alive
|
|
print("transport-lifecycle-ok")
|
|
|
|
|
|
def _run_transport_empty_command() -> None:
|
|
"""Test transport rejects empty command."""
|
|
try:
|
|
StdioTransport(command="")
|
|
print("FAIL: expected ValueError")
|
|
sys.exit(1)
|
|
except ValueError:
|
|
pass
|
|
print("transport-empty-command-ok")
|
|
|
|
|
|
def _run_lifecycle_manager_empty() -> None:
|
|
"""Test lifecycle manager starts empty."""
|
|
mgr = LspLifecycleManager()
|
|
assert len(mgr.list_running()) == 0
|
|
assert not mgr.health_check("local/test")
|
|
try:
|
|
mgr.stop_server("local/test")
|
|
print("FAIL: expected LspServerNotFoundError")
|
|
sys.exit(1)
|
|
except LspServerNotFoundError:
|
|
pass
|
|
print("lifecycle-manager-empty-ok")
|
|
|
|
|
|
def _run_runtime_validation() -> None:
|
|
"""Test runtime input validation."""
|
|
rt = LspRuntime()
|
|
errors = 0
|
|
|
|
try:
|
|
rt.start_server("", "/tmp")
|
|
except ValueError:
|
|
errors += 1
|
|
|
|
try:
|
|
rt.start_server("local/test", "")
|
|
except ValueError:
|
|
errors += 1
|
|
|
|
try:
|
|
rt.start_server("local/missing", "/tmp")
|
|
except LspServerNotFoundError:
|
|
errors += 1
|
|
|
|
assert errors == 3, f"Expected 3 validation errors, got {errors}"
|
|
print("runtime-validation-ok")
|
|
|
|
|
|
def _run_discovery() -> None:
|
|
"""Test language discovery."""
|
|
d = LanguageDiscovery()
|
|
assert d.detect_file_language("test.py") == "python"
|
|
assert d.detect_file_language("app.ts") == "typescript"
|
|
assert d.detect_file_language("main.rs") == "rust"
|
|
assert d.detect_file_language("unknown.xyz") == "plaintext"
|
|
assert d.detect_file_language("Dockerfile") == "dockerfile"
|
|
|
|
d2 = LanguageDiscovery(project_languages=["go"])
|
|
assert d2.detect_file_language("no_ext") == "go"
|
|
|
|
print("discovery-ok")
|
|
|
|
|
|
def _run_tool_adapter_modes() -> None:
|
|
"""Test tool adapter local vs runtime mode."""
|
|
config = LspServerConfig(
|
|
name="local/test",
|
|
command="echo",
|
|
languages=["python"],
|
|
capabilities=[LspCapability.DIAGNOSTICS, LspCapability.COMPLETIONS],
|
|
)
|
|
|
|
# Local mode
|
|
adapter_local = LspToolAdapter()
|
|
specs_local = adapter_local.generate_tool_specs(config)
|
|
assert len(specs_local) == 2
|
|
assert specs_local[0]["name"] == "local/test/diagnostics"
|
|
|
|
# Runtime mode
|
|
adapter_rt = LspToolAdapter(runtime=LspRuntime())
|
|
specs_rt = adapter_rt.generate_tool_specs(config)
|
|
assert len(specs_rt) == 2
|
|
assert specs_rt[0]["name"] == "local/test/diagnostics"
|
|
|
|
print("tool-adapter-modes-ok")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
cmd = sys.argv[1] if len(sys.argv) > 1 else "all"
|
|
dispatch = {
|
|
"transport-lifecycle": _run_transport_lifecycle,
|
|
"transport-empty-command": _run_transport_empty_command,
|
|
"lifecycle-manager-empty": _run_lifecycle_manager_empty,
|
|
"runtime-validation": _run_runtime_validation,
|
|
"discovery": _run_discovery,
|
|
"tool-adapter-modes": _run_tool_adapter_modes,
|
|
}
|
|
|
|
if cmd == "all":
|
|
for fn in dispatch.values():
|
|
fn()
|
|
elif cmd in dispatch:
|
|
dispatch[cmd]()
|
|
else:
|
|
print(f"Unknown command: {cmd}")
|
|
sys.exit(1)
|