git-subtree-dir: vendor/ruvector git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
248 lines
5.9 KiB
Rust
248 lines
5.9 KiB
Rust
//! DAG integration tests
|
|
|
|
use ruvector_dag::dag::{OperatorNode, OperatorType, QueryDag};
|
|
|
|
#[test]
|
|
fn test_complex_query_dag() {
|
|
// Build a realistic query DAG
|
|
let mut dag = QueryDag::new();
|
|
|
|
// Add scan nodes
|
|
let scan1 = dag.add_node(OperatorNode::seq_scan(0, "users"));
|
|
let scan2 = dag.add_node(OperatorNode::hnsw_scan(1, "vectors_idx", 64));
|
|
|
|
// Add join
|
|
let join = dag.add_node(OperatorNode::hash_join(2, "user_id"));
|
|
dag.add_edge(scan1, join).unwrap();
|
|
dag.add_edge(scan2, join).unwrap();
|
|
|
|
// Add filter and result
|
|
let filter = dag.add_node(OperatorNode::filter(3, "score > 0.5"));
|
|
dag.add_edge(join, filter).unwrap();
|
|
|
|
let result = dag.add_node(OperatorNode::new(4, OperatorType::Result));
|
|
dag.add_edge(filter, result).unwrap();
|
|
|
|
// Verify structure
|
|
assert_eq!(dag.node_count(), 5);
|
|
assert_eq!(dag.edge_count(), 4);
|
|
|
|
// Verify topological order
|
|
let order = dag.topological_sort().unwrap();
|
|
assert_eq!(order.len(), 5);
|
|
|
|
// Scans should come before join
|
|
let scan1_pos = order.iter().position(|&x| x == scan1).unwrap();
|
|
let scan2_pos = order.iter().position(|&x| x == scan2).unwrap();
|
|
let join_pos = order.iter().position(|&x| x == join).unwrap();
|
|
|
|
assert!(scan1_pos < join_pos);
|
|
assert!(scan2_pos < join_pos);
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_depths() {
|
|
let mut dag = QueryDag::new();
|
|
|
|
// Create tree structure
|
|
// Edges: 3→1, 4→1, 1→0, 2→0
|
|
// Leaves (no outgoing edges): only node 0
|
|
// Depth is computed FROM LEAVES, so node 0 = depth 0
|
|
//
|
|
// 0 (leaf, depth 0)
|
|
// / \
|
|
// 1 2 (depth 1)
|
|
// / \
|
|
// 3 4 (depth 2)
|
|
|
|
for i in 0..5 {
|
|
dag.add_node(OperatorNode::new(i, OperatorType::Result));
|
|
}
|
|
|
|
dag.add_edge(3, 1).unwrap();
|
|
dag.add_edge(4, 1).unwrap();
|
|
dag.add_edge(1, 0).unwrap();
|
|
dag.add_edge(2, 0).unwrap();
|
|
|
|
let depths = dag.compute_depths();
|
|
|
|
// All nodes should have a depth
|
|
assert!(depths.contains_key(&0));
|
|
assert!(depths.contains_key(&1));
|
|
assert!(depths.contains_key(&2));
|
|
assert!(depths.contains_key(&3));
|
|
assert!(depths.contains_key(&4));
|
|
|
|
// Leaf node 0 (no outgoing edges) has depth 0
|
|
assert_eq!(depths[&0], 0);
|
|
|
|
// Nodes 1 and 2 are parents of leaf 0, so depth 1
|
|
assert_eq!(depths[&1], 1);
|
|
assert_eq!(depths[&2], 1);
|
|
|
|
// Nodes 3 and 4 are parents of 1, so depth 2
|
|
assert_eq!(depths[&3], 2);
|
|
assert_eq!(depths[&4], 2);
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_cycle_detection() {
|
|
let mut dag = QueryDag::new();
|
|
|
|
for i in 0..3 {
|
|
dag.add_node(OperatorNode::new(i, OperatorType::Result));
|
|
}
|
|
|
|
// Create valid edges
|
|
dag.add_edge(0, 1).unwrap();
|
|
dag.add_edge(1, 2).unwrap();
|
|
|
|
// Attempt to create cycle should fail
|
|
let result = dag.add_edge(2, 0);
|
|
assert!(result.is_err());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_node_removal() {
|
|
let mut dag = QueryDag::new();
|
|
|
|
for i in 0..5 {
|
|
dag.add_node(OperatorNode::new(i, OperatorType::Result));
|
|
}
|
|
|
|
dag.add_edge(0, 1).unwrap();
|
|
dag.add_edge(1, 2).unwrap();
|
|
dag.add_edge(2, 3).unwrap();
|
|
dag.add_edge(3, 4).unwrap();
|
|
|
|
// Remove middle node
|
|
dag.remove_node(2);
|
|
|
|
assert_eq!(dag.node_count(), 4);
|
|
// Verify DAG is still valid after removal
|
|
let topo = dag.topological_sort();
|
|
assert!(topo.is_ok());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_clone() {
|
|
let mut dag = QueryDag::new();
|
|
|
|
for i in 0..5 {
|
|
dag.add_node(OperatorNode::new(i, OperatorType::Result));
|
|
}
|
|
|
|
for i in 0..4 {
|
|
dag.add_edge(i, i + 1).unwrap();
|
|
}
|
|
|
|
let cloned = dag.clone();
|
|
|
|
assert_eq!(dag.node_count(), cloned.node_count());
|
|
assert_eq!(dag.edge_count(), cloned.edge_count());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_topological_order() {
|
|
let mut dag = QueryDag::new();
|
|
|
|
// Create diamond pattern
|
|
// 0
|
|
// / \
|
|
// 1 2
|
|
// \ /
|
|
// 3
|
|
|
|
for i in 0..4 {
|
|
dag.add_node(OperatorNode::new(i, OperatorType::Result));
|
|
}
|
|
|
|
dag.add_edge(0, 1).unwrap();
|
|
dag.add_edge(0, 2).unwrap();
|
|
dag.add_edge(1, 3).unwrap();
|
|
dag.add_edge(2, 3).unwrap();
|
|
|
|
let order = dag.topological_sort().unwrap();
|
|
|
|
// Node 0 must come first
|
|
assert_eq!(order[0], 0);
|
|
|
|
// Node 3 must come last
|
|
assert_eq!(order[3], 3);
|
|
|
|
// Nodes 1 and 2 must be in the middle
|
|
assert!(order.contains(&1));
|
|
assert!(order.contains(&2));
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_parents_children() {
|
|
let mut dag = QueryDag::new();
|
|
|
|
for i in 0..4 {
|
|
dag.add_node(OperatorNode::new(i, OperatorType::Result));
|
|
}
|
|
|
|
// 0 -> 1 -> 3
|
|
// 2 ->
|
|
dag.add_edge(0, 1).unwrap();
|
|
dag.add_edge(1, 3).unwrap();
|
|
dag.add_edge(2, 3).unwrap();
|
|
|
|
// Parents of node 3
|
|
let preds = dag.parents(3);
|
|
assert_eq!(preds.len(), 2);
|
|
assert!(preds.contains(&1));
|
|
assert!(preds.contains(&2));
|
|
|
|
// Children of node 0
|
|
let succs = dag.children(0);
|
|
assert_eq!(succs.len(), 1);
|
|
assert!(succs.contains(&1));
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_leaves() {
|
|
let mut dag = QueryDag::new();
|
|
|
|
for i in 0..5 {
|
|
dag.add_node(OperatorNode::new(i, OperatorType::Result));
|
|
}
|
|
|
|
// 0 -> 2, 1 -> 2, 2 -> 3, 2 -> 4
|
|
dag.add_edge(0, 2).unwrap();
|
|
dag.add_edge(1, 2).unwrap();
|
|
dag.add_edge(2, 3).unwrap();
|
|
dag.add_edge(2, 4).unwrap();
|
|
|
|
// Get leaves using the API
|
|
let leaves = dag.leaves();
|
|
assert_eq!(leaves.len(), 2);
|
|
assert!(leaves.contains(&3));
|
|
assert!(leaves.contains(&4));
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_empty() {
|
|
let dag = QueryDag::new();
|
|
|
|
assert_eq!(dag.node_count(), 0);
|
|
assert_eq!(dag.edge_count(), 0);
|
|
|
|
let order = dag.topological_sort().unwrap();
|
|
assert!(order.is_empty());
|
|
}
|
|
|
|
#[test]
|
|
fn test_dag_single_node() {
|
|
let mut dag = QueryDag::new();
|
|
dag.add_node(OperatorNode::new(0, OperatorType::Result));
|
|
|
|
assert_eq!(dag.node_count(), 1);
|
|
assert_eq!(dag.edge_count(), 0);
|
|
|
|
let order = dag.topological_sort().unwrap();
|
|
assert_eq!(order.len(), 1);
|
|
assert_eq!(order[0], 0);
|
|
}
|