use std::fs; use archlens::run; fn create_rust_project(dir: &std::path::Path) { fs::create_dir_all(dir.join("src")).unwrap(); fs::write( dir.join("src/order.rs"), "pub struct Order {\n pub id: u64,\n}\n", ) .unwrap(); fs::write( dir.join("src/service.rs"), "pub struct OrderService {\n order: Order,\n}\n", ) .unwrap(); } fn create_multi_module_project(dir: &std::path::Path) { fs::create_dir_all(dir.join("src/orders")).unwrap(); fs::create_dir_all(dir.join("src/billing")).unwrap(); fs::write( dir.join("src/orders/order.rs"), "pub struct Order {\n pub id: u64,\n}\n", ) .unwrap(); fs::write( dir.join("src/orders/service.rs"), "pub struct OrderService {\n order: Order,\n}\n", ) .unwrap(); fs::write( dir.join("src/billing/invoice.rs"), "pub struct Invoice {\n pub total: f64,\n}\n", ) .unwrap(); } #[test] fn analyzes_rust_project_and_writes_mermaid_to_file() { let project = tempfile::tempdir().unwrap(); create_rust_project(project.path()); let output_dir = tempfile::tempdir().unwrap(); let output_file = output_dir.path().join("arch.mmd"); run(archlens::CliArgs { command: None, path: project.path().to_path_buf(), level: "type".to_string(), format: "mermaid".to_string(), output: Some(output_file.to_str().unwrap().to_string()), config: None, scope: None, exclude: vec![], include_tests: false, no_weights: false, watch: false, since: None, split_by_module: false, strict: false, check: false, verbose: 0, }) .unwrap(); let content = fs::read_to_string(&output_file).unwrap(); assert!(content.contains("classDiagram")); assert!(content.contains("Order")); assert!(content.contains("OrderService")); } #[test] fn works_without_config_file() { let project = tempfile::tempdir().unwrap(); create_rust_project(project.path()); let output_dir = tempfile::tempdir().unwrap(); let output_file = output_dir.path().join("arch.mmd"); let result = run(archlens::CliArgs { command: None, path: project.path().to_path_buf(), level: "type".to_string(), format: "mermaid".to_string(), output: Some(output_file.to_str().unwrap().to_string()), config: None, scope: None, exclude: vec![], include_tests: false, no_weights: false, watch: false, since: None, split_by_module: false, strict: false, check: false, verbose: 0, }); assert!(result.is_ok()); } #[test] fn split_by_module_writes_overview_and_per_module_files() { let project = tempfile::tempdir().unwrap(); create_multi_module_project(project.path()); let output_dir = tempfile::tempdir().unwrap(); run(archlens::CliArgs { command: None, path: project.path().to_path_buf(), level: "module".to_string(), format: "mermaid".to_string(), output: Some(output_dir.path().to_str().unwrap().to_string()), config: None, scope: None, exclude: vec![], include_tests: false, no_weights: false, watch: false, since: None, split_by_module: true, strict: false, check: false, verbose: 0, }) .unwrap(); let overview = output_dir.path().join("overview.mmd"); assert!(overview.exists(), "overview.mmd should exist"); let overview_content = fs::read_to_string(&overview).unwrap(); assert!(overview_content.contains("graph TD") || overview_content.contains("classDiagram")); let entries: Vec<_> = fs::read_dir(output_dir.path()) .unwrap() .filter_map(|e| e.ok()) .collect(); assert!( entries.len() > 1, "should have overview + at least one module file" ); } fn create_cross_module_project(dir: &std::path::Path) { fs::create_dir_all(dir.join("src/app")).unwrap(); fs::create_dir_all(dir.join("src/domain")).unwrap(); fs::write( dir.join("src/domain/order.rs"), "pub struct Order { pub id: u64 }\n", ) .unwrap(); fs::write( dir.join("src/app/service.rs"), "use crate::domain::Order;\npub struct OrderService { order: Order }\n", ) .unwrap(); } #[test] fn per_module_file_shows_cross_module_dependency_arrows() { let project = tempfile::tempdir().unwrap(); create_cross_module_project(project.path()); let output_dir = tempfile::tempdir().unwrap(); run(archlens::CliArgs { command: None, path: project.path().to_path_buf(), level: "type".to_string(), format: "mermaid".to_string(), output: Some(output_dir.path().to_str().unwrap().to_string()), config: None, scope: None, exclude: vec![], include_tests: false, no_weights: false, watch: false, since: None, split_by_module: true, strict: false, check: false, verbose: 0, }) .unwrap(); let app_file = output_dir.path().join("app.mmd"); assert!(app_file.exists(), "app.mmd should exist"); let content = fs::read_to_string(&app_file).unwrap(); assert!( content.contains("<>"), "per-module file should contain external module placeholder: {content}" ); assert!( content.contains("domain"), "per-module file should reference the domain module: {content}" ); }