use std::fs;
use std::path::{Path, PathBuf};
use crate::files::expand_paths;
fn build(root: &Path, entries: &[(&str, &str)]) {
for (rel, body) in entries {
let path = root.join(rel);
if let Some(parent) = path.parent() {
fs::create_dir_all(parent).expect("create parents");
}
fs::write(&path, body).expect("write file");
}
}
fn is_scan_targetish(path: &Path) -> bool {
matches!(
path.extension().and_then(|e| e.to_str()),
Some("rs" | "py" | "md")
)
}
#[test]
fn duplicate_paths_collapse_to_a_single_file() {
let temp = tempfile::TempDir::new().expect("tempdir");
let root = temp.path();
build(root, &[("a.rs", "")]);
let inputs = vec![root.join("a.rs"), root.to_path_buf()];
let result = expand_paths(&inputs, is_scan_targetish);
let result_strs: Vec<String> = result
.iter()
.map(|p| {
p.strip_prefix(root)
.unwrap_or(p)
.to_string_lossy()
.into_owned()
})
.collect();
assert_eq!(
result_strs,
vec!["a.rs".to_owned()],
"duplicates must collapse: {result_strs:?}"
);
}
#[test]
fn output_is_sorted_and_stable_across_calls() {
let temp = tempfile::TempDir::new().expect("tempdir");
let root = temp.path();
build(root, &[("z.rs", ""), ("a.rs", ""), ("m.rs", "")]);
let once = expand_paths(&[root.to_path_buf()], is_scan_targetish);
let twice = expand_paths(&[root.to_path_buf()], is_scan_targetish);
assert_eq!(once, twice, "two calls must return identical vectors");
let mut sorted = once.clone();
sorted.sort();
assert_eq!(
once, sorted,
"output should already be sorted, not depend on the walker"
);
}
#[test]
fn explicit_filenames_are_honoured_even_when_gitignored() {
let temp = tempfile::TempDir::new().expect("tempdir");
let root = temp.path();
build(root, &[(".gitignore", "skipped.rs\n"), ("skipped.rs", "")]);
let result = expand_paths(&[root.join("skipped.rs")], is_scan_targetish);
assert_eq!(
result,
vec![root.join("skipped.rs")],
"explicit paths skip gitignore: {result:?}"
);
}
#[test]
fn explicit_filenames_failing_predicate_are_dropped() {
let temp = tempfile::TempDir::new().expect("tempdir");
let root = temp.path();
build(root, &[("notes.txt", ""), ("keep.rs", "")]);
let result = expand_paths(
&[root.join("notes.txt"), root.join("keep.rs")],
is_scan_targetish,
);
let names: Vec<String> = result
.iter()
.map(|p| {
p.strip_prefix(root)
.unwrap_or(p)
.to_string_lossy()
.into_owned()
})
.collect();
assert_eq!(names, vec!["keep.rs".to_owned()]);
}
#[test]
fn paths_that_do_not_exist_are_silently_skipped() {
let temp = tempfile::TempDir::new().expect("tempdir");
let root = temp.path();
build(root, &[("real.rs", "")]);
let missing: PathBuf = root.join("does_not_exist.rs");
let inputs = vec![root.join("real.rs"), missing, root.to_path_buf()];
let result = expand_paths(&inputs, is_scan_targetish);
let names: Vec<String> = result
.iter()
.map(|p| {
p.strip_prefix(root)
.unwrap_or(p)
.to_string_lossy()
.into_owned()
})
.collect();
assert_eq!(names, vec!["real.rs".to_owned()]);
}