P1 correctness: - filter test files by default (--include-tests to opt in) - per-module diagrams show cross-module dependency arrows - qualified type names (Module::TypeName) fix false edges from duplicate names P2 output richness: - method parameter types and return types in class diagrams (Rust + Python) - Python pyproject.toml project analyzer (--level project for monorepos) P3 unique value: - boundary rules in archlens.toml ([rules] allow/deny, --strict enforcement) P4 nice to have: - dependency weight labels on module arrows (--no-weights to disable) - --watch mode with 500ms debounce - D2 renderer adapter (--format d2) - interactive self-contained HTML viewer (--format html) - git-aware incremental analysis (--since <ref>)
166 lines
5.3 KiB
Rust
166 lines
5.3 KiB
Rust
use std::fs;
|
|
|
|
use archlens_domain::{AnalysisConfig, Language, ports::FileDiscovery};
|
|
use archlens_walkdir::WalkdirDiscovery;
|
|
|
|
fn create_test_tree(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/service.rs"), "struct OrderService;").unwrap();
|
|
fs::write(dir.join("src/orders/model.py"), "class Order: pass").unwrap();
|
|
fs::write(dir.join("src/billing/invoice.cs"), "class Invoice {}").unwrap();
|
|
fs::write(dir.join("src/readme.txt"), "not source code").unwrap();
|
|
}
|
|
|
|
#[test]
|
|
fn discovers_rust_python_and_csharp_files() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
create_test_tree(dir.path());
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery
|
|
.discover(dir.path(), &AnalysisConfig::default())
|
|
.unwrap();
|
|
|
|
assert_eq!(files.len(), 3);
|
|
|
|
let languages: Vec<Language> = files.iter().map(|f| f.language()).collect();
|
|
assert!(languages.contains(&Language::Rust));
|
|
assert!(languages.contains(&Language::Python));
|
|
assert!(languages.contains(&Language::CSharp));
|
|
}
|
|
|
|
#[test]
|
|
fn ignores_non_source_files() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
create_test_tree(dir.path());
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery
|
|
.discover(dir.path(), &AnalysisConfig::default())
|
|
.unwrap();
|
|
|
|
let paths: Vec<&str> = files.iter().map(|f| f.path().as_str()).collect();
|
|
assert!(!paths.iter().any(|p| p.ends_with(".txt")));
|
|
}
|
|
|
|
#[test]
|
|
fn respects_exclude_patterns() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
create_test_tree(dir.path());
|
|
|
|
let config = AnalysisConfig::default().with_excludes(vec!["billing".to_string()]);
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery.discover(dir.path(), &config).unwrap();
|
|
|
|
assert_eq!(files.len(), 2);
|
|
assert!(!files.iter().any(|f| f.path().as_str().contains("billing")));
|
|
}
|
|
|
|
#[test]
|
|
fn excludes_python_test_prefix_files_by_default() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
fs::write(dir.path().join("orders.py"), "class Order: pass").unwrap();
|
|
fs::write(dir.path().join("test_orders.py"), "class TestOrder: pass").unwrap();
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery
|
|
.discover(dir.path(), &AnalysisConfig::default())
|
|
.unwrap();
|
|
|
|
assert_eq!(files.len(), 1);
|
|
assert!(files[0].path().as_str().ends_with("orders.py"));
|
|
}
|
|
|
|
#[test]
|
|
fn excludes_python_test_suffix_files_by_default() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
fs::write(dir.path().join("orders.py"), "class Order: pass").unwrap();
|
|
fs::write(dir.path().join("orders_test.py"), "class OrderTest: pass").unwrap();
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery
|
|
.discover(dir.path(), &AnalysisConfig::default())
|
|
.unwrap();
|
|
|
|
assert_eq!(files.len(), 1);
|
|
assert!(files[0].path().as_str().ends_with("orders.py"));
|
|
}
|
|
|
|
#[test]
|
|
fn excludes_files_in_tests_directory_by_default() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
fs::create_dir_all(dir.path().join("tests")).unwrap();
|
|
fs::write(dir.path().join("orders.py"), "class Order: pass").unwrap();
|
|
fs::write(dir.path().join("tests/helpers.py"), "class Helper: pass").unwrap();
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery
|
|
.discover(dir.path(), &AnalysisConfig::default())
|
|
.unwrap();
|
|
|
|
assert_eq!(files.len(), 1);
|
|
assert!(files[0].path().as_str().ends_with("orders.py"));
|
|
}
|
|
|
|
#[test]
|
|
fn excludes_rust_test_files_by_default() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
fs::write(dir.path().join("orders.rs"), "struct Order;").unwrap();
|
|
fs::write(dir.path().join("orders_tests.rs"), "struct OrdersTests;").unwrap();
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery
|
|
.discover(dir.path(), &AnalysisConfig::default())
|
|
.unwrap();
|
|
|
|
assert_eq!(files.len(), 1);
|
|
assert!(files[0].path().as_str().ends_with("orders.rs"));
|
|
}
|
|
|
|
#[test]
|
|
fn excludes_rust_files_in_tests_directory_by_default() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
fs::create_dir_all(dir.path().join("tests")).unwrap();
|
|
fs::write(dir.path().join("lib.rs"), "struct Lib;").unwrap();
|
|
fs::write(
|
|
dir.path().join("tests/integration.rs"),
|
|
"struct IntegrationTest;",
|
|
)
|
|
.unwrap();
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery
|
|
.discover(dir.path(), &AnalysisConfig::default())
|
|
.unwrap();
|
|
|
|
assert_eq!(files.len(), 1);
|
|
assert!(files[0].path().as_str().ends_with("lib.rs"));
|
|
}
|
|
|
|
#[test]
|
|
fn include_tests_flag_re_enables_test_files() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
fs::write(dir.path().join("orders.py"), "class Order: pass").unwrap();
|
|
fs::write(dir.path().join("test_orders.py"), "class TestOrder: pass").unwrap();
|
|
|
|
let config = AnalysisConfig::default().with_include_tests(true);
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery.discover(dir.path(), &config).unwrap();
|
|
|
|
assert_eq!(files.len(), 2);
|
|
}
|
|
|
|
#[test]
|
|
fn empty_directory_returns_no_files() {
|
|
let dir = tempfile::tempdir().unwrap();
|
|
|
|
let discovery = WalkdirDiscovery::new();
|
|
let files = discovery
|
|
.discover(dir.path(), &AnalysisConfig::default())
|
|
.unwrap();
|
|
|
|
assert!(files.is_empty());
|
|
}
|