use super::*;
use crate::core::property::{PropertyMapBuilder, PropertyValue};
#[test]
fn test_create_node() {
let storage = CurrentStorage::new();
let props = PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build();
let node_id = storage.create_node("Person", props).unwrap();
assert_eq!(storage.node_count(), 1);
let node = storage.get_node(node_id).unwrap();
assert_eq!(node.id, node_id);
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Alice")
);
}
#[test]
fn test_create_edge() {
let storage = CurrentStorage::new();
let alice = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = storage
.create_edge(
alice,
bob,
"KNOWS",
PropertyMapBuilder::new().insert("since", 2020i64).build(),
)
.unwrap();
assert_eq!(storage.edge_count(), 1);
let edge = storage.get_edge(edge_id).unwrap();
assert_eq!(edge.source, alice);
assert_eq!(edge.target, bob);
assert_eq!(
edge.get_property("since").and_then(|v| v.as_int()),
Some(2020)
);
}
#[test]
fn test_create_edge_invalid_nodes() {
let storage = CurrentStorage::new();
let result = storage.create_edge(
NodeId::new(999).unwrap(),
NodeId::new(1000).unwrap(),
"KNOWS",
PropertyMapBuilder::new().build(),
);
assert!(result.is_err());
}
#[test]
fn test_graph_traversal() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n2, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n1, n2, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
let outgoing = storage.get_outgoing_edges(n0);
assert_eq!(outgoing.len(), 2);
let incoming = storage.get_incoming_edges(n2);
assert_eq!(incoming.len(), 2);
assert_eq!(storage.out_degree(n0), 2);
assert_eq!(storage.in_degree(n2), 2);
}
#[test]
fn test_labeled_edges() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n2, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
let knows_edges = storage.get_outgoing_edges_with_label(n0, "KNOWS");
assert_eq!(knows_edges.len(), 1);
let follows_edges = storage.get_outgoing_edges_with_label(n0, "FOLLOWS");
assert_eq!(follows_edges.len(), 1);
let none_edges = storage.get_outgoing_edges_with_label(n0, "LOVES");
assert_eq!(none_edges.len(), 0);
}
#[test]
fn test_delete_node() {
let storage = CurrentStorage::new();
let node_id = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(storage.node_count(), 1);
let deleted = storage.delete_node(node_id).unwrap();
assert_eq!(deleted.id, node_id);
assert_eq!(storage.node_count(), 0);
assert!(storage.delete_node(node_id).is_err());
}
#[test]
fn test_delete_edge() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(storage.edge_count(), 1);
assert_eq!(storage.out_degree(n0), 1);
storage.delete_edge(edge_id).unwrap();
assert_eq!(storage.edge_count(), 0);
assert_eq!(storage.out_degree(n0), 0);
}
#[test]
fn test_create_node_with_vector_property() {
let storage = CurrentStorage::new();
let embedding = vec![0.1f32, 0.2, 0.3, 0.4, 0.5];
let props = PropertyMapBuilder::new()
.insert("name", "Document")
.insert_vector("embedding", &embedding)
.build();
let node_id = storage.create_node("Document", props).unwrap();
let node = storage.get_node(node_id).unwrap();
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Document")
);
assert_eq!(
node.get_property("embedding").and_then(|v| v.as_vector()),
Some(&embedding[..])
);
}
#[test]
fn test_create_node_with_high_dimensional_vector() {
let storage = CurrentStorage::new();
let embedding: Vec<f32> = (0..384).map(|i| i as f32 / 384.0).collect();
let props = PropertyMapBuilder::new()
.insert_vector("embedding", &embedding)
.build();
let node_id = storage.create_node("Embedding", props).unwrap();
let node = storage.get_node(node_id).unwrap();
assert_eq!(
node.get_property("embedding").and_then(|v| v.as_vector()),
Some(&embedding[..])
);
}
#[test]
fn test_create_edge_with_vector_property() {
let storage = CurrentStorage::new();
let n1 = storage
.create_node("Entity", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Entity", PropertyMapBuilder::new().build())
.unwrap();
let edge_embedding = vec![0.5f32, -0.3, 0.8];
let props = PropertyMapBuilder::new()
.insert("weight", 0.95f64)
.insert_vector("embedding", &edge_embedding)
.build();
let edge_id = storage.create_edge(n1, n2, "RELATES_TO", props).unwrap();
let edge = storage.get_edge(edge_id).unwrap();
assert_eq!(
edge.get_property("weight").and_then(|v| v.as_float()),
Some(0.95)
);
assert_eq!(
edge.get_property("embedding").and_then(|v| v.as_vector()),
Some(&edge_embedding[..])
);
}
#[test]
fn test_update_node_vector_property() {
let storage = CurrentStorage::new();
let initial_embedding = vec![0.1f32, 0.2, 0.3, 0.0];
let props = PropertyMapBuilder::new()
.insert("name", "Document")
.insert_vector("embedding", &initial_embedding)
.build();
let node_id = storage.create_node("Document", props).unwrap();
let mut node = storage.get_node(node_id).unwrap();
let updated_embedding = vec![0.9f32, 0.8, 0.7, 0.0];
let new_props = PropertyMapBuilder::new()
.insert("name", "Document")
.insert_vector("embedding", &updated_embedding)
.build();
node.properties = new_props;
storage.update_node_direct(node, 1000.into()).unwrap();
let updated_node = storage.get_node(node_id).unwrap();
assert_eq!(
updated_node
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&updated_embedding[..])
);
}
#[test]
fn test_update_edge_vector_property() {
let storage = CurrentStorage::new();
let n1 = storage
.create_node("Entity", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Entity", PropertyMapBuilder::new().build())
.unwrap();
let initial_embedding = vec![1.0f32, 0.0];
let props = PropertyMapBuilder::new()
.insert_vector("embedding", &initial_embedding)
.build();
let edge_id = storage.create_edge(n1, n2, "RELATES_TO", props).unwrap();
let mut edge = storage.get_edge(edge_id).unwrap();
let updated_embedding = vec![0.0f32, 1.0];
edge.properties = PropertyMapBuilder::new()
.insert_vector("embedding", &updated_embedding)
.build();
storage.update_edge_direct(edge).unwrap();
let updated_edge = storage.get_edge(edge_id).unwrap();
assert_eq!(
updated_edge
.get_property("embedding")
.and_then(|v| v.as_vector()),
Some(&updated_embedding[..])
);
}
#[test]
fn test_create_node_with_multiple_vector_properties() {
let storage = CurrentStorage::new();
let text_embedding = vec![0.1f32, 0.2, 0.3, 0.4];
let image_embedding = vec![0.5f32, 0.6, 0.7, 0.8];
let props = PropertyMapBuilder::new()
.insert("content", "multimodal content")
.insert_vector("text_embedding", &text_embedding)
.insert_vector("image_embedding", &image_embedding)
.build();
let node_id = storage.create_node("MultimodalDoc", props).unwrap();
let node = storage.get_node(node_id).unwrap();
assert_eq!(
node.get_property("text_embedding")
.and_then(|v| v.as_vector()),
Some(&text_embedding[..])
);
assert_eq!(
node.get_property("image_embedding")
.and_then(|v| v.as_vector()),
Some(&image_embedding[..])
);
}
#[test]
fn test_create_node_with_empty_vector() {
let storage = CurrentStorage::new();
let empty_embedding: Vec<f32> = vec![];
let props = PropertyMapBuilder::new()
.insert_vector("embedding", &empty_embedding)
.build();
let node_id = storage.create_node("EmptyVec", props).unwrap();
let node = storage.get_node(node_id).unwrap();
assert_eq!(
node.get_property("embedding").and_then(|v| v.as_vector()),
Some(&empty_embedding[..])
);
}
#[test]
fn test_create_node_with_normalized_vector() {
let storage = CurrentStorage::new();
let normalized_embedding = vec![0.5773503f32, 0.5773503, 0.5773503, 0.0]; let props = PropertyMapBuilder::new()
.insert_vector("embedding", &normalized_embedding)
.build();
let node_id = storage.create_node("NormalizedDoc", props).unwrap();
let node = storage.get_node(node_id).unwrap();
let retrieved = node
.get_property("embedding")
.and_then(|v| v.as_vector())
.expect("Embedding property should exist and be a vector");
let magnitude: f32 = retrieved.iter().map(|x| x * x).sum::<f32>().sqrt();
assert!((magnitude - 1.0).abs() < 1e-5);
}
#[test]
fn test_enable_vector_index() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
assert!(!storage.is_vector_index_enabled());
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
assert!(storage.is_vector_index_enabled());
}
#[test]
fn test_enable_vector_index_twice_fails() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("embedding", config.clone())
.unwrap();
let result = storage.enable_vector_index("embedding", config);
assert!(result.is_err());
}
#[test]
fn test_auto_index_on_create() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let embedding = vec![1.0f32, 0.0, 0.0, 0.0];
let props = PropertyMapBuilder::new()
.insert_vector("embedding", &embedding)
.build();
let node_id = storage.create_node("Document", props).unwrap();
let node = storage.get_node(node_id).unwrap();
assert_eq!(
node.get_property("embedding").and_then(|v| v.as_vector()),
Some(&embedding[..])
);
}
#[test]
fn test_auto_index_dimension_mismatch_rolls_back() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let wrong_embedding = vec![1.0f32, 0.0];
let props = PropertyMapBuilder::new()
.insert_vector("embedding", &wrong_embedding)
.build();
let result = storage.create_node("Document", props);
assert!(result.is_err());
assert_eq!(storage.node_count(), 0); }
#[test]
fn test_find_similar() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.9f32, 0.1, 0.0, 0.0];
let v3 = vec![0.0f32, 1.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v2)
.build(),
)
.unwrap();
let _node3 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v3)
.build(),
)
.unwrap();
let results = storage.find_similar(node1, 2).unwrap();
assert_eq!(results.len(), 2);
assert!(results.iter().all(|(id, _)| *id != node1));
assert_eq!(results[0].0, node2); }
#[test]
fn test_delete_node_removes_from_vector_index() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.0f32, 1.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v2)
.build(),
)
.unwrap();
let results_before = storage.find_similar_by_embedding(&v1, 10).unwrap();
assert_eq!(
results_before.len(),
2,
"Should find 2 nodes before deletion"
);
storage.delete_node(node1).unwrap();
let results_after = storage.find_similar_by_embedding(&v1, 10).unwrap();
assert_eq!(
results_after.len(),
1,
"Should find only 1 node after deletion"
);
assert_eq!(results_after[0].0, node2, "Remaining node should be node2");
let similar_to_node2 = storage.find_similar(node2, 10).unwrap();
assert!(
!similar_to_node2.iter().any(|(id, _)| *id == node1),
"Deleted node should not appear in similarity search"
);
}
#[test]
fn test_find_similar_with_label() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.9f32, 0.1, 0.0, 0.0];
let v3 = vec![0.8f32, 0.2, 0.0, 0.0];
let node1 = storage
.create_node(
"Person",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Person",
PropertyMapBuilder::new()
.insert_vector("embedding", &v2)
.build(),
)
.unwrap();
let _node3 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("embedding", &v3)
.build(),
)
.unwrap();
let results = storage.find_similar_with_label(node1, "Person", 2).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, node2);
}
#[test]
fn test_find_similar_index_not_enabled() {
let storage = CurrentStorage::new();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let result = storage.find_similar(node1, 2);
assert!(result.is_err());
}
#[test]
fn test_find_similar_node_not_found() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let result = storage.find_similar(NodeId::new(999).unwrap(), 2);
assert!(result.is_err());
}
#[test]
fn test_find_similar_property_not_found() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new().insert("name", "test").build(),
)
.unwrap();
let result = storage.find_similar(node1, 2);
assert!(result.is_err());
}
#[test]
fn test_update_node_updates_index() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.0f32, 1.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v2)
.build(),
)
.unwrap();
let v1_updated = vec![0.1f32, 0.9, 0.0, 0.0];
let mut node1_obj = storage.get_node(node1).unwrap();
node1_obj.properties = PropertyMapBuilder::new()
.insert_vector("embedding", &v1_updated)
.build();
storage.update_node_direct(node1_obj, 2000.into()).unwrap();
let results = storage.find_similar(node2, 1).unwrap();
assert_eq!(results.len(), 1);
assert_eq!(results[0].0, node1);
}
#[test]
fn test_delete_node_removes_from_index() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.9f32, 0.1, 0.0, 0.0];
let node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v2)
.build(),
)
.unwrap();
storage.delete_node_direct(node2, 3000.into()).unwrap();
let results = storage.find_similar(node1, 2).unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_create_node_without_vector_property() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new().insert("name", "test").build(),
)
.unwrap();
assert_eq!(storage.node_count(), 1);
let node = storage.get_node(node1).unwrap();
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("test")
);
}
#[test]
fn test_delete_node_with_immutable_reference() {
let storage = CurrentStorage::new();
let node_id = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(storage.node_count(), 1);
let deleted = storage.delete_node(node_id).unwrap();
assert_eq!(deleted.id, node_id);
assert_eq!(storage.node_count(), 0);
}
#[test]
fn test_delete_edge_with_immutable_reference() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(storage.edge_count(), 1);
let deleted = storage.delete_edge(edge_id).unwrap();
assert_eq!(deleted.id, edge_id);
assert_eq!(storage.edge_count(), 0);
}
#[test]
fn test_delete_operations_in_shared_context() {
let storage = CurrentStorage::new();
let node1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let node2 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge_id = storage
.create_edge(node1, node2, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
assert_eq!(storage.node_count(), 2);
assert_eq!(storage.edge_count(), 1);
fn delete_data(storage: &CurrentStorage, node_id: NodeId, edge_id: EdgeId) {
storage.delete_edge(edge_id).unwrap();
storage.delete_node(node_id).unwrap();
}
delete_data(&storage, node1, edge_id);
assert_eq!(storage.node_count(), 1);
assert_eq!(storage.edge_count(), 0);
}
#[test]
fn test_enable_multiple_vector_indexes_on_different_properties() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config1 = HnswConfig::new(384, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("title_embedding", config1)
.expect("Should enable first vector index");
let config2 = HnswConfig::new(1536, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("body_embedding", config2)
.expect("Should enable second vector index on different property");
assert!(storage.has_vector_index("title_embedding"));
assert!(storage.has_vector_index("body_embedding"));
}
#[test]
fn test_enable_same_property_twice_fails() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(384, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("embedding", config.clone())
.unwrap();
let result = storage.enable_vector_index("embedding", config);
assert!(
result.is_err(),
"Should not allow re-enabling same property"
);
}
#[test]
fn test_find_similar_in_specific_property() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config_title = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
let config_body = HnswConfig::new(8, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("title_embedding", config_title)
.unwrap();
storage
.enable_vector_index("body_embedding", config_body)
.unwrap();
let title_vec1 = vec![1.0f32, 0.0, 0.0, 0.0];
let title_vec2 = vec![0.9f32, 0.1, 0.0, 0.0];
let body_vec1 = vec![1.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let body_vec2 = vec![0.0f32, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &title_vec1)
.insert_vector("body_embedding", &body_vec1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &title_vec2)
.insert_vector("body_embedding", &body_vec2)
.build(),
)
.unwrap();
let title_results = storage
.find_similar_in("title_embedding", node1, 1)
.unwrap();
assert_eq!(title_results.len(), 1);
assert_eq!(title_results[0].0, node2);
let body_results = storage.find_similar_in("body_embedding", node1, 1).unwrap();
assert_eq!(body_results.len(), 1);
assert!(
body_results[0].1 < 0.5,
"Orthogonal vectors should have low similarity"
);
}
#[test]
fn test_find_similar_in_property_not_indexed() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("title_embedding", config)
.unwrap();
let vec1 = vec![1.0f32, 0.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &vec1)
.build(),
)
.unwrap();
let result = storage.find_similar_in("body_embedding", node1, 1);
assert!(result.is_err(), "Should fail for non-indexed property");
}
#[test]
fn test_list_vector_indexes() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let indexes = storage.list_vector_indexes();
assert!(indexes.is_empty());
let config1 = HnswConfig::new(384, DistanceMetric::Cosine).with_capacity(100);
let config2 = HnswConfig::new(1536, DistanceMetric::Euclidean).with_capacity(200);
storage
.enable_vector_index("title_embedding", config1)
.unwrap();
storage
.enable_vector_index("body_embedding", config2)
.unwrap();
let indexes = storage.list_vector_indexes();
assert_eq!(indexes.len(), 2);
let names: Vec<&str> = indexes.iter().map(|i| i.property_name.as_str()).collect();
assert!(names.contains(&"title_embedding"));
assert!(names.contains(&"body_embedding"));
let title_idx = indexes
.iter()
.find(|i| i.property_name == "title_embedding")
.unwrap();
assert_eq!(title_idx.dimensions, 384);
assert_eq!(title_idx.distance_metric, DistanceMetric::Cosine);
let body_idx = indexes
.iter()
.find(|i| i.property_name == "body_embedding")
.unwrap();
assert_eq!(body_idx.dimensions, 1536);
assert_eq!(body_idx.distance_metric, DistanceMetric::Euclidean);
}
#[test]
fn test_has_vector_index_specific_property() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
assert!(!storage.has_vector_index("title_embedding"));
assert!(!storage.has_vector_index("body_embedding"));
let config = HnswConfig::new(384, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("title_embedding", config)
.unwrap();
assert!(storage.has_vector_index("title_embedding"));
assert!(!storage.has_vector_index("body_embedding")); }
#[test]
fn test_auto_index_multiple_properties_selective() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("title_embedding", config)
.unwrap();
let title_vec = vec![1.0f32, 0.0, 0.0, 0.0];
let body_vec = vec![1.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &title_vec)
.insert_vector("body_embedding", &body_vec)
.build(),
)
.unwrap();
let title_vec2 = vec![0.9f32, 0.1, 0.0, 0.0];
let _node2 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &title_vec2)
.build(),
)
.unwrap();
let results = storage
.find_similar_in("title_embedding", node1, 1)
.unwrap();
assert_eq!(results.len(), 1);
let result = storage.find_similar_in("body_embedding", node1, 1);
assert!(result.is_err());
}
#[test]
fn test_update_node_updates_correct_property_index() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("title_embedding", config.clone())
.unwrap();
storage
.enable_vector_index("body_embedding", config)
.unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.0f32, 1.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &v1)
.insert_vector("body_embedding", &v1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &v2)
.insert_vector("body_embedding", &v2)
.build(),
)
.unwrap();
let v1_updated = vec![0.1f32, 0.9, 0.0, 0.0];
let mut node1_obj = storage.get_node(node1).unwrap();
node1_obj.properties = PropertyMapBuilder::new()
.insert_vector("title_embedding", &v1_updated)
.insert_vector("body_embedding", &v1) .build();
storage.update_node_direct(node1_obj, 2000.into()).unwrap();
let title_results = storage
.find_similar_in("title_embedding", node2, 1)
.unwrap();
assert_eq!(title_results[0].0, node1);
let body_results = storage.find_similar_in("body_embedding", node2, 1).unwrap();
assert!(body_results[0].1 < 0.5);
}
#[test]
fn test_delete_node_removes_from_all_indexes() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("title_embedding", config.clone())
.unwrap();
storage
.enable_vector_index("body_embedding", config)
.unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.9f32, 0.1, 0.0, 0.0];
let node1 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &v1)
.insert_vector("body_embedding", &v1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &v2)
.insert_vector("body_embedding", &v2)
.build(),
)
.unwrap();
storage.delete_node_direct(node2, 3000.into()).unwrap();
let title_results = storage
.find_similar_in("title_embedding", node1, 2)
.unwrap();
assert_eq!(title_results.len(), 0);
let body_results = storage.find_similar_in("body_embedding", node1, 2).unwrap();
assert_eq!(body_results.len(), 0);
}
#[test]
fn test_search_vectors_in_by_embedding() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let v2 = vec![0.9f32, 0.1, 0.0, 0.0];
let v3 = vec![0.0f32, 1.0, 0.0, 0.0];
let node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
let node2 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v2)
.build(),
)
.unwrap();
let _node3 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v3)
.build(),
)
.unwrap();
let query = vec![1.0f32, 0.0, 0.0, 0.0];
let results = storage.search_vectors_in("embedding", &query, 2).unwrap();
assert_eq!(results.len(), 2);
assert_eq!(results[0].0, node1);
assert_eq!(results[1].0, node2);
}
#[test]
fn test_dimension_mismatch_specific_property() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config_title = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
let config_body = HnswConfig::new(8, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("title_embedding", config_title)
.unwrap();
storage
.enable_vector_index("body_embedding", config_body)
.unwrap();
let wrong_title = vec![1.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let correct_body = vec![1.0f32, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0];
let result = storage.create_node(
"Document",
PropertyMapBuilder::new()
.insert_vector("title_embedding", &wrong_title)
.insert_vector("body_embedding", &correct_body)
.build(),
);
assert!(result.is_err(), "Should fail on dimension mismatch");
}
#[test]
fn test_get_outgoing_edges_iter_basic() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let e1 = storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let e2 = storage
.create_edge(n0, n2, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
let vec_result: std::collections::HashSet<EdgeId> =
storage.get_outgoing_edges(n0).into_iter().collect();
let iter_result: std::collections::HashSet<EdgeId> =
storage.get_outgoing_edges_iter(n0).collect();
assert_eq!(vec_result, iter_result);
assert!(iter_result.contains(&e1));
assert!(iter_result.contains(&e2));
}
#[test]
fn test_get_outgoing_edges_iter_empty() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let count = storage.get_outgoing_edges_iter(n0).count();
assert_eq!(count, 0);
}
#[test]
fn test_get_incoming_edges_iter_basic() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let e1 = storage
.create_edge(n0, n2, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let e2 = storage
.create_edge(n1, n2, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
let vec_result: std::collections::HashSet<EdgeId> =
storage.get_incoming_edges(n2).into_iter().collect();
let iter_result: std::collections::HashSet<EdgeId> =
storage.get_incoming_edges_iter(n2).collect();
assert_eq!(vec_result, iter_result);
assert!(iter_result.contains(&e1));
assert!(iter_result.contains(&e2));
}
#[test]
fn test_get_incoming_edges_iter_empty() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let count = storage.get_incoming_edges_iter(n0).count();
assert_eq!(count, 0);
}
#[test]
fn test_get_outgoing_edges_with_label_iter_basic() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let e1 = storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let _e2 = storage
.create_edge(n0, n2, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
let iter_result: Vec<EdgeId> = storage
.get_outgoing_edges_with_label_iter(n0, "KNOWS")
.collect();
assert_eq!(iter_result.len(), 1);
assert!(iter_result.contains(&e1));
let vec_result = storage.get_outgoing_edges_with_label(n0, "KNOWS");
assert_eq!(vec_result.len(), iter_result.len());
}
#[test]
fn test_get_outgoing_edges_with_label_iter_nonexistent_label() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let count = storage
.get_outgoing_edges_with_label_iter(n0, "LOVES")
.count();
assert_eq!(count, 0);
}
#[test]
fn test_get_incoming_edges_with_label_iter_basic() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let e1 = storage
.create_edge(n0, n2, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let _e2 = storage
.create_edge(n1, n2, "FOLLOWS", PropertyMapBuilder::new().build())
.unwrap();
let iter_result: Vec<EdgeId> = storage
.get_incoming_edges_with_label_iter(n2, "KNOWS")
.collect();
assert_eq!(iter_result.len(), 1);
assert!(iter_result.contains(&e1));
let vec_result = storage.get_incoming_edges_with_label(n2, "KNOWS");
assert_eq!(vec_result.len(), iter_result.len());
}
#[test]
fn test_get_incoming_edges_with_label_iter_nonexistent_label() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let count = storage
.get_incoming_edges_with_label_iter(n1, "LOVES")
.count();
assert_eq!(count, 0);
}
#[test]
fn test_iterator_can_be_partially_consumed() {
let storage = CurrentStorage::new();
let n0 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n1 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n2 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let n3 = storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n1, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n2, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
storage
.create_edge(n0, n3, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let first_two: Vec<EdgeId> = storage.get_outgoing_edges_iter(n0).take(2).collect();
assert_eq!(first_two.len(), 2);
}
#[test]
fn test_iterator_consistency_with_vec() {
let storage = CurrentStorage::new();
let nodes: Vec<NodeId> = (0..5)
.map(|_| {
storage
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap()
})
.collect();
for i in 1..5 {
storage
.create_edge(
nodes[0],
nodes[i],
"KNOWS",
PropertyMapBuilder::new().build(),
)
.unwrap();
}
let vec_edges: std::collections::HashSet<EdgeId> =
storage.get_outgoing_edges(nodes[0]).into_iter().collect();
let iter_edges: std::collections::HashSet<EdgeId> =
storage.get_outgoing_edges_iter(nodes[0]).collect();
assert_eq!(vec_edges, iter_edges);
}
#[test]
fn test_legacy_default_property_selection_determinism() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage
.enable_vector_index("z_prop", config.clone())
.unwrap();
storage
.enable_vector_index("a_prop", config.clone())
.unwrap();
assert_eq!(
storage.get_indexed_property_name(),
Some("a_prop".to_string())
);
assert_eq!(
storage.get_vector_property_name(),
Some("a_prop".to_string())
);
}
#[test]
fn test_legacy_vector_count() {
use crate::index::vector::DistanceMetric;
let storage = CurrentStorage::new();
assert_eq!(storage.vector_count(), 0);
let config = HnswConfig::new(4, DistanceMetric::Cosine).with_capacity(100);
storage.enable_vector_index("embedding", config).unwrap();
assert_eq!(storage.vector_count(), 0);
let v1 = vec![1.0f32, 0.0, 0.0, 0.0];
let _node1 = storage
.create_node(
"Doc",
PropertyMapBuilder::new()
.insert_vector("embedding", &v1)
.build(),
)
.unwrap();
assert_eq!(storage.vector_count(), 1);
}
#[test]
fn test_traversal_targets_and_sources() {
let storage = CurrentStorage::new();
let n0 = storage.create_node("Person", Default::default()).unwrap();
let n1 = storage.create_node("Person", Default::default()).unwrap();
let n2 = storage.create_node("Person", Default::default()).unwrap();
storage
.create_edge(n0, n1, "KNOWS", Default::default())
.unwrap();
storage
.create_edge(n0, n2, "FOLLOWS", Default::default())
.unwrap();
storage
.create_edge(n1, n2, "KNOWS", Default::default())
.unwrap();
let targets = storage.get_outgoing_targets(n0);
assert_eq!(targets.len(), 2);
assert!(targets.contains(&n1));
assert!(targets.contains(&n2));
let knows_targets = storage.get_outgoing_targets_with_label(n0, "KNOWS");
assert_eq!(knows_targets.len(), 1);
assert_eq!(knows_targets[0], n1);
let sources = storage.get_incoming_sources(n2);
assert_eq!(sources.len(), 2);
assert!(sources.contains(&n0));
assert!(sources.contains(&n1));
let knows_sources = storage.get_incoming_sources_with_label(n2, "KNOWS");
assert_eq!(knows_sources.len(), 1);
assert_eq!(knows_sources[0], n1);
}
#[test]
fn test_iterators_with_delta_and_tombstones() {
let storage = CurrentStorage::new();
let n0 = storage.create_node("Person", Default::default()).unwrap();
let n1 = storage.create_node("Person", Default::default()).unwrap();
let n2 = storage.create_node("Person", Default::default()).unwrap();
let e1 = storage
.create_edge(n0, n1, "KNOWS", Default::default())
.unwrap();
storage.compact_adjacency();
let e2 = storage
.create_edge(n0, n2, "KNOWS", Default::default())
.unwrap();
let edges: Vec<_> = storage.get_outgoing_edges_iter(n0).collect();
assert_eq!(edges.len(), 2);
assert!(edges.contains(&e1));
assert!(edges.contains(&e2));
storage.delete_edge(e1).unwrap();
let edges: Vec<_> = storage.get_outgoing_edges_iter(n0).collect();
assert_eq!(edges.len(), 1);
assert_eq!(edges[0], e2);
let edges: Vec<_> = storage
.get_outgoing_edges_with_label_iter(n0, "KNOWS")
.collect();
assert_eq!(edges.len(), 1);
assert_eq!(edges[0], e2);
let mut iter = storage.get_outgoing_edges_iter(n0);
assert_eq!(iter.len(), 1);
let (min, max) = iter.size_hint();
assert_eq!(min, 0); assert_eq!(max, Some(2));
iter.next();
assert_eq!(iter.len(), 0);
}
#[test]
fn test_find_nodes_by_property_basic_match() {
let storage = CurrentStorage::new();
let alice = storage
.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build(),
)
.unwrap();
let bob = storage
.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Bob")
.insert("age", 30i64)
.build(),
)
.unwrap();
let _charlie = storage
.create_node(
"Person",
PropertyMapBuilder::new()
.insert("name", "Charlie")
.insert("age", 25i64)
.build(),
)
.unwrap();
let mut results = storage.find_nodes_by_property("Person", "age", &PropertyValue::Int(30));
results.sort();
let mut expected = vec![alice, bob];
expected.sort();
assert_eq!(results, expected);
let results =
storage.find_nodes_by_property("Person", "name", &PropertyValue::String("Alice".into()));
assert_eq!(results, vec![alice]);
}
#[test]
fn test_find_nodes_by_property_no_match() {
let storage = CurrentStorage::new();
storage
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let results =
storage.find_nodes_by_property("Person", "name", &PropertyValue::String("Nobody".into()));
assert!(results.is_empty());
}
#[test]
fn test_find_nodes_by_property_unknown_label() {
let storage = CurrentStorage::new();
storage
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let results = storage.find_nodes_by_property(
"UnknownLabel",
"name",
&PropertyValue::String("Alice".into()),
);
assert!(results.is_empty());
}
#[test]
fn test_find_nodes_by_property_unknown_key() {
let storage = CurrentStorage::new();
storage
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let results = storage.find_nodes_by_property(
"Person",
"nonexistent_key",
&PropertyValue::String("Alice".into()),
);
assert!(results.is_empty());
}
#[test]
fn test_find_nodes_by_property_wrong_label() {
let storage = CurrentStorage::new();
storage
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
storage
.create_node(
"Company",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let results =
storage.find_nodes_by_property("Person", "name", &PropertyValue::String("Alice".into()));
assert_eq!(results.len(), 1);
}
#[test]
fn test_find_nodes_by_property_int_value() {
let storage = CurrentStorage::new();
let n1 = storage
.create_node(
"Metric",
PropertyMapBuilder::new().insert("count", 42i64).build(),
)
.unwrap();
let results = storage.find_nodes_by_property("Metric", "count", &PropertyValue::Int(42));
assert_eq!(results, vec![n1]);
}
#[test]
fn test_find_nodes_by_property_bool_value() {
let storage = CurrentStorage::new();
let n1 = storage
.create_node(
"Feature",
PropertyMapBuilder::new().insert("active", true).build(),
)
.unwrap();
storage
.create_node(
"Feature",
PropertyMapBuilder::new().insert("active", false).build(),
)
.unwrap();
let results = storage.find_nodes_by_property("Feature", "active", &PropertyValue::Bool(true));
assert_eq!(results, vec![n1]);
}
#[test]
fn test_find_nodes_by_property_float_value() {
let storage = CurrentStorage::new();
let n1 = storage
.create_node(
"Sensor",
PropertyMapBuilder::new().insert("temp", 98.6f64).build(),
)
.unwrap();
storage
.create_node(
"Sensor",
PropertyMapBuilder::new().insert("temp", 37.0f64).build(),
)
.unwrap();
let results = storage.find_nodes_by_property("Sensor", "temp", &PropertyValue::Float(98.6));
assert_eq!(results, vec![n1]);
}
#[test]
fn test_get_node_ids_by_label() {
let storage = CurrentStorage::new();
let n1 = storage
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let n2 = storage
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Bob").build(),
)
.unwrap();
let n3 = storage
.create_node(
"Company",
PropertyMapBuilder::new().insert("name", "Aletheia").build(),
)
.unwrap();
let mut person_ids = storage.get_node_ids_by_label("Person");
person_ids.sort();
let mut expected_person_ids = vec![n1, n2];
expected_person_ids.sort();
assert_eq!(person_ids.len(), 2);
assert_eq!(person_ids, expected_person_ids);
let company_ids = storage.get_node_ids_by_label("Company");
assert_eq!(company_ids.len(), 1);
assert_eq!(company_ids, vec![n3]);
let nonexistent_ids = storage.get_node_ids_by_label("Nonexistent");
assert_eq!(nonexistent_ids.len(), 0);
}