forked from HAL9000/cleveragents-core
c200fe0f86
Add hover and definition support to LspClient and LspRuntime. - LspClient.get_hover(): sends textDocument/hover, returns Hover dict or None - LspClient.get_definitions(): sends textDocument/definition, handles Location, Location[], and LocationLink[] responses - LspRuntime wrappers: input validation, file reading, language detection, 1-based to 0-based line/column conversion - try/finally for did_close() safety on both new methods - Tool adapter: HOVER and DEFINITIONS dispatch to runtime instead of raising LspNotAvailableError - Updated lsp_tool_adapter_coverage test (HOVER -> REFERENCES) - 21 Behave BDD scenarios with full path coverage Closes #1243 Co-authored-by: Hamza Khyari <hamza.khyari@cleverthis.com> Co-committed-by: Hamza Khyari <hamza.khyari@cleverthis.com>
220 lines
7.6 KiB
Python
220 lines
7.6 KiB
Python
"""Step definitions for lsp_tool_adapter_coverage.feature.
|
|
|
|
Covers uncovered lines in src/cleveragents/lsp/tool_adapter.py:
|
|
- Line 198: ValueError when config is None
|
|
- Lines 126-132: workspace-symbols handler (empty query / valid query)
|
|
- Line 137: missing file_path guard
|
|
- Line 141: diagnostics delegation
|
|
- Lines 143-147: completions delegation
|
|
- Lines 150-153: unimplemented capability fallback
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
from behave import given, then, when
|
|
|
|
from cleveragents.lsp.errors import LspNotAvailableError
|
|
from cleveragents.lsp.models import LspCapability
|
|
from cleveragents.lsp.tool_adapter import (
|
|
LspToolAdapter,
|
|
_make_runtime_handler,
|
|
)
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Givens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
_SERVER_NAME = "test/mock-server"
|
|
|
|
|
|
@given("ltacov an LspToolAdapter with no runtime")
|
|
def step_ltacov_adapter_no_runtime(context):
|
|
context.ltacov_adapter = LspToolAdapter(runtime=None)
|
|
|
|
|
|
@given("ltacov a mock LspRuntime")
|
|
def step_ltacov_mock_runtime(context):
|
|
context.ltacov_runtime = MagicMock()
|
|
context.ltacov_runtime.get_diagnostics = MagicMock(return_value=[])
|
|
context.ltacov_runtime.get_completions = MagicMock(return_value=[])
|
|
|
|
|
|
@given("ltacov the runtime get_diagnostics returns sample items")
|
|
def step_ltacov_diagnostics_returns_items(context):
|
|
context.ltacov_sample_items = [{"message": "unused import", "severity": 2}]
|
|
context.ltacov_runtime.get_diagnostics.return_value = context.ltacov_sample_items
|
|
|
|
|
|
@given("ltacov the runtime get_completions returns sample items")
|
|
def step_ltacov_completions_returns_items(context):
|
|
context.ltacov_sample_items = [{"label": "my_func", "kind": 3}]
|
|
context.ltacov_runtime.get_completions.return_value = context.ltacov_sample_items
|
|
|
|
|
|
@given("ltacov a runtime handler for workspace_symbols capability")
|
|
def step_ltacov_handler_workspace_symbols(context):
|
|
context.ltacov_handler = _make_runtime_handler(
|
|
context.ltacov_runtime, _SERVER_NAME, LspCapability.WORKSPACE_SYMBOLS
|
|
)
|
|
|
|
|
|
@given("ltacov a runtime handler for diagnostics capability")
|
|
def step_ltacov_handler_diagnostics(context):
|
|
context.ltacov_handler = _make_runtime_handler(
|
|
context.ltacov_runtime, _SERVER_NAME, LspCapability.DIAGNOSTICS
|
|
)
|
|
|
|
|
|
@given("ltacov a runtime handler for completions capability")
|
|
def step_ltacov_handler_completions(context):
|
|
context.ltacov_handler = _make_runtime_handler(
|
|
context.ltacov_runtime, _SERVER_NAME, LspCapability.COMPLETIONS
|
|
)
|
|
|
|
|
|
@given("ltacov a runtime handler for hover capability")
|
|
def step_ltacov_handler_hover(context):
|
|
context.ltacov_handler = _make_runtime_handler(
|
|
context.ltacov_runtime, _SERVER_NAME, LspCapability.HOVER
|
|
)
|
|
|
|
|
|
@given("ltacov a runtime handler for references capability")
|
|
def step_ltacov_handler_references(context):
|
|
context.ltacov_handler = _make_runtime_handler(
|
|
context.ltacov_runtime, _SERVER_NAME, LspCapability.REFERENCES
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Whens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@when("ltacov generate_tool_specs is called with None config")
|
|
def step_ltacov_generate_with_none(context):
|
|
context.ltacov_error = None
|
|
try:
|
|
context.ltacov_adapter.generate_tool_specs(None)
|
|
except ValueError as exc:
|
|
context.ltacov_error = exc
|
|
|
|
|
|
@when("ltacov the handler is called with no query argument")
|
|
def step_ltacov_handler_no_query(context):
|
|
context.ltacov_error = None
|
|
context.ltacov_result = None
|
|
try:
|
|
context.ltacov_result = context.ltacov_handler()
|
|
except Exception as exc:
|
|
context.ltacov_error = exc
|
|
|
|
|
|
@when('ltacov the handler is called with query "{query}"')
|
|
def step_ltacov_handler_with_query(context, query):
|
|
context.ltacov_error = None
|
|
context.ltacov_result = None
|
|
try:
|
|
context.ltacov_result = context.ltacov_handler(query=query)
|
|
except LspNotAvailableError as exc:
|
|
context.ltacov_error = exc
|
|
|
|
|
|
@when("ltacov the handler is called with no file_path argument")
|
|
def step_ltacov_handler_no_filepath(context):
|
|
context.ltacov_error = None
|
|
context.ltacov_result = None
|
|
try:
|
|
context.ltacov_result = context.ltacov_handler()
|
|
except Exception as exc:
|
|
context.ltacov_error = exc
|
|
|
|
|
|
@when('ltacov the handler is called with file_path "{file_path}"')
|
|
def step_ltacov_handler_with_filepath(context, file_path):
|
|
context.ltacov_error = None
|
|
context.ltacov_result = None
|
|
try:
|
|
context.ltacov_result = context.ltacov_handler(file_path=file_path)
|
|
except LspNotAvailableError as exc:
|
|
context.ltacov_error = exc
|
|
|
|
|
|
@when(
|
|
'ltacov the handler is called with file_path "{file_path}" line {line:d} column {column:d}'
|
|
)
|
|
def step_ltacov_handler_with_filepath_line_col(context, file_path, line, column):
|
|
context.ltacov_error = None
|
|
context.ltacov_result = None
|
|
try:
|
|
context.ltacov_result = context.ltacov_handler(
|
|
file_path=file_path, line=line, column=column
|
|
)
|
|
except LspNotAvailableError as exc:
|
|
context.ltacov_error = exc
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Thens
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@then('ltacov a ValueError with message "{msg}" is raised')
|
|
def step_ltacov_valueerror_raised(context, msg):
|
|
assert context.ltacov_error is not None, "Expected a ValueError but none was raised"
|
|
assert isinstance(context.ltacov_error, ValueError), (
|
|
f"Expected ValueError, got {type(context.ltacov_error).__name__}"
|
|
)
|
|
assert msg in str(context.ltacov_error), (
|
|
f"Expected message to contain '{msg}', got '{context.ltacov_error}'"
|
|
)
|
|
|
|
|
|
@then('ltacov the result is an error dict saying "{expected_msg}"')
|
|
def step_ltacov_error_dict(context, expected_msg):
|
|
assert context.ltacov_error is None, (
|
|
f"Expected no exception, but got {context.ltacov_error}"
|
|
)
|
|
assert isinstance(context.ltacov_result, dict), (
|
|
f"Expected dict, got {type(context.ltacov_result)}"
|
|
)
|
|
assert "error" in context.ltacov_result, (
|
|
f"Expected 'error' key in result: {context.ltacov_result}"
|
|
)
|
|
assert expected_msg in context.ltacov_result["error"], (
|
|
f"Expected error to contain '{expected_msg}', "
|
|
f"got '{context.ltacov_result['error']}'"
|
|
)
|
|
|
|
|
|
@then('ltacov an LspNotAvailableError is raised mentioning "{capability}"')
|
|
def step_ltacov_lsp_not_available(context, capability):
|
|
assert context.ltacov_error is not None, (
|
|
"Expected LspNotAvailableError but none was raised"
|
|
)
|
|
assert isinstance(context.ltacov_error, LspNotAvailableError), (
|
|
f"Expected LspNotAvailableError, got {type(context.ltacov_error).__name__}"
|
|
)
|
|
assert capability in str(context.ltacov_error), (
|
|
f"Expected error message to mention '{capability}', "
|
|
f"got '{context.ltacov_error}'"
|
|
)
|
|
|
|
|
|
@then('ltacov the result contains a "{key}" key with the sample items')
|
|
def step_ltacov_result_with_key(context, key):
|
|
assert context.ltacov_error is None, (
|
|
f"Expected no exception, but got {context.ltacov_error}"
|
|
)
|
|
assert isinstance(context.ltacov_result, dict), (
|
|
f"Expected dict, got {type(context.ltacov_result)}"
|
|
)
|
|
assert key in context.ltacov_result, (
|
|
f"Expected key '{key}' in result: {context.ltacov_result}"
|
|
)
|
|
assert context.ltacov_result[key] == context.ltacov_sample_items, (
|
|
f"Expected {context.ltacov_sample_items}, got {context.ltacov_result[key]}"
|
|
)
|