use crate::memory::symbol_extractor::*;
use std::path::PathBuf;
#[test]
fn test_extract_function() {
let source = r#"
pub fn add(a: i32, b: i32) -> i32 {
a + b
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (symbols, _edges) = extractor.extract(&path, source).unwrap();
assert_eq!(symbols.len(), 1);
assert_eq!(symbols[0].name, "add");
assert_eq!(symbols[0].kind, SymbolKind::Function);
}
#[test]
fn test_extract_struct() {
let source = r#"
pub struct Point {
x: f64,
y: f64,
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (symbols, _edges) = extractor.extract(&path, source).unwrap();
assert_eq!(symbols.len(), 1);
assert_eq!(symbols[0].name, "Point");
assert_eq!(symbols[0].kind, SymbolKind::Struct);
}
#[test]
fn test_extract_call_edge() {
let source = r#"
fn caller() {
callee();
}
fn callee() {
println!("called");
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (symbols, edges) = extractor.extract(&path, source).unwrap();
assert_eq!(symbols.len(), 2); assert_eq!(edges.len(), 1);
assert_eq!(edges[0].caller, "caller");
assert_eq!(edges[0].callee, "callee");
}
#[test]
fn test_extract_trait() {
let source = r#"
pub trait Drawable {
fn draw(&self);
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (symbols, _edges) = extractor.extract(&path, source).unwrap();
assert!(
symbols
.iter()
.any(|s| s.name == "Drawable" && s.kind == SymbolKind::Trait)
);
}
#[test]
fn test_extract_impl() {
let source = r#"
struct Circle;
impl Drawable for Circle {
fn draw(&self) {}
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (symbols, _edges) = extractor.extract(&path, source).unwrap();
assert!(symbols.iter().any(|s| s.kind == SymbolKind::Impl));
}
#[test]
fn test_method_call_normalized_to_bare_name() {
let source = r#"
fn populate(store: &Store) {
store.insert_symbol("a", "function", "f.rs", 1, 2);
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (_symbols, edges) = extractor.extract(&path, source).unwrap();
assert!(
edges
.iter()
.any(|e| e.caller == "populate" && e.callee == "insert_symbol")
);
}
#[test]
fn test_enum_variant_constructors_skipped() {
let source = r#"
fn wrap(x: u32) -> Option<u32> {
let a = Some(x);
let b = Ok(1u32);
inner(a)
}
fn inner(_v: Option<u32>) {}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (_symbols, edges) = extractor.extract(&path, source).unwrap();
assert!(!edges.iter().any(|e| e.callee == "Some" || e.callee == "Ok"));
assert!(
edges
.iter()
.any(|e| e.caller == "wrap" && e.callee == "inner")
);
}
#[test]
fn test_module_path_call_kept_full() {
let source = r#"
use std::sync::Arc;
fn share() -> Arc<u32> {
Arc::new(1u32)
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (_symbols, edges) = extractor.extract(&path, source).unwrap();
assert!(
edges
.iter()
.any(|e| e.caller == "share" && e.callee == "Arc::new")
);
}
#[test]
fn test_await_context_chain_inner_call_extracted() {
let source = r#"
async fn retry_db_anyhow<F, Fut, T>(operation: F, config: &DbRetryConfig) -> Result<T>
where
F: FnMut() -> Fut,
Fut: Future<Output = Result<T>>,
{
retry_db_operation(operation, config)
.await
.context("Database operation failed after retries")
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (_symbols, edges) = extractor.extract(&path, source).unwrap();
assert!(
edges
.iter()
.any(|e| e.caller == "retry_db_anyhow" && e.callee == "retry_db_operation"),
"inner call of an .await.context() chain must produce its edge, got: {edges:?}"
);
assert!(edges.iter().any(|e| e.callee == "context"));
}
#[test]
fn test_nested_call_in_arguments_extracted() {
let source = r#"
fn outer() -> u32 {
wrap(inner())
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (_symbols, edges) = extractor.extract(&path, source).unwrap();
assert!(
edges
.iter()
.any(|e| e.caller == "outer" && e.callee == "wrap")
);
assert!(
edges
.iter()
.any(|e| e.caller == "outer" && e.callee == "inner")
);
}
#[test]
fn test_impl_method_symbol_and_calls_extracted() {
let source = r#"
struct Store;
impl Store {
fn query_all(&self) -> Vec<u32> {
self.load()
}
}
"#;
let mut extractor = SymbolExtractor::new().unwrap();
let path = PathBuf::from("test.rs");
let (symbols, edges) = extractor.extract(&path, source).unwrap();
assert!(
symbols.iter().any(|s| s.name == "query_all"),
"impl method must appear in symbols, got: {symbols:?}"
);
assert!(
edges
.iter()
.any(|e| e.caller == "query_all" && e.callee == "load")
);
}