use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::io::Write;
use std::path::{Path, PathBuf};
use tempfile::TempDir;
use probe_code::search::file_search::{search_file_for_pattern, find_files_with_pattern, find_matching_filenames};
#[cfg(test)]
mod tests {
use super::*;
fn create_test_file(dir: &TempDir, filename: &str, content: &str) -> std::path::PathBuf {
let file_path = dir.path().join(filename);
let mut file = File::create(&file_path).expect("Failed to create test file");
file.write_all(content.as_bytes()).expect("Failed to write test content");
file_path
}
fn create_test_directory_structure(dir: &TempDir) -> Vec<PathBuf> {
let mut created_files = Vec::new();
let js_content = "function searchTest() {\n return 'This is a test function';\n}\n";
let rs_content = "fn search_test() -> &'static str {\n \"This is a test function\"\n}\n";
let txt_content = "This is a test file with the word search in it.\nIt has multiple lines.\n";
created_files.push(create_test_file(dir, "test.js", js_content));
created_files.push(create_test_file(dir, "test.rs", rs_content));
created_files.push(create_test_file(dir, "test.txt", txt_content));
created_files.push(create_test_file(dir, "search_result.txt", "This file's name contains search"));
let subdir_path = dir.path().join("subdir");
std::fs::create_dir(&subdir_path).expect("Failed to create subdirectory");
let subfile_content = "Another file that mentions search but in a subdirectory";
created_files.push(create_test_file(
dir,
"subdir/nested_file.txt",
subfile_content
));
let ignored_dir = dir.path().join("node_modules");
std::fs::create_dir(&ignored_dir).expect("Failed to create ignored directory");
create_test_file(
dir,
"node_modules/should_be_ignored.js",
"This file should be ignored by default"
);
created_files
}
#[test]
fn test_search_file_for_pattern() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let content = "line 1: no match\nline 2: contains search term\nline 3: no match\n";
let file_path = create_test_file(&temp_dir, "test.txt", content);
let (matched, line_matches) = search_file_for_pattern(&file_path, "search")
.expect("Failed to search file");
assert!(matched);
assert_eq!(line_matches.len(), 1);
assert!(line_matches.contains_key(&2));
let matches = line_matches.get(&2).unwrap();
assert!(!matches.is_empty());
assert!(matches.iter().any(|m| m == "search"));
}
#[test]
fn test_search_file_for_pattern_no_match() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let content = "line 1: no match\nline 2: no match\nline 3: no match\n";
let file_path = create_test_file(&temp_dir, "test.txt", content);
let (matched, line_matches) = search_file_for_pattern(&file_path, "nonexistent")
.expect("Failed to search file");
assert!(!matched);
assert_eq!(line_matches.len(), 0);
}
#[test]
fn test_search_file_for_pattern_case_insensitive() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let content = "Line 1: no match\nLine 2: contains SEARCH term\nLine 3: no match\n";
let file_path = create_test_file(&temp_dir, "test.txt", content);
let (matched, line_matches) = search_file_for_pattern(&file_path, "search")
.expect("Failed to search file");
assert!(matched);
assert_eq!(line_matches.len(), 1);
assert!(line_matches.contains_key(&2));
let matches = line_matches.get(&2).unwrap();
assert!(!matches.is_empty());
assert!(matches.iter().any(|m| m.to_uppercase() == "SEARCH"));
}
#[test]
fn test_search_file_for_pattern_word_boundaries() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let content = "line 1: ip address\nline 2: skipped\nline 3: ip\n";
let file_path = create_test_file(&temp_dir, "test.txt", content);
let (matched, line_matches) = search_file_for_pattern(&file_path, "ip")
.expect("Failed to search file");
assert!(matched);
let line_numbers: HashSet<usize> = line_matches.keys().cloned().collect();
assert_eq!(line_numbers.len(), 2);
assert!(line_numbers.contains(&1)); assert!(line_numbers.contains(&3)); assert!(!line_numbers.contains(&2));
assert!(line_matches.get(&1).unwrap().iter().any(|m| m == "ip"));
assert!(line_matches.get(&3).unwrap().iter().any(|m| m == "ip"));
}
#[test]
fn test_search_file_for_pattern_exact_mode() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let content = "line 1: ip address\nline 2: skipped\nline 3: ip\n";
let file_path = create_test_file(&temp_dir, "test.txt", content);
let (matched, line_matches) = search_file_for_pattern(&file_path, "ip")
.expect("Failed to search file");
assert!(matched);
let line_numbers: HashSet<usize> = line_matches.keys().cloned().collect();
assert_eq!(line_numbers.len(), 3); assert!(line_numbers.contains(&1)); assert!(line_numbers.contains(&2)); assert!(line_numbers.contains(&3));
assert!(line_matches.get(&1).unwrap().iter().any(|m| m == "ip"));
assert!(line_matches.get(&2).unwrap().iter().any(|m| m == "ip"));
assert!(line_matches.get(&3).unwrap().iter().any(|m| m == "ip"));
}
#[test]
fn test_find_files_with_pattern() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
let matching_files = find_files_with_pattern(
temp_dir.path(),
"search",
&[],
false
).expect("Failed to find files with pattern");
assert!(matching_files.len() >= 3);
let found_txt = matching_files.iter().any(|p| p.ends_with("test.txt"));
let found_nested = matching_files.iter().any(|p| p.ends_with("nested_file.txt"));
assert!(found_txt);
assert!(found_nested);
let found_ignored = matching_files.iter().any(|p| p.to_string_lossy().contains("node_modules"));
assert!(!found_ignored);
}
#[test]
fn test_find_matching_filenames() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
let already_found = HashSet::new();
let matching_files = find_matching_filenames(
temp_dir.path(),
&["search".to_string()],
&already_found,
&[],
false
).expect("Failed to find matching filenames");
assert!(!matching_files.is_empty());
let found_search_result = matching_files.iter().any(|p| p.ends_with("search_result.txt"));
assert!(found_search_result);
}
#[test]
fn test_find_matching_filenames_with_already_found() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let files = create_test_directory_structure(&temp_dir);
let mut already_found = HashSet::new();
for file in &files {
if file.ends_with("search_result.txt") {
already_found.insert(file.clone());
break;
}
}
let matching_files = find_matching_filenames(
temp_dir.path(),
&["search".to_string()],
&already_found,
&[],
false
).expect("Failed to find matching filenames");
let found_search_result = matching_files.iter().any(|p| p.ends_with("search_result.txt"));
assert!(!found_search_result);
}
#[test]
fn test_find_files_with_custom_ignore() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
let custom_ignores = vec!["*.txt".to_string()];
let matching_files = find_files_with_pattern(
temp_dir.path(),
"search",
&custom_ignores,
false
).expect("Failed to find files with pattern");
let found_txt = matching_files.iter().any(|p| p.to_string_lossy().ends_with(".txt"));
assert!(!found_txt);
}
}