use std::fs::{self, File};
use std::io::Write;
use std::path::PathBuf;
use std::process::Command;
use tempfile::TempDir;
fn create_test_file(dir: &TempDir, filename: &str, content: &str) -> 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(root_dir: &TempDir) {
let src_dir = root_dir.path().join("src");
fs::create_dir(&src_dir).expect("Failed to create src directory");
let rust_content = r#"
fn search_function(query: &str) -> bool {
println!("Searching for: {}", query);
query.contains("search")
}
"#;
create_test_file(root_dir, "src/search.rs", rust_content);
let js_content = r#"
// This is a JavaScript file with a search term
function searchFunction(query) {
console.log(`Searching for: ${query}`);
return query.includes('search');
}
"#;
create_test_file(root_dir, "src/search.js", js_content);
}
#[test]
fn test_cli_basic_search() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
let output = Command::new("cargo")
.args([
"run",
"--",
"search",
"search", temp_dir.path().to_str().unwrap(),
])
.output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Found"),
"Output should indicate matches were found"
);
assert!(
stdout.contains("search.rs"),
"Should find matches in Rust file"
);
assert!(
stdout.contains("search.js"),
"Should find matches in JavaScript file"
);
}
#[test]
fn test_cli_files_only() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
let output = Command::new("cargo")
.args([
"run",
"--",
"search",
"search", temp_dir.path().to_str().unwrap(),
"--files-only",
])
.output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Found"),
"Output should indicate matches were found"
);
assert!(
stdout.contains("search.rs"),
"Should find matches in Rust file"
);
assert!(
stdout.contains("search.js"),
"Should find matches in JavaScript file"
);
assert!(
!stdout.contains("fn search_function"),
"Should not include code in files-only mode"
);
assert!(
!stdout.contains("function searchFunction"),
"Should not include code in files-only mode"
);
}
#[test]
fn test_cli_filename_matching() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
create_test_file(
&temp_dir,
"search_file_without_content.txt",
"This file doesn't contain the search term anywhere in its content.",
);
let output = Command::new("cargo")
.args([
"run",
"--",
"search",
"search", temp_dir.path().to_str().unwrap(),
])
.output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Found"),
"Output should indicate matches were found"
);
println!("Command output: {stdout}");
println!("Default behavior completed successfully");
let output2 = Command::new("cargo")
.args([
"run",
"--",
"search",
"search", temp_dir.path().to_str().unwrap(),
"--exclude-filenames",
])
.output()
.expect("Failed to execute command");
assert!(output2.status.success());
let stdout2 = String::from_utf8_lossy(&output2.stdout);
println!("With exclude-filenames output: {stdout2}");
assert!(
stdout2.contains("Found"),
"Output should indicate matches were found"
);
println!("Exclude-filenames behavior completed successfully");
}
#[test]
fn test_cli_reranker() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
let output = Command::new("cargo")
.args([
"run",
"--",
"search",
"search", temp_dir.path().to_str().unwrap(),
"--reranker",
"bm25",
])
.env("DEBUG", "1") .output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Found"),
"Output should indicate matches were found"
);
println!("Command output: {stdout}");
assert!(
stdout.contains("Using bm25 for ranking")
|| stdout.contains("Using BM25 for ranking")
|| stdout.contains("BM25 ranking")
|| stdout.contains("bm25"),
"Should use BM25 reranker"
);
}
#[test]
fn test_cli_default_frequency_search() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
let output = Command::new("cargo")
.args([
"run",
"--",
"search",
"search", temp_dir.path().to_str().unwrap(),
])
.env("DEBUG", "1") .output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Found"),
"Output should indicate matches were found"
);
assert!(
stdout.contains("Frequency search enabled")
|| stdout.contains("frequency-based search")
|| !stdout.contains("exact matching"),
"Should use frequency-based search by default"
);
}
#[test]
fn test_cli_custom_ignores() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
let output = Command::new("cargo")
.args([
"run",
"--",
"search",
"search", temp_dir.path().to_str().unwrap(),
"--ignore",
"*.js",
])
.env("DEBUG", "1") .output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
println!("STDOUT: {stdout}");
println!("STDERR: {stderr}");
assert!(
stdout.contains("Found"),
"Output should indicate matches were found"
);
assert!(
stdout.contains("search.rs"),
"Should find matches in Rust file"
);
let results_start = stdout.find("Search completed in").unwrap_or(0);
let results_section = &stdout[results_start..];
if let Some(pos) = stdout.find("search.js") {
let start = if pos > 50 { pos - 50 } else { 0 };
let end = if pos + 50 < stdout.len() {
pos + 50
} else {
stdout.len()
};
let context = &stdout[start..end];
println!("Found 'search.js' in debug output at position {pos} with context: '{context}'");
}
assert!(
!results_section.contains("search.js"),
"Should not find matches in JavaScript file in the search results"
);
}
#[test]
#[ignore] fn test_cli_max_results() {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
create_test_directory_structure(&temp_dir);
for i in 1..20 {
let content = format!("// File {i} with search term\n");
create_test_file(&temp_dir, &format!("src/extra{i}.rs"), &content);
}
let output = Command::new("cargo")
.args([
"run",
"--",
"search",
"search", temp_dir.path().to_str().unwrap(),
"--max-results",
"1",
"--files-only", ])
.output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Command output: {stdout}");
assert!(
stdout.contains("Found"),
"Output should indicate matches were found"
);
assert!(
stdout.contains("Limits applied"),
"Should indicate limits were applied"
);
assert!(
stdout.contains("Max results: 1"),
"Should show max results limit"
);
assert!(
stdout.contains("Found 1 search results"),
"Should find only 1 result"
);
}