#[cfg(test)]
mod semantic_tests {
use std::path::PathBuf;
use std::sync::Arc;
use crate::semantic::adapters::AdapterRegistry;
use crate::semantic::types::SymbolKind;
use crate::semantic::{LanguageAdapter, SymbolIndex};
fn fixtures_dir() -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("src")
.join("tests")
.join("fixtures")
}
fn mk_registry() -> Arc<AdapterRegistry> {
#[allow(unused_mut)]
let mut adapters: Vec<Box<dyn LanguageAdapter>> = Vec::new();
#[cfg(feature = "semantic-ts")]
adapters.push(Box::new(crate::semantic::adapters::TypescriptAdapter));
#[cfg(feature = "semantic-python")]
adapters.push(Box::new(crate::semantic::adapters::PythonAdapter));
#[cfg(feature = "semantic-elixir")]
adapters.push(Box::new(crate::semantic::adapters::ElixirAdapter));
Arc::new(AdapterRegistry::new(adapters))
}
#[test]
#[cfg(feature = "semantic-ts")]
fn find_callers_respects_word_boundaries() {
let registry = mk_registry();
let index = SymbolIndex::new(registry);
let _file = fixtures_dir().join("callers_test.ts");
let callers = index.find_callers("run", &fixtures_dir()).unwrap();
let has_runner_def = callers.iter().any(|c| c.contains("fn runner"));
let has_running_def = callers.iter().any(|c| c.contains("fn running"));
let has_prune = callers.iter().any(|c| c.contains("prune"));
let has_run_call = callers.iter().any(|c| c.contains("run()"));
assert!(
!has_runner_def,
"should NOT match 'runner' (substring match bug)"
);
assert!(
!has_running_def,
"should NOT match 'running' (substring match bug)"
);
assert!(!has_prune, "should NOT match 'prune' (substring match bug)");
assert!(has_run_call, "should match 'run()' call inside runner()");
}
#[test]
#[cfg(feature = "semantic-ts")]
fn find_callees_works_on_tsx() {
let registry = mk_registry();
let index = SymbolIndex::new(registry);
let file = fixtures_dir().join("component.tsx");
let callees = index.find_callees(&file, "Component").unwrap();
assert!(
callees.iter().any(|c| c == "helper"),
"should find 'helper' callee in TSX Component"
);
}
#[test]
#[cfg(feature = "semantic-ts")]
fn ts_extract_does_not_panic_on_imports() {
let registry = mk_registry();
let index = SymbolIndex::new(registry);
let file = fixtures_dir().join("component.tsx");
let entry = index.ensure_file(&file).unwrap();
let functions: Vec<_> = entry
.symbols
.iter()
.filter(|s| s.kind == SymbolKind::Function)
.collect();
assert!(!functions.is_empty(), "should find at least one function");
assert!(
functions.iter().any(|s| s.name == "helper"),
"should find helper function"
);
}
#[test]
#[cfg(feature = "semantic-python")]
fn python_extracts_decorated_functions() {
let registry = mk_registry();
let index = SymbolIndex::new(registry);
let file = fixtures_dir().join("decorated.py");
let entry = index.ensure_file(&file).unwrap();
let names: Vec<&str> = entry.symbols.iter().map(|s| s.name.as_str()).collect();
assert!(
names.contains(&"cached_func"),
"should find @lru_cache decorated function 'cached_func'. Found: {:?}",
names
);
assert!(
names.contains(&"my_prop"),
"should find @property decorated method 'my_prop'. Found: {:?}",
names
);
assert!(
names.contains(&"regular_method"),
"should find undecorated method 'regular_method'. Found: {:?}",
names
);
}
#[test]
#[cfg(feature = "semantic-ts")]
fn cold_cache_auto_initializes() {
let registry = mk_registry();
let index = SymbolIndex::new(registry);
let _ = index.find_definition("nonexistent_surely").unwrap();
}
#[test]
#[cfg(feature = "semantic-ts")]
fn ensure_file_skips_binary_gracefully() {
let registry = mk_registry();
let index = SymbolIndex::new(registry);
let bin_path = fixtures_dir().join("not_a_ts_file.bin");
std::fs::write(&bin_path, b"\x00\x01\x02").unwrap();
let result = index.ensure_file(&bin_path);
let _ = result;
std::fs::remove_file(&bin_path).unwrap();
}
}