Files
wifi-densepose/crates/ruvector-graph/tests/node_tests.rs
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

387 lines
10 KiB
Rust

//! Node CRUD operation tests
//!
//! Tests for creating, reading, updating, and deleting nodes in the graph database.
use ruvector_graph::{GraphDB, Label, Node, Properties, PropertyValue};
#[test]
fn test_create_node_basic() {
let db = GraphDB::new();
let mut properties = Properties::new();
properties.insert(
"name".to_string(),
PropertyValue::String("Alice".to_string()),
);
properties.insert("age".to_string(), PropertyValue::Integer(30));
let node = Node::new(
"node1".to_string(),
vec![Label {
name: "Person".to_string(),
}],
properties,
);
let node_id = db.create_node(node).unwrap();
assert_eq!(node_id, "node1");
}
#[test]
fn test_get_node_existing() {
let db = GraphDB::new();
let mut properties = Properties::new();
properties.insert("name".to_string(), PropertyValue::String("Bob".to_string()));
let node = Node::new(
"node2".to_string(),
vec![Label {
name: "Person".to_string(),
}],
properties.clone(),
);
db.create_node(node).unwrap();
let retrieved = db.get_node("node2").unwrap();
assert_eq!(retrieved.id, "node2");
assert_eq!(
retrieved.properties.get("name"),
Some(&PropertyValue::String("Bob".to_string()))
);
}
#[test]
fn test_get_node_nonexistent() {
let db = GraphDB::new();
let result = db.get_node("nonexistent");
assert!(result.is_none());
}
#[test]
fn test_node_with_multiple_labels() {
let db = GraphDB::new();
let labels = vec![
Label {
name: "Person".to_string(),
},
Label {
name: "Employee".to_string(),
},
Label {
name: "Manager".to_string(),
},
];
let mut properties = Properties::new();
properties.insert(
"name".to_string(),
PropertyValue::String("Charlie".to_string()),
);
let node = Node::new("node3".to_string(), labels, properties);
db.create_node(node).unwrap();
let retrieved = db.get_node("node3").unwrap();
assert_eq!(retrieved.labels.len(), 3);
}
#[test]
fn test_node_with_complex_properties() {
let db = GraphDB::new();
let mut properties = Properties::new();
properties.insert(
"name".to_string(),
PropertyValue::String("David".to_string()),
);
properties.insert("age".to_string(), PropertyValue::Integer(35));
properties.insert("height".to_string(), PropertyValue::Float(1.82));
properties.insert("active".to_string(), PropertyValue::Boolean(true));
properties.insert(
"tags".to_string(),
PropertyValue::List(vec![
PropertyValue::String("developer".to_string()),
PropertyValue::String("team-lead".to_string()),
]),
);
let node = Node::new(
"node4".to_string(),
vec![Label {
name: "Person".to_string(),
}],
properties,
);
db.create_node(node).unwrap();
let retrieved = db.get_node("node4").unwrap();
assert_eq!(retrieved.properties.len(), 5);
assert!(matches!(
retrieved.properties.get("tags"),
Some(PropertyValue::List(_))
));
}
#[test]
fn test_node_with_empty_properties() {
let db = GraphDB::new();
let node = Node::new(
"node5".to_string(),
vec![Label {
name: "EmptyNode".to_string(),
}],
Properties::new(),
);
db.create_node(node).unwrap();
let retrieved = db.get_node("node5").unwrap();
assert!(retrieved.properties.is_empty());
}
#[test]
fn test_node_with_no_labels() {
let db = GraphDB::new();
let mut properties = Properties::new();
properties.insert(
"data".to_string(),
PropertyValue::String("test".to_string()),
);
let node = Node::new("node6".to_string(), vec![], properties);
db.create_node(node).unwrap();
let retrieved = db.get_node("node6").unwrap();
assert!(retrieved.labels.is_empty());
}
#[test]
fn test_node_property_update() {
let db = GraphDB::new();
let mut properties = Properties::new();
properties.insert("counter".to_string(), PropertyValue::Integer(0));
let node = Node::new(
"node7".to_string(),
vec![Label {
name: "Counter".to_string(),
}],
properties,
);
db.create_node(node).unwrap();
// TODO: Implement update_node method
// For now, we'll recreate the node with updated properties
let mut updated_properties = Properties::new();
updated_properties.insert("counter".to_string(), PropertyValue::Integer(1));
let updated_node = Node::new(
"node7".to_string(),
vec![Label {
name: "Counter".to_string(),
}],
updated_properties,
);
db.create_node(updated_node).unwrap();
let retrieved = db.get_node("node7").unwrap();
assert_eq!(
retrieved.properties.get("counter"),
Some(&PropertyValue::Integer(1))
);
}
#[test]
fn test_create_1000_nodes() {
let db = GraphDB::new();
for i in 0..1000 {
let mut properties = Properties::new();
properties.insert("index".to_string(), PropertyValue::Integer(i));
let node = Node::new(
format!("node_{}", i),
vec![Label {
name: "TestNode".to_string(),
}],
properties,
);
db.create_node(node).unwrap();
}
// Verify all nodes were created
for i in 0..1000 {
let retrieved = db.get_node(&format!("node_{}", i));
assert!(retrieved.is_some());
}
}
#[test]
fn test_node_property_null_value() {
let db = GraphDB::new();
let mut properties = Properties::new();
properties.insert("nullable".to_string(), PropertyValue::Null);
let node = Node::new(
"node8".to_string(),
vec![Label {
name: "NullTest".to_string(),
}],
properties,
);
db.create_node(node).unwrap();
let retrieved = db.get_node("node8").unwrap();
assert_eq!(
retrieved.properties.get("nullable"),
Some(&PropertyValue::Null)
);
}
#[test]
fn test_node_nested_list_properties() {
let db = GraphDB::new();
let mut properties = Properties::new();
properties.insert(
"matrix".to_string(),
PropertyValue::List(vec![
PropertyValue::List(vec![PropertyValue::Integer(1), PropertyValue::Integer(2)]),
PropertyValue::List(vec![PropertyValue::Integer(3), PropertyValue::Integer(4)]),
]),
);
let node = Node::new(
"node9".to_string(),
vec![Label {
name: "Matrix".to_string(),
}],
properties,
);
db.create_node(node).unwrap();
let retrieved = db.get_node("node9").unwrap();
match retrieved.properties.get("matrix") {
Some(PropertyValue::List(outer)) => {
assert_eq!(outer.len(), 2);
match &outer[0] {
PropertyValue::List(inner) => assert_eq!(inner.len(), 2),
_ => panic!("Expected inner list"),
}
}
_ => panic!("Expected outer list"),
}
}
// ============================================================================
// Property-based tests using proptest
// ============================================================================
#[cfg(test)]
mod property_tests {
use super::*;
use proptest::prelude::*;
fn node_id_strategy() -> impl Strategy<Value = String> {
"[a-z][a-z0-9_]{0,20}".prop_map(|s| s.to_string())
}
fn label_strategy() -> impl Strategy<Value = Label> {
"[A-Z][a-zA-Z]{0,10}".prop_map(|name| Label { name })
}
fn property_value_strategy() -> impl Strategy<Value = PropertyValue> {
prop_oneof![
any::<String>().prop_map(PropertyValue::String),
any::<i64>().prop_map(PropertyValue::Integer),
any::<f64>()
.prop_filter("Must be finite", |x| x.is_finite())
.prop_map(PropertyValue::Float),
any::<bool>().prop_map(PropertyValue::Boolean),
Just(PropertyValue::Null),
]
}
proptest! {
#[test]
fn test_node_roundtrip(
id in node_id_strategy(),
labels in prop::collection::vec(label_strategy(), 0..5),
prop_count in 0..10usize
) {
let db = GraphDB::new();
let mut properties = Properties::new();
for i in 0..prop_count {
properties.insert(
format!("prop_{}", i),
PropertyValue::String(format!("value_{}", i))
);
}
let node = Node::new(id.clone(), labels.clone(), properties.clone());
db.create_node(node).unwrap();
let retrieved = db.get_node(&id).unwrap();
assert_eq!(retrieved.id, id);
assert_eq!(retrieved.labels.len(), labels.len());
assert_eq!(retrieved.properties.len(), properties.len());
}
#[test]
fn test_property_value_consistency(
value in property_value_strategy()
) {
let db = GraphDB::new();
let mut properties = Properties::new();
properties.insert("test_prop".to_string(), value.clone());
let node = Node::new(
"test_node".to_string(),
vec![],
properties
);
db.create_node(node).unwrap();
let retrieved = db.get_node("test_node").unwrap();
assert_eq!(retrieved.properties.get("test_prop"), Some(&value));
}
#[test]
fn test_many_nodes_no_collision(
ids in prop::collection::hash_set(node_id_strategy(), 10..100)
) {
let db = GraphDB::new();
for id in &ids {
let node = Node::new(
id.clone(),
vec![],
Properties::new()
);
db.create_node(node).unwrap();
}
for id in &ids {
assert!(db.get_node(id).is_some());
}
}
}
}