#[cfg(feature = "tools")]
use rustchain::core::tools::ToolRegistry;
use rustchain::core::RuntimeContext;
#[cfg(feature = "tools")]
use rustchain::tools::{
CommandTool, CsvLoaderTool, FileCreateTool, HtmlLoaderTool, HttpTool, JsonYamlLoaderTool,
ToolCall, ToolManager,
};
use serde_json::json;
use std::fs;
use tempfile::TempDir;
mod tool_manager_tests {
use super::*;
#[tokio::test]
async fn test_file_create_tool() {
let temp_dir = TempDir::new().unwrap();
let test_file = temp_dir.path().join("test.txt");
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(FileCreateTool));
let call = ToolCall {
tool_name: "create_file".to_string(),
parameters: json!({
"path": test_file.to_str().unwrap(),
"content": "Hello, functional test!"
}),
timeout_ms: Some(5000),
metadata: Default::default(),
continue_on_error: Some(false),
};
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(
result.is_ok(),
"Tool execution should succeed but file creation should be blocked"
);
let tool_result = result.unwrap();
assert!(
!tool_result.success,
"File creation should be blocked by sandbox"
);
assert_eq!(
tool_result.error.as_ref().unwrap(),
"File creation blocked by sandbox"
);
}
#[tokio::test]
#[ignore] async fn test_command_tool() {
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(CommandTool));
let call = ToolCall {
tool_name: "command".to_string(),
parameters: json!({
"command": "echo",
"args": ["hello world"]
}),
timeout_ms: Some(5000),
metadata: Default::default(),
continue_on_error: Some(false),
};
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(result.is_ok(), "CommandTool should succeed");
let tool_result = result.unwrap();
assert!(tool_result.success);
assert!(
matches!(&tool_result.output, serde_json::Value::String(output) if output.contains("hello world")),
"Expected string output from CommandTool containing 'hello world'"
);
}
#[tokio::test]
#[ignore] async fn test_http_tool() {
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(HttpTool));
let call = ToolCall {
tool_name: "http".to_string(),
parameters: json!({
"url": "https://httpbin.org/get",
"method": "GET"
}),
timeout_ms: Some(10000),
metadata: Default::default(),
continue_on_error: Some(false),
};
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(result.is_ok(), "HttpTool should succeed for httpbin.org");
let tool_result = result.unwrap();
assert!(tool_result.success);
}
#[tokio::test]
async fn test_csv_loader_tool() {
let temp_dir = TempDir::new().unwrap();
let csv_file = temp_dir.path().join("test.csv");
let csv_content = "name,age,city\nAlice,30,New York\nBob,25,London\n";
fs::write(&csv_file, csv_content).unwrap();
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(CsvLoaderTool));
let call = ToolCall {
tool_name: "csv_loader".to_string(),
parameters: json!({
"file_path": csv_file.to_str().unwrap()
}),
timeout_ms: Some(5000),
metadata: Default::default(),
continue_on_error: Some(false),
};
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(result.is_ok(), "CsvLoaderTool should succeed");
let tool_result = result.unwrap();
assert!(tool_result.success);
if let serde_json::Value::Object(data) = &tool_result.output {
assert!(data.contains_key("text"), "Should contain text field");
assert!(
data.contains_key("metadata"),
"Should contain metadata field"
);
assert!(data.contains_key("source"), "Should contain source field");
if let Some(text) = data.get("text").and_then(|t| t.as_str()) {
assert!(text.contains("Alice"), "Should contain Alice data");
assert!(text.contains("Bob"), "Should contain Bob data");
assert!(text.contains("name"), "Should contain headers");
}
} else {
assert!(
matches!(&tool_result.output, serde_json::Value::Object(_)),
"Expected object output from CsvLoaderTool, got: {:?}",
tool_result.output
);
}
}
#[tokio::test]
async fn test_json_yaml_loader_tool() {
let temp_dir = TempDir::new().unwrap();
let json_file = temp_dir.path().join("test.json");
let json_content = r#"{"name": "test", "value": 42, "active": true}"#;
fs::write(&json_file, json_content).unwrap();
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(JsonYamlLoaderTool));
let call = ToolCall {
tool_name: "json_yaml_loader".to_string(),
parameters: json!({
"file_path": json_file.to_str().unwrap()
}),
timeout_ms: Some(5000),
metadata: Default::default(),
continue_on_error: Some(false),
};
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(result.is_ok(), "JsonYamlLoaderTool should succeed");
let tool_result = result.unwrap();
assert!(tool_result.success);
if let serde_json::Value::Object(data) = &tool_result.output {
assert!(data.contains_key("text"), "Should contain text field");
if let Some(text) = data.get("text").and_then(|t| t.as_str()) {
assert!(text.contains("test"), "Should contain test data");
assert!(text.contains("42"), "Should contain value 42");
}
} else {
assert!(
matches!(&tool_result.output, serde_json::Value::Object(_)),
"Expected object output from JsonYamlLoaderTool, got: {:?}",
tool_result.output
);
}
}
#[tokio::test]
async fn test_html_loader_tool() {
let temp_dir = TempDir::new().unwrap();
let html_file = temp_dir.path().join("test.html");
let html_content = r#"
<html>
<head><title>Test Page</title></head>
<body>
<h1>Hello World</h1>
<p>This is a test paragraph.</p>
<a href="https://example.com">Link</a>
</body>
</html>
"#;
fs::write(&html_file, html_content).unwrap();
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(HtmlLoaderTool));
let call = ToolCall {
tool_name: "html_loader".to_string(),
parameters: json!({
"file_path": html_file.to_str().unwrap()
}),
timeout_ms: Some(5000),
metadata: Default::default(),
continue_on_error: Some(false),
};
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(result.is_ok(), "HtmlLoaderTool should succeed");
let tool_result = result.unwrap();
assert!(tool_result.success);
if let serde_json::Value::Object(data) = &tool_result.output {
assert!(data.contains_key("text"), "Should contain text field");
if let Some(text) = data.get("text").and_then(|t| t.as_str()) {
assert!(text.contains("Hello World"));
assert!(text.contains("test paragraph"));
}
} else {
assert!(
matches!(&tool_result.output, serde_json::Value::Object(_)),
"Expected object output from HtmlLoaderTool, got: {:?}",
tool_result.output
);
}
}
}
mod tool_registry_tests {
use super::*;
#[tokio::test]
async fn test_tool_registry_creation() {
let mut registry = ToolRegistry::new();
registry.register_defaults();
let tools = registry.list();
assert!(
!tools.is_empty(),
"ToolRegistry should have tools registered"
);
assert!(
tools.contains(&"http".to_string()),
"HTTP tool should be registered"
);
}
#[tokio::test]
async fn test_http_tool_bridge() {
let mut registry = ToolRegistry::new();
registry.register_defaults();
let tool = registry.get("http");
assert!(tool.is_some(), "HTTP tool should be available");
let result = tool
.unwrap()
.invoke(r#"{"url": "https://httpbin.org/get", "method": "GET"}"#)
.await;
assert!(
result.is_ok(),
"HttpToolBridge should succeed for httpbin.org"
);
}
}
mod integration_tests {
#[tokio::test]
#[ignore] async fn test_github_client_integration() {
}
#[tokio::test]
#[ignore] async fn test_python_interpreter_integration() {
}
#[tokio::test]
#[ignore] async fn test_web_search_tools_integration() {
}
#[tokio::test]
#[ignore] async fn test_vector_store_integration() {
}
}
mod performance_tests {
use super::*;
#[tokio::test]
async fn test_tool_execution_performance() {
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(FileCreateTool));
let temp_dir = TempDir::new().unwrap();
for i in 0..10 {
let test_file = temp_dir.path().join(format!("test_{}.txt", i));
let call = ToolCall {
tool_name: "create_file".to_string(),
parameters: json!({
"path": test_file.to_str().unwrap(),
"content": format!("Content for file {}", i)
}),
timeout_ms: Some(5000),
metadata: Default::default(),
continue_on_error: Some(false),
};
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(result.is_ok(), "File creation {} should succeed", i);
}
}
}
mod error_handling_tests {
use super::*;
#[tokio::test]
async fn test_file_create_tool_invalid_path() {
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(FileCreateTool));
let call = ToolCall {
tool_name: "create_file".to_string(),
parameters: json!({
"path": "/invalid/path/that/does/not/exist/file.txt",
"content": "This should fail"
}),
timeout_ms: Some(5000),
metadata: Default::default(),
continue_on_error: Some(true), };
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(
result.is_ok() || result.is_err(),
"Tool should handle invalid path appropriately"
);
}
#[tokio::test]
async fn test_command_tool_invalid_command() {
let ctx = RuntimeContext::new();
let mut tool_manager = ToolManager::new();
tool_manager.register_tool(Box::new(CommandTool));
let call = ToolCall {
tool_name: "command".to_string(),
parameters: json!({
"command": "nonexistent_command_xyz123",
"args": []
}),
timeout_ms: Some(5000),
metadata: Default::default(),
continue_on_error: Some(true),
};
let result = tool_manager.execute_tool(call, &ctx).await;
assert!(
result.is_ok() || result.is_err(),
"Tool should handle invalid command appropriately"
);
}
}