use hypen_engine::ir::NodeId;
use hypen_engine::reactive::{Binding, DependencyGraph};
use slotmap::SlotMap;
use std::sync::Mutex;
lazy_static::lazy_static! {
static ref NODE_POOL: Mutex<SlotMap<NodeId, ()>> = Mutex::new(SlotMap::with_key());
}
fn node(_n: u32) -> NodeId {
let mut pool = NODE_POOL.lock().unwrap();
pool.insert(())
}
#[test]
fn test_add_dependency_basic() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
let binding = Binding::state(vec!["user".to_string(), "name".to_string()]);
graph.add_dependency(node_id, &binding,
None,
);
let nodes = graph.get_dependent_nodes("user.name");
assert!(nodes.is_some());
assert_eq!(nodes.unwrap().len(), 1);
assert!(nodes.unwrap().contains(&node_id));
}
#[test]
fn test_add_multiple_nodes_same_path() {
let mut graph = DependencyGraph::new();
let node1 = node(1);
let node2 = node(2);
let node3 = node(3);
let binding = Binding::state(vec!["count".to_string()]);
graph.add_dependency(node1, &binding,
None,
);
graph.add_dependency(node2, &binding,
None,
);
graph.add_dependency(node3, &binding,
None,
);
let nodes = graph.get_dependent_nodes("count");
assert!(nodes.is_some());
assert_eq!(nodes.unwrap().len(), 3);
}
#[test]
fn test_add_single_node_multiple_paths() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
graph.add_dependency(node_id, &Binding::state(vec!["firstName".to_string()]),
None,
);
graph.add_dependency(node_id, &Binding::state(vec!["lastName".to_string()]),
None,
);
graph.add_dependency(node_id, &Binding::state(vec!["email".to_string()]),
None,
);
assert!(graph
.get_dependent_nodes("firstName")
.unwrap()
.contains(&node_id));
assert!(graph
.get_dependent_nodes("lastName")
.unwrap()
.contains(&node_id));
assert!(graph
.get_dependent_nodes("email")
.unwrap()
.contains(&node_id));
}
#[test]
fn test_get_dependent_nodes_nonexistent_path() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
graph.add_dependency(node_id, &Binding::state(vec!["user".to_string()]),
None,
);
let nodes = graph.get_dependent_nodes("nonexistent");
assert!(nodes.is_none());
}
#[test]
fn test_remove_node_clears_all_dependencies() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
graph.add_dependency(
node_id,
&Binding::state(vec!["user".to_string(), "name".to_string()]), None,
);
graph.add_dependency(
node_id,
&Binding::state(vec!["user".to_string(), "email".to_string()]), None,
);
graph.add_dependency(node_id, &Binding::state(vec!["settings".to_string()]),
None,
);
assert!(graph
.get_dependent_nodes("user.name")
.unwrap()
.contains(&node_id));
assert!(graph
.get_dependent_nodes("user.email")
.unwrap()
.contains(&node_id));
assert!(graph
.get_dependent_nodes("settings")
.unwrap()
.contains(&node_id));
graph.remove_node(node_id);
assert!(graph
.get_dependent_nodes("user.name")
.map_or(true, |n| !n.contains(&node_id)));
assert!(graph
.get_dependent_nodes("user.email")
.map_or(true, |n| !n.contains(&node_id)));
assert!(graph
.get_dependent_nodes("settings")
.map_or(true, |n| !n.contains(&node_id)));
}
#[test]
fn test_remove_node_preserves_other_nodes() {
let mut graph = DependencyGraph::new();
let node1 = node(1);
let node2 = node(2);
let binding = Binding::state(vec!["shared".to_string()]);
graph.add_dependency(node1, &binding,
None,
);
graph.add_dependency(node2, &binding,
None,
);
graph.remove_node(node1);
let nodes = graph.get_dependent_nodes("shared").unwrap();
assert!(!nodes.contains(&node1));
assert!(nodes.contains(&node2));
assert_eq!(nodes.len(), 1);
}
#[test]
fn test_get_affected_nodes_exact_match() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
graph.add_dependency(
node_id,
&Binding::state(vec!["user".to_string(), "name".to_string()]), None,
);
let affected = graph.get_affected_nodes("user.name");
assert_eq!(affected.len(), 1);
assert!(affected.contains(&node_id));
}
#[test]
fn test_get_affected_nodes_parent_changed() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
graph.add_dependency(
node_id,
&Binding::state(vec![
"user".to_string(),
"profile".to_string(),
"name".to_string(),
]), None,
);
let affected = graph.get_affected_nodes("user");
assert_eq!(affected.len(), 1);
assert!(affected.contains(&node_id));
}
#[test]
fn test_get_affected_nodes_child_changed() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
graph.add_dependency(node_id, &Binding::state(vec!["user".to_string()]),
None,
);
let affected = graph.get_affected_nodes("user.email");
assert_eq!(affected.len(), 1);
assert!(affected.contains(&node_id));
}
#[test]
fn test_get_affected_nodes_unrelated_path() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
graph.add_dependency(
node_id,
&Binding::state(vec!["user".to_string(), "name".to_string()]), None,
);
let affected = graph.get_affected_nodes("settings");
assert_eq!(affected.len(), 0);
}
#[test]
fn test_get_affected_nodes_similar_but_different_paths() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
graph.add_dependency(node_id, &Binding::state(vec!["user".to_string()]),
None,
);
let affected = graph.get_affected_nodes("username");
assert_eq!(affected.len(), 0);
}
#[test]
fn test_clear_removes_all_dependencies() {
let mut graph = DependencyGraph::new();
for i in 0..10 {
let node_id = node(i);
graph.add_dependency(node_id, &Binding::state(vec![format!("path{}", i)]),
None,
);
}
assert!(graph.get_dependent_nodes("path0").is_some());
assert!(graph.get_dependent_nodes("path5").is_some());
graph.clear();
assert!(graph.get_dependent_nodes("path0").is_none());
assert!(graph.get_dependent_nodes("path5").is_none());
assert!(graph.get_dependent_nodes("path9").is_none());
}
#[test]
fn test_affected_nodes_with_multiple_dependencies() {
let mut graph = DependencyGraph::new();
let node1 = node(1); let node2 = node(2); let node3 = node(3);
graph.add_dependency(node1, &Binding::state(vec!["user".to_string()]),
None,
);
graph.add_dependency(
node2,
&Binding::state(vec!["user".to_string(), "name".to_string()]), None,
);
graph.add_dependency(
node3,
&Binding::state(vec![
"user".to_string(),
"profile".to_string(),
"avatar".to_string(),
]), None,
);
let affected = graph.get_affected_nodes("user.profile");
assert_eq!(affected.len(), 2); assert!(affected.contains(&node1));
assert!(!affected.contains(&node2)); assert!(affected.contains(&node3));
}
#[test]
fn test_dependency_graph_default() {
let graph = DependencyGraph::default();
let nodes = graph.get_dependent_nodes("anything");
assert!(nodes.is_none());
}
#[test]
fn test_add_same_dependency_twice_idempotent() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
let binding = Binding::state(vec!["count".to_string()]);
graph.add_dependency(node_id, &binding,
None,
);
graph.add_dependency(node_id, &binding,
None,
);
let nodes = graph.get_dependent_nodes("count");
assert!(nodes.is_some());
assert_eq!(nodes.unwrap().len(), 1);
}
#[test]
fn test_deeply_nested_path_tracking() {
let mut graph = DependencyGraph::new();
let node_id = node(1);
let binding = Binding::state(vec![
"a".to_string(),
"b".to_string(),
"c".to_string(),
"d".to_string(),
"e".to_string(),
]);
graph.add_dependency(node_id, &binding,
None,
);
let affected_root = graph.get_affected_nodes("a");
let affected_mid = graph.get_affected_nodes("a.b.c");
let affected_leaf = graph.get_affected_nodes("a.b.c.d.e");
let affected_deeper = graph.get_affected_nodes("a.b.c.d.e.f");
assert!(affected_root.contains(&node_id)); assert!(affected_mid.contains(&node_id)); assert!(affected_leaf.contains(&node_id)); assert!(affected_deeper.contains(&node_id)); }