use crates_docs::{AppConfig, CratesDocsServer};
use std::time::Duration;
#[tokio::test]
async fn test_http_server_actual_start() {
let port = super::get_random_port();
let mut config = AppConfig::default();
config.server.port = port;
config.server.transport_mode = "http".to_string();
config.server.host = "127.0.0.1".to_string();
let server = CratesDocsServer::new_async(config).await.unwrap();
let handle = tokio::spawn(async move { server.run_http().await });
let result = tokio::time::timeout(
Duration::from_secs(5),
super::wait_for_server(port, Duration::from_secs(3)),
)
.await;
assert!(result.is_ok(), "Server startup timed out");
assert!(result.unwrap().is_ok(), "Server failed to start");
let client = super::create_test_client();
let url = format!("http://127.0.0.1:{}/health", port);
let response = client.get(&url).send().await;
assert!(response.is_ok(), "Failed to connect to server");
let response = response.unwrap();
assert!(response.status().is_success(), "Health check failed");
handle.abort();
}
#[tokio::test]
async fn test_server_health_check() {
let port = super::get_random_port();
let mut config = AppConfig::default();
config.server.port = port;
config.server.transport_mode = "http".to_string();
config.server.host = "127.0.0.1".to_string();
let server = CratesDocsServer::new_async(config).await.unwrap();
let handle = tokio::spawn(async move { server.run_http().await });
let result = tokio::time::timeout(
Duration::from_secs(5),
super::wait_for_health_check(port, Duration::from_secs(3)),
)
.await;
assert!(result.is_ok(), "Health check timed out");
assert!(result.unwrap().is_ok(), "Health check failed");
let client = super::create_test_client();
let url = format!("http://127.0.0.1:{}/health", port);
let response = client
.get(&url)
.send()
.await
.expect("Failed to send health check request");
assert_eq!(response.status().as_u16(), 200);
handle.abort();
}
#[tokio::test]
async fn test_server_mcp_protocol() {
let port = super::get_random_port();
let mut config = AppConfig::default();
config.server.port = port;
config.server.transport_mode = "http".to_string();
config.server.host = "127.0.0.1".to_string();
let server = CratesDocsServer::new_async(config).await.unwrap();
let handle = tokio::spawn(async move { server.run_http().await });
let result = tokio::time::timeout(
Duration::from_secs(5),
super::wait_for_server(port, Duration::from_secs(3)),
)
.await;
assert!(
result.is_ok() && result.unwrap().is_ok(),
"Server failed to start"
);
let client = super::create_test_client();
let init_request = super::create_initialize_request(1);
let url = format!("http://127.0.0.1:{}/mcp", port);
let response = client
.post(&url)
.header("Accept", "application/json, text/event-stream")
.json(&init_request)
.send()
.await;
assert!(response.is_ok(), "MCP request failed");
let response = response.unwrap();
assert!(
response.status().is_success(),
"MCP request returned error status"
);
let body_text = response.text().await.expect("Failed to read response");
let json_str = super::extract_sse_json(&body_text);
let body: serde_json::Value =
serde_json::from_str(json_str).expect("Failed to parse response as JSON");
assert!(
body.get("jsonrpc").is_some(),
"Response missing jsonrpc field"
);
assert_eq!(body["jsonrpc"], "2.0", "Invalid jsonrpc version");
assert!(body.get("id").is_some(), "Response missing id field");
handle.abort();
}
#[tokio::test]
async fn test_sse_server_actual_start() {
let port = super::get_random_port();
let mut config = AppConfig::default();
config.server.port = port;
config.server.transport_mode = "sse".to_string();
config.server.host = "127.0.0.1".to_string();
config.server.enable_sse = true;
let server = CratesDocsServer::new_async(config).await.unwrap();
let handle = tokio::spawn(async move { server.run_sse().await });
let result = tokio::time::timeout(
Duration::from_secs(5),
super::wait_for_server(port, Duration::from_secs(3)),
)
.await;
assert!(result.is_ok(), "SSE server startup timed out");
assert!(result.unwrap().is_ok(), "SSE server failed to start");
let client = super::create_test_client();
let url = format!("http://127.0.0.1:{}/sse", port);
let response = client.get(&url).send().await;
assert!(response.is_ok(), "Failed to connect to SSE endpoint");
handle.abort();
}
#[tokio::test]
async fn test_hybrid_server_actual_start() {
let port = super::get_random_port();
let mut config = AppConfig::default();
config.server.port = port;
config.server.transport_mode = "hybrid".to_string();
config.server.host = "127.0.0.1".to_string();
config.server.enable_sse = true;
let server = CratesDocsServer::new_async(config).await.unwrap();
let handle = tokio::spawn(async move {
let config = crates_docs::server::transport::HyperServerConfig::hybrid();
crates_docs::server::transport::run_hyper_server(&server, config).await
});
let result = tokio::time::timeout(
Duration::from_secs(5),
super::wait_for_server(port, Duration::from_secs(3)),
)
.await;
assert!(result.is_ok(), "Hybrid server startup timed out");
assert!(result.unwrap().is_ok(), "Hybrid server failed to start");
let client = super::create_test_client();
let url = format!("http://127.0.0.1:{}/health", port);
let response = client.get(&url).send().await;
assert!(response.is_ok(), "Failed to connect to health endpoint");
assert!(
response.unwrap().status().is_success(),
"Health check failed"
);
handle.abort();
}
#[tokio::test]
async fn test_server_startup_timeout() {
let port = super::get_random_port();
let mut config = AppConfig::default();
config.server.port = port;
config.server.transport_mode = "http".to_string();
config.server.host = "127.0.0.1".to_string();
let server = CratesDocsServer::new_async(config).await.unwrap();
let handle = tokio::spawn(async move { server.run_http().await });
let client = super::create_test_client();
let url = format!("http://127.0.0.1:{}/health", port);
let result = tokio::time::timeout(Duration::from_millis(100), client.get(&url).send()).await;
match result {
Ok(Ok(_)) => {
}
Ok(Err(_)) | Err(_) => {
}
}
handle.abort();
}
#[tokio::test]
async fn test_multiple_servers_different_ports() {
let port1 = super::get_random_port();
let port2 = super::get_random_port();
assert_ne!(port1, port2, "Random ports should be different");
let mut config1 = AppConfig::default();
config1.server.port = port1;
config1.server.transport_mode = "http".to_string();
config1.server.host = "127.0.0.1".to_string();
let mut config2 = AppConfig::default();
config2.server.port = port2;
config2.server.transport_mode = "http".to_string();
config2.server.host = "127.0.0.1".to_string();
let server1 = CratesDocsServer::new_async(config1).await.unwrap();
let server2 = CratesDocsServer::new_async(config2).await.unwrap();
let handle1 = tokio::spawn(async move { server1.run_http().await });
let handle2 = tokio::spawn(async move { server2.run_http().await });
let result1 = tokio::time::timeout(
Duration::from_secs(5),
super::wait_for_server(port1, Duration::from_secs(3)),
)
.await;
let result2 = tokio::time::timeout(
Duration::from_secs(5),
super::wait_for_server(port2, Duration::from_secs(3)),
)
.await;
assert!(
result1.is_ok() && result1.unwrap().is_ok(),
"Server 1 failed to start"
);
assert!(
result2.is_ok() && result2.unwrap().is_ok(),
"Server 2 failed to start"
);
let client = super::create_test_client();
let url1 = format!("http://127.0.0.1:{}/health", port1);
let url2 = format!("http://127.0.0.1:{}/health", port2);
let response1 = client.get(&url1).send().await;
let response2 = client.get(&url2).send().await;
assert!(
response1.is_ok() && response1.unwrap().status().is_success(),
"Server 1 health check failed"
);
assert!(
response2.is_ok() && response2.unwrap().status().is_success(),
"Server 2 health check failed"
);
handle1.abort();
handle2.abort();
}