use crate::helpers::{fixture_path, AftProcess};
fn setup_extract_fixture() -> (tempfile::TempDir, String) {
let fixtures = fixture_path("extract_function");
let tmp = tempfile::tempdir().expect("create temp dir");
for entry in std::fs::read_dir(&fixtures).expect("read fixtures dir") {
let entry = entry.expect("read entry");
let src = entry.path();
if src.is_file() {
let dst = tmp.path().join(entry.file_name());
std::fs::copy(&src, &dst).expect("copy fixture file");
}
}
let root = tmp.path().display().to_string();
(tmp, root)
}
fn configure(aft: &mut AftProcess, root: &str) {
let resp = aft.send(&format!(
r#"{{"id":"cfg","command":"configure","project_root":"{}"}}"#,
root
));
assert_eq!(
resp["success"], true,
"configure should succeed: {:?}",
resp
);
}
#[test]
fn extract_function_basic_ts() {
let (_tmp, root) = setup_extract_fixture();
let mut aft = AftProcess::spawn();
configure(&mut aft, &root);
let file = format!("{}/sample.ts", root);
let resp = aft.send(&format!(
r#"{{"id":"1","command":"extract_function","file":"{}","name":"doProcess","start_line":6,"end_line":10}}"#,
file
));
assert_eq!(resp["success"], true, "extract should succeed: {:?}", resp);
assert_eq!(resp["name"], "doProcess");
let params = resp["parameters"].as_array().expect("parameters array");
assert!(
params.len() >= 2,
"should have at least 2 parameters (items, prefix), got {:?}",
params
);
let content = std::fs::read_to_string(&file).expect("read file");
assert!(
content.contains("function doProcess"),
"should contain the extracted function:\n{}",
content
);
assert!(
content.contains("doProcess("),
"should contain the call site:\n{}",
content
);
aft.shutdown();
}
#[test]
fn extract_function_with_return_value() {
let (_tmp, root) = setup_extract_fixture();
let mut aft = AftProcess::spawn();
configure(&mut aft, &root);
let file = format!("{}/sample.ts", root);
let resp = aft.send(&format!(
r#"{{"id":"1","command":"extract_function","file":"{}","name":"computeValues","start_line":14,"end_line":16}}"#,
file
));
assert_eq!(resp["success"], true, "extract should succeed: {:?}", resp);
let params = resp["parameters"].as_array().expect("parameters array");
assert!(
!params.is_empty(),
"should have at least one parameter (x), got {:?}",
params
);
let return_type = resp["return_type"].as_str().unwrap();
assert!(
return_type == "variable" || return_type == "expression",
"should detect return value, got: {}",
return_type
);
let content = std::fs::read_to_string(&file).expect("read file");
assert!(
content.contains("function computeValues"),
"should contain extracted function:\n{}",
content
);
aft.shutdown();
}
#[test]
fn extract_function_python() {
let (_tmp, root) = setup_extract_fixture();
let mut aft = AftProcess::spawn();
configure(&mut aft, &root);
let file = format!("{}/sample.py", root);
let resp = aft.send(&format!(
r#"{{"id":"1","command":"extract_function","file":"{}","name":"do_process","start_line":6,"end_line":10}}"#,
file
));
assert_eq!(
resp["success"], true,
"python extract should succeed: {:?}",
resp
);
assert_eq!(resp["name"], "do_process");
let params = resp["parameters"].as_array().expect("parameters array");
assert!(
params.len() >= 2,
"should detect parameters from enclosing scope, got {:?}",
params
);
let content = std::fs::read_to_string(&file).expect("read file");
assert!(
content.contains("def do_process"),
"should use Python def syntax:\n{}",
content
);
aft.shutdown();
}
#[test]
fn extract_function_dry_run() {
let (_tmp, root) = setup_extract_fixture();
let mut aft = AftProcess::spawn();
configure(&mut aft, &root);
let file = format!("{}/sample.ts", root);
let before = std::fs::read_to_string(&file).unwrap();
let resp = aft.send(&format!(
r#"{{"id":"1","command":"extract_function","file":"{}","name":"preview","start_line":6,"end_line":10,"dry_run":true}}"#,
file
));
assert_eq!(resp["success"], true, "dry_run should succeed: {:?}", resp);
assert_eq!(resp["dry_run"], true, "should flag dry_run");
assert!(resp["diff"].as_str().is_some(), "should have diff");
assert!(resp["parameters"].is_array(), "should have parameters");
assert!(
resp["return_type"].as_str().is_some(),
"should have return_type"
);
let after = std::fs::read_to_string(&file).unwrap();
assert_eq!(before, after, "file should be unchanged after dry_run");
aft.shutdown();
}
#[test]
fn extract_function_unsupported_language() {
let (_tmp, root) = setup_extract_fixture();
let mut aft = AftProcess::spawn();
configure(&mut aft, &root);
let file = format!("{}/test.rs", root);
std::fs::write(&file, "fn main() {\n let x = 1;\n}\n").unwrap();
let resp = aft.send(&format!(
r#"{{"id":"1","command":"extract_function","file":"{}","name":"foo","start_line":2,"end_line":3}}"#,
file
));
assert_eq!(resp["success"], false, "should fail: {:?}", resp);
assert_eq!(resp["code"], "unsupported_language");
aft.shutdown();
}
#[test]
fn extract_function_this_reference() {
let (_tmp, root) = setup_extract_fixture();
let mut aft = AftProcess::spawn();
configure(&mut aft, &root);
let file = format!("{}/sample_this.ts", root);
let resp = aft.send(&format!(
r#"{{"id":"1","command":"extract_function","file":"{}","name":"extracted","start_line":5,"end_line":8}}"#,
file
));
assert_eq!(resp["success"], false, "should fail: {:?}", resp);
assert_eq!(resp["code"], "this_reference_in_range");
let msg = resp["message"].as_str().unwrap_or("");
assert!(
msg.contains("this"),
"error message should mention 'this': {}",
msg
);
aft.shutdown();
}