docs: add openspec specs for integration tests changes

This commit is contained in:
LIghtJUNction
2026-03-24 16:58:39 +08:00
parent 40a68c755e
commit c0010de837
4 changed files with 273 additions and 0 deletions

View File

@@ -0,0 +1,62 @@
# Internal Integration Tests Specification
## ADDED Requirements
### Requirement: MCP client integration with echo server
The MCP client SHALL be able to connect to a real MCP echo server fixture and perform tool operations.
#### Scenario: MCP client initialization
- **WHEN** McpClient is instantiated
- **THEN** client exists with `connected = False`
#### Scenario: MCP client connect without config is noop
- **WHEN** `connect()` is called without server configuration
- **THEN** client remains in disconnected state
#### Scenario: MCP client connects to echo server (deferred)
- **WHEN** MCP client attempts to connect to echo MCP server
- **THEN** connection is established and verified
- **NOTE**: Test skipped - MCP ClientSession.initialize() protocol handshake is complex
#### Scenario: MCP client lists tools from server (deferred)
- **WHEN** `list_tools()` is called on connected client
- **THEN** returns list of available tools from server
- **NOTE**: Test skipped due to protocol complexity
#### Scenario: MCP client calls echo tool (deferred)
- **WHEN** `call_tool("echo", {"message": "test"})` is called
- **THEN** returns echoed result
- **NOTE**: Test skipped due to protocol complexity
### Requirement: ACP client integration with echo server
The ACP client SHALL be able to connect to a real ACP echo server fixture over TCP and Unix sockets.
#### Scenario: ACP client initial state
- **WHEN** AstrbotAcpClient is instantiated
- **THEN** `connected = False`, `_reader = None`, `_writer = None`
#### Scenario: ACP client connects to TCP server
- **WHEN** `connect_to_server(host, port)` is called with TCP echo server
- **THEN** client connects successfully with `connected = True`
- **AND** `_reader` and `_writer` are set
#### Scenario: ACP client connects to Unix socket
- **WHEN** `connect_to_unix_socket(socket_path)` is called with Unix socket echo server
- **THEN** client connects successfully with `connected = True`
- **AND** `_reader` and `_writer` are set
### Requirement: Integration test fixtures
The integration test fixtures SHALL provide real protocol servers for testing.
#### Scenario: Echo MCP server handles stdio protocol
- **WHEN** MCP server receives JSON-RPC request on stdin
- **THEN** responds with proper Content-Length headers on stdout
- **AND** handles `initialize`, `tools/list`, `tools/call` methods
#### Scenario: Echo ACP server handles stream protocol
- **WHEN** ACP server receives request on TCP/Unix socket
- **THEN** responds with JSON-RPC responses
- **AND** handles `initialize`, `echo`, and `{server}/{tool}` methods

View File

@@ -0,0 +1,2 @@
schema: spec-driven
created: 2026-03-24

View File

@@ -0,0 +1,184 @@
# Design: LSP Integration Tests
## LSP Echo Server Fixture
Create a simple stdio-based LSP server for testing:
```python
# tests/integration/fixtures/echo_lsp_server.py
"""
Simple LSP server that echoes back requests.
Used for testing the LSP client.
"""
import json
import sys
def main():
while True:
line = sys.stdin.readline()
if not line:
break
request = json.loads(line)
msg_id = request.get("id")
method = request.get("method")
# Handle initialize
if method == "initialize":
print(json.dumps({
"jsonrpc": "2.0",
"id": msg_id,
"result": {
"capabilities": {"textDocumentSync": 1}
}
}))
sys.stdout.flush()
# Handle initialized notification (no response)
elif method == "initialized":
continue
# Handle shutdown
elif method == "shutdown":
print(json.dumps({
"jsonrpc": "2.0",
"id": msg_id,
"result": None
}))
sys.stdout.flush()
# Echo any textDocument/didOpen
elif method == "textDocument/didOpen":
print(json.dumps({
"jsonrpc": "2.0",
"id": msg_id,
"result": None
}))
sys.stdout.flush()
# Echo any request
elif msg_id is not None:
print(json.dumps({
"jsonrpc": "2.0",
"id": msg_id,
"result": {"echo": True}
}))
sys.stdout.flush()
```
## LSP Client Integration Tests
```python
# tests/integration/test_lsp_integration.py
"""
LSP Integration Tests.
Tests the LSP client against a real LSP server fixture.
"""
from __future__ import annotations
import os
import pytest
from astrbot._internal.protocols.lsp.client import AstrbotLspClient
@pytest.mark.anyio
async def test_lsp_client_initialization():
"""Test LSP client can be initialized."""
client = AstrbotLspClient()
assert client is not None
assert not client.connected
@pytest.mark.anyio
async def test_lsp_client_connect_to_echo_server():
"""Test LSP client can connect to echo LSP server."""
client = AstrbotLspClient()
test_dir = os.path.dirname(os.path.abspath(__file__))
server_path = os.path.join(test_dir, "fixtures", "echo_lsp_server.py")
await client.connect_to_server(
command=["python", server_path],
workspace_uri="file:///tmp"
)
try:
assert client.connected
finally:
await client.shutdown()
@pytest.mark.anyio
async def test_lsp_client_send_request():
"""Test LSP client can send a request and receive response."""
client = AstrbotLspClient()
test_dir = os.path.dirname(os.path.abspath(__file__))
server_path = os.path.join(test_dir, "fixtures", "echo_lsp_server.py")
await client.connect_to_server(
command=["python", server_path],
workspace_uri="file:///tmp"
)
try:
assert client.connected
# Send a custom request
result = await client.send_request("custom/echo", {"message": "test"})
assert result is not None
finally:
await client.shutdown()
@pytest.mark.anyio
async def test_lsp_client_send_notification():
"""Test LSP client can send a notification (no response)."""
client = AstrbotLspClient()
test_dir = os.path.dirname(os.path.abspath(__file__))
server_path = os.path.join(test_dir, "fixtures", "echo_lsp_server.py")
await client.connect_to_server(
command=["python", server_path],
workspace_uri="file:///tmp"
)
try:
assert client.connected
# Send a notification (should not raise)
await client.send_notification("custom/notify", {"data": "test"})
finally:
await client.shutdown()
```
## Test Fixtures Location
```
tests/
├── integration/
│ ├── fixtures/
│ │ ├── echo_lsp_server.py # New
│ │ ├── echo_mcp_server.py # Existing
│ │ └── echo_acp_server.py # Existing
│ ├── test_lsp_integration.py # New
│ ├── test_mcp_integration.py # Existing
│ └── test_acp_integration.py # Existing
```
## Running Tests
```bash
# Run all integration tests
uv run pytest tests/integration/ -v
# Run LSP integration only
uv run pytest tests/integration/test_lsp_integration.py -v
```

View File

@@ -0,0 +1,25 @@
# LSP Integration Tests
## Problem
The `_internal` architecture includes an LSP (Language Server Protocol) client implementation at `astrbot/_internal/protocols/lsp/client.py`, but there are no integration tests validating that it can connect to and communicate with a real LSP server. The orchestrator manages an `AstrbotLspClient` instance (`self.lsp`), but this functionality remains untested.
## Solution
Create integration tests that:
1. Build an LSP echo server fixture (stdio-based)
2. Test the LSP client can connect, send requests, and receive responses
3. Verify the JSON-RPC 2.0 protocol communication works end-to-end
## Why This Matters
- Architecture claims must be validated through tests
- LSP functionality is used by the orchestrator for language intelligence
- Integration tests catch regressions that unit tests miss
- Validates the JSON-RPC 2.0 message framing and async communication
## Scope
- Create `tests/integration/fixtures/echo_lsp_server.py`
- Create `tests/integration/test_lsp_integration.py`
- Ensure tests can run in CI