Files
fermata/tests/core_botignore.rs
T
g4borg 97001e1544 chore: rename packages/ to crates/
Move all 29 workspace members from packages/<name>/ to crates/<name>/.
Updates: workspace Cargo.toml (members + path deps), justfile, root
CLAUDE.md, scripts/build/CARGO_INSTALL.md, docs/architecture/crates.md
(renamed from packages.md), structural references in docs/architecture
and docs/configuration, per-crate CLAUDE.md self-references. Historical
plans, reports, and building/ docs are left untouched.

No behavior change; just check-all stays green and fermata tests pass.
2026-04-30 21:58:57 +02:00

136 lines
4.2 KiB
Rust

use dirigent_fermata::core::botignore::BotignoreSet;
use std::fs;
use tempfile::TempDir;
#[test]
fn matches_simple_pattern() {
let tmp = TempDir::new().unwrap();
let root = tmp.path();
fs::write(root.join(".botignore"), ".env\nsecrets/\n").unwrap();
let set = BotignoreSet::load(root).unwrap();
let env = root.join(".env");
fs::write(&env, "").unwrap();
let m = set.matched(&env).unwrap();
assert!(m.is_some(), ".env should be matched");
assert_eq!(m.unwrap().pattern, ".env");
}
#[test]
fn does_not_match_unrelated_files() {
let tmp = TempDir::new().unwrap();
let root = tmp.path();
fs::write(root.join(".botignore"), ".env\n").unwrap();
let set = BotignoreSet::load(root).unwrap();
let other = root.join("README.md");
fs::write(&other, "").unwrap();
assert!(set.matched(&other).unwrap().is_none());
}
#[test]
fn negation_pattern_excludes() {
let tmp = TempDir::new().unwrap();
let root = tmp.path();
fs::write(root.join(".botignore"), "*.log\n!keep.log\n").unwrap();
let set = BotignoreSet::load(root).unwrap();
let blocked = root.join("foo.log");
fs::write(&blocked, "").unwrap();
assert!(set.matched(&blocked).unwrap().is_some());
let allowed = root.join("keep.log");
fs::write(&allowed, "").unwrap();
assert!(set.matched(&allowed).unwrap().is_none());
}
#[test]
fn empty_or_missing_botignore_is_ok() {
let tmp = TempDir::new().unwrap();
let set = BotignoreSet::load(tmp.path()).unwrap();
let any = tmp.path().join("anything.txt");
std::fs::write(&any, "").unwrap();
assert!(set.matched(&any).unwrap().is_none());
}
#[test]
fn nested_botignore_is_scoped_to_its_directory() {
// A `.botignore` in a subdirectory only applies under that subdirectory,
// matching gitignore semantics: a sibling file with the same name at the
// root is NOT affected.
let tmp = TempDir::new().unwrap();
let root = tmp.path();
fs::create_dir_all(root.join("frontend")).unwrap();
fs::write(root.join("frontend/.botignore"), "secret.key\n").unwrap();
let set = BotignoreSet::load(root).unwrap();
let blocked = root.join("frontend/secret.key");
fs::write(&blocked, "").unwrap();
let m = set
.matched(&blocked)
.unwrap()
.expect("frontend/secret.key should match");
let src = m.source.to_string_lossy().replace('\\', "/");
assert!(
src.ends_with("frontend/.botignore"),
"Rule.source should point at the nested file; was {}",
src,
);
let unblocked = root.join("secret.key");
fs::write(&unblocked, "").unwrap();
assert!(
set.matched(&unblocked).unwrap().is_none(),
"top-level secret.key should NOT be matched (rule scoped to frontend/)",
);
}
#[test]
fn nested_botignore_anchored_pattern_is_local() {
// A leading `/` anchors the pattern to the directory of the .botignore
// file it's declared in.
let tmp = TempDir::new().unwrap();
let root = tmp.path();
fs::create_dir_all(root.join("frontend")).unwrap();
fs::write(root.join("frontend/.botignore"), "/secret.key\n").unwrap();
let set = BotignoreSet::load(root).unwrap();
let blocked = root.join("frontend/secret.key");
fs::write(&blocked, "").unwrap();
assert!(set.matched(&blocked).unwrap().is_some());
let unblocked = root.join("secret.key");
fs::write(&unblocked, "").unwrap();
assert!(
set.matched(&unblocked).unwrap().is_none(),
"anchored /secret.key should NOT match outside frontend/",
);
}
#[test]
fn nested_botignore_overrides_root() {
let tmp = TempDir::new().unwrap();
let root = tmp.path();
fs::write(root.join(".botignore"), "*.log\n").unwrap();
fs::create_dir_all(root.join("logs")).unwrap();
fs::write(root.join("logs/.botignore"), "!keep.log\n").unwrap();
let set = BotignoreSet::load(root).unwrap();
let blocked = root.join("logs/foo.log");
fs::write(&blocked, "").unwrap();
assert!(set.matched(&blocked).unwrap().is_some());
let kept = root.join("logs/keep.log");
fs::write(&kept, "").unwrap();
assert!(
set.matched(&kept).unwrap().is_none(),
"logs/.botignore should un-ignore keep.log",
);
}