use anyhow::Result;
use serde_json::json;
use std::process::Stdio;
use tempfile::TempDir;
use tokio::io::{AsyncBufReadExt, AsyncWriteExt, BufReader};
use tokio::process::Command;
#[tokio::test]
#[ignore = "RMCP stdio transport has initialization issues"]
async fn test_rmcp_server_initialization() -> Result<()> {
let mut child = Command::new("./target/release/context-creator")
.arg("--rmcp")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?;
let stdin = child.stdin.take().unwrap();
let stdout = child.stdout.take().unwrap();
let mut reader = BufReader::new(stdout);
let mut writer = stdin;
let init_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "0.1.0",
"capabilities": {}
}
});
writer
.write_all(format!("{init_request}\n").as_bytes())
.await?;
writer.flush().await?;
let mut line = String::new();
reader.read_line(&mut line).await?;
let response: serde_json::Value = serde_json::from_str(&line)?;
assert_eq!(response["jsonrpc"], "2.0");
assert_eq!(response["id"], 1);
assert!(response["result"]["capabilities"].is_object());
child.kill().await?;
Ok(())
}
#[tokio::test]
#[ignore = "RMCP stdio transport has initialization issues"]
async fn test_analyze_local_tool() -> Result<()> {
let temp_dir = TempDir::new()?;
std::fs::write(
temp_dir.path().join("test.rs"),
r#"
fn main() {
println!("Hello, world!");
}
fn add(a: i32, b: i32) -> i32 {
a + b
}
"#,
)?;
let mut child = Command::new("./target/release/context-creator")
.arg("--rmcp")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?;
let stdin = child.stdin.take().unwrap();
let stdout = child.stdout.take().unwrap();
let mut reader = BufReader::new(stdout);
let mut writer = stdin;
let init_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "0.1.0",
"capabilities": {}
}
});
writer
.write_all(format!("{init_request}\n").as_bytes())
.await?;
writer.flush().await?;
let mut line = String::new();
reader.read_line(&mut line).await?;
line.clear();
let tool_request = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "analyze_local",
"arguments": {
"prompt": "What does the add function do?",
"path": temp_dir.path(),
"include_patterns": [],
"ignore_patterns": [],
"include_imports": false,
"max_tokens": 1000,
"llm_tool": "gemini",
"include_context": false
}
}
});
writer
.write_all(format!("{tool_request}\n").as_bytes())
.await?;
writer.flush().await?;
reader.read_line(&mut line).await?;
let response: serde_json::Value = serde_json::from_str(&line)?;
assert!(response["result"]["answer"].is_string());
assert!(response["result"]["file_count"].is_number());
assert!(response["result"]["token_count"].is_number());
child.kill().await?;
Ok(())
}
#[tokio::test]
#[ignore = "RMCP stdio transport has initialization issues"]
async fn test_list_tools() -> Result<()> {
let mut child = Command::new("./target/release/context-creator")
.arg("--rmcp")
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.stderr(Stdio::null())
.spawn()?;
let stdin = child.stdin.take().unwrap();
let stdout = child.stdout.take().unwrap();
let mut reader = BufReader::new(stdout);
let mut writer = stdin;
let init_request = json!({
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": {
"protocolVersion": "0.1.0",
"capabilities": {}
}
});
writer
.write_all(format!("{init_request}\n").as_bytes())
.await?;
writer.flush().await?;
let mut line = String::new();
reader.read_line(&mut line).await?;
line.clear();
let list_tools_request = json!({
"jsonrpc": "2.0",
"id": 2,
"method": "tools/list",
"params": {}
});
writer
.write_all(format!("{list_tools_request}\n").as_bytes())
.await?;
writer.flush().await?;
reader.read_line(&mut line).await?;
let response: serde_json::Value = serde_json::from_str(&line)?;
let tools = response["result"]["tools"].as_array().unwrap();
assert!(tools.len() >= 6);
let tool_names: Vec<&str> = tools.iter().map(|t| t["name"].as_str().unwrap()).collect();
assert!(tool_names.contains(&"analyze_local"));
assert!(tool_names.contains(&"analyze_remote"));
assert!(tool_names.contains(&"file_metadata"));
assert!(tool_names.contains(&"search"));
assert!(tool_names.contains(&"diff"));
assert!(tool_names.contains(&"semantic_search"));
child.kill().await?;
Ok(())
}