use std::collections::{HashMap, HashSet};
use std::fs;
use std::path::Path;
use tempfile::TempDir;
use probe_code::search::elastic_query::Expr;
use probe_code::search::query::QueryPlan;
use probe_code::search::{perform_probe, SearchOptions};
fn create_test_files(temp_dir: &Path) {
let file1_path = temp_dir.join("file1.rs");
let file1_content = r#"
// This file contains keywordAlpha and keywordBeta
fn test_function() {
// This is keywordAlpha
let x = 1;
// This is keywordBeta
let y = 2;
println!("Result: {}", x + y);
}
"#;
let file2_path = temp_dir.join("file2.rs");
let file2_content = r#"
// This file contains keywordAlpha and keywordGamma
fn another_function() {
// This is keywordAlpha
let x = 1;
// This is keywordGamma
let z = 3;
println!("Result: {}", x + z);
}
"#;
let file3_path = temp_dir.join("file3.rs");
let file3_content = r#"
// This file contains keywordBeta and keywordGamma
fn third_function() {
// This is keywordBeta
let y = 2;
// This is keywordGamma
let z = 3;
println!("Result: {}", y + z);
}
"#;
let file4_path = temp_dir.join("file4.rs");
let file4_content = r#"
// This file contains keywordAlpha, keywordBeta, and keywordGamma
fn all_keywords_function() {
// This is keywordAlpha
let x = 1;
// This is keywordBeta
let y = 2;
// This is keywordGamma
let z = 3;
println!("Result: {}", x + y + z);
}
"#;
fs::write(file1_path, file1_content).unwrap();
fs::write(file2_path, file2_content).unwrap();
fs::write(file3_path, file3_content).unwrap();
fs::write(file4_path, file4_content).unwrap();
}
#[test]
fn test_required_term_query() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
create_test_files(temp_path);
let queries = vec!["keywordAlpha OR keywordBeta OR keywordGamma".to_string()];
let custom_ignores: Vec<String> = vec![];
println!("Testing query: {queries:?}");
let options = SearchOptions {
path: temp_path,
queries: &queries,
files_only: false,
custom_ignores: &custom_ignores,
exclude_filenames: false,
language: None,
reranker: "hybrid",
frequency_search: false,
max_results: None,
max_bytes: None,
max_tokens: None,
allow_tests: true,
no_merge: false,
merge_threshold: Some(5),
dry_run: false,
session: None,
timeout: 30,
exact: false,
};
println!("Temp path: {temp_path:?}");
println!("Files in temp directory:");
for entry in std::fs::read_dir(temp_path).unwrap() {
let entry = entry.unwrap();
println!(" {:?}", entry.path());
}
let search_results = perform_probe(&options).unwrap();
println!("Search results: {} items", search_results.results.len());
for result in &search_results.results {
println!(" File: {}", result.file);
}
assert!(
!search_results.results.is_empty(),
"Search should return results"
);
let file_names: Vec<&str> = search_results
.results
.iter()
.map(|r| r.file.as_str())
.collect();
println!("Found {} results", search_results.results.len());
for result in &search_results.results {
println!("File: {}", result.file);
}
assert!(
file_names.iter().any(|&name| name.contains("file1")),
"Should find file1 which contains keywordAlpha OR keywordBeta"
);
assert!(
file_names.iter().any(|&name| name.contains("file2")),
"Should find file2 which contains keywordAlpha OR keywordGamma"
);
assert!(
file_names.iter().any(|&name| name.contains("file3")),
"Should find file3 which contains keywordBeta OR keywordGamma"
);
assert!(
file_names.iter().any(|&name| name.contains("file4")),
"Should find file4 which contains keywordAlpha, keywordBeta, and keywordGamma"
);
}
#[test]
fn test_excluded_term_query() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
create_test_files(temp_path);
let queries = vec!["(key OR word OR keyword) -keywordGamma".to_string()];
let custom_ignores: Vec<String> = vec![];
println!("Test files created in: {temp_path:?}");
for entry in std::fs::read_dir(temp_path).unwrap() {
let entry = entry.unwrap();
println!(" {:?}", entry.path());
let content = std::fs::read_to_string(entry.path()).unwrap();
println!(
" Content of {:?}:\n{}",
entry.path().file_name().unwrap(),
content
);
}
let options = SearchOptions {
path: temp_path,
queries: &queries,
files_only: false,
custom_ignores: &custom_ignores,
exclude_filenames: false,
language: None,
reranker: "hybrid",
frequency_search: false,
max_results: None,
max_bytes: None,
max_tokens: None,
allow_tests: true,
no_merge: false,
merge_threshold: Some(5),
dry_run: false,
session: None,
timeout: 30,
exact: false,
};
println!("Executing search with query: {queries:?}");
println!(
"Path: {:?}, frequency_search: {}",
options.path, options.frequency_search
);
let search_results = perform_probe(&options).unwrap();
assert!(
!search_results.results.is_empty(),
"Search should return results"
);
let file_names: Vec<&str> = search_results
.results
.iter()
.map(|r| r.file.as_str())
.collect();
println!(
"Excluded term query results: {} items",
search_results.results.len()
);
for result in &search_results.results {
println!(" File: {}", result.file);
}
assert!(
file_names.iter().any(|&name| name.contains("file1")),
"Should find file1 which contains key OR word OR keyword but not keywordGamma"
);
assert!(
!file_names.iter().any(|&name| name.contains("file2")),
"Should not find file2 which contains keywordGamma"
);
assert!(
!file_names.iter().any(|&name| name.contains("file3")),
"Should not find file3 which contains keywordGamma"
);
assert!(
!file_names.iter().any(|&name| name.contains("file4")),
"Should not find file4 which contains keywordGamma"
);
}
#[test]
fn test_or_query() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
create_test_files(temp_path);
let queries = vec!["keywordAlpha OR keywordBeta".to_string()];
let custom_ignores: Vec<String> = vec![];
let options = SearchOptions {
path: temp_path,
queries: &queries,
files_only: true, custom_ignores: &custom_ignores,
exclude_filenames: false,
language: None,
reranker: "hybrid",
frequency_search: true, max_results: None,
max_bytes: None,
max_tokens: None,
allow_tests: true,
no_merge: false,
merge_threshold: Some(5),
dry_run: false,
session: None,
timeout: 30,
exact: false,
};
println!("Test files created in: {temp_path:?}");
for entry in std::fs::read_dir(temp_path).unwrap() {
let entry = entry.unwrap();
println!(" {:?}", entry.path());
let content = std::fs::read_to_string(entry.path()).unwrap();
println!(
" Content of {:?}:\n{}",
entry.path().file_name().unwrap(),
content
);
}
println!("Executing search with query: {queries:?}");
println!(
"Path: {:?}, frequency_search: {}",
options.path, options.frequency_search
);
let search_results = perform_probe(&options).unwrap();
assert!(
!search_results.results.is_empty(),
"Search should return results"
);
let file_names: Vec<&str> = search_results
.results
.iter()
.map(|r| r.file.as_str())
.collect();
println!("Found files with 'keywordAlpha OR keywordBeta':");
for name in &file_names {
println!(" {name}");
}
assert!(
file_names.iter().any(|&name| name.contains("file1")),
"Should find file1 which contains keywordAlpha and keywordBeta"
);
assert!(
file_names.iter().any(|&name| name.contains("file2")),
"Should find file2 which contains keywordAlpha"
);
assert!(
file_names.iter().any(|&name| name.contains("file3")),
"Should find file3 which contains keywordBeta"
);
assert!(
file_names.iter().any(|&name| name.contains("file4")),
"Should find file4 which contains keywordAlpha and keywordBeta"
);
}
#[test]
fn test_complex_query_or() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
create_test_files(temp_path);
let queries = vec!["keywordAlpha OR keywordBeta".to_string()];
let custom_ignores: Vec<String> = vec![];
let options = SearchOptions {
path: temp_path,
queries: &queries,
files_only: false,
custom_ignores: &custom_ignores,
exclude_filenames: false,
language: None,
reranker: "hybrid",
frequency_search: true, max_results: None,
max_bytes: None,
max_tokens: None,
allow_tests: true,
no_merge: false,
merge_threshold: Some(5),
dry_run: false,
session: None,
timeout: 30,
exact: false,
};
println!("Test files created in: {temp_path:?}");
for entry in std::fs::read_dir(temp_path).unwrap() {
let entry = entry.unwrap();
println!(" {:?}", entry.path());
let content = std::fs::read_to_string(entry.path()).unwrap();
println!(
" Content of {:?}:\n{}",
entry.path().file_name().unwrap(),
content
);
}
println!("Executing search with query: {queries:?}");
println!(
"Path: {:?}, frequency_search: {}",
options.path, options.frequency_search
);
let search_results = perform_probe(&options).unwrap();
assert!(
!search_results.results.is_empty(),
"Search should return results"
);
let file_names: Vec<&str> = search_results
.results
.iter()
.map(|r| r.file.as_str())
.collect();
println!("Found files with 'keywordAlpha OR keywordBeta':");
for name in &file_names {
println!(" {name}");
}
assert!(
file_names.iter().any(|&name| name.contains("file1")),
"Should find file1 which has keywordAlpha and keywordBeta"
);
assert!(
file_names.iter().any(|&name| name.contains("file2")),
"Should find file2 which has keywordAlpha"
);
assert!(
file_names.iter().any(|&name| name.contains("file3")),
"Should find file3 which has keywordBeta"
);
assert!(
file_names.iter().any(|&name| name.contains("file4")),
"Should find file4 which has keywordAlpha and keywordBeta"
);
}
#[test]
fn test_complex_query_exclusion() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
create_test_files(temp_path);
let queries = vec!["\"keywordAlpha\" -keywordGamma".to_string()];
let custom_ignores: Vec<String> = vec![];
let options = SearchOptions {
path: temp_path,
queries: &queries,
files_only: false,
custom_ignores: &custom_ignores,
exclude_filenames: false,
language: None,
reranker: "hybrid",
frequency_search: false,
max_results: None,
max_bytes: None,
max_tokens: None,
allow_tests: true,
no_merge: false,
merge_threshold: Some(5),
dry_run: false,
session: None,
timeout: 30,
exact: false,
};
println!("Executing search with query: {queries:?}");
println!(
"Path: {:?}, frequency_search: {}",
options.path, options.frequency_search
);
let search_results = perform_probe(&options).unwrap();
assert!(
!search_results.results.is_empty(),
"Search should return results for query: {queries:?}"
);
let file_names: Vec<&str> = search_results
.results
.iter()
.map(|r| r.file.as_str())
.collect();
println!("Found files with 'keywordAlpha -keywordGamma':");
for name in &file_names {
println!(" {name}");
}
assert!(
file_names.iter().any(|&name| name.contains("file1")),
"Should find file1 which has keywordAlpha but no keywordGamma"
);
assert!(
!file_names.iter().any(|&name| name.contains("file2")),
"Should not find file2 which has keywordGamma"
);
assert!(
!file_names.iter().any(|&name| name.contains("file4")),
"Should not find file4 which has keywordGamma"
);
}
#[test]
fn test_underscore_handling_integration() {
let temp_dir = TempDir::new().unwrap();
let temp_path = temp_dir.path();
let file_path = temp_path.join("underscore_test.rs");
let file_content = r#"
// This file contains key word score
fn test_function() {
// This has key, word, and score
let x = 1;
// This also has key word score
let y = 2;
println!("Result: {}", x + y);
}
"#;
fs::write(file_path, file_content).unwrap();
let queries = vec!["key OR word OR score".to_string()];
let custom_ignores: Vec<String> = vec![];
let options = SearchOptions {
path: temp_path,
queries: &queries,
files_only: false,
custom_ignores: &custom_ignores,
exclude_filenames: false,
language: None,
reranker: "hybrid",
frequency_search: false,
max_results: None,
max_bytes: None,
max_tokens: None,
allow_tests: true,
no_merge: false,
merge_threshold: Some(5),
dry_run: false,
session: None,
timeout: 30,
exact: false,
};
let search_results = perform_probe(&options).unwrap();
assert!(
!search_results.results.is_empty(),
"Search should return results"
);
let file_names: Vec<&str> = search_results
.results
.iter()
.map(|r| r.file.as_str())
.collect();
println!("Found files with 'key OR word OR score':");
for name in &file_names {
println!(" {name}");
}
assert!(
file_names.iter().any(|&name| name.contains("underscore_test")),
"Should find underscore_test.rs which contains at least one of the terms: key, word, or score"
);
for result in &search_results.results {
println!("Result code: {}", result.code);
assert!(
result.code.contains("key")
|| result.code.contains("word")
|| result.code.contains("score"),
"Result code should contain at least one of the terms: 'key', 'word', or 'score'"
);
}
}
#[test]
fn test_filter_code_block_with_ast() {
let ast = Expr::And(
Box::new(Expr::Term {
keywords: vec!["keywordAlpha".to_string()],
field: None,
required: false,
excluded: false,
exact: false,
}),
Box::new(Expr::Term {
keywords: vec!["keywordBeta".to_string()],
field: None,
required: false,
excluded: true,
exact: false,
}),
);
let mut term_indices = HashMap::new();
term_indices.insert("keywordAlpha".to_string(), 0);
term_indices.insert("keywordBeta".to_string(), 1);
let plan = QueryPlan {
ast,
term_indices,
excluded_terms: {
let mut set = HashSet::new();
set.insert("keywordBeta".to_string());
set
},
exact: false,
};
let mut term_matches = HashMap::new();
let mut lines1 = HashSet::new();
lines1.insert(1);
lines1.insert(2);
term_matches.insert(0, lines1);
let block_lines = (1, 5);
let debug_mode = false;
use probe_code::search::file_processing::filter_code_block_with_ast;
assert!(
filter_code_block_with_ast(block_lines, &term_matches, &plan, debug_mode),
"Block should match because it has keywordAlpha but not keywordBeta"
);
let mut lines2 = HashSet::new();
lines2.insert(3);
lines2.insert(4);
term_matches.insert(1, lines2);
assert!(
!filter_code_block_with_ast(block_lines, &term_matches, &plan, debug_mode),
"Block should not match because it has keywordBeta which is excluded"
);
}
#[test]
fn test_filter_tokenized_block() {
let ast = Expr::And(
Box::new(Expr::Term {
keywords: vec!["keywordAlpha".to_string()],
field: None,
required: false,
excluded: false,
exact: false,
}),
Box::new(Expr::Term {
keywords: vec!["keywordBeta".to_string()],
field: None,
required: false,
excluded: true,
exact: false,
}),
);
let mut term_indices = HashMap::new();
term_indices.insert("keywordAlpha".to_string(), 0);
term_indices.insert("keywordBeta".to_string(), 1);
let plan = QueryPlan {
ast,
term_indices: term_indices.clone(),
excluded_terms: {
let mut set = HashSet::new();
set.insert("keywordBeta".to_string());
set
},
exact: false,
};
use probe_code::search::file_processing::filter_tokenized_block;
let tokenized_content = vec!["keywordAlpha".to_string()];
let debug_mode = false;
assert!(
filter_tokenized_block(&tokenized_content, &term_indices, &plan, debug_mode),
"Block should match because it has keywordAlpha but not keywordBeta"
);
let tokenized_content = vec!["keywordAlpha".to_string(), "keywordBeta".to_string()];
assert!(
!filter_tokenized_block(&tokenized_content, &term_indices, &plan, debug_mode),
"Block should not match because it has keywordBeta which is excluded"
);
let tokenized_content = vec!["other".to_string()];
assert!(
!filter_tokenized_block(&tokenized_content, &term_indices, &plan, debug_mode),
"Block should not match because it doesn't have keywordAlpha"
);
let tokenized_content: Vec<String> = vec![];
assert!(
!filter_tokenized_block(&tokenized_content, &term_indices, &plan, debug_mode),
"Empty block should not match"
);
let ast_or = Expr::Or(
Box::new(Expr::Term {
keywords: vec!["keywordAlpha".to_string()],
field: None,
required: false,
excluded: false,
exact: false,
}),
Box::new(Expr::Term {
keywords: vec!["keywordGamma".to_string()],
field: None,
required: false,
excluded: false,
exact: false,
}),
);
let mut term_indices_or = HashMap::new();
term_indices_or.insert("keywordAlpha".to_string(), 0);
term_indices_or.insert("keywordGamma".to_string(), 2);
let plan_or = QueryPlan {
ast: ast_or,
term_indices: term_indices_or.clone(),
excluded_terms: HashSet::new(),
exact: false,
};
let tokenized_content = vec!["keywordGamma".to_string()];
assert!(
filter_tokenized_block(&tokenized_content, &term_indices_or, &plan_or, debug_mode),
"Block should match because it has keywordGamma (part of OR expression)"
);
let tokenized_content = vec!["keywordAlpha".to_string(), "keywordGamma".to_string()];
assert!(
filter_tokenized_block(&tokenized_content, &term_indices_or, &plan_or, debug_mode),
"Block should match because it has both keywords in OR expression"
);
}