use std::process::Stdio; use std::time::{Duration, Instant}; use tokio::process::Command; fn get_stub_exe() -> std::path::PathBuf { let manifest_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR")); let debug_dir = manifest_dir.parent().unwrap().join("target").join("debug"); debug_dir.join(format!("mcp-memory-stub{}", std::env::consts::EXE_SUFFIX)) } #[tokio::test] async fn test_stub_connection_refused() { let _ = std::process::Command::new("cargo") .arg("build") .arg("--bin") .arg("mcp-memory-stub") .status(); let target = "http://127.0.0.1:49999"; let start = Instant::now(); let mut child = Command::new(get_stub_exe()) .arg("--target") .arg(target) .stdin(Stdio::null()) // close stdin immediately to simulate EOF .spawn() .expect("Failed to execute stub"); let res = tokio::time::timeout(Duration::from_secs(5), child.wait()).await; let elapsed = start.elapsed(); assert!( res.is_ok(), "Stub hung on connection refused! Took {:?}", elapsed ); } #[tokio::test] async fn test_stub_handles_eof_cleanly() { let _ = std::process::Command::new("cargo") .arg("build") .arg("--bin") .arg("mcp-memory-stub") .status(); let target = "http://127.0.0.1:49998"; let mut child = Command::new(get_stub_exe()) .arg("--target") .arg(target) .stdin(Stdio::piped()) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .expect("Failed to execute stub"); if let Some(mut stdin) = child.stdin.take() { use tokio::io::AsyncWriteExt; let msg = "Content-Length: 51\r\n\r\n{\"jsonrpc\":\"2.0\",\"method\":\"tools/list\",\"params\":{},\"id\":1}"; stdin.write_all(msg.as_bytes()).await.unwrap(); } // stdin dropped here let start = Instant::now(); let res = tokio::time::timeout(Duration::from_secs(5), child.wait()).await; let elapsed = start.elapsed(); assert!(res.is_ok(), "Stub hung after EOF! Took {:?}", elapsed); } #[tokio::test] async fn test_stub_sse_fallback_failure() { let _ = std::process::Command::new("cargo") .arg("build") .arg("--bin") .arg("mcp-memory-stub") .status(); let listener = tokio::net::TcpListener::bind("127.0.0.1:0").await.unwrap(); let local_addr = listener.local_addr().unwrap(); let target = format!("http://127.0.0.1:{}", local_addr.port()); tokio::spawn(async move { while let Ok((mut socket, _)) = listener.accept().await { use tokio::io::AsyncReadExt; let mut buf = [0; 1024]; let _ = socket.read(&mut buf).await; drop(socket); } }); let start = Instant::now(); let mut child = Command::new(get_stub_exe()) .arg("--target") .arg(target) .stdin(Stdio::null()) .spawn() .expect("Failed to execute stub"); let res = tokio::time::timeout(Duration::from_secs(5), child.wait()).await; let elapsed = start.elapsed(); assert!( res.is_ok(), "Stub hung on fallback failure! Took {:?}", elapsed ); }