test: Implemented tests for the MCP server lifecycle

This commit is contained in:
2026-05-01 10:27:49 -06:00
parent 975484cc2b
commit 1df6114ff3
6 changed files with 1001 additions and 58 deletions
+200
View File
@@ -137,3 +137,203 @@ impl McpFactory {
Ok(handle)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::mcp::{JsonField, McpServer, McpTransportType};
use std::collections::HashMap;
fn stdio_spec(
command: &str,
args: Option<Vec<String>>,
env: Option<HashMap<String, JsonField>>,
) -> McpServer {
McpServer {
transport_type: McpTransportType::Stdio,
command: Some(command.to_string()),
args,
env,
cwd: None,
url: None,
headers: None,
}
}
fn remote_spec(
transport: McpTransportType,
url: &str,
headers: Option<HashMap<String, String>>,
) -> McpServer {
McpServer {
transport_type: transport,
command: None,
args: None,
env: None,
cwd: None,
url: Some(url.to_string()),
headers,
}
}
#[test]
fn key_from_stdio_spec_captures_command_args_env() {
let mut env = HashMap::new();
env.insert("TOKEN".into(), JsonField::Str("abc".into()));
let spec = stdio_spec("npx", Some(vec!["-y".into(), "server".into()]), Some(env));
let key = McpServerKey::from_spec("my-server", &spec);
assert_eq!(key.name, "my-server");
match &key.transport {
McpTransportKey::Stdio { command, args, env } => {
assert_eq!(command, "npx");
assert_eq!(args, &["-y", "server"]);
assert_eq!(env, &[("TOKEN".to_string(), "abc".to_string())]);
}
_ => panic!("expected Stdio transport key"),
}
}
#[test]
fn key_from_stdio_spec_sorts_args_and_env() {
let mut env = HashMap::new();
env.insert("Z_VAR".into(), JsonField::Str("z".into()));
env.insert("A_VAR".into(), JsonField::Int(42));
let spec = stdio_spec(
"cmd",
Some(vec!["charlie".into(), "alpha".into(), "bravo".into()]),
Some(env),
);
let key = McpServerKey::from_spec("s", &spec);
match &key.transport {
McpTransportKey::Stdio { args, env, .. } => {
assert_eq!(args, &["alpha", "bravo", "charlie"]);
assert_eq!(env[0].0, "A_VAR");
assert_eq!(env[0].1, "42");
assert_eq!(env[1].0, "Z_VAR");
assert_eq!(env[1].1, "z");
}
_ => panic!("expected Stdio"),
}
}
#[test]
fn key_from_stdio_spec_defaults_empty_when_none() {
let spec = stdio_spec("echo", None, None);
let key = McpServerKey::from_spec("bare", &spec);
match &key.transport {
McpTransportKey::Stdio { command, args, env } => {
assert_eq!(command, "echo");
assert!(args.is_empty());
assert!(env.is_empty());
}
_ => panic!("expected Stdio"),
}
}
#[test]
fn key_from_remote_http_spec() {
let spec = remote_spec(McpTransportType::Http, "http://localhost:8080", None);
let key = McpServerKey::from_spec("http-srv", &spec);
assert_eq!(key.name, "http-srv");
match &key.transport {
McpTransportKey::Remote {
transport_type,
url,
headers,
} => {
assert_eq!(*transport_type, McpTransportType::Http);
assert_eq!(url, "http://localhost:8080");
assert!(headers.is_empty());
}
_ => panic!("expected Remote"),
}
}
#[test]
fn key_from_remote_sse_spec_with_sorted_headers() {
let mut hdrs = HashMap::new();
hdrs.insert("Z-Key".into(), "z-val".into());
hdrs.insert("A-Key".into(), "a-val".into());
let spec = remote_spec(McpTransportType::Sse, "http://sse.example.com", Some(hdrs));
let key = McpServerKey::from_spec("sse-srv", &spec);
match &key.transport {
McpTransportKey::Remote { headers, .. } => {
assert_eq!(headers[0], ("A-Key".to_string(), "a-val".to_string()));
assert_eq!(headers[1], ("Z-Key".to_string(), "z-val".to_string()));
}
_ => panic!("expected Remote"),
}
}
#[test]
fn key_equality_same_spec_produces_equal_keys() {
let spec = stdio_spec("npx", Some(vec!["a".into()]), None);
let k1 = McpServerKey::from_spec("s", &spec);
let k2 = McpServerKey::from_spec("s", &spec);
assert_eq!(k1, k2);
}
#[test]
fn key_inequality_different_names() {
let spec = stdio_spec("npx", None, None);
let k1 = McpServerKey::from_spec("a", &spec);
let k2 = McpServerKey::from_spec("b", &spec);
assert_ne!(k1, k2);
}
#[test]
fn key_inequality_different_commands() {
let s1 = stdio_spec("npx", None, None);
let s2 = stdio_spec("node", None, None);
let k1 = McpServerKey::from_spec("s", &s1);
let k2 = McpServerKey::from_spec("s", &s2);
assert_ne!(k1, k2);
}
#[test]
fn key_env_bool_and_int_coerce_to_string() {
let mut env = HashMap::new();
env.insert("FLAG".into(), JsonField::Bool(true));
env.insert("PORT".into(), JsonField::Int(3000));
let spec = stdio_spec("cmd", None, Some(env));
let key = McpServerKey::from_spec("s", &spec);
match &key.transport {
McpTransportKey::Stdio { env, .. } => {
let map: HashMap<&str, &str> =
env.iter().map(|(k, v)| (k.as_str(), v.as_str())).collect();
assert_eq!(map["FLAG"], "true");
assert_eq!(map["PORT"], "3000");
}
_ => panic!("expected Stdio"),
}
}
#[test]
fn factory_try_get_active_returns_none_when_empty() {
let factory = McpFactory::default();
let spec = stdio_spec("cmd", None, None);
let key = McpServerKey::from_spec("s", &spec);
assert!(factory.try_get_active(&key).is_none());
}
#[test]
fn factory_try_get_active_returns_none_for_unknown_key() {
let factory = McpFactory::default();
let spec = stdio_spec("cmd", None, None);
let key = McpServerKey::from_spec("s", &spec);
assert!(factory.try_get_active(&key).is_none());
}
#[test]
fn factory_default_has_empty_active_map() {
let factory = McpFactory::default();
let map = factory.active.lock();
assert!(map.is_empty());
}
}