diff --git a/wtismycode-core/tests/callee_resolution.rs b/wtismycode-core/tests/callee_resolution.rs new file mode 100644 index 0000000..6ad31f7 --- /dev/null +++ b/wtismycode-core/tests/callee_resolution.rs @@ -0,0 +1,76 @@ +//! Tests for resolve_callee_to_symbol_id functionality +//! +//! Verifies that call expressions are correctly resolved to qualified symbol IDs. + +use std::path::Path; +use wtismycode_core::{Config, scanner::FileScanner, python_analyzer::PythonAnalyzer}; + +#[test] +fn test_resolve_callee_to_symbol_id() { + let config_path = "tests/golden/test_project/wtismycode.toml"; + let config = Config::load_from_file(Path::new(config_path)).expect("Failed to load config"); + let project_root = Path::new("tests/golden/test_project"); + let scanner = FileScanner::new(config.clone()); + let python_files = scanner.scan_python_files(project_root).expect("Failed to scan"); + let analyzer = PythonAnalyzer::new(config); + + let mut parsed_modules = Vec::new(); + for file_path in python_files { + parsed_modules.push(analyzer.parse_module(&file_path).expect("Failed to parse")); + } + + let model = analyzer.resolve_symbols(&parsed_modules).expect("Failed to resolve"); + + // Verify that symbol call edges exist and have been resolved + assert!(!model.edges.symbol_call_edges.is_empty(), "Should have symbol call edges"); + + // Check that at least some edges reference known symbols (resolved correctly) + let resolved_count = model.edges.symbol_call_edges.iter() + .filter(|edge| model.symbols.contains_key(&edge.to_id)) + .count(); + + println!("Total call edges: {}", model.edges.symbol_call_edges.len()); + println!("Resolved to known symbols: {}", resolved_count); + + // At least some calls should resolve to known symbols + assert!(resolved_count > 0, "At least some calls should resolve to known symbol IDs"); + + // Verify that same-module calls are resolved with module:: prefix + for edge in &model.edges.symbol_call_edges { + assert!(edge.from_id.contains("::"), "from_id should be qualified: {}", edge.from_id); + // to_id should also be qualified (module::symbol format) + assert!(edge.to_id.contains("::"), "to_id should be qualified: {}", edge.to_id); + } +} + +#[test] +fn test_callee_resolution_cross_module() { + let config_path = "tests/golden/test_project/wtismycode.toml"; + let config = Config::load_from_file(Path::new(config_path)).expect("Failed to load config"); + let project_root = Path::new("tests/golden/test_project"); + let scanner = FileScanner::new(config.clone()); + let python_files = scanner.scan_python_files(project_root).expect("Failed to scan"); + let analyzer = PythonAnalyzer::new(config); + + let mut parsed_modules = Vec::new(); + for file_path in python_files { + parsed_modules.push(analyzer.parse_module(&file_path).expect("Failed to parse")); + } + + let model = analyzer.resolve_symbols(&parsed_modules).expect("Failed to resolve"); + + // Check that modules have outbound/inbound relationships + let modules_with_outbound = model.modules.values() + .filter(|m| !m.outbound_modules.is_empty()) + .count(); + + println!("Modules with outbound deps: {}", modules_with_outbound); + + // Verify fan-in/fan-out metrics were computed + let symbols_with_metrics = model.symbols.values() + .filter(|s| s.metrics.fan_in > 0 || s.metrics.fan_out > 0) + .count(); + + println!("Symbols with non-zero metrics: {}", symbols_with_metrics); + assert!(symbols_with_metrics > 0, "Some symbols should have fan-in or fan-out > 0"); +}