Feature: LSP StdioTransport coverage
  As a developer maintaining the LSP transport layer
  I need thorough tests for StdioTransport
  So that all error paths and normal flows are exercised

  # ── start() error paths ─────────────────────────────────────────

  @tdd_issue @tdd_issue_7044
  Scenario: ltcov start cleans up subprocess when post-Popen init fails
    Given ltcov I create a StdioTransport for command "cat"
    And ltcov Popen is mocked to succeed with a running process
    And ltcov logger.info after spawn raises RuntimeError
    When ltcov I try to start the transport
    Then ltcov an error occurred during start()
    And ltcov the internal process should be None
    And ltcov the mock process should have been terminated
    And ltcov the mock process should have been waited on

  Scenario: ltcov start raises RuntimeError when already alive
    Given ltcov I create a StdioTransport for command "cat"
    And ltcov the transport has a running mock process
    When ltcov I try to start the transport
    Then ltcov the error should be a RuntimeError with message "already started"

  @tdd_issue @tdd_issue_7044
  Scenario: ltcov start is alive after successful spawn
    Given ltcov I create a StdioTransport for command "cat"
    When ltcov I try to start the transport
    Then ltcov the transport should be alive
    And ltcov the internal process should not be None

  Scenario: ltcov is_alive returns False when no process
    Given ltcov I create a StdioTransport for command "cat"
    When ltcov I check if the transport is alive
    Then ltcov the is_alive result should be false

  Scenario: ltcov start raises LspError on FileNotFoundError
    Given ltcov I create a StdioTransport for command "nonexistent_binary_xyz"
    And ltcov Popen is mocked to raise FileNotFoundError
    When ltcov I try to start the transport
    Then ltcov the error should be an LspError with message "not found"

  Scenario: ltcov start raises LspError on OSError
    Given ltcov I create a StdioTransport for command "bad_command"
    And ltcov Popen is mocked to raise OSError with "Permission denied"
    When ltcov I try to start the transport
    Then ltcov the error should be an LspError with message "Failed to start"

  # ── stop() paths ────────────────────────────────────────────────

  Scenario: ltcov stop returns None when not started
    Given ltcov I create a StdioTransport for command "echo"
    When ltcov I stop the transport
    Then ltcov the stop result should be None

  Scenario: ltcov stop returns exit code when process already exited
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process that already exited with code 0
    When ltcov I stop the transport
    Then ltcov the stop result should be 0
    And ltcov the internal process should be None

  Scenario: ltcov stop returns exit code when process already exited with error
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process that already exited with code 1
    When ltcov I stop the transport
    Then ltcov the stop result should be 1

  Scenario: ltcov stop force kills on timeout
    Given ltcov I create a StdioTransport for command "sleep"
    And ltcov the transport has a mock process that ignores terminate
    When ltcov I stop the transport with timeout 0.1
    Then ltcov the mock process should have been killed
    And ltcov the stop result should be an integer

  # ── send_message() paths ────────────────────────────────────────

  Scenario: ltcov send_message raises RuntimeError when not started
    Given ltcov I create a StdioTransport for command "echo"
    When ltcov I try to send a message
    Then ltcov the error should be a RuntimeError with message "not started"

  Scenario: ltcov send_message raises RuntimeError when stdin is None
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with no stdin
    When ltcov I try to send a message
    Then ltcov the error should be a RuntimeError with message "not started"

  Scenario: ltcov send_message writes Content-Length framed JSON-RPC
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with writable stdin
    When ltcov I send the message {"jsonrpc":"2.0","method":"initialize","id":1}
    Then ltcov the written bytes should contain "Content-Length:"
    And ltcov the written bytes should contain "initialize"

  Scenario: ltcov send_message raises BrokenPipeError on dead process
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with broken stdin
    When ltcov I try to send a message
    Then ltcov the error should be a BrokenPipeError

  # ── read_message() paths ────────────────────────────────────────

  Scenario: ltcov read_message raises RuntimeError when not started
    Given ltcov I create a StdioTransport for command "echo"
    When ltcov I try to read a message
    Then ltcov the error should be a RuntimeError with message "not started"

  Scenario: ltcov read_message raises RuntimeError when stdout is None
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with no stdout
    When ltcov I try to read a message
    Then ltcov the error should be a RuntimeError with message "not started"

  Scenario: ltcov read_message returns None on select timeout
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with empty stdout
    And ltcov select is mocked to return not ready
    When ltcov I read a message with timeout 1.0
    Then ltcov the read result should be None

  Scenario: ltcov read_message returns None on EOF
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with stdout that returns EOF
    When ltcov I read a message with timeout 1.0
    Then ltcov the read result should be None

  Scenario: ltcov read_message uses default timeout when None
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with stdout that returns EOF
    When ltcov I read a message with default timeout
    Then ltcov the read result should be None

  Scenario: ltcov read_message parses valid Content-Length message
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with a valid JSON-RPC response
    When ltcov I read a message with timeout 5.0
    Then ltcov the read result should have key "jsonrpc" with value "2.0"

  Scenario: ltcov read_message returns None on invalid content-length value
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with invalid content-length header
    When ltcov I read a message with timeout 5.0
    Then ltcov the read result should be None

  Scenario: ltcov read_message returns None when no content-length header
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with no content-length header
    When ltcov I read a message with timeout 5.0
    Then ltcov the read result should be None

  Scenario: ltcov read_message raises ValueError on oversized message
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with oversized content-length
    When ltcov I try to read a message
    Then ltcov the error should be a ValueError with message "too large"

  Scenario: ltcov read_message returns None on body select timeout
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with headers but body times out
    When ltcov I read a message with timeout 5.0
    Then ltcov the read result should be None

  Scenario: ltcov read_message returns None on truncated body
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with truncated body
    When ltcov I read a message with timeout 5.0
    Then ltcov the read result should be None

  Scenario: ltcov read_message returns None on invalid JSON body
    Given ltcov I create a StdioTransport for command "echo"
    And ltcov the transport has a mock process with invalid JSON body
    When ltcov I read a message with timeout 5.0
    Then ltcov the read result should be None

  # ── subprocess cleanup on failed initialization (issue #10597) ────────────

  Scenario: ltcov _process is reset to None after FileNotFoundError in start
    Given ltcov I create a StdioTransport for command "nonexistent_binary_xyz"
    And ltcov Popen is mocked to raise FileNotFoundError
    When ltcov I try to start the transport
    Then ltcov the error should be an LspError with message "not found"
    And ltcov _process must be None after start failure

  Scenario: ltcov _process is reset to None after OSError in start
    Given ltcov I create a StdioTransport for command "bad_command"
    And ltcov Popen is mocked to raise OSError with "Permission denied"
    When ltcov I try to start the transport
    Then ltcov the error should be an LspError with message "Failed to start"
    And ltcov _process must be None after start failure
