feat: directory moves — mirrored batch relocation of every indexed file under a dir
- MovePlan gains moves[] (file move = 1 relocation), left_behind[] for unindexable files that deliberately stay, prune_dirs[] applied after the batch (remove_dir only succeeds when empty, leftovers keep their home) - plan::dir: nesting/target-exists guards, merged per-file rewrites - apply: atomic N-rename batch (per-file git mv), rollback restores every move, rewrite and created dir newest-first - mv_reject accepts dirs; --json adds moved_files/would_move_files only for real dir moves (single-file contract byte-identical); summary shows (N files) and left-behind count - engine rollback/prune tests moved to tests/apply.rs (public API; a mid-batch failure cannot be forced through the CLI), java-rule e2e split into its own binary to keep every file under the 250-line budget
This commit is contained in:
parent
b96deeaf2f
commit
6f7a2190e6
17 changed files with 842 additions and 293 deletions
|
|
@ -141,49 +141,6 @@ fn missing_project() -> tempfile::TempDir {
|
|||
common::copy_fixture("java", "fix_missing")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_import_adds_unique_and_reports_ambiguous() {
|
||||
let tmp = missing_project();
|
||||
jmove(&tmp, &["fix", "--rule", "java/missing-import", "--json"])
|
||||
.success()
|
||||
.stdout(
|
||||
predicate::str::contains("\"rule\": \"java/missing-import\"")
|
||||
.and(predicate::str::contains("\"applied\": false"))
|
||||
.and(predicate::str::contains("\"com.example.a.Config\""))
|
||||
.and(predicate::str::contains("\"com.example.b.Config\"")),
|
||||
);
|
||||
let calc = read(&in_root(
|
||||
tmp.path(),
|
||||
"src/main/java/com/example/app/Calc.java",
|
||||
));
|
||||
assert!(
|
||||
calc.contains("package com.example.app;\nimport com.example.util.Maths;\n"),
|
||||
"{calc}"
|
||||
);
|
||||
// The ambiguous `Config` is never guessed at.
|
||||
let refer = read(&in_root(
|
||||
tmp.path(),
|
||||
"src/main/java/com/example/c/Refer.java",
|
||||
));
|
||||
assert!(!refer.contains("import com.example."), "{refer}");
|
||||
jmove(&tmp, &["check"]).success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn missing_import_dry_run_then_stable_reapply() {
|
||||
let tmp = missing_project();
|
||||
jmove(&tmp, &["fix", "--dry-run"])
|
||||
.success()
|
||||
.stdout(predicate::str::contains("+import com.example.util.Maths;"));
|
||||
jmove(&tmp, &["fix"])
|
||||
.success()
|
||||
.stdout(predicate::str::contains("fixed 3 issues in 2 files"));
|
||||
// Only manual (ambiguous) findings remain: nothing further applies.
|
||||
jmove(&tmp, &["fix"])
|
||||
.success()
|
||||
.stdout(predicate::str::contains("nothing to change"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unused_delete_and_missing_insert_coexist_in_one_file() {
|
||||
// Dual.java: the unused import is deleted while the missing `Maths`
|
||||
|
|
@ -198,56 +155,6 @@ fn unused_delete_and_missing_insert_coexist_in_one_file() {
|
|||
jmove(&tmp, &["check"]).success();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn three_rules_converge_on_one_project() {
|
||||
// App.java: unused import inside an out-of-order block (the order
|
||||
// rewrite overlaps the deletion, so it defers to run 2).
|
||||
// C.java: out-of-order block + a bare `Maths` reference (insertion at
|
||||
// the block boundary coexists with the rewrite in run 1).
|
||||
let tmp = common::copy_fixture("java", "order");
|
||||
jmove(&tmp, &["fix", "--dry-run", "--json"])
|
||||
.success()
|
||||
.stdout(
|
||||
predicate::str::contains("\"rule\": \"java/import-order\"")
|
||||
.and(predicate::str::contains("\"applied\": false"))
|
||||
.and(predicate::str::contains("skipped")),
|
||||
);
|
||||
let app = "src/main/java/com/example/app/App.java";
|
||||
let c = "src/main/java/com/example/app/C.java";
|
||||
jmove(&tmp, &["fix"])
|
||||
.success()
|
||||
.stdout(predicate::str::contains("fixed 3 issues in 2 files"));
|
||||
let app_text = read(&in_root(tmp.path(), app));
|
||||
// Unused is gone; the deferred order fix has not touched the block.
|
||||
assert!(!app_text.contains("Unneeded"), "{app_text}");
|
||||
assert!(
|
||||
app_text.contains("import java.util.List;\nimport com.example.util.Maths;"),
|
||||
"{app_text}"
|
||||
);
|
||||
jmove(&tmp, &["fix"])
|
||||
.success()
|
||||
.stdout(predicate::str::contains("fixed 2 issues in 2 files"));
|
||||
let c_text = read(&in_root(tmp.path(), c));
|
||||
assert!(
|
||||
c_text.contains(
|
||||
"import com.example.util.Maths;\nimport java.util.List;\nimport java.util.Map;\n"
|
||||
),
|
||||
"{c_text}"
|
||||
);
|
||||
let app_text = read(&in_root(tmp.path(), app));
|
||||
assert!(
|
||||
app_text.contains(
|
||||
"import com.example.util.Maths;\nimport java.util.List;\nimport java.util.Map;\n"
|
||||
),
|
||||
"{app_text}"
|
||||
);
|
||||
// Fixed point.
|
||||
jmove(&tmp, &["fix"])
|
||||
.success()
|
||||
.stdout(predicate::str::contains("nothing to change"));
|
||||
jmove(&tmp, &["check"]).success();
|
||||
}
|
||||
|
||||
fn ts_unused() -> tempfile::TempDir {
|
||||
common::copy_fixture("typescript", "unused")
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue