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", ); }