use sqlitegraph::{BackendDirection, NeighborQuery, SnapshotId, config::GraphConfig, open_graph};
mod helpers;
use helpers::v2_fixture_builders::*;
#[test]
fn default_writer_is_v2_format() {
let (mut graph, _temp_dir) = create_test_graph();
let node_id = add_node_v2(
&mut graph,
1,
"TestNode",
"test_node",
serde_json::json!({"test": true}),
);
let _edge_id = add_edge_v2(
&mut graph,
node_id,
node_id,
"self_ref",
serde_json::json!({"weight": 1.0}),
);
let node = graph.get_node(SnapshotId::current(), node_id).unwrap();
assert_eq!(node.name, "test_node");
let neighbors = graph
.neighbors(
SnapshotId::current(),
node_id,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: None,
},
)
.unwrap();
assert_eq!(
neighbors.len(),
1,
"Should have 1 outgoing neighbor (self-reference)"
);
assert_eq!(
neighbors[0], node_id,
"Self-reference should point to same node"
);
let incoming_neighbors = graph
.neighbors(
SnapshotId::current(),
node_id,
NeighborQuery {
direction: BackendDirection::Incoming,
edge_type: None,
},
)
.unwrap();
assert_eq!(
incoming_neighbors.len(),
1,
"Should have 1 incoming neighbor (self-reference)"
);
assert_eq!(
incoming_neighbors[0], node_id,
"Incoming self-reference should point to same node"
);
}
#[test]
fn default_reader_is_v2_format() {
let (mut graph, temp_dir) = create_test_graph();
let source_id = add_node_v2(
&mut graph,
1,
"Source",
"source",
serde_json::json!({"role": "source"}),
);
let target_id = add_node_v2(
&mut graph,
2,
"Target",
"target",
serde_json::json!({"role": "target"}),
);
add_edge_v2(
&mut graph,
source_id,
target_id,
"test_edge",
serde_json::json!({"weight": 2.5}),
);
let graph = flush_and_reopen(graph, &temp_dir);
let source_node = graph.get_node(SnapshotId::current(), source_id).unwrap();
let target_node = graph.get_node(SnapshotId::current(), target_id).unwrap();
assert_eq!(source_node.name, "source");
assert_eq!(target_node.name, "target");
let outgoing = graph
.neighbors(
SnapshotId::current(),
source_id,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: None,
},
)
.unwrap();
assert_eq!(outgoing.len(), 1, "Source should have 1 outgoing neighbor");
assert_eq!(outgoing[0], target_id, "Outgoing neighbor should be target");
let incoming = graph
.neighbors(
SnapshotId::current(),
target_id,
NeighborQuery {
direction: BackendDirection::Incoming,
edge_type: None,
},
)
.unwrap();
assert_eq!(incoming.len(), 1, "Target should have 1 incoming neighbor");
assert_eq!(incoming[0], source_id, "Incoming neighbor should be source");
}
#[test]
fn edge_insertion_updates_v2_clusters() {
let (mut graph, temp_dir) = create_test_graph();
let center_id = add_node_v2(
&mut graph,
0,
"Center",
"center",
serde_json::json!({"type": "center"}),
);
let mut target_ids = Vec::new();
for i in 1..=3 {
let target_id = add_node_v2(
&mut graph,
i,
"Target",
&format!("target_{}", i),
serde_json::json!({"index": i, "type": "target"}),
);
add_edge_v2(
&mut graph,
center_id,
target_id,
"connects",
serde_json::json!({"index": i}),
);
target_ids.push(target_id);
}
let graph = flush_and_reopen(graph, &temp_dir);
let center_outgoing = graph
.neighbors(
SnapshotId::current(),
center_id,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: None,
},
)
.unwrap();
assert_eq!(
center_outgoing.len(),
3,
"Center should have 3 outgoing neighbors"
);
let mut sorted_outgoing = center_outgoing.clone();
sorted_outgoing.sort();
let mut sorted_targets = target_ids.clone();
sorted_targets.sort();
assert_eq!(
sorted_outgoing, sorted_targets,
"All target nodes should be neighbors"
);
for target_id in &target_ids {
let target_incoming = graph
.neighbors(
SnapshotId::current(),
*target_id,
NeighborQuery {
direction: BackendDirection::Incoming,
edge_type: None,
},
)
.unwrap();
assert_eq!(
target_incoming.len(),
1,
"Each target should have 1 incoming neighbor"
);
assert_eq!(
target_incoming[0], center_id,
"Incoming neighbor should be center"
);
}
}
#[test]
fn bfs_uses_v2_clustered_iteration() {
let (mut graph, _temp_dir) = create_test_graph();
let mut node_ids = Vec::new();
for i in 1..=4 {
let node_id = add_node_v2(
&mut graph,
i,
"ChainNode",
&format!("node_{}", i),
serde_json::json!({"index": i, "type": "chain"}),
);
node_ids.push(node_id);
}
for i in 0..3 {
add_simple_edge_v2(&mut graph, node_ids[i], node_ids[i + 1], "next");
}
let bfs_result = graph.bfs(SnapshotId::current(), node_ids[0], 3).unwrap();
assert_eq!(bfs_result.len(), 4, "BFS should reach all 4 nodes");
let mut result_sorted = bfs_result.clone();
result_sorted.sort();
let mut expected_sorted = node_ids.clone();
expected_sorted.sort();
assert_eq!(
result_sorted, expected_sorted,
"BFS should find all nodes in chain"
);
}
#[test]
fn adjacency_uses_clustered_metadata_by_default() {
let (mut graph, _temp_dir) = create_test_graph();
let producer_id = add_node_v2(
&mut graph,
1,
"Producer",
"producer",
serde_json::json!({"role": "producer"}),
);
let consumer_id = add_node_v2(
&mut graph,
2,
"Consumer",
"consumer",
serde_json::json!({"role": "consumer"}),
);
add_edge_v2(
&mut graph,
producer_id,
consumer_id,
"produces",
serde_json::json!({
"product": "data",
"quantity": 42,
"timestamp": "2024-01-01T00:00:00Z",
"metadata": {"quality": "high", "certified": true}
}),
);
let producer_outgoing = graph
.neighbors(
SnapshotId::current(),
producer_id,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: None,
},
)
.unwrap();
assert_eq!(
producer_outgoing.len(),
1,
"Producer should have 1 outgoing neighbor"
);
assert_eq!(
producer_outgoing[0], consumer_id,
"Producer should connect to consumer"
);
let filtered_outgoing = graph
.neighbors(
SnapshotId::current(),
producer_id,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: Some("produces".to_string()),
},
)
.unwrap();
assert_eq!(
filtered_outgoing.len(),
1,
"Filtered query should find the 'produces' edge"
);
let wrong_filter = graph
.neighbors(
SnapshotId::current(),
producer_id,
NeighborQuery {
direction: BackendDirection::Outgoing,
edge_type: Some("does_not_exist".to_string()),
},
)
.unwrap();
assert_eq!(
wrong_filter.len(),
0,
"Non-existent edge type should return empty results"
);
}