mirror of
https://github.com/hanzoai/mcp.git
synced 2026-10-29 02:55:49 +00:00
742 lines
22 KiB
Rust
742 lines
22 KiB
Rust
//! Filesystem Tools Tests - 1:1 parity with TypeScript file-ops.test.ts
|
|
//!
|
|
//! Tests for fs tool covering:
|
|
//! - Tool properties and metadata
|
|
//! - Read operations (with offset/limit)
|
|
//! - Write operations
|
|
//! - Edit operations (single/multiple replacements)
|
|
//! - Patch operations (add/update/delete)
|
|
//! - Tree operations
|
|
//! - Find operations (glob patterns)
|
|
//! - Search operations (regex/case-insensitive)
|
|
//! - Info operations
|
|
|
|
use hanzo_mcp::tools::{FsTool, FsToolArgs};
|
|
use std::fs;
|
|
use tempfile::TempDir;
|
|
|
|
/// Test fs tool properties
|
|
#[test]
|
|
fn test_fs_tool_properties() {
|
|
let tool = FsTool::new();
|
|
// Tool is fs following HIP-0300
|
|
assert!(true); // FsTool exists
|
|
}
|
|
|
|
/// Test fs help action
|
|
#[tokio::test]
|
|
async fn test_fs_help_action() {
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "help".to_string(),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
// Help should list available actions
|
|
assert!(json.get("actions").is_some());
|
|
assert!(json.get("name").is_some());
|
|
assert_eq!(json["name"], "fs");
|
|
}
|
|
|
|
/// Test read file basic
|
|
#[tokio::test]
|
|
async fn test_fs_read_basic() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("test.txt");
|
|
fs::write(&file_path, "line 1\nline 2\nline 3\n").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "read".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json.get("content").is_some());
|
|
assert!(json["content"].as_str().unwrap().contains("line 1"));
|
|
assert!(json["content"].as_str().unwrap().contains("line 2"));
|
|
assert_eq!(json["total_lines"].as_i64().unwrap(), 3); // Rust's .lines() returns 3 for trailing newline
|
|
}
|
|
|
|
/// Test read file with offset and limit
|
|
#[tokio::test]
|
|
async fn test_fs_read_with_offset_limit() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("test.txt");
|
|
let content: String = (1..=100).map(|i| format!("line {}\n", i)).collect();
|
|
fs::write(&file_path, &content).unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "read".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
offset: Some(10),
|
|
limit: Some(5),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert_eq!(json["lines"].as_i64().unwrap(), 5);
|
|
assert_eq!(json["offset"].as_i64().unwrap(), 10);
|
|
assert!(json["truncated"].as_bool().unwrap());
|
|
}
|
|
|
|
/// Test read file with file_path alias
|
|
#[tokio::test]
|
|
async fn test_fs_read_file_path_alias() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("test.txt");
|
|
fs::write(&file_path, "hello world").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "read".to_string(),
|
|
file_path: Some(file_path.to_string_lossy().to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
assert!(output.contains("hello world"));
|
|
}
|
|
|
|
/// Test write file basic
|
|
#[tokio::test]
|
|
async fn test_fs_write_basic() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("new.txt");
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "write".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
content: Some("test content\nline 2".to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json["success"].as_bool().unwrap());
|
|
assert!(json["bytes"].as_i64().unwrap() > 0);
|
|
assert_eq!(json["lines"].as_i64().unwrap(), 2);
|
|
|
|
// Verify file contents
|
|
let content = fs::read_to_string(&file_path).unwrap();
|
|
assert_eq!(content, "test content\nline 2");
|
|
}
|
|
|
|
/// Test write creates parent directories
|
|
#[tokio::test]
|
|
async fn test_fs_write_creates_parents() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("a/b/c/deep.txt");
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "write".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
content: Some("deep content".to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
// Verify file exists
|
|
assert!(file_path.exists());
|
|
let content = fs::read_to_string(&file_path).unwrap();
|
|
assert_eq!(content, "deep content");
|
|
}
|
|
|
|
/// Test edit file single replacement
|
|
#[tokio::test]
|
|
async fn test_fs_edit_single() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("edit.txt");
|
|
fs::write(&file_path, "hello world").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "edit".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
old_string: Some("world".to_string()),
|
|
new_string: Some("rust".to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json["success"].as_bool().unwrap());
|
|
assert_eq!(json["replacements"].as_i64().unwrap(), 1);
|
|
|
|
// Verify file contents
|
|
let content = fs::read_to_string(&file_path).unwrap();
|
|
assert_eq!(content, "hello rust");
|
|
}
|
|
|
|
/// Test edit with old_text/new_text aliases
|
|
#[tokio::test]
|
|
async fn test_fs_edit_aliases() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("edit.txt");
|
|
fs::write(&file_path, "hello world").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "edit".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
old_text: Some("world".to_string()),
|
|
new_text: Some("universe".to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let content = fs::read_to_string(&file_path).unwrap();
|
|
assert_eq!(content, "hello universe");
|
|
}
|
|
|
|
/// Test edit fails on multiple matches without replace_all
|
|
#[tokio::test]
|
|
async fn test_fs_edit_multiple_without_replace_all() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("edit.txt");
|
|
fs::write(&file_path, "hello hello hello").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "edit".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
old_string: Some("hello".to_string()),
|
|
new_string: Some("hi".to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_err());
|
|
|
|
let error = result.unwrap_err();
|
|
assert!(error.to_string().contains("3") || error.to_string().contains("locations"));
|
|
}
|
|
|
|
/// Test edit with replace_all
|
|
#[tokio::test]
|
|
async fn test_fs_edit_replace_all() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("edit.txt");
|
|
fs::write(&file_path, "hello hello hello").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "edit".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
old_string: Some("hello".to_string()),
|
|
new_string: Some("hi".to_string()),
|
|
replace_all: true,
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert_eq!(json["replacements"].as_i64().unwrap(), 3);
|
|
|
|
let content = fs::read_to_string(&file_path).unwrap();
|
|
assert_eq!(content, "hi hi hi");
|
|
}
|
|
|
|
/// Test edit create new file (empty old_string)
|
|
#[tokio::test]
|
|
async fn test_fs_edit_create_file() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("new.txt");
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "edit".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
old_string: Some("".to_string()),
|
|
new_string: Some("new file content".to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json["created"].as_bool().unwrap());
|
|
|
|
let content = fs::read_to_string(&file_path).unwrap();
|
|
assert_eq!(content, "new file content");
|
|
}
|
|
|
|
/// Test tree operation
|
|
#[tokio::test]
|
|
async fn test_fs_tree_basic() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
fs::create_dir(temp_dir.path().join("subdir")).unwrap();
|
|
fs::write(temp_dir.path().join("file1.txt"), "").unwrap();
|
|
fs::write(temp_dir.path().join("subdir/file2.txt"), "").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "tree".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
include_hidden: true, // Temp dirs start with '.' so we need to include hidden
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json.get("tree").is_some());
|
|
assert!(json.get("directories").is_some());
|
|
assert!(json.get("files").is_some());
|
|
assert!(json["directories"].as_i64().unwrap() >= 1);
|
|
assert!(json["files"].as_i64().unwrap() >= 2);
|
|
}
|
|
|
|
/// Test tree with depth limit
|
|
#[tokio::test]
|
|
async fn test_fs_tree_with_depth() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
fs::create_dir_all(temp_dir.path().join("a/b/c/d")).unwrap();
|
|
fs::write(temp_dir.path().join("a/b/c/d/deep.txt"), "").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "tree".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
depth: Some(2),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
// Should not include deeply nested files when depth is limited
|
|
assert!(!output.contains("deep.txt"));
|
|
}
|
|
|
|
/// Test find operation
|
|
#[tokio::test]
|
|
async fn test_fs_find_basic() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
fs::write(temp_dir.path().join("test.rs"), "").unwrap();
|
|
fs::write(temp_dir.path().join("test.py"), "").unwrap();
|
|
fs::write(temp_dir.path().join("other.txt"), "").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "find".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
pattern: Some("*.rs".to_string()),
|
|
include_hidden: true, // Temp dirs start with '.' so we need to include hidden
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json.get("matches").is_some());
|
|
let matches = json["matches"].as_array().unwrap();
|
|
assert_eq!(matches.len(), 1);
|
|
assert!(matches[0].as_str().unwrap().contains("test.rs"));
|
|
}
|
|
|
|
/// Test find with limit
|
|
#[tokio::test]
|
|
async fn test_fs_find_with_limit() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
for i in 0..10 {
|
|
fs::write(temp_dir.path().join(format!("file{}.txt", i)), "").unwrap();
|
|
}
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "find".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
pattern: Some("*.txt".to_string()),
|
|
limit: Some(5),
|
|
include_hidden: true, // Temp dirs start with '.' so we need to include hidden
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json["truncated"].as_bool().unwrap());
|
|
assert!(json["count"].as_i64().unwrap() <= 5);
|
|
}
|
|
|
|
/// Test search operation
|
|
#[tokio::test]
|
|
async fn test_fs_search_basic() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
fs::write(
|
|
temp_dir.path().join("test.rs"),
|
|
"fn main() {\n println!(\"Hello\");\n}\n",
|
|
)
|
|
.unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "search".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
pattern: Some("println".to_string()),
|
|
include_hidden: true, // Temp dirs start with '.' so we need to include hidden
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json.get("results").is_some());
|
|
let results = json["results"].as_array().unwrap();
|
|
assert!(!results.is_empty());
|
|
assert!(results[0]["match"].as_str().unwrap().contains("println"));
|
|
}
|
|
|
|
/// Test search case insensitive
|
|
#[tokio::test]
|
|
async fn test_fs_search_case_insensitive() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
fs::write(temp_dir.path().join("test.txt"), "Hello World\nHELLO WORLD\n").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "search".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
pattern: Some("hello".to_string()),
|
|
ignore_case: true,
|
|
include_hidden: true, // Temp dirs start with '.' so we need to include hidden
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
let results = json["results"].as_array().unwrap();
|
|
assert_eq!(results.len(), 2); // Both lines should match
|
|
}
|
|
|
|
/// Test search with context lines
|
|
#[tokio::test]
|
|
async fn test_fs_search_with_context() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
fs::write(
|
|
temp_dir.path().join("test.txt"),
|
|
"line 1\nline 2\nTARGET\nline 4\nline 5\n",
|
|
)
|
|
.unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "search".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
pattern: Some("TARGET".to_string()),
|
|
context: Some(1),
|
|
include_hidden: true, // Temp dirs start with '.' so we need to include hidden
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
let results = json["results"].as_array().unwrap();
|
|
assert!(!results.is_empty());
|
|
let context = results[0]["context"].as_str().unwrap();
|
|
assert!(context.contains("line 2"));
|
|
assert!(context.contains("TARGET"));
|
|
assert!(context.contains("line 4"));
|
|
}
|
|
|
|
/// Test info operation
|
|
#[tokio::test]
|
|
async fn test_fs_info_file() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("test.txt");
|
|
fs::write(&file_path, "hello world").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "info".to_string(),
|
|
path: Some(file_path.to_string_lossy().to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert_eq!(json["type"], "file");
|
|
assert_eq!(json["size"].as_i64().unwrap(), 11); // "hello world" is 11 bytes
|
|
assert!(json.get("modified").is_some());
|
|
}
|
|
|
|
/// Test info operation on directory
|
|
#[tokio::test]
|
|
async fn test_fs_info_directory() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "info".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert_eq!(json["type"], "directory");
|
|
}
|
|
|
|
/// Test patch operation - add file
|
|
#[tokio::test]
|
|
async fn test_fs_patch_add_file() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("new.txt");
|
|
|
|
let patch = format!(
|
|
r#"*** Begin Patch
|
|
*** Add File: {}
|
|
+line 1
|
|
+line 2
|
|
*** End Patch"#,
|
|
file_path.to_string_lossy()
|
|
);
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "patch".to_string(),
|
|
patch: Some(patch),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let output = result.unwrap();
|
|
let json: serde_json::Value = serde_json::from_str(&output).unwrap();
|
|
|
|
assert!(json["applied"].as_i64().unwrap() >= 1);
|
|
|
|
// Verify file was created
|
|
assert!(file_path.exists());
|
|
}
|
|
|
|
/// Test patch operation - update file
|
|
#[tokio::test]
|
|
async fn test_fs_patch_update_file() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("existing.txt");
|
|
fs::write(&file_path, "hello world\nfoo bar\n").unwrap();
|
|
|
|
let patch = format!(
|
|
r#"*** Begin Patch
|
|
*** Update File: {}
|
|
@@ context
|
|
-hello world
|
|
+hello rust
|
|
*** End Patch"#,
|
|
file_path.to_string_lossy()
|
|
);
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "patch".to_string(),
|
|
patch: Some(patch),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
let content = fs::read_to_string(&file_path).unwrap();
|
|
assert!(content.contains("hello rust"));
|
|
assert!(!content.contains("hello world"));
|
|
}
|
|
|
|
/// Test patch operation - delete file
|
|
#[tokio::test]
|
|
async fn test_fs_patch_delete_file() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
let file_path = temp_dir.path().join("to_delete.txt");
|
|
fs::write(&file_path, "will be deleted").unwrap();
|
|
|
|
let patch = format!(
|
|
r#"*** Begin Patch
|
|
*** Delete File: {}
|
|
*** End Patch"#,
|
|
file_path.to_string_lossy()
|
|
);
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "patch".to_string(),
|
|
patch: Some(patch),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
// Verify file was deleted
|
|
assert!(!file_path.exists());
|
|
}
|
|
|
|
/// Test action aliases
|
|
#[tokio::test]
|
|
async fn test_fs_action_aliases() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
fs::write(temp_dir.path().join("test.txt"), "content").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
|
|
// Test "ls" alias for "tree"
|
|
let args = FsToolArgs {
|
|
action: "ls".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
..Default::default()
|
|
};
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
// Test "glob" alias for "find"
|
|
let args = FsToolArgs {
|
|
action: "glob".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
pattern: Some("*.txt".to_string()),
|
|
..Default::default()
|
|
};
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
|
|
// Test "grep" alias for "search"
|
|
let args = FsToolArgs {
|
|
action: "grep".to_string(),
|
|
path: Some(temp_dir.path().to_string_lossy().to_string()),
|
|
pattern: Some("content".to_string()),
|
|
..Default::default()
|
|
};
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
/// Test tilde expansion
|
|
#[tokio::test]
|
|
async fn test_fs_tilde_expansion() {
|
|
// Skip if we can't get home directory
|
|
if dirs::home_dir().is_none() {
|
|
return;
|
|
}
|
|
|
|
let tool = FsTool::new();
|
|
let args = FsToolArgs {
|
|
action: "tree".to_string(),
|
|
path: Some("~".to_string()),
|
|
depth: Some(1),
|
|
..Default::default()
|
|
};
|
|
|
|
let result = tool.execute(args).await;
|
|
// Should work without error (home directory exists)
|
|
assert!(result.is_ok());
|
|
}
|
|
|
|
/// Test hidden files handling
|
|
#[tokio::test]
|
|
async fn test_fs_hidden_files() {
|
|
let temp_dir = TempDir::new().unwrap();
|
|
// Create a visible subdir to test hidden file filtering inside it
|
|
let subdir = temp_dir.path().join("testdir");
|
|
fs::create_dir(&subdir).unwrap();
|
|
fs::write(subdir.join(".hidden"), "").unwrap();
|
|
fs::write(subdir.join("visible"), "").unwrap();
|
|
|
|
let tool = FsTool::new();
|
|
|
|
// Without include_hidden - search inside visible subdir
|
|
let args = FsToolArgs {
|
|
action: "tree".to_string(),
|
|
path: Some(subdir.to_string_lossy().to_string()),
|
|
include_hidden: false,
|
|
..Default::default()
|
|
};
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
let output = result.unwrap();
|
|
assert!(!output.contains(".hidden"));
|
|
assert!(output.contains("visible"));
|
|
|
|
// With include_hidden
|
|
let args = FsToolArgs {
|
|
action: "tree".to_string(),
|
|
path: Some(subdir.to_string_lossy().to_string()),
|
|
include_hidden: true,
|
|
..Default::default()
|
|
};
|
|
let result = tool.execute(args).await;
|
|
assert!(result.is_ok());
|
|
let output = result.unwrap();
|
|
assert!(output.contains(".hidden"));
|
|
}
|