feat: implement all P1/P2/P3/P4 improvements from issue backlog
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>)
This commit is contained in:
@@ -136,3 +136,74 @@ fn extracts_composition_from_class_level_annotations() {
|
||||
assert_eq!(composition[0].source(), "Definition");
|
||||
assert_eq!(composition[0].target(), "Gad");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_python_class_methods() {
|
||||
let source = "class OrderService:\n def process(self):\n pass\n def cancel(self):\n pass\n";
|
||||
let result = analyze_python(source, "service.py");
|
||||
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "OrderService")
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
element.methods().iter().any(|m| m.contains("process")),
|
||||
"expected 'process' method, got: {:?}",
|
||||
element.methods()
|
||||
);
|
||||
assert!(
|
||||
element.methods().iter().any(|m| m.contains("cancel")),
|
||||
"expected 'cancel' method, got: {:?}",
|
||||
element.methods()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_python_method_typed_params() {
|
||||
let source = "class OrderService:\n def process(self, order: Order, count: int) -> None:\n pass\n";
|
||||
let result = analyze_python(source, "service.py");
|
||||
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "OrderService")
|
||||
.unwrap();
|
||||
|
||||
let method = element
|
||||
.methods()
|
||||
.iter()
|
||||
.find(|m| m.contains("process"))
|
||||
.unwrap();
|
||||
assert!(
|
||||
method.contains("order: Order"),
|
||||
"missing typed param: {method}"
|
||||
);
|
||||
assert!(
|
||||
method.contains("count: int"),
|
||||
"missing typed param: {method}"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_python_method_return_annotation() {
|
||||
let source = "class OrderService:\n def get(self) -> Order:\n pass\n";
|
||||
let result = analyze_python(source, "service.py");
|
||||
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "OrderService")
|
||||
.unwrap();
|
||||
|
||||
let method = element
|
||||
.methods()
|
||||
.iter()
|
||||
.find(|m| m.contains("get"))
|
||||
.unwrap();
|
||||
assert!(
|
||||
method.contains("-> Order"),
|
||||
"expected return type, got: {method}"
|
||||
);
|
||||
}
|
||||
|
||||
@@ -128,3 +128,145 @@ fn extracts_mod_declarations() {
|
||||
assert!(imports.iter().any(|r| r.target() == "crate::models"));
|
||||
assert!(imports.iter().any(|r| r.target() == "crate::services"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_rust_method_with_typed_params() {
|
||||
let source = r#"
|
||||
pub struct OrderService;
|
||||
impl OrderService {
|
||||
pub fn process(&self, order: Order, count: u64) {}
|
||||
}
|
||||
"#;
|
||||
let result = analyze_rust(source, "service.rs");
|
||||
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "OrderService")
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
element
|
||||
.methods()
|
||||
.iter()
|
||||
.any(|m| m.contains("order: Order") && m.contains("count: u64")),
|
||||
"expected typed params in method, got: {:?}",
|
||||
element.methods()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_rust_method_return_type() {
|
||||
let source = r#"
|
||||
pub struct OrderService;
|
||||
impl OrderService {
|
||||
pub fn get(&self) -> Order {}
|
||||
}
|
||||
"#;
|
||||
let result = analyze_rust(source, "service.rs");
|
||||
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "OrderService")
|
||||
.unwrap();
|
||||
|
||||
assert!(
|
||||
element.methods().iter().any(|m| m.contains("-> Order")),
|
||||
"expected return type in method, got: {:?}",
|
||||
element.methods()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_rust_method_params_and_return() {
|
||||
let source = r#"
|
||||
pub struct OrderService;
|
||||
impl OrderService {
|
||||
pub fn process(&self, order: Order) -> Result<(), Error> {}
|
||||
}
|
||||
"#;
|
||||
let result = analyze_rust(source, "service.rs");
|
||||
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "OrderService")
|
||||
.unwrap();
|
||||
|
||||
let method = element
|
||||
.methods()
|
||||
.iter()
|
||||
.find(|m| m.contains("process"))
|
||||
.unwrap();
|
||||
assert!(method.contains("order: Order"), "missing param: {method}");
|
||||
assert!(method.contains("->"), "missing return arrow: {method}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_rust_static_method_params() {
|
||||
let source = r#"
|
||||
pub struct Finder;
|
||||
impl Finder {
|
||||
pub fn detect(path: &str, count: usize) -> bool { false }
|
||||
}
|
||||
"#;
|
||||
let result = analyze_rust(source, "finder.rs");
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "Finder")
|
||||
.unwrap();
|
||||
let method = element
|
||||
.methods()
|
||||
.iter()
|
||||
.find(|m| m.contains("detect"))
|
||||
.unwrap();
|
||||
assert!(method.contains("path"), "missing path param: {method}");
|
||||
assert!(method.contains("count"), "missing count param: {method}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_rust_private_method_params() {
|
||||
let source = r#"
|
||||
pub struct WalkdirDiscovery;
|
||||
impl WalkdirDiscovery {
|
||||
fn detect_language(path: &std::path::Path) -> Option<String> { None }
|
||||
}
|
||||
"#;
|
||||
let result = analyze_rust(source, "discovery.rs");
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "WalkdirDiscovery")
|
||||
.unwrap();
|
||||
let method = element
|
||||
.methods()
|
||||
.iter()
|
||||
.find(|m| m.contains("detect_language"))
|
||||
.unwrap();
|
||||
assert!(method.contains("path"), "missing path param: {method}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn extracts_rust_method_reference_param() {
|
||||
let source = r#"
|
||||
use std::path::Path;
|
||||
pub struct WalkdirDiscovery;
|
||||
impl WalkdirDiscovery {
|
||||
fn detect_language(path: &Path) -> Option<String> { None }
|
||||
}
|
||||
"#;
|
||||
let result = analyze_rust(source, "discovery.rs");
|
||||
let element = result
|
||||
.elements()
|
||||
.iter()
|
||||
.find(|e| e.name() == "WalkdirDiscovery")
|
||||
.unwrap();
|
||||
let method = element
|
||||
.methods()
|
||||
.iter()
|
||||
.find(|m| m.contains("detect_language"))
|
||||
.unwrap();
|
||||
assert!(method.contains("path"), "missing path param: {method}");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user