use sqlitegraph::{
BackendDirection, NeighborQuery, SnapshotId,
backend::native::adjacency::{AdjacencyIterator, Direction},
backend::native::{EdgeStore, GraphFile, NodeStore},
config::GraphConfig,
open_graph,
};
use tempfile::TempDir;
fn create_test_graph_for_iterator() -> (Box<dyn sqlitegraph::GraphBackend>, TempDir) {
let temp_dir = TempDir::new().expect("Failed to create temp dir");
let db_path = temp_dir.path().join("test_adjacency_iterator.db");
let cfg = GraphConfig::new(sqlitegraph::BackendKind::Native);
let graph = open_graph(&db_path, &cfg).expect("Failed to create graph");
(graph, temp_dir)
}
#[test]
fn test_edge_store_iterator_consumption_causes_infinite_reads() {
let (mut graph, temp_dir) = create_test_graph_for_iterator();
let node1_id = graph
.insert_node(sqlitegraph::NodeSpec {
kind: "Function".to_string(),
name: "node1".to_string(),
file_path: Some("/src/node1.rs".to_string()),
data: serde_json::json!({"lines": 10}),
})
.expect("Failed to insert node1");
let node2_id = graph
.insert_node(sqlitegraph::NodeSpec {
kind: "Function".to_string(),
name: "node2".to_string(),
file_path: Some("/src/node2.rs".to_string()),
data: serde_json::json!({"lines": 20}),
})
.expect("Failed to insert node2");
graph
.insert_edge(sqlitegraph::EdgeSpec {
from: node1_id,
to: node2_id,
edge_type: "CALLS".to_string(),
data: serde_json::json!({"line": 5}),
})
.expect("Failed to insert edge");
let db_path = temp_dir.path().join("test_adjacency_iterator.db");
let mut graph_file = GraphFile::open(&db_path).expect("Failed to open graph file");
let mut edge_store = EdgeStore::new(&mut graph_file);
let mut iterator = edge_store.iter_neighbors(
node1_id as sqlitegraph::backend::native::types::NativeNodeId,
Direction::Outgoing,
);
let mut iteration_count = 0;
let mut neighbors_found = Vec::new();
let max_iterations = 100;
for neighbor in &mut iterator {
iteration_count += 1;
neighbors_found.push(neighbor);
if iteration_count > max_iterations {
panic!(
"INFINITE LOOP DETECTED: Exceeded {} iterations for simple graph with 1 edge. \
Current neighbors found: {:?}. \
This indicates the iterator is not properly advancing or terminating.",
max_iterations, neighbors_found
);
}
}
assert_eq!(neighbors_found.len(), 1, "Should find exactly 1 neighbor");
assert_eq!(
neighbors_found[0], node2_id as sqlitegraph::backend::native::types::NativeNodeId,
"Should find node2 as neighbor of node1"
);
assert!(
iteration_count <= 3, "Too many iterations ({}) for simple 1-neighbor graph. Expected <= 3.",
iteration_count
);
}
#[test]
fn test_adjacency_iterator_repeated_node_reads_fixed() {
let (graph, temp_dir) = create_test_graph_for_iterator();
let node1_id = graph
.insert_node(sqlitegraph::NodeSpec {
kind: "Type".to_string(),
name: "test_node".to_string(),
file_path: None,
data: serde_json::json!({"test": true}),
})
.expect("Failed to insert test node");
let node2_id = graph
.insert_node(sqlitegraph::NodeSpec {
kind: "Type".to_string(),
name: "target_node".to_string(),
file_path: None,
data: serde_json::json!({"target": true}),
})
.expect("Failed to insert target node");
graph
.insert_edge(sqlitegraph::EdgeSpec {
from: node1_id,
to: node2_id,
edge_type: "CONNECTS".to_string(),
data: serde_json::json!({"weight": 1.0}),
})
.expect("Failed to insert edge");
let neighbors = graph
.neighbors(
SnapshotId::current(),
node1_id,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: None,
},
)
.expect("Failed to get neighbors");
assert_eq!(neighbors.len(), 1, "Should find exactly 1 neighbor");
assert_eq!(
neighbors[0], node2_id,
"Should find target_node as neighbor"
);
println!("✅ Adjacency iterator repeated reads test PASSED - no infinite loops detected");
}
#[test]
fn test_adjacency_iterator_proper_advancement() {
let (mut graph, temp_dir) = create_test_graph_for_iterator();
let node1_id = graph
.insert_node(sqlitegraph::NodeSpec {
kind: "Function".to_string(),
name: "main".to_string(),
file_path: Some("/src/main.rs".to_string()),
data: serde_json::json!({"lines": 100}),
})
.expect("Failed to insert main node");
let node2_id = graph
.insert_node(sqlitegraph::NodeSpec {
kind: "Function".to_string(),
name: "helper".to_string(),
file_path: Some("/src/helper.rs".to_string()),
data: serde_json::json!({"lines": 50}),
})
.expect("Failed to insert helper node");
let node3_id = graph
.insert_node(sqlitegraph::NodeSpec {
kind: "Function".to_string(),
name: "util".to_string(),
file_path: Some("/src/util.rs".to_string()),
data: serde_json::json!({"lines": 75}),
})
.expect("Failed to insert util node");
graph
.insert_edge(sqlitegraph::EdgeSpec {
from: node1_id,
to: node2_id,
edge_type: "CALLS".to_string(),
data: serde_json::json!({"line": 10}),
})
.expect("Failed to insert main->helper edge");
graph
.insert_edge(sqlitegraph::EdgeSpec {
from: node1_id,
to: node3_id,
edge_type: "USES".to_string(),
data: serde_json::json!({"line": 15}),
})
.expect("Failed to insert main->util edge");
let neighbors = graph
.neighbors(
SnapshotId::current(),
node1_id,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: None,
},
)
.expect("Failed to get neighbors");
assert_eq!(neighbors.len(), 2, "Should find 2 neighbors");
assert!(neighbors.contains(&node2_id), "Should find helper");
assert!(neighbors.contains(&node3_id), "Should find util");
println!(
"✅ Adjacency iterator advancement test PASSED - found {} neighbors",
neighbors.len()
);
}