use fast_fs::{read_dir_recursive, TraversalOptions};
use std::fs::{self, File};
use std::io::Write;
use tempfile::tempdir;
#[tokio::test]
async fn test_with_default_options() {
let dir = tempdir().unwrap();
fs::create_dir(dir.path().join(".git")).unwrap();
File::create(dir.path().join("main.rs")).unwrap();
File::create(dir.path().join("lib.rs")).unwrap();
fs::create_dir(dir.path().join("src")).unwrap();
File::create(dir.path().join("src/mod.rs")).unwrap();
let mut gitignore = File::create(dir.path().join(".gitignore")).unwrap();
writeln!(gitignore, "target/").unwrap();
fs::create_dir(dir.path().join("target")).unwrap();
File::create(dir.path().join("target/debug.txt")).unwrap();
let options = TraversalOptions::default();
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
let paths: Vec<String> = entries
.iter()
.map(|e| e.name.clone())
.collect();
assert!(paths.contains(&"main.rs".to_string()));
assert!(paths.contains(&"lib.rs".to_string()));
assert!(paths.contains(&"src".to_string()));
assert!(!paths.contains(&"target".to_string()));
assert!(!paths.contains(&"debug.txt".to_string()));
}
#[tokio::test]
async fn test_depth_limit() {
let dir = tempdir().unwrap();
File::create(dir.path().join("root.txt")).unwrap();
fs::create_dir(dir.path().join("level1")).unwrap();
File::create(dir.path().join("level1/file1.txt")).unwrap();
fs::create_dir(dir.path().join("level1/level2")).unwrap();
File::create(dir.path().join("level1/level2/file2.txt")).unwrap();
fs::create_dir(dir.path().join("level1/level2/level3")).unwrap();
File::create(dir.path().join("level1/level2/level3/file3.txt")).unwrap();
let options = TraversalOptions::default().with_max_depth(0);
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
assert_eq!(entries.len(), 2);
let options = TraversalOptions::default().with_max_depth(1);
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
assert_eq!(entries.len(), 4);
let options = TraversalOptions::default().with_max_depth(2);
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
assert_eq!(entries.len(), 6);
let paths: Vec<String> = entries.iter().map(|e| e.name.clone()).collect();
assert!(!paths.contains(&"file3.txt".to_string()));
}
#[tokio::test]
async fn test_gitignore_respected() {
let dir = tempdir().unwrap();
fs::create_dir(dir.path().join(".git")).unwrap();
File::create(dir.path().join("main.rs")).unwrap();
fs::create_dir(dir.path().join("target")).unwrap();
File::create(dir.path().join("target/output.bin")).unwrap();
fs::create_dir(dir.path().join("build")).unwrap();
File::create(dir.path().join("build/temp.o")).unwrap();
let mut gitignore = File::create(dir.path().join(".gitignore")).unwrap();
writeln!(gitignore, "target/").unwrap();
writeln!(gitignore, "build/").unwrap();
let options = TraversalOptions::default();
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
let paths: Vec<String> = entries.iter().map(|e| e.name.clone()).collect();
assert!(paths.contains(&"main.rs".to_string()));
assert!(!paths.contains(&"target".to_string()));
assert!(!paths.contains(&"build".to_string()));
assert!(!paths.contains(&"output.bin".to_string()));
}
#[tokio::test]
async fn test_node_modules_skipped() {
let dir = tempdir().unwrap();
fs::create_dir(dir.path().join(".git")).unwrap();
File::create(dir.path().join("package.json")).unwrap();
File::create(dir.path().join("index.js")).unwrap();
fs::create_dir(dir.path().join("node_modules")).unwrap();
File::create(dir.path().join("node_modules/package.js")).unwrap();
let mut gitignore = File::create(dir.path().join(".gitignore")).unwrap();
writeln!(gitignore, "node_modules/").unwrap();
let options = TraversalOptions::default();
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
let paths: Vec<String> = entries.iter().map(|e| e.name.clone()).collect();
assert!(paths.contains(&"package.json".to_string()));
assert!(paths.contains(&"index.js".to_string()));
assert!(!paths.contains(&"node_modules".to_string()));
}
#[tokio::test]
async fn test_rust_target_skipped() {
let dir = tempdir().unwrap();
fs::create_dir(dir.path().join(".git")).unwrap();
File::create(dir.path().join("Cargo.toml")).unwrap();
fs::create_dir(dir.path().join("src")).unwrap();
File::create(dir.path().join("src/main.rs")).unwrap();
fs::create_dir(dir.path().join("target")).unwrap();
fs::create_dir(dir.path().join("target/debug")).unwrap();
File::create(dir.path().join("target/debug/binary")).unwrap();
let mut gitignore = File::create(dir.path().join(".gitignore")).unwrap();
writeln!(gitignore, "target/").unwrap();
let options = TraversalOptions::default();
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
let paths: Vec<String> = entries.iter().map(|e| e.name.clone()).collect();
assert!(paths.contains(&"Cargo.toml".to_string()));
assert!(paths.contains(&"src".to_string()));
assert!(!paths.contains(&"target".to_string()));
assert!(!paths.contains(&"binary".to_string()));
}
#[tokio::test]
async fn test_extension_filter() {
let dir = tempdir().unwrap();
File::create(dir.path().join("main.rs")).unwrap();
File::create(dir.path().join("lib.rs")).unwrap();
File::create(dir.path().join("README.md")).unwrap();
File::create(dir.path().join("build.py")).unwrap();
fs::create_dir(dir.path().join("src")).unwrap();
File::create(dir.path().join("src/module.rs")).unwrap();
File::create(dir.path().join("src/script.sh")).unwrap();
let options = TraversalOptions::default()
.with_extensions(&["rs"])
.with_gitignore(false);
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
let file_names: Vec<String> = entries
.iter()
.filter(|e| !e.is_dir)
.map(|e| e.name.clone())
.collect();
assert_eq!(file_names.len(), 3);
assert!(file_names.contains(&"main.rs".to_string()));
assert!(file_names.contains(&"lib.rs".to_string()));
assert!(file_names.contains(&"module.rs".to_string()));
let dir_names: Vec<String> = entries
.iter()
.filter(|e| e.is_dir)
.map(|e| e.name.clone())
.collect();
assert!(dir_names.contains(&"src".to_string()));
}
#[tokio::test]
async fn test_include_hidden() {
let dir = tempdir().unwrap();
File::create(dir.path().join("visible.txt")).unwrap();
File::create(dir.path().join(".hidden")).unwrap();
fs::create_dir(dir.path().join(".config")).unwrap();
File::create(dir.path().join(".config/settings.json")).unwrap();
let options = TraversalOptions::default().with_gitignore(false);
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
let paths: Vec<String> = entries.iter().map(|e| e.name.clone()).collect();
assert!(paths.contains(&"visible.txt".to_string()));
assert!(!paths.contains(&".hidden".to_string()));
assert!(!paths.contains(&".config".to_string()));
let options = TraversalOptions::default()
.include_hidden()
.with_gitignore(false);
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
let paths: Vec<String> = entries.iter().map(|e| e.name.clone()).collect();
assert!(paths.contains(&"visible.txt".to_string()));
assert!(paths.contains(&".hidden".to_string()));
assert!(paths.contains(&".config".to_string()));
assert!(paths.contains(&"settings.json".to_string()));
}
#[tokio::test]
async fn test_symlink_loop_detection() {
let dir = tempdir().unwrap();
fs::create_dir(dir.path().join("subdir")).unwrap();
File::create(dir.path().join("subdir/file.txt")).unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::symlink;
let _ = symlink(dir.path(), dir.path().join("subdir/loop_link"));
}
let options = TraversalOptions::default().with_gitignore(false);
let result = read_dir_recursive(dir.path(), options).await;
assert!(result.is_ok());
let entries = result.unwrap();
let paths: Vec<String> = entries.iter().map(|e| e.name.clone()).collect();
assert!(paths.contains(&"subdir".to_string()));
assert!(paths.contains(&"file.txt".to_string()));
}
#[tokio::test]
async fn test_empty_directory() {
let dir = tempdir().unwrap();
let options = TraversalOptions::default().with_gitignore(false);
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
assert_eq!(entries.len(), 0);
}
#[tokio::test]
async fn test_combined_filters() {
let dir = tempdir().unwrap();
fs::create_dir(dir.path().join(".git")).unwrap();
File::create(dir.path().join("main.rs")).unwrap();
File::create(dir.path().join(".hidden.rs")).unwrap();
File::create(dir.path().join("script.py")).unwrap();
fs::create_dir(dir.path().join("src")).unwrap();
File::create(dir.path().join("src/lib.rs")).unwrap();
fs::create_dir(dir.path().join("target")).unwrap();
File::create(dir.path().join("target/output.rs")).unwrap();
let mut gitignore = File::create(dir.path().join(".gitignore")).unwrap();
writeln!(gitignore, "target/").unwrap();
let options = TraversalOptions::default()
.with_extensions(&["rs"]);
let entries = read_dir_recursive(dir.path(), options).await.unwrap();
let file_names: Vec<String> = entries
.iter()
.filter(|e| !e.is_dir)
.map(|e| e.name.clone())
.collect();
assert_eq!(file_names.len(), 2);
assert!(file_names.contains(&"main.rs".to_string()));
assert!(file_names.contains(&"lib.rs".to_string()));
}