use interstellar::graph_elements::{InMemoryEdge, InMemoryVertex};
use interstellar::prelude::*;
use interstellar::storage::{BatchError, Graph, GraphStorage};
use interstellar::StorageError;
use std::collections::HashMap;
use std::collections::HashSet;
use std::sync::Arc;
use std::thread;
#[test]
fn cow_graph_basic_vertex_operations() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([
("name".to_string(), Value::String("Alice".into())),
("age".to_string(), Value::Int(30)),
("active".to_string(), Value::Bool(true)),
]),
);
let bob = graph.add_vertex(
"Person",
HashMap::from([
("name".to_string(), Value::String("Bob".into())),
("age".to_string(), Value::Int(25)),
]),
);
let software = graph.add_vertex(
"Software",
HashMap::from([("name".to_string(), Value::String("GraphDB".into()))]),
);
assert_eq!(graph.vertex_count(), 3);
let snap = graph.snapshot();
let alice_v = snap.get_vertex(alice).unwrap();
assert_eq!(alice_v.label, "Person");
assert_eq!(
alice_v.properties.get("name"),
Some(&Value::String("Alice".into()))
);
let bob_v = snap.get_vertex(bob).unwrap();
assert_eq!(bob_v.properties.get("age"), Some(&Value::Int(25)));
let sw_v = snap.get_vertex(software).unwrap();
assert_eq!(sw_v.label, "Software");
}
#[test]
fn cow_graph_basic_edge_operations() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("Person", HashMap::new());
let bob = graph.add_vertex("Person", HashMap::new());
let charlie = graph.add_vertex("Person", HashMap::new());
let knows1 = graph
.add_edge(
alice,
bob,
"KNOWS",
HashMap::from([("since".to_string(), Value::Int(2020))]),
)
.unwrap();
let _knows2 = graph
.add_edge(bob, charlie, "KNOWS", HashMap::new())
.unwrap();
let knows3 = graph
.add_edge(charlie, alice, "KNOWS", HashMap::new())
.unwrap();
assert_eq!(graph.edge_count(), 3);
let snap = graph.snapshot();
let edge = snap.get_edge(knows1).unwrap();
assert_eq!(edge.src, alice);
assert_eq!(edge.dst, bob);
assert_eq!(edge.label, "KNOWS");
assert_eq!(edge.properties.get("since"), Some(&Value::Int(2020)));
let out_edges: Vec<_> = snap.out_edges(alice).collect();
assert_eq!(out_edges.len(), 1);
assert_eq!(out_edges[0].id, knows1);
let in_edges: Vec<_> = snap.in_edges(alice).collect();
assert_eq!(in_edges.len(), 1);
assert_eq!(in_edges[0].id, knows3);
}
#[test]
fn cow_graph_property_updates() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let bob = graph.add_vertex("Person", HashMap::new());
let edge = graph.add_edge(alice, bob, "KNOWS", HashMap::new()).unwrap();
graph
.set_vertex_property(alice, "age", Value::Int(30))
.unwrap();
graph
.set_vertex_property(alice, "name", Value::String("Alicia".into()))
.unwrap();
graph
.set_edge_property(edge, "weight", Value::Float(0.5))
.unwrap();
let snap = graph.snapshot();
let v = snap.get_vertex(alice).unwrap();
assert_eq!(
v.properties.get("name"),
Some(&Value::String("Alicia".into()))
);
assert_eq!(v.properties.get("age"), Some(&Value::Int(30)));
let e = snap.get_edge(edge).unwrap();
assert_eq!(e.properties.get("weight"), Some(&Value::Float(0.5)));
}
#[test]
fn cow_graph_remove_operations() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("Person", HashMap::new());
let bob = graph.add_vertex("Person", HashMap::new());
let charlie = graph.add_vertex("Person", HashMap::new());
let e1 = graph.add_edge(alice, bob, "KNOWS", HashMap::new()).unwrap();
let e2 = graph
.add_edge(alice, charlie, "KNOWS", HashMap::new())
.unwrap();
let _e3 = graph
.add_edge(bob, charlie, "KNOWS", HashMap::new())
.unwrap();
assert_eq!(graph.vertex_count(), 3);
assert_eq!(graph.edge_count(), 3);
graph.remove_edge(e1).unwrap();
assert_eq!(graph.edge_count(), 2);
graph.remove_vertex(alice).unwrap();
assert_eq!(graph.vertex_count(), 2);
assert_eq!(graph.edge_count(), 1);
let snap = graph.snapshot();
assert!(snap.get_vertex(alice).is_none());
assert!(snap.get_vertex(bob).is_some());
assert!(snap.get_edge(e2).is_none()); }
#[test]
fn cow_snapshot_isolation_basic() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let snap_before = graph.snapshot();
graph
.set_vertex_property(alice, "name", Value::String("Alicia".into()))
.unwrap();
graph.add_vertex("Person", HashMap::new());
let snap_after = graph.snapshot();
let v_before = snap_before.get_vertex(alice).unwrap();
assert_eq!(
v_before.properties.get("name"),
Some(&Value::String("Alice".into()))
);
assert_eq!(snap_before.vertex_count(), 1);
let v_after = snap_after.get_vertex(alice).unwrap();
assert_eq!(
v_after.properties.get("name"),
Some(&Value::String("Alicia".into()))
);
assert_eq!(snap_after.vertex_count(), 2);
}
#[test]
fn cow_snapshot_survives_heavy_modification() {
let graph = Arc::new(Graph::new());
for i in 0..1000 {
graph.add_vertex("Node", HashMap::from([("id".to_string(), Value::Int(i))]));
}
let snap = graph.snapshot();
assert_eq!(snap.vertex_count(), 1000);
for i in 1000..2000 {
graph.add_vertex("Node", HashMap::from([("id".to_string(), Value::Int(i))]));
}
for i in 0..500u64 {
graph.remove_vertex(VertexId(i)).unwrap();
}
assert_eq!(snap.vertex_count(), 1000);
let v = snap.get_vertex(VertexId(0)).unwrap();
assert_eq!(v.properties.get("id"), Some(&Value::Int(0)));
let new_snap = graph.snapshot();
assert_eq!(new_snap.vertex_count(), 1500); }
#[test]
fn cow_snapshot_can_outlive_graph() {
let snap = {
let graph = Arc::new(Graph::new());
graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Bob".into()))]),
);
graph.snapshot()
};
assert_eq!(snap.vertex_count(), 2);
let names: Vec<_> = snap
.all_vertices()
.map(|v| v.properties.get("name").cloned())
.collect();
assert_eq!(names.len(), 2);
}
#[test]
fn cow_multiple_snapshots_independent() {
let graph = Arc::new(Graph::new());
graph.add_vertex("A", HashMap::new());
let snap1 = graph.snapshot();
graph.add_vertex("B", HashMap::new());
let snap2 = graph.snapshot();
graph.add_vertex("C", HashMap::new());
let snap3 = graph.snapshot();
graph.add_vertex("D", HashMap::new());
assert_eq!(snap1.vertex_count(), 1);
assert_eq!(snap2.vertex_count(), 2);
assert_eq!(snap3.vertex_count(), 3);
assert_eq!(graph.vertex_count(), 4);
assert!(snap1.vertices_with_label("A").next().is_some());
assert!(snap1.vertices_with_label("B").next().is_none());
assert!(snap2.vertices_with_label("B").next().is_some());
assert!(snap2.vertices_with_label("C").next().is_none());
}
#[test]
fn cow_batch_atomic_success() {
let graph = Arc::new(Graph::new());
let result = graph.batch(|ctx| {
let alice = ctx.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let bob = ctx.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Bob".into()))]),
);
let charlie = ctx.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Charlie".into()))]),
);
ctx.add_edge(alice, bob, "KNOWS", HashMap::new())?;
ctx.add_edge(bob, charlie, "KNOWS", HashMap::new())?;
ctx.add_edge(charlie, alice, "KNOWS", HashMap::new())?;
Ok((alice, bob, charlie))
});
assert!(result.is_ok());
let (alice, bob, charlie) = result.unwrap();
assert_eq!(graph.vertex_count(), 3);
assert_eq!(graph.edge_count(), 3);
let snap = graph.snapshot();
assert!(snap.get_vertex(alice).is_some());
assert!(snap.get_vertex(bob).is_some());
assert!(snap.get_vertex(charlie).is_some());
let out_edges: Vec<_> = snap.out_edges(alice).collect();
assert_eq!(out_edges.len(), 1);
}
#[test]
fn cow_batch_atomic_rollback() {
let graph = Arc::new(Graph::new());
graph.add_vertex(
"Existing",
HashMap::from([("name".to_string(), Value::String("Existing".into()))]),
);
let initial_version = graph.version();
let result: Result<(), BatchError> = graph.batch(|ctx| {
ctx.add_vertex("New1", HashMap::new());
ctx.add_vertex("New2", HashMap::new());
ctx.add_edge(VertexId(999), VertexId(998), "INVALID", HashMap::new())?;
Ok(())
});
assert!(result.is_err());
assert_eq!(graph.vertex_count(), 1);
assert_eq!(graph.edge_count(), 0);
assert_eq!(graph.version(), initial_version);
let snap = graph.snapshot();
assert!(snap.vertices_with_label("Existing").next().is_some());
assert!(snap.vertices_with_label("New1").next().is_none());
}
#[test]
fn cow_batch_with_property_updates() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
graph
.set_vertex_property(alice, "age", Value::Int(30))
.unwrap();
graph
.set_vertex_property(alice, "city", Value::String("NYC".into()))
.unwrap();
let edge_id = graph
.batch(|ctx| {
let bob = ctx.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Bob".into()))]),
);
let edge = ctx.add_edge(
alice,
bob,
"KNOWS",
HashMap::from([("since".to_string(), Value::Int(2020))]),
)?;
Ok(edge)
})
.unwrap();
let snap = graph.snapshot();
let v = snap.get_vertex(alice).unwrap();
assert_eq!(v.properties.get("age"), Some(&Value::Int(30)));
assert_eq!(v.properties.get("city"), Some(&Value::String("NYC".into())));
let edge = snap.get_edge(edge_id).unwrap();
assert_eq!(edge.properties.get("since"), Some(&Value::Int(2020)));
}
#[test]
#[cfg(feature = "gql")]
fn cow_gql_create_vertex() {
let graph = Arc::new(Graph::new());
graph
.gql("CREATE (:Person {name: 'Alice', age: 30})")
.unwrap();
graph
.gql("CREATE (:Person {name: 'Bob', age: 25})")
.unwrap();
graph.gql("CREATE (:Software {name: 'GraphDB'})").unwrap();
assert_eq!(graph.vertex_count(), 3);
let snap = graph.snapshot();
let people: Vec<_> = snap.vertices_with_label("Person").collect();
assert_eq!(people.len(), 2);
let software: Vec<_> = snap.vertices_with_label("Software").collect();
assert_eq!(software.len(), 1);
}
#[test]
#[cfg(feature = "gql")]
fn cow_gql_create_edges() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let bob = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Bob".into()))]),
);
graph
.add_edge(
alice,
bob,
"KNOWS",
HashMap::from([("since".to_string(), Value::Int(2020))]),
)
.unwrap();
assert_eq!(graph.edge_count(), 1);
let snap = graph.snapshot();
let edges: Vec<_> = snap.edges_with_label("KNOWS").collect();
assert_eq!(edges.len(), 1);
assert_eq!(edges[0].properties.get("since"), Some(&Value::Int(2020)));
}
#[test]
#[cfg(feature = "gql")]
fn cow_gql_query_on_snapshot() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([
("name".to_string(), Value::String("Alice".into())),
("age".to_string(), Value::Int(30)),
]),
);
let bob = graph.add_vertex(
"Person",
HashMap::from([
("name".to_string(), Value::String("Bob".into())),
("age".to_string(), Value::Int(25)),
]),
);
graph.add_edge(alice, bob, "KNOWS", HashMap::new()).unwrap();
let snap = graph.snapshot();
let people: Vec<_> = snap.vertices_with_label("Person").collect();
assert_eq!(people.len(), 2);
let names: Vec<_> = people
.iter()
.filter_map(|v| v.properties.get("name"))
.collect();
assert_eq!(names.len(), 2);
}
#[test]
#[cfg(feature = "gql")]
fn cow_gql_set_property() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
graph
.gql("MATCH (p:Person) WHERE p.name = 'Alice' SET p.age = 30")
.unwrap();
let snap = graph.snapshot();
let v = snap.get_vertex(alice).unwrap();
assert_eq!(v.properties.get("age"), Some(&Value::Int(30)));
}
#[test]
fn cow_traversal_basic() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let bob = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Bob".into()))]),
);
let software = graph.add_vertex(
"Software",
HashMap::from([("name".to_string(), Value::String("GraphDB".into()))]),
);
graph.add_edge(alice, bob, "KNOWS", HashMap::new()).unwrap();
graph
.add_edge(alice, software, "CREATED", HashMap::new())
.unwrap();
let snap = graph.snapshot();
let all_vertices: Vec<_> = snap.all_vertices().collect();
assert_eq!(all_vertices.len(), 3);
let people: Vec<_> = snap.vertices_with_label("Person").collect();
assert_eq!(people.len(), 2);
let alice_out: Vec<_> = snap.out_edges(alice).collect();
assert_eq!(alice_out.len(), 2);
let labels: Vec<_> = alice_out.iter().map(|e| e.label.as_str()).collect();
assert!(labels.contains(&"KNOWS"));
assert!(labels.contains(&"CREATED"));
}
#[test]
fn cow_traversal_multi_hop() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex(
"Node",
HashMap::from([("name".to_string(), Value::String("A".into()))]),
);
let b = graph.add_vertex(
"Node",
HashMap::from([("name".to_string(), Value::String("B".into()))]),
);
let c = graph.add_vertex(
"Node",
HashMap::from([("name".to_string(), Value::String("C".into()))]),
);
let d = graph.add_vertex(
"Node",
HashMap::from([("name".to_string(), Value::String("D".into()))]),
);
graph.add_edge(a, b, "NEXT", HashMap::new()).unwrap();
graph.add_edge(b, c, "NEXT", HashMap::new()).unwrap();
graph.add_edge(c, d, "NEXT", HashMap::new()).unwrap();
let snap = graph.snapshot();
let a_out: Vec<_> = snap.out_edges(a).collect();
assert_eq!(a_out.len(), 1);
assert_eq!(a_out[0].dst, b);
let b_out: Vec<_> = snap.out_edges(b).collect();
assert_eq!(b_out.len(), 1);
assert_eq!(b_out[0].dst, c);
let d_in: Vec<_> = snap.in_edges(d).collect();
assert_eq!(d_in.len(), 1);
assert_eq!(d_in[0].src, c);
}
#[test]
fn cow_concurrent_readers() {
let graph = Arc::new(Graph::new());
for i in 0..100 {
graph.add_vertex("Node", HashMap::from([("id".to_string(), Value::Int(i))]));
}
let snapshot = graph.snapshot();
let handles: Vec<_> = (0..10)
.map(|_| {
let snap = snapshot.clone();
thread::spawn(move || {
let count = snap.vertex_count();
assert_eq!(count, 100);
for i in 0..100u64 {
let v = snap.get_vertex(VertexId(i));
assert!(v.is_some());
}
count
})
})
.collect();
for handle in handles {
let count = handle.join().unwrap();
assert_eq!(count, 100);
}
}
#[test]
fn cow_concurrent_readers_with_writer() {
let graph = Arc::new(Graph::new());
for i in 0..50 {
graph.add_vertex("Node", HashMap::from([("id".to_string(), Value::Int(i))]));
}
let reader_snapshot = graph.snapshot();
let writer_graph = Arc::clone(&graph);
let writer = thread::spawn(move || {
for i in 50..100 {
writer_graph.add_vertex("Node", HashMap::from([("id".to_string(), Value::Int(i))]));
}
});
let readers: Vec<_> = (0..5)
.map(|_| {
let snap = reader_snapshot.clone();
thread::spawn(move || {
assert_eq!(snap.vertex_count(), 50);
})
})
.collect();
writer.join().unwrap();
for reader in readers {
reader.join().unwrap();
}
assert_eq!(graph.vertex_count(), 100);
}
#[test]
fn cow_snapshot_sent_across_threads() {
let graph = Arc::new(Graph::new());
graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let snapshot = graph.snapshot();
let handle = thread::spawn(move || {
assert_eq!(snapshot.vertex_count(), 1);
let v = snapshot.get_vertex(VertexId(0)).unwrap();
v.properties.get("name").cloned()
});
let name = handle.join().unwrap();
assert_eq!(name, Some(Value::String("Alice".into())));
}
#[test]
fn cow_label_index_vertices() {
let graph = Arc::new(Graph::new());
for _ in 0..100 {
graph.add_vertex("Person", HashMap::new());
}
for _ in 0..50 {
graph.add_vertex("Software", HashMap::new());
}
for _ in 0..25 {
graph.add_vertex("Company", HashMap::new());
}
let snap = graph.snapshot();
let people: Vec<_> = snap.vertices_with_label("Person").collect();
let software: Vec<_> = snap.vertices_with_label("Software").collect();
let companies: Vec<_> = snap.vertices_with_label("Company").collect();
let unknown: Vec<_> = snap.vertices_with_label("Unknown").collect();
assert_eq!(people.len(), 100);
assert_eq!(software.len(), 50);
assert_eq!(companies.len(), 25);
assert_eq!(unknown.len(), 0);
}
#[test]
fn cow_label_index_edges() {
let graph = Arc::new(Graph::new());
let vertices: Vec<_> = (0..10)
.map(|_| graph.add_vertex("Node", HashMap::new()))
.collect();
for i in 0..9 {
graph
.add_edge(vertices[i], vertices[i + 1], "NEXT", HashMap::new())
.unwrap();
}
for i in 0..5 {
graph
.add_edge(vertices[i], vertices[9 - i], "LINK", HashMap::new())
.unwrap();
}
let snap = graph.snapshot();
let next_edges: Vec<_> = snap.edges_with_label("NEXT").collect();
let link_edges: Vec<_> = snap.edges_with_label("LINK").collect();
let unknown_edges: Vec<_> = snap.edges_with_label("UNKNOWN").collect();
assert_eq!(next_edges.len(), 9);
assert_eq!(link_edges.len(), 5);
assert_eq!(unknown_edges.len(), 0);
}
#[test]
fn cow_scale_10k_vertices() {
let graph = Arc::new(Graph::new());
let vertices: Vec<_> = (0..10_000)
.map(|i| graph.add_vertex("Node", HashMap::from([("id".to_string(), Value::Int(i))])))
.collect();
assert_eq!(graph.vertex_count(), 10_000);
for i in 0..10_000 {
for j in 1..=5 {
let dst_idx = (i + j) % 10_000;
graph
.add_edge(vertices[i], vertices[dst_idx], "CONNECTS", HashMap::new())
.unwrap();
}
}
assert_eq!(graph.edge_count(), 50_000);
let snap = graph.snapshot();
let v = snap.get_vertex(vertices[5000]).unwrap();
assert_eq!(v.properties.get("id"), Some(&Value::Int(5000)));
let out: Vec<_> = snap.out_edges(vertices[0]).collect();
assert_eq!(out.len(), 5);
}
#[test]
fn cow_many_snapshots() {
let graph = Arc::new(Graph::new());
let mut snapshots = Vec::new();
for i in 0..100 {
graph.add_vertex("Node", HashMap::from([("id".to_string(), Value::Int(i))]));
snapshots.push(graph.snapshot());
}
for (i, snap) in snapshots.iter().enumerate() {
assert_eq!(snap.vertex_count() as usize, i + 1);
}
}
#[test]
fn cow_version_increments() {
let graph = Arc::new(Graph::new());
assert_eq!(graph.version(), 0);
graph.add_vertex("A", HashMap::new());
assert_eq!(graph.version(), 1);
graph.add_vertex("B", HashMap::new());
assert_eq!(graph.version(), 2);
let v = graph.add_vertex("C", HashMap::new());
assert_eq!(graph.version(), 3);
graph.set_vertex_property(v, "prop", Value::Int(1)).unwrap();
assert_eq!(graph.version(), 4);
graph.remove_vertex(v).unwrap();
assert_eq!(graph.version(), 5);
}
#[test]
fn cow_snapshot_captures_version() {
let graph = Arc::new(Graph::new());
graph.add_vertex("A", HashMap::new());
let snap1 = graph.snapshot();
graph.add_vertex("B", HashMap::new());
let snap2 = graph.snapshot();
graph.add_vertex("C", HashMap::new());
let snap3 = graph.snapshot();
assert_eq!(snap1.version(), 1);
assert_eq!(snap2.version(), 2);
assert_eq!(snap3.version(), 3);
}
#[test]
fn cow_error_vertex_not_found() {
let graph = Arc::new(Graph::new());
let result = graph.set_vertex_property(VertexId(999), "prop", Value::Int(1));
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
let result = graph.remove_vertex(VertexId(999));
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
}
#[test]
fn cow_error_edge_not_found() {
let graph = Arc::new(Graph::new());
let result = graph.set_edge_property(EdgeId(999), "prop", Value::Int(1));
assert!(matches!(result, Err(StorageError::EdgeNotFound(_))));
let result = graph.remove_edge(EdgeId(999));
assert!(matches!(result, Err(StorageError::EdgeNotFound(_))));
}
#[test]
fn cow_error_edge_missing_vertices() {
let graph = Arc::new(Graph::new());
let v1 = graph.add_vertex("Node", HashMap::new());
let result = graph.add_edge(VertexId(999), v1, "EDGE", HashMap::new());
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
let result = graph.add_edge(v1, VertexId(999), "EDGE", HashMap::new());
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
}
#[test]
fn cow_schema_set_and_get() {
use interstellar::schema::{PropertyType, SchemaBuilder, ValidationMode};
let graph = Arc::new(Graph::new());
let schema = SchemaBuilder::new()
.mode(ValidationMode::Strict)
.vertex("Person")
.property("name", PropertyType::String)
.optional("age", PropertyType::Int)
.done()
.build();
graph.set_schema(Some(schema.clone()));
let retrieved = graph.schema();
assert!(retrieved.is_some());
let retrieved = retrieved.unwrap();
assert!(retrieved.vertex_schema("Person").is_some());
}
#[test]
fn cow_index_create_and_drop() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
graph
.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
)
.unwrap();
assert!(graph.has_index("idx_Person_agev"));
assert_eq!(graph.index_count(), 1);
assert!(graph.supports_indexes());
graph
.create_index(
IndexBuilder::vertex()
.label("User")
.property("email")
.unique()
.build()
.unwrap(),
)
.unwrap();
assert!(graph.has_index("uniq_User_emailv"));
assert_eq!(graph.index_count(), 2);
let indexes = graph.list_indexes();
assert_eq!(indexes.len(), 2);
graph.drop_index("idx_Person_agev").unwrap();
assert!(!graph.has_index("idx_Person_agev"));
assert_eq!(graph.index_count(), 1);
let result = graph.drop_index("non_existent");
assert!(result.is_err());
}
#[test]
fn cow_index_duplicate_name_error() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
graph
.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
)
.unwrap();
let result = graph.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
);
assert!(result.is_err());
}
#[test]
fn cow_index_populated_on_creation() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
graph.add_vertex(
"Person",
HashMap::from([
("name".to_string(), Value::String("Alice".into())),
("age".to_string(), Value::Int(30)),
]),
);
graph.add_vertex(
"Person",
HashMap::from([
("name".to_string(), Value::String("Bob".into())),
("age".to_string(), Value::Int(25)),
]),
);
graph.add_vertex(
"Person",
HashMap::from([
("name".to_string(), Value::String("Charlie".into())),
("age".to_string(), Value::Int(30)),
]),
);
graph
.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
)
.unwrap();
let results: Vec<_> = graph
.vertices_by_property(Some("Person"), "age", &Value::Int(30))
.collect();
assert_eq!(results.len(), 2);
let names: Vec<_> = results
.iter()
.filter_map(|v| v.properties.get("name").and_then(|n| n.as_str()))
.collect();
assert!(names.contains(&"Alice"));
assert!(names.contains(&"Charlie"));
}
#[test]
fn cow_index_maintained_on_insert() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
graph
.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
)
.unwrap();
graph.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(30))]),
);
graph.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(25))]),
);
graph.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(30))]),
);
let results: Vec<_> = graph
.vertices_by_property(Some("Person"), "age", &Value::Int(30))
.collect();
assert_eq!(results.len(), 2);
}
#[test]
fn cow_index_maintained_on_remove() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(30))]),
);
let bob = graph.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(30))]),
);
graph
.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
)
.unwrap();
let results: Vec<_> = graph
.vertices_by_property(Some("Person"), "age", &Value::Int(30))
.collect();
assert_eq!(results.len(), 2);
graph.remove_vertex(alice).unwrap();
let results: Vec<_> = graph
.vertices_by_property(Some("Person"), "age", &Value::Int(30))
.collect();
assert_eq!(results.len(), 1);
assert_eq!(results[0].id, bob);
}
#[test]
fn cow_index_maintained_on_property_update() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(30))]),
);
graph
.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
)
.unwrap();
let results: Vec<_> = graph
.vertices_by_property(Some("Person"), "age", &Value::Int(30))
.collect();
assert_eq!(results.len(), 1);
graph
.set_vertex_property(alice, "age", Value::Int(31))
.unwrap();
let results: Vec<_> = graph
.vertices_by_property(Some("Person"), "age", &Value::Int(30))
.collect();
assert_eq!(results.len(), 0);
let results: Vec<_> = graph
.vertices_by_property(Some("Person"), "age", &Value::Int(31))
.collect();
assert_eq!(results.len(), 1);
}
#[test]
fn cow_index_edge_property() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("Person", HashMap::new());
let bob = graph.add_vertex("Person", HashMap::new());
let charlie = graph.add_vertex("Person", HashMap::new());
graph
.add_edge(
alice,
bob,
"KNOWS",
HashMap::from([("since".to_string(), Value::Int(2020))]),
)
.unwrap();
graph
.add_edge(
bob,
charlie,
"KNOWS",
HashMap::from([("since".to_string(), Value::Int(2022))]),
)
.unwrap();
graph
.add_edge(
charlie,
alice,
"KNOWS",
HashMap::from([("since".to_string(), Value::Int(2020))]),
)
.unwrap();
graph
.create_index(
IndexBuilder::edge()
.label("KNOWS")
.property("since")
.build()
.unwrap(),
)
.unwrap();
let results: Vec<_> = graph
.edges_by_property(Some("KNOWS"), "since", &Value::Int(2020))
.collect();
assert_eq!(results.len(), 2);
}
#[test]
fn cow_index_unique_constraint() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
graph
.create_index(
IndexBuilder::vertex()
.label("User")
.property("email")
.unique()
.build()
.unwrap(),
)
.unwrap();
graph.add_vertex(
"User",
HashMap::from([(
"email".to_string(),
Value::String("alice@example.com".into()),
)]),
);
let results: Vec<_> = graph
.vertices_by_property(
Some("User"),
"email",
&Value::String("alice@example.com".into()),
)
.collect();
assert_eq!(results.len(), 1);
}
#[test]
fn cow_index_range_query() {
use interstellar::index::IndexBuilder;
use std::ops::Bound;
let graph = Arc::new(Graph::new());
for age in [18, 21, 25, 30, 35, 40, 50, 60] {
graph.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(age))]),
);
}
graph
.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
)
.unwrap();
let results: Vec<_> = graph
.vertices_by_property_range(
Some("Person"),
"age",
Bound::Included(&Value::Int(25)),
Bound::Excluded(&Value::Int(40)),
)
.collect();
assert_eq!(results.len(), 3); }
#[test]
fn cow_index_no_label_filter() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
graph
.create_index(
IndexBuilder::vertex()
.property("created_at")
.build()
.unwrap(),
)
.unwrap();
graph.add_vertex(
"Person",
HashMap::from([("created_at".to_string(), Value::Int(1000))]),
);
graph.add_vertex(
"Company",
HashMap::from([("created_at".to_string(), Value::Int(1000))]),
);
graph.add_vertex(
"Product",
HashMap::from([("created_at".to_string(), Value::Int(2000))]),
);
let results: Vec<_> = graph
.vertices_by_property(None, "created_at", &Value::Int(1000))
.collect();
assert_eq!(results.len(), 2);
}
#[test]
fn cow_index_with_batch_operations() {
use interstellar::index::IndexBuilder;
let graph = Arc::new(Graph::new());
graph
.create_index(
IndexBuilder::vertex()
.label("Person")
.property("age")
.build()
.unwrap(),
)
.unwrap();
graph
.batch(|ctx| {
ctx.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(30))]),
);
ctx.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(25))]),
);
ctx.add_vertex(
"Person",
HashMap::from([("age".to_string(), Value::Int(30))]),
);
Ok(())
})
.unwrap();
assert_eq!(graph.vertex_count(), 3);
}
#[test]
fn cow_unified_traversal_add_v_basic() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let result = g.add_v("Person").next();
assert!(result.is_some());
let vertex = result.unwrap();
let _id = vertex.id();
assert_eq!(graph.vertex_count(), 1);
}
#[test]
fn cow_unified_traversal_add_v_with_properties() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let result = g
.add_v("Person")
.property("name", "Alice")
.property("age", 30i64)
.next();
assert!(result.is_some());
let vertex = result.unwrap();
let vertex_id = vertex.id();
let snap = graph.snapshot();
let stored = snap.get_vertex(vertex_id).unwrap();
assert_eq!(stored.label, "Person");
assert_eq!(
stored.properties.get("name"),
Some(&Value::String("Alice".into()))
);
assert_eq!(stored.properties.get("age"), Some(&Value::Int(30)));
}
#[test]
fn cow_unified_traversal_add_v_multiple() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
g.add_v("Person").property("name", "Alice").iterate();
g.add_v("Person").property("name", "Bob").iterate();
g.add_v("Software").property("name", "GraphDB").iterate();
assert_eq!(graph.vertex_count(), 3);
let snap = graph.snapshot();
let person_count = snap.all_vertices().filter(|v| v.label == "Person").count();
let software_count = snap
.all_vertices()
.filter(|v| v.label == "Software")
.count();
assert_eq!(person_count, 2);
assert_eq!(software_count, 1);
}
#[test]
fn cow_unified_traversal_add_v_to_list() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let results = g.add_v("Person").to_list();
assert_eq!(results.len(), 1);
let _id = results[0].id();
assert_eq!(graph.vertex_count(), 1);
}
#[test]
fn cow_unified_traversal_query_after_mutation() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
g.add_v("Person").property("name", "Alice").iterate();
g.add_v("Person").property("name", "Bob").iterate();
let g2 = graph.gremlin(Arc::clone(&graph));
let results = g2.v().has_label("Person").to_list();
assert_eq!(results.len(), 2);
}
#[test]
fn cow_unified_traversal_add_e_from_source() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let bob = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Bob".into()))]),
);
let g = graph.gremlin(Arc::clone(&graph));
let result = g.add_e("KNOWS").from_id(alice).to_id(bob).next();
assert!(result.is_some());
let edge = result.unwrap();
let _id = edge.id();
assert_eq!(graph.edge_count(), 1);
}
#[test]
fn cow_unified_traversal_add_e_with_properties() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("Person", HashMap::new());
let bob = graph.add_vertex("Person", HashMap::new());
let g = graph.gremlin(Arc::clone(&graph));
let result = g
.add_e("KNOWS")
.from_id(alice)
.to_id(bob)
.property("since", 2020i64)
.next();
let edge = result.unwrap();
let edge_id = edge.id();
let snap = graph.snapshot();
let stored = snap.get_edge(edge_id).unwrap();
assert_eq!(stored.label, "KNOWS");
assert_eq!(stored.properties.get("since"), Some(&Value::Int(2020)));
}
#[test]
fn cow_unified_traversal_full_workflow() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let alice_vertex = g
.add_v("Person")
.property("name", "Alice")
.property("age", 30i64)
.next()
.unwrap();
let bob_vertex = g
.add_v("Person")
.property("name", "Bob")
.property("age", 25i64)
.next()
.unwrap();
let alice_id = alice_vertex.id();
let bob_id = bob_vertex.id();
g.add_e("KNOWS")
.from_id(alice_id)
.to_id(bob_id)
.property("since", 2020i64)
.iterate();
assert_eq!(graph.vertex_count(), 2);
assert_eq!(graph.edge_count(), 1);
let g2 = graph.gremlin(Arc::clone(&graph));
let people = g2.v().has_label("Person").to_list();
assert_eq!(people.len(), 2);
let g3 = graph.gremlin(Arc::clone(&graph));
let alice_out = g3.v_id(alice_id).out_label("KNOWS").to_list();
assert_eq!(alice_out.len(), 1);
}
#[test]
fn cow_unified_traversal_drop_vertex() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let vertex = g.add_v("Person").property("name", "Alice").next().unwrap();
let vertex_id = vertex.id();
assert_eq!(graph.vertex_count(), 1);
let g2 = graph.gremlin(Arc::clone(&graph));
g2.v_id(vertex_id).drop().iterate();
assert_eq!(graph.vertex_count(), 0);
}
#[test]
fn cow_unified_traversal_drop_edge() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("Person", HashMap::new());
let bob = graph.add_vertex("Person", HashMap::new());
let edge = graph.add_edge(alice, bob, "KNOWS", HashMap::new()).unwrap();
assert_eq!(graph.edge_count(), 1);
let g = graph.gremlin(Arc::clone(&graph));
g.e_ids([edge]).drop().iterate();
assert_eq!(graph.edge_count(), 0);
assert_eq!(graph.vertex_count(), 2);
}
#[test]
fn cow_unified_traversal_v_returns_all_vertices() {
let graph = Arc::new(Graph::new());
graph.add_vertex("Person", HashMap::new());
graph.add_vertex("Person", HashMap::new());
graph.add_vertex("Software", HashMap::new());
let g = graph.gremlin(Arc::clone(&graph));
let results = g.v().to_list();
assert_eq!(results.len(), 3);
}
#[test]
fn cow_unified_traversal_v_id_returns_specific_vertex() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
graph.add_vertex("Person", HashMap::new());
let g = graph.gremlin(Arc::clone(&graph));
let results = g.v_id(alice).to_list();
assert_eq!(results.len(), 1);
assert_eq!(results[0].id(), alice);
}
#[test]
fn cow_unified_traversal_chained_steps() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let bob = graph.add_vertex(
"Person",
HashMap::from([("name".to_string(), Value::String("Bob".into()))]),
);
let software = graph.add_vertex("Software", HashMap::new());
graph.add_edge(alice, bob, "KNOWS", HashMap::new()).unwrap();
graph
.add_edge(alice, software, "CREATED", HashMap::new())
.unwrap();
graph
.add_edge(bob, software, "USES", HashMap::new())
.unwrap();
let g = graph.gremlin(Arc::clone(&graph));
let friends = g.v_id(alice).out_label("KNOWS").to_list();
assert_eq!(friends.len(), 1);
let g2 = graph.gremlin(Arc::clone(&graph));
let sw_related = g2.v_id(software).in_().has_label("Person").to_list();
assert_eq!(sw_related.len(), 2);
}
#[test]
fn cow_unified_traversal_count() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
g.add_v("Person").iterate();
g.add_v("Person").iterate();
g.add_v("Software").iterate();
let g2 = graph.gremlin(Arc::clone(&graph));
let total = g2.v().count();
assert_eq!(total, 3);
let g3 = graph.gremlin(Arc::clone(&graph));
let people = g3.v().has_label("Person").count();
assert_eq!(people, 2);
}
#[test]
fn cow_unified_traversal_has_next() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
assert!(!g.v().has_next());
let g2 = graph.gremlin(Arc::clone(&graph));
g2.add_v("Person").iterate();
let g3 = graph.gremlin(Arc::clone(&graph));
assert!(g3.v().has_next());
}
#[test]
fn cow_add_v_id_returns_integer() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let result = g.add_v("Person").property("name", "Alice").id().next();
assert!(result.is_some());
match result.unwrap() {
Value::Int(id) => assert!(id >= 0),
other => panic!("Expected Int, got {:?}", other),
}
assert_eq!(graph.vertex_count(), 1);
}
#[test]
fn cow_add_v_without_id_returns_vertex() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let result = g.add_v("Person").next();
assert!(result.is_some());
let _vertex_id = result.unwrap().id();
}
#[test]
fn cow_add_e_id_returns_integer() {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex("Person", HashMap::new());
let bob = graph.add_vertex("Person", HashMap::new());
let g = graph.gremlin(Arc::clone(&graph));
let result = g.add_e("KNOWS").from_id(alice).to_id(bob).next();
assert!(result.is_some());
let edge = result.unwrap();
let _edge_id = edge.id();
assert_eq!(graph.edge_count(), 1);
}
#[test]
fn cow_add_v_id_to_list_returns_integers() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let id1 = g.add_v("Person").id().next().unwrap();
let id2 = g.add_v("Person").id().next().unwrap();
let id3 = g.add_v("Software").id().next().unwrap();
assert!(matches!(id1, Value::Int(_)));
assert!(matches!(id2, Value::Int(_)));
assert!(matches!(id3, Value::Int(_)));
assert_ne!(id1, id2);
assert_ne!(id2, id3);
assert_eq!(graph.vertex_count(), 3);
}
#[test]
fn cow_add_v_id_can_be_used_for_edges() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let alice_id = match g.add_v("Person").property("name", "Alice").id().next() {
Some(Value::Int(id)) => VertexId(id as u64),
other => panic!("Expected Int, got {:?}", other),
};
let bob_id = match g.add_v("Person").property("name", "Bob").id().next() {
Some(Value::Int(id)) => VertexId(id as u64),
other => panic!("Expected Int, got {:?}", other),
};
g.add_e("KNOWS")
.from_id(alice_id)
.to_id(bob_id)
.property("since", 2020i64)
.iterate();
assert_eq!(graph.vertex_count(), 2);
assert_eq!(graph.edge_count(), 1);
let g2 = graph.gremlin(Arc::clone(&graph));
let alice_friends: Vec<Value> = g2
.v_id(alice_id)
.out_label("KNOWS")
.values("name")
.to_list();
assert_eq!(alice_friends.len(), 1);
assert_eq!(alice_friends[0], Value::String("Bob".to_string()));
}
fn create_typed_test_graph() -> Arc<Graph> {
let graph = Arc::new(Graph::new());
let alice = graph.add_vertex(
"person",
HashMap::from([("name".to_string(), Value::String("Alice".into()))]),
);
let bob = graph.add_vertex(
"person",
HashMap::from([("name".to_string(), Value::String("Bob".into()))]),
);
let charlie = graph.add_vertex(
"person",
HashMap::from([("name".to_string(), Value::String("Charlie".into()))]),
);
graph.add_edge(alice, bob, "knows", HashMap::new()).unwrap();
graph
.add_edge(bob, charlie, "knows", HashMap::new())
.unwrap();
graph
}
#[test]
fn cow_typed_vertex_next_returns_graph_vertex() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let vertex: Option<InMemoryVertex> = g.v().next();
assert!(vertex.is_some());
let v = vertex.unwrap();
assert!(v.label().is_some());
assert!(v.property("name").is_some());
}
#[test]
fn cow_typed_vertex_to_list_returns_vec_graph_vertex() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let vertices: Vec<InMemoryVertex> = g.v().to_list();
assert_eq!(vertices.len(), 3);
for v in &vertices {
assert!(v.label().is_some());
}
}
#[test]
fn cow_typed_vertex_one_returns_result_graph_vertex() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let result: Result<InMemoryVertex, interstellar::error::TraversalError> =
g.v().has_value("name", Value::String("Alice".into())).one();
assert!(result.is_ok());
let alice = result.unwrap();
assert_eq!(alice.property("name"), Some(Value::String("Alice".into())));
}
#[test]
fn cow_typed_vertex_to_set_returns_hashset_graph_vertex() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let vertices: HashSet<InMemoryVertex> = g.v().to_set();
assert_eq!(vertices.len(), 3);
}
#[test]
fn cow_typed_vertex_count_returns_u64() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let count: u64 = g.v().count();
assert_eq!(count, 3);
}
#[test]
fn cow_typed_edge_next_returns_graph_edge() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let edge: Option<InMemoryEdge> = g.e().next();
assert!(edge.is_some());
let e = edge.unwrap();
assert_eq!(e.label(), Some("knows".to_string()));
assert!(e.out_v().is_some());
assert!(e.in_v().is_some());
}
#[test]
fn cow_typed_edge_to_list_returns_vec_graph_edge() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let edges: Vec<InMemoryEdge> = g.e().to_list();
assert_eq!(edges.len(), 2);
for e in &edges {
assert_eq!(e.label(), Some("knows".to_string()));
}
}
#[test]
fn cow_typed_edge_one_returns_result_graph_edge() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex("person", HashMap::new());
let b = graph.add_vertex("person", HashMap::new());
graph.add_edge(a, b, "knows", HashMap::new()).unwrap();
let g = graph.gremlin(Arc::clone(&graph));
let result: Result<InMemoryEdge, interstellar::error::TraversalError> = g.e().one();
assert!(result.is_ok());
assert_eq!(result.unwrap().label(), Some("knows".to_string()));
}
#[test]
fn cow_typed_edge_to_set_returns_hashset_graph_edge() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let edges: HashSet<InMemoryEdge> = g.e().to_set();
assert_eq!(edges.len(), 2);
}
#[test]
fn cow_typed_edge_count_returns_u64() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let count: u64 = g.e().count();
assert_eq!(count, 2);
}
#[test]
fn cow_typed_scalar_next_returns_value() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let value: Option<Value> = g.v().values("name").next();
assert!(value.is_some());
assert!(matches!(value.unwrap(), Value::String(_)));
}
#[test]
fn cow_typed_scalar_to_list_returns_vec_value() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let values: Vec<Value> = g.v().values("name").to_list();
assert_eq!(values.len(), 3);
for v in &values {
assert!(matches!(v, Value::String(_)));
}
}
#[test]
fn cow_typed_scalar_count_returns_u64() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let count: u64 = g.v().values("name").count();
assert_eq!(count, 3);
}
#[test]
fn cow_typed_add_v_next_returns_graph_vertex() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let vertex: Option<InMemoryVertex> = g.add_v("person").property("name", "Test").next();
assert!(vertex.is_some());
let v = vertex.unwrap();
assert_eq!(v.label(), Some("person".to_string()));
assert_eq!(v.property("name"), Some(Value::String("Test".into())));
}
#[test]
fn cow_typed_add_v_to_list_returns_vec_graph_vertex() {
let graph = Arc::new(Graph::new());
let g = graph.gremlin(Arc::clone(&graph));
let vertices: Vec<InMemoryVertex> = g.add_v("person").to_list();
assert_eq!(vertices.len(), 1);
assert_eq!(vertices[0].label(), Some("person".to_string()));
}
#[test]
fn cow_typed_add_e_next_returns_graph_edge() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex("person", HashMap::new());
let b = graph.add_vertex("person", HashMap::new());
let g = graph.gremlin(Arc::clone(&graph));
let edge: Option<InMemoryEdge> = g.add_e("knows").from_id(a).to_id(b).next();
assert!(edge.is_some());
let e = edge.unwrap();
assert_eq!(e.label(), Some("knows".to_string()));
}
#[test]
fn cow_typed_add_e_to_list_returns_vec_graph_edge() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex("person", HashMap::new());
let b = graph.add_vertex("person", HashMap::new());
let g = graph.gremlin(Arc::clone(&graph));
let edges: Vec<InMemoryEdge> = g.add_e("knows").from_id(a).to_id(b).to_list();
assert_eq!(edges.len(), 1);
assert_eq!(edges[0].label(), Some("knows".to_string()));
}
#[test]
fn cow_typed_vertex_to_edge_transformation() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let edges: Vec<InMemoryEdge> = g.v().out_e().to_list();
assert_eq!(edges.len(), 2);
}
#[test]
fn cow_typed_edge_to_vertex_transformation() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let vertices: Vec<InMemoryVertex> = g.e().out_v().to_list();
assert_eq!(vertices.len(), 2);
}
#[test]
fn cow_typed_vertex_to_scalar_transformation() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let names: Vec<Value> = g.v().values("name").to_list();
assert_eq!(names.len(), 3);
}
#[test]
fn cow_typed_graph_vertex_methods() {
let graph = create_typed_test_graph();
let g = graph.gremlin(Arc::clone(&graph));
let alice: InMemoryVertex = g
.v()
.has_value("name", Value::String("Alice".into()))
.one()
.unwrap();
let _id: VertexId = alice.id();
let _label: Option<String> = alice.label();
let _prop: Option<Value> = alice.property("name");
let _props: HashMap<String, Value> = alice.properties();
let _exists: bool = alice.exists();
let friends: Vec<InMemoryVertex> = alice.out("knows").to_list();
assert_eq!(friends.len(), 1);
}
#[test]
fn cow_typed_graph_edge_methods() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex("person", HashMap::new());
let b = graph.add_vertex("person", HashMap::new());
graph.add_edge(a, b, "knows", HashMap::new()).unwrap();
let g = graph.gremlin(Arc::clone(&graph));
let edge: InMemoryEdge = g.e().one().unwrap();
let _id: EdgeId = edge.id();
let _label: Option<String> = edge.label();
let _out_v: Option<InMemoryVertex> = edge.out_v();
let _in_v: Option<InMemoryVertex> = edge.in_v();
let _both: Option<(InMemoryVertex, InMemoryVertex)> = edge.both_v();
let _props: HashMap<String, Value> = edge.properties();
let _exists: bool = edge.exists();
}