use anyhow::Result;
use defer::defer;
use lumin::traverse::{TraverseOptions, traverse_directory};
use serial_test::serial;
use std::path::Path;
mod test_helpers;
use test_helpers::{TEST_DIR, TestEnvironment};
#[cfg(test)]
mod traverse_tests {
use super::*;
#[test]
#[serial]
fn test_traverse_default_options() -> Result<()> {
let _env = TestEnvironment::setup()?;
let options = TraverseOptions::default();
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(!results.is_empty());
assert!(results.iter().any(|r| r.file_type == "rs"));
assert!(results.iter().any(|r| r.file_type == "md"));
assert!(results.iter().any(|r| r.file_type == "py"));
assert!(
!results
.iter()
.any(|r| r.file_path.to_string_lossy().contains(".hidden"))
);
assert!(
!results
.iter()
.any(|r| r.file_path.to_string_lossy().ends_with(".tmp"))
);
assert!(
!results
.iter()
.any(|r| r.file_path.to_string_lossy().ends_with(".log"))
);
Ok(())
}
#[test]
#[serial]
fn test_traverse_include_binary() -> Result<()> {
let _env = TestEnvironment::setup()?;
let mut options = TraverseOptions::default();
options.only_text_files = false;
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(results.iter().any(|r| r.file_type == "jpg"
|| r.file_type == "png"
|| r.file_path.to_string_lossy().contains("binary_executable")));
Ok(())
}
#[test]
#[serial]
fn test_traverse_respect_gitignore() -> Result<()> {
let _env = TestEnvironment::setup()?;
let hidden_path = Path::new(TEST_DIR).join(".hidden");
assert!(
hidden_path.exists(),
"Test setup error: .hidden directory doesn't exist"
);
assert!(
std::fs::read_dir(hidden_path)?.next().is_some(),
"Test setup error: .hidden directory is empty"
);
let options = TraverseOptions::default();
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(
!results
.iter()
.any(|r| r.file_path.to_string_lossy().contains(".hidden")),
"Found .hidden files when respecting gitignore"
);
assert!(
!results
.iter()
.any(|r| r.file_path.to_string_lossy().ends_with(".tmp")),
"Found .tmp files when respecting gitignore"
);
assert!(
!results
.iter()
.any(|r| r.file_path.to_string_lossy().ends_with(".log")),
"Found .log files when respecting gitignore"
);
Ok(())
}
#[test]
#[serial]
fn test_traverse_ignore_gitignore() -> Result<()> {
let _env = TestEnvironment::setup()?;
let mut options = TraverseOptions::default();
options.respect_gitignore = false;
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(
results
.iter()
.any(|r| r.file_path.to_string_lossy().contains(".hidden")),
"Did not find .hidden files when ignoring gitignore"
);
assert!(
results
.iter()
.any(|r| r.file_path.to_string_lossy().ends_with(".tmp")),
"Did not find .tmp files when ignoring gitignore"
);
assert!(
results
.iter()
.any(|r| r.file_path.to_string_lossy().ends_with(".log")),
"Did not find .log files when ignoring gitignore"
);
Ok(())
}
#[test]
#[serial]
fn test_is_hidden() -> Result<()> {
let _env = TestEnvironment::setup()?;
let mut options = TraverseOptions::default();
options.respect_gitignore = false;
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
for result in &results {
if result.file_path.to_string_lossy().contains(".hidden") {
assert!(
result.is_hidden(),
"File in .hidden directory not marked as hidden"
);
}
}
Ok(())
}
#[test]
#[serial]
fn test_traverse_case_sensitive() -> Result<()> {
let _env = TestEnvironment::setup()?;
let mut options = TraverseOptions::default();
options.case_sensitive = true;
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(!results.is_empty());
Ok(())
}
#[test]
#[serial]
fn test_traverse_with_pattern() -> Result<()> {
let _env = TestEnvironment::setup()?;
let mut options = TraverseOptions::default();
options.pattern = Some("**/*.rs".to_string());
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(!results.is_empty());
assert!(results.iter().all(|r| r.file_type == "rs"));
let mut options = TraverseOptions::default();
options.pattern = Some("**/*.md".to_string());
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(!results.is_empty());
assert!(results.iter().all(|r| r.file_type == "md"));
let mut options = TraverseOptions::default();
options.pattern = Some("**/docs/**".to_string());
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(!results.is_empty());
assert!(
results
.iter()
.all(|r| r.file_path.to_string_lossy().contains("/docs/"))
);
let mut options = TraverseOptions::default();
options.pattern = Some("README".to_string());
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(!results.is_empty());
assert!(
results
.iter()
.any(|r| r.file_path.to_string_lossy().contains("README"))
);
let mut options = TraverseOptions::default();
options.pattern = Some("contributing".to_string()); options.case_sensitive = false;
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert!(!results.is_empty());
assert!(results.iter().any(|r| {
r.file_path
.to_string_lossy()
.to_lowercase()
.contains("contributing")
}));
Ok(())
}
#[test]
#[serial]
fn test_traverse_with_prefix_pattern() -> Result<()> {
let _env = TestEnvironment::setup()?;
let prefix_files = [
format!("{}/test_prefix_file1.txt", TEST_DIR),
format!("{}/test_prefix_file2.md", TEST_DIR),
format!("{}/other_file.txt", TEST_DIR),
format!("{}/docs/test_prefix_file3.txt", TEST_DIR),
];
for file_path in &prefix_files {
let path = Path::new(file_path);
if let Some(parent) = path.parent() {
std::fs::create_dir_all(parent)?;
}
std::fs::write(path, format!("Test content for {}", file_path))?;
}
let _cleanup = defer::defer(|| {
for file_path in &prefix_files {
let _ = std::fs::remove_file(file_path);
}
});
let mut options = TraverseOptions::default();
options.pattern = Some("test_prefix_*".to_string());
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert_eq!(
results.len(),
2,
"Should match exactly 2 files at the root level"
);
assert!(results.iter().any(|r| {
r.file_path
.to_string_lossy()
.ends_with("test_prefix_file1.txt")
}));
assert!(results.iter().any(|r| {
r.file_path
.to_string_lossy()
.ends_with("test_prefix_file2.md")
}));
assert!(!results.iter().any(|r| {
r.file_path
.to_string_lossy()
.contains("docs/test_prefix_file3.txt")
}));
let mut options = TraverseOptions::default();
options.pattern = Some("**/test_prefix_*".to_string());
let results = traverse_directory(Path::new(TEST_DIR), &options)?;
assert_eq!(
results.len(),
3,
"Should match all 3 files with the prefix in any directory"
);
assert!(results.iter().any(|r| {
r.file_path
.to_string_lossy()
.contains("docs/test_prefix_file3.txt")
}));
Ok(())
}
}