use splice::graph::CodeGraph;
use splice::ingest::cpp::extract_cpp_symbols;
use splice::patch::apply_patch_with_validation;
use splice::resolve::resolve_symbol;
use splice::symbol::Language;
use splice::validate::AnalyzerMode;
use tempfile::TempDir;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpp_patch_succeeds_with_all_gates() {
let workspace_dir = TempDir::new().expect("Failed to create temp workspace");
let workspace_path = workspace_dir.path();
let cpp_path = workspace_path.join("test.cpp");
let source = r#"
int greet(const char* name) {
return 0;
}
int farewell(const char* name) {
return 0;
}
"#;
std::fs::write(&cpp_path, source).expect("Failed to write test.cpp");
let graph_db_path = workspace_path.join("graph.db");
let mut code_graph =
CodeGraph::open(&graph_db_path).expect("Failed to open graph database");
let symbols =
extract_cpp_symbols(&cpp_path, source.as_bytes()).expect("Failed to parse test.cpp");
assert_eq!(symbols.len(), 2, "Expected 2 functions");
for symbol in &symbols {
code_graph
.store_symbol_with_file_and_language(
&cpp_path,
&symbol.name,
symbol.kind.as_str(),
Language::Cpp,
symbol.byte_start,
symbol.byte_end,
symbol.line_start,
symbol.line_end,
symbol.col_start,
symbol.col_end,
)
.expect("Failed to store symbol");
}
let resolved = resolve_symbol(&code_graph, Some(&cpp_path), Some("function"), "greet")
.expect("Failed to resolve greet function");
let greet_symbol = &symbols[0];
assert_eq!(resolved.name, "greet");
assert_eq!(resolved.byte_start, greet_symbol.byte_start);
assert_eq!(resolved.byte_end, greet_symbol.byte_end);
let new_body = r#"
int greet(const char* name) {
return 42;
}
"#;
let result = apply_patch_with_validation(
&cpp_path,
resolved.byte_start,
resolved.byte_end,
new_body.trim(),
workspace_path,
Language::Cpp,
AnalyzerMode::Off,
false, false, );
if result.is_ok() {
let new_content =
std::fs::read_to_string(&cpp_path).expect("Failed to read patched file");
assert!(
new_content.contains("return 42;"),
"Patched content should be present"
);
assert!(
!new_content.contains("return 0;") || new_content.matches("return 0;").count() == 1,
"Old greet content should be gone (farewell still has return 0)"
);
} else {
println!("g++ not available, skipping full patch validation test");
}
}
#[test]
fn test_cpp_patch_rejected_on_syntax_gate() {
let workspace_dir = TempDir::new().expect("Failed to create temp workspace");
let workspace_path = workspace_dir.path();
let cpp_path = workspace_path.join("test.cpp");
let source = r#"
int valid_function() {
return 42;
}
"#;
std::fs::write(&cpp_path, source).expect("Failed to write test.cpp");
let graph_db_path = workspace_path.join("graph.db");
let mut code_graph =
CodeGraph::open(&graph_db_path).expect("Failed to open graph database");
let symbols =
extract_cpp_symbols(&cpp_path, source.as_bytes()).expect("Failed to parse test.cpp");
let symbol = &symbols[0];
code_graph
.store_symbol_with_file_and_language(
&cpp_path,
&symbol.name,
symbol.kind.as_str(),
Language::Cpp,
symbol.byte_start,
symbol.byte_end,
symbol.line_start,
symbol.line_end,
symbol.col_start,
symbol.col_end,
)
.expect("Failed to store symbol");
let resolved = resolve_symbol(
&code_graph,
Some(&cpp_path),
Some("function"),
"valid_function",
)
.expect("Failed to resolve function");
let replaced_content =
std::fs::read_to_string(&cpp_path).expect("Failed to read replaced file");
let invalid_patch = r#"
int valid_function() {
return 42;
"#;
let result = apply_patch_with_validation(
&cpp_path,
resolved.byte_start,
resolved.byte_end,
invalid_patch.trim(),
workspace_path,
Language::Cpp,
AnalyzerMode::Off,
false, false, );
assert!(result.is_err(), "Patch should fail on syntax error");
let current_content =
std::fs::read_to_string(&cpp_path).expect("Failed to read current file");
assert_eq!(
replaced_content, current_content,
"File should be unchanged after failed patch (atomic rollback)"
);
}
#[test]
fn test_c_patch_succeeds_with_validation() {
let workspace_dir = TempDir::new().expect("Failed to create temp workspace");
let workspace_path = workspace_dir.path();
let c_path = workspace_path.join("test.c");
let source = r#"
int get_number(void) {
return 10;
}
"#;
std::fs::write(&c_path, source).expect("Failed to write test.c");
let graph_db_path = workspace_path.join("graph.db");
let mut code_graph =
CodeGraph::open(&graph_db_path).expect("Failed to open graph database");
let symbols =
extract_cpp_symbols(&c_path, source.as_bytes()).expect("Failed to parse test.c");
let symbol = &symbols[0];
code_graph
.store_symbol_with_file_and_language(
&c_path,
&symbol.name,
symbol.kind.as_str(),
Language::C,
symbol.byte_start,
symbol.byte_end,
symbol.line_start,
symbol.line_end,
symbol.col_start,
symbol.col_end,
)
.expect("Failed to store symbol");
let resolved = resolve_symbol(&code_graph, Some(&c_path), Some("function"), "get_number")
.expect("Failed to resolve function");
let new_body = r#"
int get_number(void) {
return 20;
}
"#;
let result = apply_patch_with_validation(
&c_path,
resolved.byte_start,
resolved.byte_end,
new_body.trim(),
workspace_path,
Language::C,
AnalyzerMode::Off,
false, false, );
if result.is_ok() {
let new_content =
std::fs::read_to_string(&c_path).expect("Failed to read patched file");
assert!(
new_content.contains("return 20;"),
"Patched content should be present"
);
} else {
println!("gcc not available, skipping C patch validation");
}
}
}