use std::fs;
use std::path::PathBuf;
use std::process::Command;
use probe_code::extract::{
extract_file_paths_from_git_diff, format_and_print_extraction_results, is_git_diff_format,
process_file_for_extraction,
};
#[test]
fn test_process_file_for_extraction_full_file() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.txt");
let content = "Line 1\nLine 2\nLine 3\n";
fs::write(&file_path, content).unwrap();
let result = process_file_for_extraction(&file_path, None, None, None, false, 0, None).unwrap();
assert_eq!(result.file, file_path.to_string_lossy().to_string());
assert_eq!(result.lines, (1, 3)); assert_eq!(result.node_type, "file");
assert_eq!(result.code, content);
let non_existent = temp_dir.path().join("non_existent.txt");
let err =
process_file_for_extraction(&non_existent, None, None, None, false, 0, None).unwrap_err();
assert!(err.to_string().contains("does not exist"));
}
#[test]
fn test_process_file_for_extraction_with_line() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn main() {
println!("Hello, world!");
let x = 42;
if x > 0 {
println!("Positive");
} else {
println!("Non-positive");
}
}
struct Point {
x: i32,
y: i32,
}
impl Point {
fn new(x: i32, y: i32) -> Self {
Self { x, y }
}
}
"#;
fs::write(&file_path, content).unwrap();
let result =
process_file_for_extraction(&file_path, Some(3), None, None, false, 0, None).unwrap();
assert_eq!(result.file, file_path.to_string_lossy().to_string());
assert!(result.lines.0 <= 3 && result.lines.1 >= 3);
assert!(result.code.contains("fn main()"));
assert!(result.code.contains("Hello, world!"));
let result =
process_file_for_extraction(&file_path, Some(13), None, None, false, 0, None).unwrap();
assert_eq!(result.file, file_path.to_string_lossy().to_string());
assert!(result.lines.0 <= 13 && result.lines.1 >= 13);
assert!(result.code.contains("struct Point"));
assert!(result.code.contains("x: i32"));
assert!(result.code.contains("y: i32"));
let result =
process_file_for_extraction(&file_path, Some(1000), None, None, false, 0, None).unwrap();
assert!(result.lines.0 <= result.lines.1);
assert!(result.lines.1 <= content.lines().count());
}
#[test]
fn test_process_file_for_extraction_fallback() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.xyz");
let mut content = String::from(
"Line 1\nLine 2\nLine 3\nLine 4\nLine 5\nLine 6\nLine 7\nLine 8\nLine 9\nLine 10\n",
);
content.push_str("Line 11\nLine 12\nLine 13\nLine 14\nLine 15\nLine 16\nLine 17\nLine 18\nLine 19\nLine 20\n");
content.push_str("Line 21\nLine 22\nLine 23\nLine 24\nLine 25\n");
fs::write(&file_path, content).unwrap();
let result =
process_file_for_extraction(&file_path, Some(15), None, None, false, 10, None).unwrap();
assert_eq!(result.file, file_path.to_string_lossy().to_string());
assert_eq!(result.node_type, "context");
let start_line = result.lines.0;
let end_line = result.lines.1;
assert!(start_line <= 15 && end_line >= 15);
assert!(end_line - start_line >= 10);
let result =
process_file_for_extraction(&file_path, Some(2), None, None, false, 10, None).unwrap();
assert!(result.lines.0 <= 2); assert!(result.lines.1 >= 2);
let result =
process_file_for_extraction(&file_path, Some(25), None, None, false, 10, None).unwrap();
assert!(result.lines.0 <= 25); assert_eq!(result.lines.1, 25);
let result =
process_file_for_extraction(&file_path, Some(15), None, None, false, 5, None).unwrap();
assert_eq!(result.file, file_path.to_string_lossy().to_string());
assert_eq!(result.node_type, "context");
let start_line = result.lines.0;
let end_line = result.lines.1;
assert!(start_line <= 15 && end_line >= 15);
assert!(end_line - start_line >= 5); assert!(end_line - start_line <= 11); }
#[test]
fn test_format_and_print_extraction_results() {
let result = probe_code::models::SearchResult {
file: "test_file.rs".to_string(),
lines: (1, 5),
node_type: "function".to_string(),
code: "fn test() {\n println!(\"Hello\");\n}".to_string(),
matched_by_filename: None,
rank: None,
score: None,
tfidf_score: None,
bm25_score: None,
tfidf_rank: None,
bm25_rank: None,
new_score: None,
hybrid2_rank: None,
combined_score_rank: None,
file_unique_terms: None,
file_total_matches: None,
file_match_rank: None,
block_unique_terms: None,
block_total_matches: None,
parent_file_id: None,
block_id: None,
matched_keywords: None,
tokenized_content: None,
};
let results = vec![result];
format_and_print_extraction_results(&results, "terminal", None, None, None).unwrap();
format_and_print_extraction_results(&results, "markdown", None, None, None).unwrap();
format_and_print_extraction_results(&results, "plain", None, None, None).unwrap();
format_and_print_extraction_results(&results, "json", None, None, None).unwrap();
format_and_print_extraction_results(&results, "xml", None, None, None).unwrap();
format_and_print_extraction_results(
&results,
"terminal",
None,
Some("Test system prompt"),
Some("Test user instructions"),
)
.unwrap();
}
#[test]
fn test_json_format_extraction_results() {
use serde_json::Value;
use std::process::Command;
use tempfile::TempDir;
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn test() {
println!("Hello");
}
"#;
fs::write(&file_path, content).unwrap();
println!("File path: {}", file_path.display());
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
file_path.to_string_lossy().as_ref(),
"--format",
"json",
"--allow-tests", "--prompt",
"engineer",
"--instructions",
"Extract the main function",
])
.output()
.expect("Failed to execute command");
println!(
"Command stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
println!(
"Command stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.status.success(),
"Command failed with status: {}",
output.status
);
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
fn extract_json_from_output(output: &str) -> &str {
if let Some(start_index) = output.find('{') {
&output[start_index..]
} else {
output
}
}
println!("Command stdout: {stdout}");
let json_str = extract_json_from_output(&stdout);
println!("Extracted JSON: {json_str}");
let json_value: Value = serde_json::from_str(json_str).expect("Failed to parse JSON output");
assert!(json_value.is_object(), "JSON output should be an object");
assert!(
json_value.get("results").is_some(),
"JSON output should have a 'results' field"
);
assert!(
json_value.get("summary").is_some(),
"JSON output should have a 'summary' field"
);
let results_array = json_value.get("results").unwrap().as_array().unwrap();
assert_eq!(
results_array.len(),
1,
"Results array should have 1 element"
);
let result_obj = &results_array[0];
assert!(
result_obj.get("file").is_some(),
"Result should have a 'file' field"
);
assert!(
result_obj.get("lines").is_some(),
"Result should have a 'lines' field"
);
assert!(
result_obj.get("node_type").is_some(),
"Result should have a 'node_type' field"
);
assert!(
result_obj.get("code").is_some(),
"Result should have a 'code' field"
);
assert!(result_obj
.get("file")
.unwrap()
.as_str()
.unwrap()
.contains("test_file.rs"));
assert!(
result_obj.get("node_type").is_some(),
"Result should have a node_type field"
);
assert!(
result_obj
.get("code")
.unwrap()
.as_str()
.unwrap()
.contains("fn test()")
&& result_obj
.get("code")
.unwrap()
.as_str()
.unwrap()
.contains("println!(\"Hello\")")
);
let lines = result_obj.get("lines").unwrap().as_array().unwrap();
assert_eq!(lines.len(), 2, "Lines should be an array with 2 elements");
assert!(
lines[0].as_u64().unwrap() >= 1,
"Start line should be at least 1"
);
assert!(
lines[1].as_u64().unwrap() >= lines[0].as_u64().unwrap(),
"End line should be at least start line"
);
let summary = json_value.get("summary").unwrap();
assert!(summary.is_object(), "Summary should be an object");
assert!(
summary.get("count").is_some(),
"Summary should have a 'count' field"
);
assert!(
summary.get("total_bytes").is_some(),
"Summary should have a 'total_bytes' field"
);
assert!(
summary.get("total_tokens").is_some(),
"Summary should have a 'total_tokens' field"
);
assert_eq!(summary.get("count").unwrap().as_u64().unwrap(), 1);
assert!(
json_value.get("system_prompt").is_some(),
"JSON output should have a 'system_prompt' field"
);
assert!(
json_value.get("user_instructions").is_some(),
"JSON output should have a 'user_instructions' field"
);
assert!(
json_value
.get("user_instructions")
.unwrap()
.as_str()
.unwrap()
== "Extract the main function",
"user_instructions should match the provided value"
);
}
#[test]
fn test_xml_format_extraction_results() {
use roxmltree::{Document, Node};
use std::process::Command;
use tempfile::TempDir;
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn test() {
println!("Hello");
}
"#;
fs::write(&file_path, content).unwrap();
println!("File path: {}", file_path.display());
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
file_path.to_string_lossy().as_ref(),
"--format",
"xml",
"--allow-tests", "--prompt",
"engineer",
"--instructions",
"Extract the main function",
])
.output()
.expect("Failed to execute command");
println!(
"Command stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
println!(
"Command stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.status.success(),
"Command failed with status: {}",
output.status
);
let stdout = String::from_utf8_lossy(&output.stdout);
fn extract_xml_from_output(output: &str) -> &str {
if let Some(start_index) = output.find("<?xml") {
&output[start_index..]
} else {
output
}
}
println!("Command stdout: {stdout}");
let xml_str = extract_xml_from_output(&stdout);
println!("Extracted XML: {xml_str}");
let doc = Document::parse(xml_str).expect("Failed to parse XML output");
let root = doc.root_element();
assert_eq!(
root.tag_name().name(),
"probe_results",
"Root element should be 'probe_results'"
);
let results_nodes: Vec<Node> = root
.children()
.filter(|n| n.is_element() && n.tag_name().name() == "result")
.collect();
assert_eq!(
results_nodes.len(),
1,
"Should have exactly one result element"
);
let result_node = &results_nodes[0];
let file = result_node
.children()
.find(|n| n.is_element() && n.tag_name().name() == "file");
assert!(file.is_some(), "Result should have a file element");
assert!(
file.unwrap().text().unwrap().contains("test_file.rs"),
"File element should contain the correct file path"
);
let node_type = result_node
.children()
.find(|n| n.is_element() && n.tag_name().name() == "node_type");
if let Some(node_type) = node_type {
if node_type.text().unwrap() != "file" {
let lines = result_node
.children()
.find(|n| n.is_element() && n.tag_name().name() == "lines");
assert!(lines.is_some(), "Result should have a lines element");
}
let system_prompt = root
.children()
.find(|n| n.is_element() && n.tag_name().name() == "system_prompt");
assert!(
system_prompt.is_some(),
"Should have a system_prompt element"
);
let user_instructions = root
.children()
.find(|n| n.is_element() && n.tag_name().name() == "user_instructions");
assert!(
user_instructions.is_some(),
"Should have a user_instructions element"
);
assert_eq!(
user_instructions.unwrap().text().unwrap(),
"Extract the main function",
"user_instructions should match the provided value"
);
}
let _node_type = result_node
.children()
.find(|n| n.is_element() && n.tag_name().name() == "node_type");
let code = result_node
.children()
.find(|n| n.is_element() && n.tag_name().name() == "code");
assert!(code.is_some(), "Result should have a code element");
assert!(
code.unwrap().text().unwrap().contains("fn test()")
&& code
.unwrap()
.text()
.unwrap()
.contains("println!(\"Hello\")"),
"Code element should contain the correct code"
);
let summary = root
.children()
.find(|n| n.is_element() && n.tag_name().name() == "summary");
assert!(summary.is_some(), "Should have a summary element");
if let Some(summary) = summary {
let count = summary
.children()
.find(|n| n.is_element() && n.tag_name().name() == "count");
assert!(count.is_some(), "Summary should have a count element");
assert_eq!(count.unwrap().text().unwrap(), "1", "Count should be 1");
let total_bytes = summary
.children()
.find(|n| n.is_element() && n.tag_name().name() == "total_bytes");
assert!(
total_bytes.is_some(),
"Summary should have a total_bytes element"
);
let total_tokens = summary
.children()
.find(|n| n.is_element() && n.tag_name().name() == "total_tokens");
assert!(
total_tokens.is_some(),
"Summary should have a total_tokens element"
);
}
}
#[test]
fn test_process_file_for_extraction_with_range() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.txt");
let mut content = String::new();
for i in 1..=20 {
content.push_str(&format!("Line {i}\n"));
}
fs::write(&file_path, &content).unwrap();
let result =
process_file_for_extraction(&file_path, Some(1), Some(10), None, false, 0, None).unwrap();
assert_eq!(result.file, file_path.to_string_lossy().to_string());
assert_eq!(result.lines, (1, 10));
assert_eq!(result.node_type, "range");
let expected_content = content.lines().take(10).collect::<Vec<_>>().join("\n");
assert_eq!(result.code, expected_content);
let result =
process_file_for_extraction(&file_path, Some(5), Some(15), None, false, 0, None).unwrap();
assert_eq!(result.lines, (5, 15));
let expected_content = content
.lines()
.skip(4)
.take(11)
.collect::<Vec<_>>()
.join("\n");
assert_eq!(result.code, expected_content);
let result =
process_file_for_extraction(&file_path, Some(10), Some(5), None, false, 0, None).unwrap();
assert!(result.lines.0 <= result.lines.1);
assert!(result.lines.1 <= content.lines().count());
let result =
process_file_for_extraction(&file_path, Some(15), Some(25), None, false, 0, None).unwrap();
assert!(result.lines.0 <= 15);
assert!(result.lines.1 <= content.lines().count());
assert_eq!(result.node_type, "range");
}
#[test]
fn test_integration_extract_command() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn main() {
println!("Hello, world!");
}
struct Point {
x: i32,
y: i32,
}
"#;
fs::write(&file_path, content).unwrap();
println!("Current directory: {:?}", std::env::current_dir().unwrap());
println!("File path: {file_path:?}");
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
println!("Project directory: {project_dir:?}");
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
file_path.to_string_lossy().as_ref(),
"--allow-tests", ])
.current_dir(&project_dir) .output()
.expect("Failed to execute command");
println!(
"Command stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
println!(
"Command stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("File:"));
assert!(stdout.contains("fn main()"));
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
&format!("{}:3", file_path.to_string_lossy()),
"--allow-tests", ])
.current_dir(&project_dir) .output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("File:"));
assert!(stdout.contains("fn main()"));
assert!(stdout.contains("Hello, world!"));
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
file_path.to_string_lossy().as_ref(),
"--format",
"markdown",
"--allow-tests", ])
.current_dir(&project_dir) .output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("## File:"));
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
&format!("{}:2-7", file_path.to_string_lossy()),
"--allow-tests", ])
.current_dir(&project_dir) .output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(stdout.contains("File:"));
assert!(
stdout.contains("fn main()")
|| stdout.contains("println!(\"Hello, world!\")")
|| stdout.contains("struct Point")
);
}
#[test]
fn test_integration_extract_command_json_format() {
use serde_json::Value;
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn main() {
// This contains special characters: "quotes", 'apostrophes', <tags>, &ersands
println!("Hello, \"world\"!");
let message = 'A';
let html = "<div>Content & More</div>";
}
"#;
fs::write(&file_path, content).unwrap();
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
file_path.to_string_lossy().as_ref(),
"--format",
"json",
"--allow-tests", ])
.output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
fn extract_json_from_output(output: &str) -> &str {
if let Some(start_index) = output.find('{') {
&output[start_index..]
} else {
output
}
}
let json_str = extract_json_from_output(&stdout);
let json_value: Value = serde_json::from_str(json_str).expect("Failed to parse JSON output");
assert!(json_value.is_object(), "JSON output should be an object");
assert!(
json_value.get("results").is_some(),
"JSON output should have a 'results' field"
);
assert!(
json_value.get("summary").is_some(),
"JSON output should have a 'summary' field"
);
let results = json_value.get("results").unwrap().as_array().unwrap();
assert!(!results.is_empty(), "Results array should not be empty");
let first_result = &results[0];
assert!(
first_result.get("file").is_some(),
"Result should have a 'file' field"
);
assert!(
first_result.get("code").is_some(),
"Result should have a 'code' field"
);
let code = first_result.get("code").unwrap().as_str().unwrap();
assert!(
code.contains("\"quotes\""),
"Double quotes should be properly escaped in JSON"
);
assert!(
code.contains("'apostrophes'"),
"Apostrophes should be properly escaped in JSON"
);
assert!(
code.contains("<tags>"),
"Tags should be properly escaped in JSON"
);
assert!(
code.contains("&ersands"),
"Ampersands should be properly escaped in JSON"
);
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
&format!("{}:3", file_path.to_string_lossy()),
"--format",
"json",
"--allow-tests", ])
.current_dir(&project_dir) .output()
.expect("Failed to execute command");
println!(
"Command stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
println!(
"Command stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.status.success(),
"Command failed with status: {}",
output.status
);
let stdout = String::from_utf8_lossy(&output.stdout);
let json_str = extract_json_from_output(&stdout);
let json_value: Value = serde_json::from_str(json_str).expect("Failed to parse JSON output");
let results = json_value.get("results").unwrap().as_array().unwrap();
assert!(!results.is_empty(), "Results array should not be empty");
let first_result = &results[0];
let code = first_result.get("code").unwrap().as_str().unwrap();
assert!(
code.contains("fn main()"),
"Code should contain the main function"
);
assert!(
code.contains("Hello, \\\"world\\\"!"),
"Code should contain the println statement with escaped quotes"
);
}
#[test]
fn test_is_git_diff_format() {
let diff_content = r#"diff --git a/tests/property_tests.rs b/tests/property_tests.rs
index cb2cb64..3717769 100644
--- a/tests/property_tests.rs
+++ b/tests/property_tests.rs
@@ -45,7 +45,7 @@ proptest! {
Err(_) => return proptest::test_runner::TestCaseResult::Ok(()), // Skip invalid queries
};
- let patterns = create_structured_patterns(&plan);
+ let patterns = create_structured_patterns(&plan, false);
"#;
assert!(
is_git_diff_format(diff_content),
"Should detect git diff format"
);
let diff_content_with_whitespace = r#"
diff --git a/tests/property_tests.rs b/tests/property_tests.rs
index cb2cb64..3717769 100644
"#;
assert!(
is_git_diff_format(diff_content_with_whitespace),
"Should detect git diff format with leading whitespace"
);
let non_diff_content = r#"This is not a git diff format
It's just some text
"#;
assert!(
!is_git_diff_format(non_diff_content),
"Should not detect git diff format"
);
}
#[test]
fn test_extract_file_paths_from_git_diff() {
let diff_content = r#"diff --git a/tests/property_tests.rs b/tests/property_tests.rs
index cb2cb64..3717769 100644
--- a/tests/property_tests.rs
+++ b/tests/property_tests.rs
@@ -45,7 +45,7 @@ proptest! {
Err(_) => return proptest::test_runner::TestCaseResult::Ok(()), // Skip invalid queries
};
- let patterns = create_structured_patterns(&plan);
+ let patterns = create_structured_patterns(&plan, false);
// Check that we have at least one pattern for each term
for (term, &idx) in &plan.term_indices {
"#;
let file_paths = extract_file_paths_from_git_diff(diff_content, true);
assert_eq!(file_paths.len(), 1, "Should extract exactly one file path");
let (path, start_line, end_line, symbol, _specific_lines) = &file_paths[0];
assert_eq!(
path,
&PathBuf::from("tests/property_tests.rs"),
"Should extract the correct file path"
);
assert_eq!(
*start_line,
Some(48),
"Should extract the correct line number"
);
assert_eq!(*end_line, Some(48), "End line should be 48");
assert_eq!(*symbol, None, "Symbol should be None");
}
#[test]
fn test_extract_file_paths_from_git_diff_multiple_files() {
let diff_content = r#"diff --git a/tests/property_tests.rs b/tests/property_tests.rs
index cb2cb64..3717769 100644
--- a/tests/property_tests.rs
+++ b/tests/property_tests.rs
@@ -45,7 +45,7 @@ proptest! {
Err(_) => return proptest::test_runner::TestCaseResult::Ok(()), // Skip invalid queries
};
- let patterns = create_structured_patterns(&plan);
+ let patterns = create_structured_patterns(&plan, false);
// Check that we have at least one pattern for each term
for (term, &idx) in &plan.term_indices {
diff --git a/tests/tokenization_tests.rs b/tests/tokenization_tests.rs
index abcdef1..1234567 100644
--- a/tests/tokenization_tests.rs
+++ b/tests/tokenization_tests.rs
@@ -20,7 +20,7 @@ fn test_tokenize_with_stemming() {
let tokens = tokenize_with_stemming("running runs runner");
- assert_eq!(tokens, vec!["run", "run", "runner"]);
+ assert_eq!(tokens, vec!["run", "run", "run"]);
}
"#;
let file_paths = extract_file_paths_from_git_diff(diff_content, true);
assert_eq!(file_paths.len(), 2, "Should extract exactly two file paths");
let mut sorted_paths = file_paths.clone();
sorted_paths.sort_by(|a, b| a.0.cmp(&b.0));
let (path1, start_line1, end_line1, symbol1, _specific_lines1) = &sorted_paths[0];
assert_eq!(
path1,
&PathBuf::from("tests/property_tests.rs"),
"Should extract the correct file path"
);
assert_eq!(
*start_line1,
Some(48),
"Should extract the correct line number"
);
assert_eq!(*end_line1, Some(48), "End line should be 48");
assert_eq!(*symbol1, None, "Symbol should be None");
let (path2, start_line2, end_line2, symbol2, _specific_lines2) = &sorted_paths[1];
assert_eq!(
path2,
&PathBuf::from("tests/tokenization_tests.rs"),
"Should extract the correct file path"
);
assert_eq!(
*start_line2,
Some(22),
"Should extract the correct line number"
);
assert_eq!(*end_line2, Some(22), "End line should be 22");
assert_eq!(*symbol2, None, "Symbol should be None");
}
#[test]
fn test_integration_extract_command_with_diff_flag() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn main() {
println!("Hello, world!");
}
struct Point {
x: i32,
y: i32,
}
"#;
fs::write(&file_path, content).unwrap();
let diff_path = temp_dir.path().join("test.diff");
let diff_content = format!(
r#"diff --git a/{0} b/{0}
index cb2cb64..3717769 100644
--- a/{0}
+++ b/{0}
@@ -3,1 +3,1 @@ fn main() {{
- println!("Hello, world!");
+ println!("Hello, universe!");
"#,
file_path.file_name().unwrap().to_string_lossy()
);
fs::write(&diff_path, &diff_content).unwrap();
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
"--diff",
diff_path.to_string_lossy().as_ref(),
"--allow-tests", ])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "Command failed to execute");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Command output: {stdout}");
assert!(
stdout.contains("Files to extract:"),
"Output should contain file list"
);
assert!(
stdout.contains("test_file.rs"),
"Output should contain the extracted file name"
);
}
#[test]
fn test_integration_extract_command_with_auto_diff_detection() {
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn main() {
println!("Hello, world!");
}
struct Point {
x: i32,
y: i32,
}
"#;
fs::write(&file_path, content).unwrap();
let diff_path = temp_dir.path().join("test.diff");
let diff_content = format!(
r#"diff --git a/{0} b/{0}
index cb2cb64..3717769 100644
--- a/{0}
+++ b/{0}
@@ -3,1 +3,1 @@ fn main() {{
- println!("Hello, world!");
+ println!("Hello, universe!");
"#,
file_path.file_name().unwrap().to_string_lossy()
);
fs::write(&diff_path, &diff_content).unwrap();
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
diff_path.to_string_lossy().as_ref(),
"--allow-tests", ])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "Command failed to execute");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Command output: {stdout}");
assert!(
stdout.contains("Files to extract:"),
"Output should contain file list"
);
assert!(
stdout.contains("test_file.rs"),
"Output should contain the extracted file name"
);
}
#[test]
fn test_integration_extract_command_xml_format() {
use roxmltree::Document;
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn main() {
// This contains special characters: "quotes", 'apostrophes', <tags>, &ersands
println!("Hello, \"world\"!");
let message = 'A';
let html = "<div>Content & More</div>";
}
"#;
fs::write(&file_path, content).unwrap();
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
file_path.to_string_lossy().as_ref(),
"--format",
"xml",
"--allow-tests", ])
.current_dir(&project_dir) .output()
.expect("Failed to execute command");
assert!(output.status.success());
let stdout = String::from_utf8_lossy(&output.stdout);
fn extract_xml_from_output(output: &str) -> &str {
if let Some(start_index) = output.find("<?xml") {
&output[start_index..]
} else {
output
}
}
let xml_str = extract_xml_from_output(&stdout);
let doc = Document::parse(xml_str).expect("Failed to parse XML output");
let root = doc.root_element();
assert_eq!(
root.tag_name().name(),
"probe_results",
"Root element should be 'probe_results'"
);
let results: Vec<_> = root
.children()
.filter(|n| n.is_element() && n.tag_name().name() == "result")
.collect();
assert!(
!results.is_empty(),
"Should have at least one result element"
);
let first_result = &results[0];
let file = first_result
.children()
.find(|n| n.is_element() && n.tag_name().name() == "file");
assert!(file.is_some(), "Result should have a file element");
let code = first_result
.children()
.find(|n| n.is_element() && n.tag_name().name() == "code");
assert!(code.is_some(), "Result should have a code element");
if let Some(code_elem) = code {
let code_text = code_elem.text().unwrap();
assert!(
code_text.contains("\"quotes\""),
"Double quotes should be preserved in CDATA"
);
assert!(
code_text.contains("'apostrophes'"),
"Apostrophes should be preserved in CDATA"
);
assert!(
code_text.contains("<tags>"),
"Tags should be preserved in CDATA"
);
assert!(
code_text.contains("&ersands"),
"Ampersands should be preserved in CDATA"
);
}
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
&format!("{}:3", file_path.to_string_lossy()),
"--format",
"xml",
"--allow-tests", ])
.current_dir(&project_dir) .output()
.expect("Failed to execute command");
println!(
"Command stdout: {}",
String::from_utf8_lossy(&output.stdout)
);
println!(
"Command stderr: {}",
String::from_utf8_lossy(&output.stderr)
);
assert!(
output.status.success(),
"Command failed with status: {}",
output.status
);
let stdout = String::from_utf8_lossy(&output.stdout);
let xml_str = extract_xml_from_output(&stdout);
let doc = Document::parse(xml_str).expect("Failed to parse XML output");
let root = doc.root_element();
let results: Vec<_> = root
.children()
.filter(|n| n.is_element() && n.tag_name().name() == "result")
.collect();
assert!(
!results.is_empty(),
"Should have at least one result element"
);
let first_result = &results[0];
if let Some(code_elem) = first_result
.children()
.find(|n| n.is_element() && n.tag_name().name() == "code")
{
let code_text = code_elem.text().unwrap();
assert!(
code_text.contains("fn main()"),
"Code should contain the main function"
);
assert!(
code_text.contains("Hello") && code_text.contains("world"),
"Code should contain the println statement"
);
}
}
#[test]
fn test_integration_extract_command_with_multiple_files_diff() {
let temp_dir = tempfile::tempdir().unwrap();
let original_dir = std::env::current_dir().unwrap();
std::env::set_current_dir(&temp_dir).unwrap();
let file_path1 = PathBuf::from("property_tests.rs");
let content1 = r#"
fn test_create_structured_patterns() {
let plan = create_query_plan("test query", false).unwrap();
let patterns = create_structured_patterns(&plan);
// Check that we have at least one pattern for each term
for (term, &idx) in &plan.term_indices {
assert!(!patterns[idx].is_empty());
}
}
"#;
fs::write(&file_path1, content1).unwrap();
let file_path2 = PathBuf::from("tokenization_tests.rs");
let content2 = r#"
fn test_tokenize_with_stemming() {
let tokens = tokenize_with_stemming("running runs runner");
assert_eq!(tokens, vec!["run", "run", "runner"]);
}
"#;
fs::write(&file_path2, content2).unwrap();
let status = Command::new("git")
.args(["init"])
.status()
.expect("Failed to initialize git repo");
assert!(status.success(), "Failed to initialize git repo");
let status = Command::new("git")
.args(["config", "user.name", "Test User"])
.status()
.expect("Failed to configure git");
assert!(status.success(), "Failed to configure git");
let status = Command::new("git")
.args(["config", "user.email", "test@example.com"])
.status()
.expect("Failed to configure git");
assert!(status.success(), "Failed to configure git");
let status = Command::new("git")
.args(["add", "."])
.status()
.expect("Failed to add files to git");
assert!(status.success(), "Failed to add files to git");
let status = Command::new("git")
.args(["commit", "-m", "Initial commit"])
.status()
.expect("Failed to commit files");
assert!(status.success(), "Failed to commit files");
let content1_modified = r#"
fn test_create_structured_patterns() {
let plan = create_query_plan("test query", false).unwrap();
let patterns = create_structured_patterns(&plan, false);
// Check that we have at least one pattern for each term
for (term, &idx) in &plan.term_indices {
assert!(!patterns[idx].is_empty());
}
}
"#;
fs::write(&file_path1, content1_modified).unwrap();
let content2_modified = r#"
fn test_tokenize_with_stemming() {
let tokens = tokenize_with_stemming("running runs runner");
assert_eq!(tokens, vec!["run", "run", "run"]);
}
"#;
fs::write(&file_path2, content2_modified).unwrap();
let diff_output = Command::new("git")
.args(["diff", "property_tests.rs", "tokenization_tests.rs"])
.output()
.expect("Failed to create git diff");
assert!(diff_output.status.success(), "Failed to create git diff");
let diff_content = String::from_utf8_lossy(&diff_output.stdout).to_string();
let diff_path = PathBuf::from("changes.diff");
fs::write(&diff_path, &diff_content).unwrap();
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
diff_path.to_string_lossy().as_ref(),
"--allow-tests", ])
.output()
.expect("Failed to execute command");
std::env::set_current_dir(original_dir).unwrap();
assert!(output.status.success(), "Command failed to execute");
let stdout = String::from_utf8_lossy(&output.stdout);
println!("Command output: {stdout}");
assert!(
stdout.contains("Files to extract:"),
"Output should contain file list"
);
assert!(
stdout.contains("changes.diff"),
"Output should contain the diff file name"
);
assert!(
stdout.contains("property_tests.rs"),
"Output should contain the first file name in diff content"
);
assert!(
stdout.contains("tokenization_tests.rs"),
"Output should contain the second file name in diff content"
);
let file_count = stdout.matches("File:").count();
assert_eq!(file_count, 1, "Should process the diff file");
}
#[test]
fn test_keep_input_option_with_stdin() {
use std::io::Write;
use std::process::{Command, Stdio};
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn main() {
println!("Hello, world!");
}
"#;
fs::write(&file_path, content).unwrap();
let input_content = format!("{}", file_path.to_string_lossy());
let mut child = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
"--keep-input",
"--allow-tests", ])
.stdin(Stdio::piped())
.stdout(Stdio::piped())
.spawn()
.expect("Failed to spawn command");
{
let stdin = child.stdin.as_mut().expect("Failed to open stdin");
stdin
.write_all(input_content.as_bytes())
.expect("Failed to write to stdin");
}
let output = child.wait_with_output().expect("Failed to read stdout");
assert!(output.status.success(), "Command failed to execute");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Original Input:"),
"Output should contain 'Original Input:' section"
);
assert!(
stdout.contains(&input_content),
"Output should contain the original input content"
);
}
#[test]
fn test_keep_input_option_with_clipboard() {
use arboard::Clipboard;
use std::process::Command;
let clipboard_result = Clipboard::new();
if clipboard_result.is_err() {
println!("Skipping clipboard test as clipboard access is not available");
return;
}
let project_dir = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"));
let temp_dir = tempfile::tempdir().unwrap();
let file_path = temp_dir.path().join("test_file.rs");
let content = r#"
fn main() {
println!("Hello, world!");
}
"#;
fs::write(&file_path, content).unwrap();
let input_content = format!("{}", file_path.to_string_lossy());
let mut clipboard = Clipboard::new().unwrap();
clipboard.set_text(&input_content).unwrap();
let output = Command::new("cargo")
.args([
"run",
"--manifest-path",
project_dir.join("Cargo.toml").to_string_lossy().as_ref(),
"--",
"extract",
"--from-clipboard",
"--keep-input",
"--allow-tests", ])
.output()
.expect("Failed to execute command");
assert!(output.status.success(), "Command failed to execute");
let stdout = String::from_utf8_lossy(&output.stdout);
assert!(
stdout.contains("Original Input:"),
"Output should contain 'Original Input:' section"
);
assert!(
stdout.contains(&input_content),
"Output should contain the original input content"
);
}