mod fakes; use std::path::Path; use archlens_application::use_cases::build_code_graph::BuildCodeGraph; use archlens_domain::{ AnalysisConfig, AnalysisResult, AnalysisWarning, CodeElement, CodeElementKind, CodeGraph, DiagramLevel, FilePath, Language, SourceFile, }; use fakes::{FakeFileDiscovery, FakeProjectAnalyzer, FakeSourceAnalyzer}; #[test] fn project_level_returns_project_analyzer_graph() { let mut cg = CodeGraph::new(); cg.add_element( CodeElement::new( "MyApp", CodeElementKind::Project, FilePath::new("Cargo.toml").unwrap(), 1, ) .unwrap(), ); let use_case = BuildCodeGraph { discovery: FakeFileDiscovery::empty(), source_analyzer: FakeSourceAnalyzer::new(), project_analyzer: Some(Box::new(FakeProjectAnalyzer::new(cg))), }; let result = use_case .execute( Path::new("."), &AnalysisConfig::default(), DiagramLevel::Project, ) .unwrap(); assert_eq!(result.graph.elements().len(), 1); assert_eq!(result.graph.elements()[0].name(), "MyApp"); assert!(result.warnings.is_empty()); } #[test] fn project_level_without_analyzer_returns_error() { let use_case = BuildCodeGraph { discovery: FakeFileDiscovery::empty(), source_analyzer: FakeSourceAnalyzer::new(), project_analyzer: None, }; let err = use_case .execute( Path::new("."), &AnalysisConfig::default(), DiagramLevel::Project, ) .unwrap_err(); assert!(err.to_string().contains("no project analyzer")); } #[test] fn type_level_uses_source_analyzer_not_project() { let files = vec![SourceFile::new( FilePath::new("src/order.rs").unwrap(), Language::Rust, )]; let discovery = FakeFileDiscovery::new(files); let analyzer = FakeSourceAnalyzer::new().with_result( "src/order.rs", AnalysisResult::new( vec![ CodeElement::new( "Order", CodeElementKind::Struct, FilePath::new("src/order.rs").unwrap(), 1, ) .unwrap(), ], vec![], vec![], ), ); let mut project_cg = CodeGraph::new(); project_cg.add_element( CodeElement::new( "ProjectElement", CodeElementKind::Project, FilePath::new("Cargo.toml").unwrap(), 1, ) .unwrap(), ); let use_case = BuildCodeGraph { discovery, source_analyzer: analyzer, project_analyzer: Some(Box::new(FakeProjectAnalyzer::new(project_cg))), }; let result = use_case .execute( Path::new("."), &AnalysisConfig::default(), DiagramLevel::Type, ) .unwrap(); // Source element present, project element NOT merged (Type level skips merge) assert_eq!(result.graph.elements().len(), 1); assert_eq!(result.graph.elements()[0].name(), "Order"); } #[test] fn module_level_without_project_analyzer_succeeds() { let use_case = BuildCodeGraph { discovery: FakeFileDiscovery::empty(), source_analyzer: FakeSourceAnalyzer::new(), project_analyzer: None, }; let result = use_case .execute( Path::new("."), &AnalysisConfig::default(), DiagramLevel::Module, ) .unwrap(); assert!(result.graph.elements().is_empty()); } #[test] fn warnings_from_source_analysis_are_propagated() { let files = vec![SourceFile::new( FilePath::new("src/broken.rs").unwrap(), Language::Rust, )]; let discovery = FakeFileDiscovery::new(files); let analyzer = FakeSourceAnalyzer::new().with_result( "src/broken.rs", AnalysisResult::new( vec![], vec![], vec![ AnalysisWarning::new( FilePath::new("src/broken.rs").unwrap(), 5, "unparseable block", ) .unwrap(), ], ), ); let use_case = BuildCodeGraph { discovery, source_analyzer: analyzer, project_analyzer: None, }; let result = use_case .execute( Path::new("."), &AnalysisConfig::default(), DiagramLevel::Module, ) .unwrap(); assert_eq!(result.warnings.len(), 1); assert_eq!(result.warnings[0].message(), "unparseable block"); }