#[cfg_attr(coverage_nightly, coverage(off))]
#[cfg(test)]
mod tests {
use super::*;
use crate::models::dag::{DependencyGraph, Edge, EdgeType, NodeInfo, NodeType};
use rustc_hash::FxHashMap;
fn create_test_node(id: &str, node_type: NodeType) -> NodeInfo {
NodeInfo {
id: id.to_string(),
label: id.to_string(),
node_type,
file_path: format!("{}.rs", id),
line_number: 1,
complexity: 5,
metadata: FxHashMap::default(),
}
}
fn create_test_graph() -> DependencyGraph {
let mut graph = DependencyGraph::new();
graph.add_node(create_test_node("a", NodeType::Function));
graph.add_node(create_test_node("b", NodeType::Function));
graph.add_node(create_test_node("c", NodeType::Function));
graph.add_node(create_test_node("d", NodeType::Class));
graph.edges.push(Edge {
from: "a".to_string(),
to: "b".to_string(),
edge_type: EdgeType::Calls,
weight: 1,
});
graph.edges.push(Edge {
from: "b".to_string(),
to: "c".to_string(),
edge_type: EdgeType::Calls,
weight: 1,
});
graph.edges.push(Edge {
from: "c".to_string(),
to: "d".to_string(),
edge_type: EdgeType::Uses,
weight: 1,
});
graph
}
#[test]
fn test_dag_builder_new() {
let builder = DagBuilder::new();
assert!(builder.function_map.is_empty());
assert!(builder.type_map.is_empty());
}
fn project_with(files: Vec<FileContext>) -> ProjectContext {
ProjectContext {
project_type: "rust".to_string(),
summary: crate::services::context::ProjectSummary {
total_files: files.len(),
total_functions: 0,
total_structs: 0,
total_enums: 0,
total_traits: 0,
total_impls: 0,
dependencies: vec![],
},
files,
graph: None,
}
}
fn rust_file(path: &str, items: Vec<AstItem>) -> FileContext {
FileContext {
path: path.to_string(),
language: "rust".to_string(),
items,
complexity_metrics: None,
}
}
#[test]
fn test_import_edge_resolves_to_sibling_module() {
let project = project_with(vec![
rust_file(
"src/main.rs",
vec![
AstItem::Use {
path: "helper".to_string(),
line: 1,
},
AstItem::Function {
name: "main".to_string(),
visibility: "private".to_string(),
is_async: false,
line: 4,
},
],
),
rust_file(
"src/helper.rs",
vec![AstItem::Function {
name: "helper_one".to_string(),
visibility: "pub".to_string(),
is_async: false,
line: 1,
}],
),
]);
let graph = DagBuilder::build_from_project(&project);
let import_edges: Vec<_> = graph
.edges
.iter()
.filter(|e| e.edge_type == EdgeType::Imports)
.collect();
assert_eq!(
import_edges.len(),
1,
"expected 1 import edge, got {:?}",
graph.edges
);
assert_eq!(import_edges[0].from, "src_main");
assert_eq!(import_edges[0].to, "src_helper");
}
#[test]
fn test_import_edge_resolves_crate_relative_path() {
let project = project_with(vec![
rust_file(
"src/main.rs",
vec![AstItem::Use {
path: "crate::services::helper::Thing".to_string(),
line: 1,
}],
),
rust_file("src/services/helper.rs", vec![]),
]);
let graph = DagBuilder::build_from_project(&project);
assert_eq!(graph.edges.len(), 1);
assert_eq!(graph.edges[0].from, "src_main");
assert_eq!(graph.edges[0].to, "src_services_helper");
}
#[test]
fn test_external_import_creates_no_edge() {
let project = project_with(vec![rust_file(
"src/main.rs",
vec![AstItem::Use {
path: "serde::Serialize".to_string(),
line: 1,
}],
)]);
let graph = DagBuilder::build_from_project(&project);
assert!(
graph.edges.is_empty(),
"unresolvable imports must not create edges: {:?}",
graph.edges
);
}
#[test]
fn test_empty_project_yields_empty_graph() {
let graph = DagBuilder::build_from_project(&project_with(vec![]));
assert!(graph.nodes.is_empty());
assert!(graph.edges.is_empty());
}
#[test]
fn test_prune_graph_pagerank_no_pruning_needed() {
let graph = create_test_graph();
let pruned = prune_graph_pagerank(&graph, 10);
assert_eq!(pruned.nodes.len(), graph.nodes.len());
assert_eq!(pruned.edges.len(), graph.edges.len());
}
#[test]
fn test_prune_graph_pagerank_with_pruning() {
let graph = create_test_graph();
let pruned = prune_graph_pagerank(&graph, 2);
assert!(pruned.nodes.len() <= 2);
for edge in &pruned.edges {
assert!(pruned.nodes.contains_key(&edge.from));
assert!(pruned.nodes.contains_key(&edge.to));
}
}
#[test]
fn test_prune_graph_pagerank_adds_centrality_metadata() {
let graph = create_test_graph();
let pruned = prune_graph_pagerank(&graph, 3);
for node in pruned.nodes.values() {
assert!(node.metadata.contains_key("centrality"));
}
}
#[test]
fn test_add_pagerank_scores() {
let graph = create_test_graph();
let scored = add_pagerank_scores(graph.clone());
assert_eq!(scored.nodes.len(), graph.nodes.len());
assert_eq!(scored.edges.len(), graph.edges.len());
for node in scored.nodes.values() {
assert!(node.metadata.contains_key("centrality"));
}
}
#[test]
fn test_add_pagerank_scores_empty_graph() {
let graph = DependencyGraph::new();
let scored = add_pagerank_scores(graph.clone());
assert!(scored.nodes.is_empty());
assert!(scored.edges.is_empty());
}
#[test]
fn test_prune_graph_empty() {
let graph = DependencyGraph::new();
let pruned = prune_graph_pagerank(&graph, 5);
assert!(pruned.nodes.is_empty());
assert!(pruned.edges.is_empty());
}
#[test]
fn test_prune_graph_single_node() {
let mut graph = DependencyGraph::new();
graph.add_node(create_test_node("single", NodeType::Function));
let pruned = prune_graph_pagerank(&graph, 5);
assert_eq!(pruned.nodes.len(), 1);
assert!(pruned.nodes.contains_key("single"));
}
#[test]
fn test_edge_type_priority_in_finalize() {
let mut graph = DependencyGraph::new();
for i in 0..10 {
graph.add_node(create_test_node(&format!("node{i}"), NodeType::Function));
}
graph.edges.push(Edge {
from: "node0".to_string(),
to: "node1".to_string(),
edge_type: EdgeType::Inherits, weight: 1,
});
graph.edges.push(Edge {
from: "node1".to_string(),
to: "node2".to_string(),
edge_type: EdgeType::Imports, weight: 1,
});
graph.edges.push(Edge {
from: "node2".to_string(),
to: "node3".to_string(),
edge_type: EdgeType::Uses,
weight: 1,
});
let scored = add_pagerank_scores(graph.clone());
assert_eq!(scored.edges.len(), 3);
}
#[test]
fn test_dependency_graph_clone() {
let graph = create_test_graph();
let cloned = graph.clone();
assert_eq!(cloned.nodes.len(), graph.nodes.len());
assert_eq!(cloned.edges.len(), graph.edges.len());
}
}