use cratedocs_mcp::{tools::DocRouter, transport::jsonrpc_frame_codec::JsonRpcFrameCodec}; use mcp_server::Router; use serde_json::{json, Value}; use tokio_util::codec::Decoder; #[tokio::test] async fn test_doc_router_initialization() { let router = DocRouter::new(); // Basic properties should be correct assert_eq!(router.name(), "rust-docs"); assert!(router.capabilities().tools.is_some()); // Tools should be available and correctly configured let tools = router.list_tools(); assert_eq!(tools.len(), 3); // Check specific tool schemas let lookup_crate_tool = tools.iter().find(|t| t.name == "lookup_crate").unwrap(); let schema: Value = serde_json::from_value(lookup_crate_tool.input_schema.clone()).unwrap(); assert_eq!(schema["type"], "object"); assert!(schema["required"].as_array().unwrap().contains(&json!("crate_name"))); } #[test] fn test_jsonrpc_codec_functionality() { let mut codec = JsonRpcFrameCodec; let json_rpc = r#"{"jsonrpc":"2.0","method":"lookup_crate","params":{"crate_name":"tokio"},"id":1}"#; let mut buffer = tokio_util::bytes::BytesMut::from(json_rpc); buffer.extend_from_slice(b"\n"); let decoded = codec.decode(&mut buffer).unwrap().unwrap(); assert_eq!(decoded, json_rpc); } #[tokio::test] async fn test_invalid_parameters_handling() { let router = DocRouter::new(); // Call lookup_crate with missing required parameter let result = router.call_tool("lookup_crate", json!({})).await; assert!(matches!(result, Err(mcp_core::ToolError::InvalidParameters(_)))); // Call with invalid tool name let result = router.call_tool("invalid_tool", json!({})).await; assert!(matches!(result, Err(mcp_core::ToolError::NotFound(_)))); } // This test requires network access #[tokio::test] #[ignore = "Requires network access"] async fn test_end_to_end_crate_lookup() { let router = DocRouter::new(); // Look up a well-known crate let result = router.call_tool( "lookup_crate", json!({ "crate_name": "serde" }) ).await; assert!(result.is_ok()); let content = result.unwrap(); assert_eq!(content.len(), 1); // The response should be HTML from docs.rs match &content[0] { mcp_core::Content::Text(text) => { assert!(text.text.contains("")); assert!(text.text.contains("serde")); }, _ => panic!("Expected text content"), } } // Test resource and prompt API error cases (since they're not implemented) #[tokio::test] async fn test_unimplemented_apis() { let router = DocRouter::new(); // Resources should return an empty list assert!(router.list_resources().is_empty()); // Reading a resource should fail let result = router.read_resource("test").await; assert!(result.is_err()); // Prompts should return an empty list assert!(router.list_prompts().is_empty()); // Getting a prompt should fail let result = router.get_prompt("test").await; assert!(result.is_err()); }