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
Add Behave feature/step pairs that exercise uncovered branches across handlers, LSP, CLI, and service layers to reach the coverage gate. ISSUES CLOSED: #1232
223 lines
12 KiB
Gherkin
223 lines
12 KiB
Gherkin
Feature: LSP Client coverage
|
|
As a developer maintaining LspClient
|
|
I want thorough BDD tests for every code path
|
|
So that coverage for client.py exceeds 90%
|
|
|
|
# ── Property: server_capabilities (line 73) ────────────────────────
|
|
|
|
Scenario: lccov access server_capabilities before initialize
|
|
Given lccov I create an LspClient with a mock transport
|
|
Then lccov server_capabilities should be an empty dict
|
|
|
|
# ── _send_request: success with params (lines 100-109, 111, 114-117, 130, 141) ──
|
|
|
|
Scenario: lccov send_request with params returns result
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport echoes back a successful response
|
|
When lccov I call _send_request with method "test/method" and params
|
|
Then lccov the request result should contain the expected data
|
|
And lccov the sent message should include params
|
|
|
|
# ── _send_request: success without params (lines 100-104, 106 skip) ──
|
|
|
|
Scenario: lccov send_request without params omits params key
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport echoes back a successful response
|
|
When lccov I call _send_request with method "test/noparams" and no params
|
|
Then lccov the request result should contain the expected data
|
|
And lccov the sent message should not include params key
|
|
|
|
# ── _send_request: server returns JSON-RPC error (lines 131-138) ──
|
|
|
|
Scenario: lccov send_request raises LspError on JSON-RPC error
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport returns a JSON-RPC error response
|
|
When lccov I call _send_request expecting an error
|
|
Then lccov an LspError should be stored with message containing "LSP error"
|
|
|
|
# ── _send_request: server closed connection (lines 117, 121-126) ──
|
|
|
|
Scenario: lccov send_request raises LspError when server dies
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport returns None and is not alive
|
|
When lccov I call _send_request expecting an error
|
|
Then lccov an LspError should be stored with message containing "closed connection"
|
|
|
|
# ── _send_request: transport alive but no response yet (lines 117, 121-122 continue loop) ──
|
|
|
|
Scenario: lccov send_request retries when transport alive but no data
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport returns None then a valid response while alive
|
|
When lccov I call _send_request with method "test/retry" and no params
|
|
Then lccov the request result should contain the expected data
|
|
|
|
# ── _send_request: timeout (lines 146-149) ────────────────────────
|
|
|
|
Scenario: lccov send_request raises LspError on timeout
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport always returns None but stays alive
|
|
When lccov I call _send_request with a very short timeout expecting error
|
|
Then lccov an LspError should be stored with message containing "Timed out"
|
|
|
|
# ── _send_request: notification while waiting (line 144) ───────────
|
|
|
|
Scenario: lccov send_request queues unrelated messages as notifications
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport returns a notification then the response
|
|
When lccov I call _send_request with method "test/mixed" and no params
|
|
Then lccov the request result should contain the expected data
|
|
And lccov the pending notifications should contain the queued message
|
|
|
|
# ── _send_notification: with params (lines 163-169) ────────────────
|
|
|
|
Scenario: lccov send_notification with params
|
|
Given lccov I create an LspClient with a mock transport
|
|
When lccov I call _send_notification with method "test/note" and params
|
|
Then lccov the transport should have received a notification with params
|
|
|
|
# ── _send_notification: without params (lines 163-165, 167 skip) ──
|
|
|
|
Scenario: lccov send_notification without params
|
|
Given lccov I create an LspClient with a mock transport
|
|
When lccov I call _send_notification with method "test/note" and no params
|
|
Then lccov the transport should have received a notification without params key
|
|
|
|
# ── _handle_notification: publishDiagnostics (lines 173-184) ───────
|
|
|
|
Scenario: lccov handle_notification stores publishDiagnostics
|
|
Given lccov I create an LspClient with a mock transport
|
|
When lccov I handle a publishDiagnostics notification for "file:///test.py"
|
|
Then lccov the diagnostics cache for "file:///test.py" should have 2 items
|
|
|
|
# ── _handle_notification: other notification (lines 173-174, 187) ──
|
|
|
|
Scenario: lccov handle_notification queues non-diagnostic notifications
|
|
Given lccov I create an LspClient with a mock transport
|
|
When lccov I handle a window/logMessage notification
|
|
Then lccov the pending notifications deque should have 1 item
|
|
|
|
# ── initialize: success without file:// prefix (lines 208, 215-268, 272 skip, 275-294) ──
|
|
|
|
Scenario: lccov initialize with plain path succeeds
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport echoes back an initialize result with capabilities
|
|
When lccov I call initialize with workspace "/tmp/project"
|
|
Then lccov the client should be initialized
|
|
And lccov server_capabilities should include "textDocumentSync"
|
|
And lccov the workspace URI sent should start with "file://"
|
|
|
|
# ── initialize: workspace already has file:// prefix (lines 215-216) ──
|
|
|
|
Scenario: lccov initialize with file URI keeps prefix
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport echoes back an initialize result with capabilities
|
|
When lccov I call initialize with workspace "file:///already/uri"
|
|
Then lccov the client should be initialized
|
|
And lccov the workspace URI sent should be "file:///already/uri"
|
|
|
|
# ── initialize: with initialization_options (lines 272-273) ──────
|
|
|
|
Scenario: lccov initialize with initialization_options includes them
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport echoes back an initialize result with capabilities
|
|
When lccov I call initialize with workspace "/tmp/project" and init options
|
|
Then lccov the client should be initialized
|
|
And lccov the sent params should include initializationOptions
|
|
|
|
# ── initialize: already initialized (lines 208-211) ────────────────
|
|
|
|
Scenario: lccov initialize raises error when already initialized
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the client is already marked as initialized
|
|
When lccov I call initialize expecting an error
|
|
Then lccov an LspError should be stored with message containing "already initialized"
|
|
|
|
# ── shutdown: not initialized (lines 302-307) ──────────────────────
|
|
|
|
Scenario: lccov shutdown when not initialized returns early
|
|
Given lccov I create an LspClient with a mock transport
|
|
When lccov I call shutdown
|
|
Then lccov no request should have been sent to the transport
|
|
|
|
# ── shutdown: success path (lines 302, 309, 311-312, 320-321, 323) ──
|
|
|
|
Scenario: lccov shutdown sends shutdown request then exit notification
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the client is already marked as initialized
|
|
And lccov the transport echoes back a shutdown response
|
|
When lccov I call shutdown
|
|
Then lccov the client should not be initialized after shutdown
|
|
And lccov the transport should have received shutdown and exit messages
|
|
|
|
# ── shutdown: shutdown request raises LspError (lines 313-317) ─────
|
|
|
|
Scenario: lccov shutdown handles LspError from shutdown request gracefully
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the client is already marked as initialized
|
|
And lccov the transport will raise LspError on shutdown request
|
|
When lccov I call shutdown
|
|
Then lccov the client should not be initialized after shutdown
|
|
|
|
# ── shutdown: exit notification raises BrokenPipeError (line 320) ──
|
|
|
|
Scenario: lccov shutdown suppresses BrokenPipeError on exit
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the client is already marked as initialized
|
|
And lccov the transport echoes back a shutdown response but raises BrokenPipeError on exit
|
|
When lccov I call shutdown
|
|
Then lccov the client should not be initialized after shutdown
|
|
|
|
# ── did_open (lines 342-349) ───────────────────────────────────────
|
|
|
|
Scenario: lccov did_open sends textDocument/didOpen notification
|
|
Given lccov I create an LspClient with a mock transport
|
|
When lccov I call did_open for "file:///test.py" with language "python"
|
|
Then lccov the transport should have received a didOpen notification
|
|
|
|
# ── did_close (lines 360-365) ──────────────────────────────────────
|
|
|
|
Scenario: lccov did_close sends notification and clears diagnostics
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov there are cached diagnostics for "file:///test.py"
|
|
When lccov I call did_close for "file:///test.py"
|
|
Then lccov the transport should have received a didClose notification
|
|
And lccov the diagnostics cache for "file:///test.py" should be empty
|
|
|
|
# ── get_diagnostics (lines 382-389) ────────────────────────────────
|
|
|
|
Scenario: lccov get_diagnostics drains messages and returns cached
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport has queued diagnostic notifications for drain
|
|
When lccov I call get_diagnostics for "file:///test.py"
|
|
Then lccov the returned diagnostics list should have 1 item
|
|
|
|
Scenario: lccov get_diagnostics returns empty for unknown URI
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport returns None immediately for drain
|
|
When lccov I call get_diagnostics for "file:///unknown.py"
|
|
Then lccov the returned diagnostics list should have 0 items
|
|
|
|
# ── get_completions: CompletionList (lines 407-411, 416-417) ───────
|
|
|
|
Scenario: lccov get_completions returns items from CompletionList
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport returns a CompletionList response
|
|
When lccov I call get_completions for "file:///test.py" at line 0 char 5
|
|
Then lccov the completions should have 2 items
|
|
|
|
# ── get_completions: plain list (lines 418-419) ────────────────────
|
|
|
|
Scenario: lccov get_completions returns items from plain list
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport returns a plain list completion response
|
|
When lccov I call get_completions for "file:///test.py" at line 0 char 5
|
|
Then lccov the completions should have 1 items
|
|
|
|
# ── get_completions: unexpected result type (line 420) ─────────────
|
|
|
|
Scenario: lccov get_completions returns empty for unexpected result
|
|
Given lccov I create an LspClient with a mock transport
|
|
And lccov the transport returns a scalar completion response
|
|
When lccov I call get_completions for "file:///test.py" at line 0 char 5
|
|
Then lccov the completions should have 0 items
|