diff --git a/features/lsp_actor_service_wiring.feature b/features/lsp_actor_service_wiring.feature index 993a46749..2d446b23b 100644 --- a/features/lsp_actor_service_wiring.feature +++ b/features/lsp_actor_service_wiring.feature @@ -1,7 +1,7 @@ Feature: LSP Actor Service — wire LspRuntime and LspToolAdapter into actor execution Background: - Given a clean LSP registry + Given a clean LSP actor service registry And a test workspace directory Scenario: LspActorService activates bindings and generates tool specs diff --git a/features/steps/lsp_actor_service_steps.py b/features/steps/lsp_actor_service_steps.py index fcc90e999..286ded742 100644 --- a/features/steps/lsp_actor_service_steps.py +++ b/features/steps/lsp_actor_service_steps.py @@ -3,23 +3,56 @@ from __future__ import annotations import tempfile -from pathlib import Path from typing import Any from behave import given, then, when from cleveragents.application.services.lsp_actor_service import LspActorService +from cleveragents.lsp.client import LspClient +from cleveragents.lsp.lifecycle import LspLifecycleManager from cleveragents.lsp.models import LspBinding, LspCapability, LspServerConfig from cleveragents.lsp.registry import LspRegistry -@given("a clean LSP registry") +class _MockLifecycleManager(LspLifecycleManager): + """Stub lifecycle manager that does not spawn real processes.""" + + def __init__(self) -> None: + self._servers: dict[str, object] = {} + self._started: set[str] = set() + + def start_server(self, config: LspServerConfig, workspace_path: str) -> LspClient: + """Record the server as started without spawning a process.""" + self._started.add(config.name) + mock_client: LspClient = LspClient.__new__(LspClient) + return mock_client + + def stop_server(self, name: str) -> None: + """Record the server as stopped.""" + self._started.discard(name) + + def health_check(self, name: str) -> bool: + """Return True if the server was started.""" + return name in self._started + + def stop_all(self) -> None: + """Clear all started servers.""" + self._started.clear() + + +def _make_mock_lifecycle() -> LspLifecycleManager: + """Create a mock lifecycle manager that does not spawn real processes.""" + return _MockLifecycleManager() + + +@given("a clean LSP actor service registry") def step_clean_lsp_registry(context: Any) -> None: """Initialize a clean LSP registry.""" context.registry = LspRegistry() context.service = LspActorService() - # Override the service's registry with our test registry + # Override the service's registry and lifecycle with test doubles context.service._runtime._registry = context.registry + context.service._runtime._lifecycle = _make_mock_lifecycle() @given("a test workspace directory") @@ -37,11 +70,11 @@ def step_register_lsp_server(context: Any, server_name: str) -> None: try: capabilities.append(LspCapability[cap_name]) except KeyError: - raise ValueError(f"Unknown capability: {cap_name}") + raise ValueError(f"Unknown capability: {cap_name}") from None config = LspServerConfig( name=server_name, - command=["echo", "mock-server"], + command="echo", capabilities=capabilities, ) context.registry.register(config) diff --git a/src/cleveragents/application/services/lsp_actor_service.py b/src/cleveragents/application/services/lsp_actor_service.py index a1703fbbd..44d914632 100644 --- a/src/cleveragents/application/services/lsp_actor_service.py +++ b/src/cleveragents/application/services/lsp_actor_service.py @@ -13,13 +13,14 @@ Based on docs/specification.md LSP Integration (Server Lifecycle). from __future__ import annotations -import logging from typing import Any +import structlog + from cleveragents.lsp.runtime import LspRuntime from cleveragents.lsp.tool_adapter import LspToolAdapter -logger = logging.getLogger(__name__) +logger = structlog.get_logger(__name__) class LspActorService: