use anyhow::Result;
use serde_json::json;
use super::mcp_client::McpClient;
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_initialize_handshake() -> Result<()> {
let mut client = McpClient::spawn()?;
let response = client.initialize()?;
assert!(
response.get("result").is_some(),
"Response should have 'result' field"
);
let result = &response["result"];
assert_eq!(
result["protocolVersion"], "2024-11-05",
"Protocol version should match"
);
assert!(
result["capabilities"]["tools"].is_object(),
"Should expose tools capability"
);
assert_eq!(
result["serverInfo"]["name"], "mcpls",
"Server name should be 'mcpls'"
);
Ok(())
}
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_list_tools() -> Result<()> {
let mut client = McpClient::spawn()?;
client.initialize()?;
let response = client.list_tools()?;
let tools = response["result"]["tools"]
.as_array()
.unwrap_or_else(|| panic!("tools should be an array"));
assert_eq!(tools.len(), 8, "Should have exactly 8 tools");
let tool_names: Vec<&str> = tools.iter().filter_map(|t| t["name"].as_str()).collect();
assert!(
tool_names.contains(&"get_hover"),
"Should have get_hover tool"
);
assert!(
tool_names.contains(&"get_definition"),
"Should have get_definition tool"
);
assert!(
tool_names.contains(&"get_references"),
"Should have get_references tool"
);
assert!(
tool_names.contains(&"get_diagnostics"),
"Should have get_diagnostics tool"
);
assert!(
tool_names.contains(&"rename_symbol"),
"Should have rename_symbol tool"
);
assert!(
tool_names.contains(&"get_completions"),
"Should have get_completions tool"
);
assert!(
tool_names.contains(&"get_document_symbols"),
"Should have get_document_symbols tool"
);
assert!(
tool_names.contains(&"format_document"),
"Should have format_document tool"
);
Ok(())
}
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_tool_schemas() -> Result<()> {
let mut client = McpClient::spawn()?;
client.initialize()?;
let response = client.list_tools()?;
let tools = response["result"]["tools"]
.as_array()
.unwrap_or_else(|| panic!("tools should be an array"));
for tool in tools {
let tool_name = tool["name"]
.as_str()
.unwrap_or_else(|| panic!("Tool should have name field"));
assert!(
tool["name"].is_string(),
"Tool '{tool_name}' should have name as string"
);
assert!(
tool["description"].is_string(),
"Tool '{tool_name}' should have description as string"
);
assert!(
tool["inputSchema"].is_object(),
"Tool '{tool_name}' should have inputSchema as object"
);
let schema = &tool["inputSchema"];
assert_eq!(
schema["type"], "object",
"Tool '{tool_name}' schema type should be 'object'"
);
assert!(
schema["properties"].is_object(),
"Tool '{tool_name}' schema should have properties object"
);
}
Ok(())
}
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_invalid_tool_call() -> Result<()> {
let mut client = McpClient::spawn()?;
client.initialize()?;
let result = client.call_tool("non_existent_tool", &json!({}));
assert!(result.is_err(), "Should return error for non-existent tool");
if let Err(err) = result {
let error_msg = format!("{err:?}");
assert!(
error_msg.contains("error") || error_msg.contains("Error"),
"Error message should indicate failure"
);
}
Ok(())
}
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_tool_call_missing_params() -> Result<()> {
let mut client = McpClient::spawn()?;
client.initialize()?;
let result = client.call_tool("get_hover", &json!({}));
assert!(
result.is_err(),
"Should return error for missing required parameters"
);
if let Err(err) = result {
let error_msg = format!("{err:?}");
assert!(
error_msg.contains("error") || error_msg.contains("Error"),
"Error message should indicate parameter validation failure"
);
}
Ok(())
}
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_tool_call_invalid_file() -> Result<()> {
let mut client = McpClient::spawn()?;
client.initialize()?;
let result = client.call_tool(
"get_hover",
&json!({
"file_path": "/nonexistent/path/to/file.rs",
"line": 1,
"character": 1
}),
);
assert!(result.is_err(), "Should return error for non-existent file");
Ok(())
}
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_tool_call_invalid_position() -> Result<()> {
use std::fs;
use tempfile::TempDir;
let mut client = McpClient::spawn()?;
client.initialize()?;
let temp_dir = TempDir::new()?;
let test_file = temp_dir.path().join("test.rs");
fs::write(&test_file, "fn main() {}\n")?;
let result = client.call_tool(
"get_definition",
&json!({
"file_path": test_file.to_string_lossy(),
"line": 9999,
"character": 9999
}),
);
if let Ok(response) = result {
let result_field = &response["result"];
assert!(
result_field.is_null() || result_field.is_array() || result_field.is_object(),
"Should return null or empty result for invalid position"
);
}
Ok(())
}
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_complete_workflow() -> Result<()> {
let mut client = McpClient::spawn()?;
let init_response = client.initialize()?;
assert!(init_response.get("result").is_some());
let list_response = client.list_tools()?;
let tools = list_response["result"]["tools"]
.as_array()
.unwrap_or_else(|| panic!("tools should be an array"));
assert!(!tools.is_empty(), "Should have tools available");
let _result = client.call_tool("get_diagnostics", &json!({"file_path": "test.rs"}));
Ok(())
}
#[test]
#[ignore = "Requires mcpls binary built"]
fn test_e2e_multiple_requests() -> Result<()> {
let mut client = McpClient::spawn()?;
let response1 = client.initialize()?;
assert!(response1.get("result").is_some());
let response2 = client.list_tools()?;
assert!(response2.get("result").is_some());
let response3 = client.list_tools()?;
assert!(response3.get("result").is_some());
assert_ne!(
response1.get("id"),
response2.get("id"),
"Different requests should have different IDs"
);
assert_ne!(
response2.get("id"),
response3.get("id"),
"Different requests should have different IDs"
);
Ok(())
}