use std::time::{Duration, Instant};
use sqlitegraph::{EdgeSpec, GraphConfig, NodeSpec, SnapshotId, open_graph};
#[test]
fn test_sequential_bfs_no_contention() {
let chain_size = 500;
let temp_dir = tempfile::TempDir::new().expect("Failed to create temp dir");
let db_path = temp_dir.path().join("test_sequential_bfs.db");
let graph = open_graph(&db_path, &GraphConfig::native()).expect("Failed to create graph");
let mut node_ids = Vec::with_capacity(chain_size);
for i in 0..chain_size {
let node_id = graph
.insert_node(NodeSpec {
kind: "Node".to_string(),
name: format!("node_{}", i),
file_path: None,
data: serde_json::json!({"id": i}),
})
.expect("Failed to insert node");
node_ids.push(node_id);
}
for i in 0..chain_size.saturating_sub(1) {
graph
.insert_edge(EdgeSpec {
from: node_ids[i],
to: node_ids[i + 1],
edge_type: "chain".to_string(),
data: serde_json::json!({"order": i}),
})
.expect("Failed to insert edge");
}
let _temp_dir = temp_dir;
let num_traversals = 4;
let timeout_secs = 30;
for traversal_id in 0..num_traversals {
let start_node = node_ids[0];
let start_time = Instant::now();
let result = graph.bfs(SnapshotId::current(), start_node, chain_size as u32);
let elapsed = start_time.elapsed();
match result {
Ok(visited) => {
assert!(
!visited.is_empty(),
"Traversal {} found no nodes",
traversal_id
);
assert!(
elapsed < Duration::from_secs(timeout_secs),
"Traversal {} took too long: {:?}",
traversal_id,
elapsed
);
}
Err(e) => {
panic!("Traversal {} failed: {:?}", traversal_id, e);
}
}
}
assert!(
true,
"All {} traversals completed successfully",
num_traversals
);
}
#[test]
fn test_write_read_mix() {
let chain_size = 100; let temp_dir = tempfile::TempDir::new().expect("Failed to create temp dir");
let db_path = temp_dir.path().join("test_write_read_mix.db");
let graph = open_graph(&db_path, &GraphConfig::native()).expect("Failed to create graph");
let mut node_ids = Vec::with_capacity(chain_size);
for i in 0..chain_size {
let node_id = graph
.insert_node(NodeSpec {
kind: "Node".to_string(),
name: format!("node_{}", i),
file_path: None,
data: serde_json::json!({"id": i}),
})
.expect("Failed to insert node");
node_ids.push(node_id);
}
for i in 0..chain_size.saturating_sub(1) {
graph
.insert_edge(EdgeSpec {
from: node_ids[i],
to: node_ids[i + 1],
edge_type: "chain".to_string(),
data: serde_json::json!({"order": i}),
})
.expect("Failed to insert edge");
}
let _temp_dir = temp_dir;
for iteration in 0..5 {
if iteration % 2 == 0 {
for i in (0..chain_size.saturating_sub(2)).step_by(10) {
let _ = graph.insert_edge(EdgeSpec {
from: node_ids[i],
to: node_ids[i + 2],
edge_type: "skip".to_string(),
data: serde_json::json!({"skip": i, "iteration": iteration}),
});
}
}
let start_time = Instant::now();
let result = graph.bfs(SnapshotId::current(), node_ids[iteration % chain_size], 50);
let elapsed = start_time.elapsed();
match result {
Ok(visited) => {
assert!(!visited.is_empty(), "BFS {} found no nodes", iteration);
assert!(
elapsed < Duration::from_secs(10),
"BFS {} took too long: {:?}",
iteration,
elapsed
);
}
Err(e) => {
panic!("BFS {} failed: {:?}", iteration, e);
}
}
}
assert!(true, "Write/read mix completed successfully");
}
#[test]
fn test_multiple_traversal_isolation() {
let chain_size = 200;
let temp_dir = tempfile::TempDir::new().expect("Failed to create temp dir");
let db_path = temp_dir.path().join("test_isolation.db");
let graph = open_graph(&db_path, &GraphConfig::native()).expect("Failed to create graph");
let mut node_ids = Vec::with_capacity(chain_size);
for i in 0..chain_size {
let node_id = graph
.insert_node(NodeSpec {
kind: "Node".to_string(),
name: format!("node_{}", i),
file_path: None,
data: serde_json::json!({"id": i}),
})
.expect("Failed to insert node");
node_ids.push(node_id);
}
for i in 0..chain_size.saturating_sub(1) {
graph
.insert_edge(EdgeSpec {
from: node_ids[i],
to: node_ids[i + 1],
edge_type: "chain".to_string(),
data: serde_json::json!({"order": i}),
})
.expect("Failed to insert edge");
}
let _temp_dir = temp_dir;
let num_traversals = 4;
for traversal_id in 0..num_traversals {
for iteration in 0..3 {
let start_index = (traversal_id + iteration) % 50;
let start_node = node_ids[start_index];
let result = graph.bfs(SnapshotId::current(), start_node, 50);
assert!(
result.is_ok(),
"Traversal {} iteration {} failed: {:?}",
traversal_id,
iteration,
result.err()
);
let visited = result.unwrap();
assert!(
!visited.is_empty(),
"Traversal {} iteration {} found no nodes",
traversal_id,
iteration
);
}
}
assert!(true, "Isolation test completed successfully");
}
#[test]
fn test_no_deadlock_multiple_traversals() {
let chain_size = 300;
let temp_dir = tempfile::TempDir::new().expect("Failed to create temp dir");
let db_path = temp_dir.path().join("test_deadlock.db");
let graph = open_graph(&db_path, &GraphConfig::native()).expect("Failed to create graph");
let mut node_ids = Vec::with_capacity(chain_size);
for i in 0..chain_size {
let node_id = graph
.insert_node(NodeSpec {
kind: "Node".to_string(),
name: format!("node_{}", i),
file_path: None,
data: serde_json::json!({"id": i}),
})
.expect("Failed to insert node");
node_ids.push(node_id);
}
for i in 0..chain_size.saturating_sub(1) {
graph
.insert_edge(EdgeSpec {
from: node_ids[i],
to: node_ids[i + 1],
edge_type: "chain".to_string(),
data: serde_json::json!({"order": i}),
})
.expect("Failed to insert edge");
}
let _temp_dir = temp_dir;
let start = Instant::now();
let timeout = Duration::from_secs(20);
let num_traversals = 6;
for traversal_id in 0..num_traversals {
for i in 0..5 {
let start_index = (traversal_id * 5 + i) % 50;
let start_node = node_ids[start_index];
let result = graph.bfs(SnapshotId::current(), start_node, 100);
if result.is_ok() && !result.unwrap().is_empty() {
} else {
panic!("Traversal {} iteration {} failed", traversal_id, i);
}
if start.elapsed() > timeout {
panic!("Deadlock detected - traversals did not complete in time");
}
}
}
let elapsed = start.elapsed();
assert!(
elapsed < timeout,
"Test took too long: {:?} (possible blocking)",
elapsed
);
assert!(
true,
"All {} traversals completed successfully without deadlock",
num_traversals * 5
);
}