use nika::ast::InvokeParams;
use nika::error::NikaError;
use nika::mcp::McpClient;
use serde_json::json;
async fn execute_invoke(
invoke: &InvokeParams,
client: &McpClient,
) -> Result<serde_json::Value, NikaError> {
invoke.validate()?;
if let Some(tool) = &invoke.tool {
let params = invoke.params.clone().unwrap_or(serde_json::Value::Null);
let result = client.call_tool(tool, params).await?;
let text = result.text();
match serde_json::from_str(&text) {
Ok(v) => Ok(v),
Err(_) => Ok(serde_json::Value::String(text)),
}
} else if let Some(resource) = &invoke.resource {
let content = client.read_resource(resource).await?;
match content.text {
Some(text) => match serde_json::from_str(&text) {
Ok(v) => Ok(v),
Err(_) => Ok(serde_json::Value::String(text)),
},
None => Ok(serde_json::Value::Null),
}
} else {
unreachable!("validate() ensures tool or resource is set")
}
}
#[tokio::test]
async fn test_invoke_execution_tool_call() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: Some("novanet_generate".to_string()),
params: Some(json!({"mode": "block", "entity": "qr-code"})),
resource: None,
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(
result.is_ok(),
"Tool call should succeed: {:?}",
result.err()
);
let value = result.unwrap();
assert!(value.is_object(), "Result should be a JSON object: {value}");
}
#[tokio::test]
async fn test_invoke_execution_tool_call_minimal() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: Some("novanet_describe".to_string()),
params: None,
resource: None,
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(result.is_ok(), "Tool call without params should succeed");
let value = result.unwrap();
assert!(
value.get("nodes").is_some() || value.get("arcs").is_some(),
"Should contain graph stats: {value}"
);
}
#[tokio::test]
async fn test_invoke_execution_tool_call_with_params() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: Some("novanet_generate".to_string()),
params: Some(json!({
"entity": "qr-code",
"locale": "fr-FR",
"forms": ["text", "title"]
})),
resource: None,
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(result.is_ok());
let value = result.unwrap();
assert_eq!(value.get("entity"), Some(&json!("qr-code")));
assert_eq!(value.get("locale"), Some(&json!("fr-FR")));
}
#[tokio::test]
async fn test_invoke_execution_resource_read() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: None,
params: None,
resource: Some("entity://qr-code".to_string()),
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(
result.is_ok(),
"Resource read should succeed: {:?}",
result.err()
);
let value = result.unwrap();
assert!(value.is_object(), "Result should be a JSON object: {value}");
}
#[tokio::test]
async fn test_invoke_execution_resource_read_neo4j_uri() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: None,
params: None,
resource: Some("neo4j://entity/qr-code".to_string()),
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(result.is_ok());
let value = result.unwrap();
assert_eq!(value.get("id"), Some(&json!("qr-code")));
assert_eq!(value.get("type"), Some(&json!("Entity")));
}
#[tokio::test]
async fn test_invoke_execution_fails_with_both_tool_and_resource() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: Some("novanet_generate".to_string()),
params: None,
resource: Some("entity://qr-code".to_string()),
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(
result.is_err(),
"Should fail when both tool and resource are set"
);
match result.unwrap_err() {
NikaError::ValidationError { reason } => {
assert!(
reason.contains("mutually exclusive"),
"Error should mention mutual exclusivity: {reason}"
);
}
err => panic!("Expected ValidationError, got: {err:?}"),
}
}
#[tokio::test]
async fn test_invoke_execution_fails_with_neither_tool_nor_resource() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: None,
params: None,
resource: None,
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(
result.is_err(),
"Should fail when neither tool nor resource is set"
);
match result.unwrap_err() {
NikaError::ValidationError { reason } => {
assert!(
reason.contains("must be specified"),
"Error should mention requirement: {reason}"
);
}
err => panic!("Expected ValidationError, got: {err:?}"),
}
}
#[tokio::test]
async fn test_invoke_execution_fails_when_not_connected() {
let config = nika::mcp::McpConfig::new("novanet", "echo");
let client = McpClient::new(config).unwrap();
assert!(!client.is_connected());
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: Some("novanet_generate".to_string()),
params: None,
resource: None,
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(result.is_err(), "Should fail when client not connected");
match result.unwrap_err() {
NikaError::McpNotConnected { name } => {
assert_eq!(name, "novanet");
}
err => panic!("Expected McpNotConnected, got: {err:?}"),
}
}
#[tokio::test]
async fn test_invoke_execution_unknown_tool_returns_generic_response() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: Some("unknown_tool".to_string()),
params: Some(json!({"key": "value"})),
resource: None,
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(result.is_ok(), "Unknown tool should return generic success");
let value = result.unwrap();
assert_eq!(value.get("tool"), Some(&json!("unknown_tool")));
assert_eq!(value.get("status"), Some(&json!("success")));
}
#[tokio::test]
async fn test_invoke_execution_with_empty_params() {
let client = McpClient::mock("novanet");
let invoke = InvokeParams {
mcp: Some("novanet".to_string()),
tool: Some("novanet_describe".to_string()),
params: Some(json!({})),
resource: None,
timeout: None,
};
let result = execute_invoke(&invoke, &client).await;
assert!(result.is_ok(), "Empty params should be valid");
}