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
152 lines
4.5 KiB
Python
152 lines
4.5 KiB
Python
"""Helper script for Robot Framework LSP registry smoke tests.
|
|
|
|
Usage:
|
|
python helper_lsp_registry.py <command>
|
|
|
|
Commands:
|
|
register-and-get Register an LSP server and retrieve it
|
|
list-filter Register servers and list with namespace filter
|
|
remove-server Register and remove an LSP server
|
|
runtime-stub Verify runtime stub raises in local mode
|
|
tool-adapter Verify tool adapter generates specs
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from pathlib import Path
|
|
|
|
# Ensure src is on the path
|
|
src_dir = str(Path(__file__).resolve().parents[1] / "src")
|
|
if src_dir not in sys.path:
|
|
sys.path.insert(0, src_dir)
|
|
|
|
from cleveragents.lsp.errors import ( # noqa: E402
|
|
LspError,
|
|
LspNotAvailableError,
|
|
LspServerNotFoundError,
|
|
)
|
|
from cleveragents.lsp.models import ( # noqa: E402
|
|
LspCapability,
|
|
LspServerConfig,
|
|
)
|
|
from cleveragents.lsp.registry import LspRegistry # noqa: E402
|
|
from cleveragents.lsp.runtime import LspRuntime # noqa: E402
|
|
from cleveragents.lsp.tool_adapter import LspToolAdapter # noqa: E402
|
|
|
|
|
|
def _make_config(
|
|
name: str,
|
|
command: str = "stub-cmd",
|
|
languages: list[str] | None = None,
|
|
capabilities: list[LspCapability] | None = None,
|
|
) -> LspServerConfig:
|
|
return LspServerConfig(
|
|
name=name,
|
|
command=command,
|
|
languages=languages or [],
|
|
capabilities=capabilities or [],
|
|
)
|
|
|
|
|
|
def cmd_register_and_get() -> None:
|
|
registry = LspRegistry()
|
|
config = _make_config("local/pyright", "pyright-langserver", ["python"])
|
|
registry.register(config)
|
|
|
|
result = registry.get("local/pyright")
|
|
assert result is not None, "LSP server not found"
|
|
assert result.name == "local/pyright"
|
|
assert "python" in result.languages
|
|
print("lsp-registry-ok")
|
|
|
|
|
|
def cmd_list_filter() -> None:
|
|
registry = LspRegistry()
|
|
registry.register(_make_config("local/pyright", languages=["python"]))
|
|
registry.register(_make_config("local/tsserver", languages=["typescript"]))
|
|
registry.register(_make_config("devops/clangd", languages=["c"]))
|
|
|
|
all_servers = registry.list_servers()
|
|
assert len(all_servers) == 3
|
|
|
|
local_servers = registry.list_servers(namespace="local")
|
|
assert len(local_servers) == 2
|
|
|
|
python_servers = registry.list_servers(language="python")
|
|
assert len(python_servers) == 1
|
|
|
|
print("lsp-list-ok")
|
|
|
|
|
|
def cmd_remove_server() -> None:
|
|
registry = LspRegistry()
|
|
registry.register(_make_config("local/pyright", languages=["python"]))
|
|
|
|
assert registry.remove("local/pyright") is True
|
|
assert registry.get("local/pyright") is None
|
|
assert registry.remove("local/pyright") is False
|
|
print("lsp-remove-ok")
|
|
|
|
|
|
def cmd_runtime_stub() -> None:
|
|
runtime = LspRuntime()
|
|
|
|
# The functional runtime raises LspServerNotFoundError for
|
|
# unregistered servers (not LspNotAvailableError like the old stub).
|
|
for fn_name, args in [
|
|
("start_server", ("local/pyright", "/tmp")),
|
|
("stop_server", ("local/pyright",)),
|
|
("get_diagnostics", ("local/pyright", "/tmp/test.py")),
|
|
("get_completions", ("local/pyright", "/tmp/test.py", 1, 1)),
|
|
]:
|
|
try:
|
|
getattr(runtime, fn_name)(*args)
|
|
print(f"FAIL: {fn_name} should have raised an error")
|
|
sys.exit(1)
|
|
except (LspNotAvailableError, LspServerNotFoundError, LspError):
|
|
pass
|
|
|
|
print("lsp-runtime-stub-ok")
|
|
|
|
|
|
def cmd_tool_adapter() -> None:
|
|
config = _make_config(
|
|
"local/pyright",
|
|
languages=["python"],
|
|
capabilities=[LspCapability.DIAGNOSTICS, LspCapability.COMPLETIONS],
|
|
)
|
|
adapter = LspToolAdapter()
|
|
specs = adapter.generate_tool_specs(config)
|
|
|
|
assert len(specs) == 2, f"Expected 2 specs, got {len(specs)}"
|
|
names = {s["name"] for s in specs}
|
|
assert "local/pyright/diagnostics" in names
|
|
assert "local/pyright/completions" in names
|
|
|
|
# Verify handler raises in local mode
|
|
try:
|
|
specs[0]["handler"]()
|
|
print("FAIL: handler should have raised LspNotAvailableError")
|
|
sys.exit(1)
|
|
except LspNotAvailableError:
|
|
pass
|
|
|
|
print("lsp-tool-adapter-ok")
|
|
|
|
|
|
COMMANDS = {
|
|
"register-and-get": cmd_register_and_get,
|
|
"list-filter": cmd_list_filter,
|
|
"remove-server": cmd_remove_server,
|
|
"runtime-stub": cmd_runtime_stub,
|
|
"tool-adapter": cmd_tool_adapter,
|
|
}
|
|
|
|
|
|
if __name__ == "__main__":
|
|
if len(sys.argv) < 2 or sys.argv[1] not in COMMANDS:
|
|
print(f"Usage: {sys.argv[0]} <{'|'.join(COMMANDS)}>", file=sys.stderr)
|
|
sys.exit(1)
|
|
COMMANDS[sys.argv[1]]()
|