Files
archlens/crates/domain/tests/boundary_rule_tests.rs
Gabriel Kaszewski fdd85011a4
Some checks failed
CI / Check / Test (push) Failing after 1m33s
Architecture Docs / Generate diagrams (push) Successful in 3m21s
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>)
2026-06-17 09:51:45 +02:00

81 lines
2.5 KiB
Rust

use archlens_domain::{
BoundaryRule, CodeElement, CodeElementKind, CodeGraph, FilePath, ModuleName, Relationship,
RelationshipKind, RuleViolation, check_boundary_rules,
};
fn make_element(name: &str, module: &str) -> CodeElement {
CodeElement::new(
name,
CodeElementKind::Class,
FilePath::new(&format!("src/{name}.rs")).unwrap(),
1,
)
.unwrap()
.with_module(ModuleName::new(module).unwrap())
}
fn graph_with_edge(
src_name: &str,
src_module: &str,
tgt_name: &str,
tgt_module: &str,
) -> CodeGraph {
let mut graph = CodeGraph::new();
graph.add_element(make_element(src_name, src_module));
graph.add_element(make_element(tgt_name, tgt_module));
graph.add_relationship(
Relationship::new(src_name, tgt_name, RelationshipKind::Composition).unwrap(),
);
graph.qualify()
}
#[test]
fn boundary_rule_parses_source_and_target() {
let rule = BoundaryRule::parse("Application --> Domain").unwrap();
assert_eq!(rule.source(), "Application");
assert_eq!(rule.target(), "Domain");
}
#[test]
fn check_returns_denied_violation_when_deny_rule_matches_edge() {
let graph = graph_with_edge("Service", "Domain", "Adapter", "Adapters");
let deny = vec![BoundaryRule::parse("Domain --> Adapters").unwrap()];
let violations = check_boundary_rules(&graph, &[], &deny);
assert_eq!(violations.len(), 1);
assert_eq!(violations[0].source_module(), "Domain");
assert_eq!(violations[0].target_module(), "Adapters");
assert!(matches!(
violations[0].kind(),
archlens_domain::RuleKind::Denied
));
}
#[test]
fn check_returns_no_violation_when_edge_matches_allow_rule() {
let graph = graph_with_edge("Service", "Application", "Order", "Domain");
let allow = vec![BoundaryRule::parse("Application --> Domain").unwrap()];
let violations = check_boundary_rules(&graph, &allow, &[]);
assert!(
violations.is_empty(),
"expected no violations, got: {}",
violations.len()
);
}
#[test]
fn check_returns_not_allowed_when_edge_absent_from_allow_list() {
let graph = graph_with_edge("Repo", "Adapters", "Order", "Domain");
// Only Application --> Domain is allowed; Adapters --> Domain is not in the list
let allow = vec![BoundaryRule::parse("Application --> Domain").unwrap()];
let violations = check_boundary_rules(&graph, &allow, &[]);
assert_eq!(violations.len(), 1);
assert_eq!(violations[0].source_module(), "Adapters");
assert_eq!(violations[0].target_module(), "Domain");
}