use magellan::{CodeGraph, FileFilter};
use std::fs;
use tempfile::TempDir;
#[test]
fn test_gitignore_and_ignore_honored() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let db_path = root.join("test.db");
fs::write(root.join(".gitignore"), "ignored_dir/**\ntarget/\n").unwrap();
fs::write(root.join(".ignore"), "*.tmp\n").unwrap();
fs::create_dir_all(root.join("ignored_dir")).unwrap();
fs::create_dir_all(root.join("src")).unwrap();
fs::create_dir_all(root.join("target")).unwrap();
fs::write(root.join("src/lib.rs"), "fn lib() {}").unwrap();
fs::write(root.join("ignored_dir/code.rs"), "fn ignored() {}").unwrap();
fs::write(root.join("target/lib.rs"), "fn target() {}").unwrap();
fs::write(root.join("main.tmp"), "temporary content").unwrap();
let filter = FileFilter::new(root, &[], &[]).unwrap();
let mut graph = CodeGraph::open(&db_path).unwrap();
let result =
magellan::graph::scan::scan_directory_with_filter(&mut graph, root, &filter, None).unwrap();
assert_eq!(result.indexed, 1);
let ignored_dir_diag = result
.diagnostics
.iter()
.find(|d| d.path().contains("ignored_dir"));
assert!(ignored_dir_diag.is_some());
let target_diag = result
.diagnostics
.iter()
.find(|d| d.path().contains("target"));
assert!(target_diag.is_some());
let tmp_diag = result
.diagnostics
.iter()
.find(|d| d.path().ends_with(".tmp"));
assert!(tmp_diag.is_some());
let symbols = graph
.symbols_in_file(&root.join("src/lib.rs").to_string_lossy())
.unwrap();
assert!(!symbols.is_empty());
}
#[test]
fn test_include_pattern_restricts() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let db_path = root.join("test.db");
fs::create_dir_all(root.join("src")).unwrap();
fs::create_dir_all(root.join("tests")).unwrap();
fs::create_dir_all(root.join("examples")).unwrap();
fs::write(root.join("src/lib.rs"), "fn lib() {}").unwrap();
fs::write(root.join("src/main.rs"), "fn main() {}").unwrap();
fs::write(root.join("tests/test.rs"), "fn test() {}").unwrap();
fs::write(root.join("examples/demo.rs"), "fn demo() {}").unwrap();
let filter = FileFilter::new(root, &["src/**".to_string()], &[]).unwrap();
let mut graph = CodeGraph::open(&db_path).unwrap();
let result =
magellan::graph::scan::scan_directory_with_filter(&mut graph, root, &filter, None).unwrap();
assert_eq!(result.indexed, 2);
let tests_diag = result
.diagnostics
.iter()
.find(|d| d.path().contains("tests"));
assert!(tests_diag.is_some());
let examples_diag = result
.diagnostics
.iter()
.find(|d| d.path().contains("examples"));
assert!(examples_diag.is_some());
let lib_symbols = graph
.symbols_in_file(&root.join("src/lib.rs").to_string_lossy())
.unwrap();
assert!(!lib_symbols.is_empty());
let main_symbols = graph
.symbols_in_file(&root.join("src/main.rs").to_string_lossy())
.unwrap();
assert!(!main_symbols.is_empty());
}
#[test]
fn test_exclude_overrides_include() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let db_path = root.join("test.db");
fs::create_dir_all(root.join("src")).unwrap();
fs::write(root.join("src/lib.rs"), "fn lib() {}").unwrap();
fs::write(root.join("src/test.rs"), "fn test() {}").unwrap();
fs::write(root.join("src/test_helper.rs"), "fn helper() {}").unwrap();
let filter =
FileFilter::new(root, &["src/**".to_string()], &["**/*test*.rs".to_string()]).unwrap();
let mut graph = CodeGraph::open(&db_path).unwrap();
let result =
magellan::graph::scan::scan_directory_with_filter(&mut graph, root, &filter, None).unwrap();
assert_eq!(result.indexed, 1);
let test_diag = result
.diagnostics
.iter()
.find(|d| d.path() == "src/test.rs");
assert!(test_diag.is_some());
assert!(
matches!(test_diag.unwrap(), magellan::WatchDiagnostic::Skipped { reason, .. }
if matches!(reason, magellan::SkipReason::ExcludedByGlob))
);
let helper_diag = result
.diagnostics
.iter()
.find(|d| d.path() == "src/test_helper.rs");
assert!(helper_diag.is_some());
let lib_symbols = graph
.symbols_in_file(&root.join("src/lib.rs").to_string_lossy())
.unwrap();
assert!(!lib_symbols.is_empty());
}
#[test]
fn test_error_on_one_file_continues() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let db_path = root.join("test.db");
fs::write(root.join("good1.rs"), "fn good1() {}").unwrap();
fs::write(root.join("good2.rs"), "fn good2() {}").unwrap();
let bad_file = root.join("bad.rs");
fs::write(&bad_file, "fn bad() {}").unwrap();
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&bad_file).unwrap().permissions();
perms.set_mode(0o000);
fs::set_permissions(&bad_file, perms).unwrap();
let filter = FileFilter::new(root, &[], &[]).unwrap();
let mut graph = CodeGraph::open(&db_path).unwrap();
let result =
magellan::graph::scan::scan_directory_with_filter(&mut graph, root, &filter, None)
.unwrap();
assert!(result.indexed >= 2);
let bad_diag = result.diagnostics.iter().find(|d| d.path() == "bad.rs");
assert!(bad_diag.is_some());
let good1_symbols = graph
.symbols_in_file(&root.join("good1.rs").to_string_lossy())
.unwrap();
assert!(!good1_symbols.is_empty());
let good2_symbols = graph
.symbols_in_file(&root.join("good2.rs").to_string_lossy())
.unwrap();
assert!(!good2_symbols.is_empty());
let mut perms = fs::metadata(&bad_file).unwrap().permissions();
perms.set_mode(0o644);
fs::set_permissions(&bad_file, perms).unwrap();
}
#[cfg(not(unix))]
{
let filter = FileFilter::new(root, &[], &[]).unwrap();
let mut graph = CodeGraph::open(&db_path).unwrap();
let result =
magellan::graph::scan::scan_directory_with_filter(&mut graph, root, &filter, None)
.unwrap();
assert_eq!(result.indexed, 3);
}
}
#[test]
fn test_deterministic_filtering() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let db_dir = TempDir::new().unwrap();
let db_path1 = db_dir.path().join("test1.db");
let db_path2 = db_dir.path().join("test2.db");
fs::write(root.join(".gitignore"), "ignored.rs\n").unwrap();
fs::write(root.join("z.rs"), "fn z() {}").unwrap();
fs::write(root.join("a.rs"), "fn a() {}").unwrap();
fs::write(root.join("m.rs"), "fn m() {}").unwrap();
fs::write(root.join("ignored.rs"), "fn ignored() {}").unwrap();
let mut graph1 = CodeGraph::open(&db_path1).unwrap();
let filter1 = FileFilter::new(root, &[], &[]).unwrap();
let result1 =
magellan::graph::scan::scan_directory_with_filter(&mut graph1, root, &filter1, None)
.unwrap();
let mut graph2 = CodeGraph::open(&db_path2).unwrap();
let filter2 = FileFilter::new(root, &[], &[]).unwrap();
let result2 =
magellan::graph::scan::scan_directory_with_filter(&mut graph2, root, &filter2, None)
.unwrap();
assert_eq!(result1.indexed, result2.indexed);
assert_eq!(result1.indexed, 3);
let mut diags1 = result1.diagnostics.clone();
let mut diags2 = result2.diagnostics.clone();
diags1.sort();
diags2.sort();
assert_eq!(diags1.len(), diags2.len());
assert!(diags1.iter().any(|d| d.path() == "ignored.rs"));
assert!(diags2.iter().any(|d| d.path() == "ignored.rs"));
assert!(diags1.iter().any(|d| d.path() == ".gitignore"));
assert!(diags2.iter().any(|d| d.path() == ".gitignore"));
}
#[test]
fn test_internal_ignores_precedence() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let db_path = root.join("test.db");
fs::write(root.join(".gitignore"), "!*.db\n").unwrap();
fs::write(root.join("data.db"), "database content").unwrap();
let filter = FileFilter::new(root, &[], &[]).unwrap();
let mut graph = CodeGraph::open(&db_path).unwrap();
let result =
magellan::graph::scan::scan_directory_with_filter(&mut graph, root, &filter, None).unwrap();
assert_eq!(result.indexed, 0);
let db_diag = result.diagnostics.iter().find(|d| d.path() == "data.db");
assert!(db_diag.is_some());
match db_diag.unwrap() {
magellan::WatchDiagnostic::Skipped { reason, .. } => {
assert_eq!(reason, &magellan::SkipReason::IgnoredInternal);
}
_ => panic!("Expected Skipped diagnostic"),
}
}
#[test]
fn test_root_gitignore() {
let temp_dir = TempDir::new().unwrap();
let root = temp_dir.path();
let db_path = root.join("test.db");
fs::create_dir_all(root.join("src/subdir")).unwrap();
fs::write(root.join(".gitignore"), "root_ignored.rs\nsrc_ignored.rs\n").unwrap();
fs::write(root.join("root_ignored.rs"), "fn x() {}").unwrap();
fs::write(root.join("root_included.rs"), "fn y() {}").unwrap();
fs::write(root.join("src/src_ignored.rs"), "fn z() {}").unwrap();
fs::write(root.join("src/subdir/nested.rs"), "fn w() {}").unwrap();
let filter = FileFilter::new(root, &[], &[]).unwrap();
let mut graph = CodeGraph::open(&db_path).unwrap();
let result =
magellan::graph::scan::scan_directory_with_filter(&mut graph, root, &filter, None).unwrap();
assert!(result.indexed >= 2);
let root_included_symbols = graph
.symbols_in_file(&root.join("root_included.rs").to_string_lossy())
.unwrap();
assert!(!root_included_symbols.is_empty());
let nested_symbols = graph
.symbols_in_file(&root.join("src/subdir/nested.rs").to_string_lossy())
.unwrap();
assert!(!nested_symbols.is_empty());
let root_ignored_diag = result
.diagnostics
.iter()
.find(|d| d.path() == "root_ignored.rs");
assert!(root_ignored_diag.is_some());
let src_ignored_diag = result
.diagnostics
.iter()
.find(|d| d.path().contains("src_ignored.rs"));
assert!(src_ignored_diag.is_some());
}