use std::path::Path;
use crate::git_changes::Changeset;
use crate::scan_scope::resolve;
use crate::test_repo;
#[test]
fn uncommitted_scope_excludes_committed_branch_work() {
let dir = test_repo::temp_dir("abcop_uncommitted");
let _guard = test_repo::cwd_lock();
seed_branch_and_uncommitted_work(&dir);
let cs = resolve_in_dir(&dir, false, true)
.expect("scope resolves")
.expect("changeset present");
let seen = format!("{:?}", cs.files);
assert!(
cs.files.contains_key("a.rb"),
"unstaged edit selected; saw {seen}"
);
assert!(
cs.files.contains_key("b.rb"),
"staged new file selected; saw {seen}"
);
assert!(
cs.files.contains_key("c.rb"),
"untracked file selected; saw {seen}"
);
assert!(
!cs.files.contains_key("d.rb"),
"committed branch work excluded; saw {seen}"
);
let _ = std::fs::remove_dir_all(&dir);
}
fn seed_branch_and_uncommitted_work(dir: &Path) {
test_repo::seed_repo_with_base_commit(dir);
std::fs::write(dir.join("d.rb"), "def committed\nend\n").unwrap();
test_repo::commit_all(dir, "branch work");
test_repo::stage_three_kinds_of_uncommitted_work(dir);
}
#[test]
fn uncommitted_scope_fails_outside_a_repository() {
let dir = test_repo::temp_dir("abcop_uncommitted_norepo");
let _guard = test_repo::cwd_lock();
let err = resolve_in_dir(&dir, false, true).expect_err("non-repository must fail");
assert!(
err.contains("no usable git repository"),
"expected repository error; saw: {err}"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn default_prefers_uncommitted_work_when_the_tree_is_dirty() {
let dir = test_repo::temp_dir("abcop_default_dirty");
let _guard = test_repo::cwd_lock();
seed_branch_and_uncommitted_work(&dir);
let cs = resolve_in_dir(&dir, false, false)
.expect("scope resolves")
.expect("changeset present");
let seen = format!("{:?}", cs.files);
assert!(
cs.files.contains_key("a.rb")
&& cs.files.contains_key("b.rb")
&& cs.files.contains_key("c.rb"),
"uncommitted work selected; saw {seen}"
);
assert!(
!cs.files.contains_key("d.rb"),
"committed branch work excluded; saw {seen}"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn default_covers_branch_work_when_the_tree_is_clean() {
let dir = test_repo::temp_dir("abcop_default_clean");
let _guard = test_repo::cwd_lock();
test_repo::seed_repo_with_base_commit(&dir);
test_repo::checkout_new_branch(&dir, "feature/work");
std::fs::write(dir.join("d.rb"), "def committed\nend\n").unwrap();
test_repo::commit_all(&dir, "branch work");
let cs = resolve_in_dir(&dir, false, false)
.expect("scope resolves")
.expect("changeset present");
let seen = format!("{:?}", cs.files);
assert!(
cs.files.contains_key("d.rb"),
"clean tree scans the branch scope; saw {seen}"
);
assert!(
!cs.files.contains_key("a.rb"),
"branch scope diffs the merge base, not the full tree; saw {seen}"
);
let _ = std::fs::remove_dir_all(&dir);
}
#[test]
fn default_falls_back_to_full_tree_outside_a_repository() {
let dir = test_repo::temp_dir("abcop_default_norepo");
let _guard = test_repo::cwd_lock();
let cs = resolve_in_dir(&dir, false, false).expect("full-tree fallback is not an error");
assert!(cs.is_none(), "no repository means no changeset; saw {cs:?}");
let _ = std::fs::remove_dir_all(&dir);
}
fn resolve_in_dir(dir: &Path, mr: bool, uncommitted: bool) -> Result<Option<Changeset>, String> {
let old = std::env::current_dir().unwrap();
std::env::set_current_dir(dir).unwrap();
let result = std::panic::catch_unwind(|| resolve(mr, uncommitted, false, false, false));
std::env::set_current_dir(old).unwrap();
result.unwrap()
}