Files
cleveragents-core/features/lsp_lifecycle_coverage.feature
T
freemo 31472b5413
CI / benchmark-publish (pull_request) Has been skipped
CI / build (pull_request) Successful in 21s
CI / helm (pull_request) Successful in 22s
CI / lint (pull_request) Successful in 3m20s
CI / quality (pull_request) Successful in 3m43s
CI / typecheck (pull_request) Successful in 3m58s
CI / security (pull_request) Successful in 4m8s
CI / integration_tests (pull_request) Successful in 9m33s
CI / unit_tests (pull_request) Successful in 10m12s
CI / docker (pull_request) Successful in 1m28s
CI / coverage (pull_request) Successful in 12m18s
CI / e2e_tests (pull_request) Successful in 19m51s
CI / status-check (pull_request) Successful in 1s
CI / build (push) Successful in 15s
CI / helm (push) Successful in 22s
CI / lint (push) Successful in 3m18s
CI / quality (push) Successful in 3m41s
CI / typecheck (push) Successful in 3m57s
CI / benchmark-regression (push) Has been skipped
CI / security (push) Successful in 4m10s
CI / unit_tests (push) Failing after 6m58s
CI / docker (push) Has been skipped
CI / integration_tests (push) Successful in 9m15s
CI / coverage (push) Successful in 12m26s
CI / e2e_tests (push) Successful in 23m11s
CI / status-check (push) Failing after 1s
CI / benchmark-publish (push) Successful in 28m31s
CI / benchmark-regression (pull_request) Successful in 54m53s
test(coverage): add Behave scenarios for 39 under-covered modules
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate.

ISSUES CLOSED: #1232
2026-03-31 21:47:12 +00:00

185 lines
8.8 KiB
Gherkin

Feature: LSP Lifecycle Manager coverage
Exercise every code path in cleveragents.lsp.lifecycle to reach full
branch and line coverage for the LspLifecycleManager and _ManagedServer
classes.
Background:
Given llcov a fresh LspLifecycleManager instance
# ---------- start_server: brand-new server (happy path) ----------
Scenario: Start a new LSP server successfully
Given llcov a server config named "local/pyright" with command "pyright-langserver"
When llcov I start the server for workspace "/tmp/ws"
Then llcov the returned client should not be None
And llcov the server "local/pyright" should be registered
# ---------- start_server: reuse existing alive server ----------
Scenario: Reuse an existing alive server increments ref count
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
When llcov I start the server again for workspace "/tmp/ws"
Then llcov the returned client should be the same as the original
And llcov the ref count for "local/pyright" should be 2
# ---------- start_server: initialize failure rolls back ----------
Scenario: Start server rolls back transport when initialize fails
Given llcov a server config named "local/broken" with command "broken-server"
And llcov the LspClient initialize will raise an error
When llcov I attempt to start the server for workspace "/tmp/ws"
Then llcov a start error should have been raised
And llcov the transport should have been stopped after init failure
# ---------- start_server: race condition (another thread wins) ----------
Scenario: Start server detects a race condition and reuses the winner
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov another thread already registered "local/pyright" before Phase 3
When llcov I start the server for workspace "/tmp/ws" with race
Then llcov the returned client should be the race winner client
And llcov the losing transport should have been stopped
# ---------- stop_server: decrement but still > 0 ----------
Scenario: Stop server decrements ref count but keeps server alive
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
And llcov the server is started again to bump ref count
When llcov I stop the server "local/pyright"
Then llcov the server "local/pyright" should still be registered
And llcov the ref count for "local/pyright" should be 1
# ---------- stop_server: last reference, full shutdown ----------
Scenario: Stop server shuts down when last reference is released
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
When llcov I stop the server "local/pyright"
Then llcov the server "local/pyright" should not be registered
And llcov shutdown should have been called on the client
# ---------- stop_server: server not found ----------
Scenario: Stop server raises error for unknown server
When llcov I attempt to stop server "local/unknown"
Then llcov a LspServerNotFoundError should have been raised
# ---------- _shutdown_server: graceful shutdown ----------
Scenario: Shutdown server calls client shutdown and transport stop
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
When llcov I trigger shutdown on the managed server
Then llcov the client shutdown method should have been called
And llcov the transport stop method should have been called
# ---------- _shutdown_server: shutdown exception is caught ----------
Scenario: Shutdown server catches exception from client shutdown
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
And llcov the client shutdown will raise an exception
When llcov I trigger shutdown on the managed server
Then llcov the transport stop method should have been called despite error
# ---------- get_client: found ----------
Scenario: Get client returns the client for a running server
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
When llcov I get the client for "local/pyright"
Then llcov the returned client should not be None
# ---------- get_client: not found ----------
Scenario: Get client raises error for unknown server
When llcov I attempt to get the client for "local/missing"
Then llcov a LspServerNotFoundError should have been raised
# ---------- health_check: server alive ----------
Scenario: Health check returns true for alive server
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
When llcov I health check "local/pyright"
Then llcov the health check result should be true
# ---------- health_check: server not registered ----------
Scenario: Health check returns false for unknown server
When llcov I health check "local/nonexistent"
Then llcov the health check result should be false
# ---------- restart_server: happy path ----------
Scenario: Restart server stops old transport and creates new client
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
When llcov I restart the server "local/pyright"
Then llcov the returned client should not be None
And llcov the old transport should have been stopped
And llcov the managed server should have the new transport and client
# ---------- restart_server: not found ----------
Scenario: Restart server raises error for unknown server
When llcov I attempt to restart server "local/ghost"
Then llcov a LspServerNotFoundError should have been raised
# ---------- restart_server: init failure during restart ----------
Scenario: Restart server stops new transport when re-initialize fails
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
And llcov the LspClient initialize will raise on restart
When llcov I attempt to restart server "local/pyright"
Then llcov a restart error should have been raised
And llcov the new transport should have been stopped after restart failure
# ---------- stop_all: multiple servers ----------
Scenario: Stop all shuts down every registered server
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
Given llcov a server config named "local/ruff" with command "ruff-langserver"
And llcov the second server is already started for workspace "/tmp/ws2"
When llcov I stop all servers
Then llcov no servers should be registered
# ---------- stop_all: with shutdown error ----------
Scenario: Stop all handles shutdown errors gracefully
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
And llcov the client shutdown will raise an exception
When llcov I stop all servers
Then llcov no servers should be registered
# ---------- stop_all: transport.stop() raises (covers lines 273-277) ----------
Scenario: Stop all catches transport stop error in shutdown
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
And llcov the transport stop will raise an exception
When llcov I stop all servers
Then llcov no servers should be registered
# ---------- list_running: with servers ----------
Scenario: List running returns status for all servers
Given llcov a server config named "local/pyright" with command "pyright-langserver"
And llcov the server is already started for workspace "/tmp/ws"
When llcov I list running servers
Then llcov the running list should contain 1 entry
And llcov the running entry for "local/pyright" should have workspace "/tmp/ws"
And llcov the running entry for "local/pyright" should have alive status true
And llcov the running entry for "local/pyright" should have ref count 1
# ---------- list_running: empty ----------
Scenario: List running returns empty list when no servers
When llcov I list running servers
Then llcov the running list should contain 0 entries