use std::net::TcpListener;
use std::time::Duration;
pub fn get_random_port() -> u16 {
let listener = TcpListener::bind("127.0.0.1:0").expect("Failed to bind to random port");
let port = listener.local_addr().unwrap().port();
drop(listener);
port
}
pub async fn wait_for_server(port: u16, timeout: Duration) -> Result<(), String> {
let start = std::time::Instant::now();
let addr = format!("127.0.0.1:{}", port);
while start.elapsed() < timeout {
match tokio::net::TcpStream::connect(&addr).await {
Ok(_) => return Ok(()),
Err(_) => tokio::time::sleep(Duration::from_millis(50)).await,
}
}
Err(format!(
"Server failed to start on port {} within {:?}",
port, timeout
))
}
pub async fn wait_for_health_check(port: u16, timeout: Duration) -> Result<(), String> {
let client = reqwest::Client::new();
let url = format!("http://127.0.0.1:{}/health", port);
let start = std::time::Instant::now();
while start.elapsed() < timeout {
match client
.get(&url)
.timeout(Duration::from_secs(1))
.send()
.await
{
Ok(response) if response.status().is_success() => return Ok(()),
_ => tokio::time::sleep(Duration::from_millis(100)).await,
}
}
Err(format!(
"Health check failed on port {} within {:?}",
port, timeout
))
}
pub fn create_test_client() -> reqwest::Client {
reqwest::Client::builder()
.timeout(Duration::from_secs(10))
.connect_timeout(Duration::from_secs(5))
.build()
.expect("Failed to create test client")
}
pub fn create_mcp_request(
id: u64,
method: &str,
params: Option<serde_json::Value>,
) -> serde_json::Value {
let mut request = serde_json::json!({
"jsonrpc": "2.0",
"id": id,
"method": method,
});
if let Some(p) = params {
request["params"] = p;
}
request
}
pub fn create_initialize_request(id: u64) -> serde_json::Value {
serde_json::json!({
"jsonrpc": "2.0",
"id": id,
"method": "initialize",
"params": {
"protocolVersion": "2024-11-05",
"capabilities": {},
"clientInfo": {
"name": "test-client",
"version": "1.0.0"
}
}
})
}
pub fn create_tools_list_request(id: u64) -> serde_json::Value {
serde_json::json!({
"jsonrpc": "2.0",
"id": id,
"method": "tools/list"
})
}
pub fn create_tools_call_request(
id: u64,
name: &str,
arguments: serde_json::Value,
) -> serde_json::Value {
serde_json::json!({
"jsonrpc": "2.0",
"id": id,
"method": "tools/call",
"params": {
"name": name,
"arguments": arguments
}
})
}
pub fn extract_sse_json(body_text: &str) -> &str {
body_text
.lines()
.find(|line| line.starts_with("data: "))
.map(|line| line.strip_prefix("data: ").unwrap_or(line))
.unwrap_or(body_text)
}
pub mod external_api_tests;
pub mod server_start_tests;
pub mod transport_tests;