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