use std::collections::HashMap;
use crate::error::StorageError;
use crate::storage::{Edge, Vertex};
use crate::value::{EdgeId, Value, VertexId};
pub trait GraphAccess: Send + Sync + Clone + 'static {
fn get_vertex(&self, id: VertexId) -> Option<Vertex>;
fn get_edge(&self, id: EdgeId) -> Option<Edge>;
fn out_edge_ids(&self, vertex: VertexId) -> Vec<EdgeId>;
fn in_edge_ids(&self, vertex: VertexId) -> Vec<EdgeId>;
fn set_vertex_property(
&self,
id: VertexId,
key: &str,
value: Value,
) -> Result<(), StorageError>;
fn set_edge_property(&self, id: EdgeId, key: &str, value: Value) -> Result<(), StorageError>;
fn add_edge(
&self,
src: VertexId,
dst: VertexId,
label: &str,
properties: HashMap<String, Value>,
) -> Result<EdgeId, StorageError>;
fn remove_vertex(&self, id: VertexId) -> Result<(), StorageError>;
fn remove_edge(&self, id: EdgeId) -> Result<(), StorageError>;
}
#[cfg(test)]
mod tests {
use super::*;
use crate::storage::Graph;
use std::sync::Arc;
#[test]
fn arc_graph_implements_graph_access() {
let graph = Arc::new(Graph::new());
let id = graph.add_vertex(
"person",
HashMap::from([("name".to_string(), "Alice".into())]),
);
let v = graph.get_vertex(id).unwrap();
assert_eq!(v.label, "person");
assert_eq!(
v.properties.get("name"),
Some(&Value::String("Alice".to_string()))
);
graph
.set_vertex_property(id, "age", Value::Int(30))
.unwrap();
let v = graph.get_vertex(id).unwrap();
assert_eq!(v.properties.get("age"), Some(&Value::Int(30)));
}
#[test]
fn arc_graph_edge_operations() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex("person", HashMap::new());
let b = graph.add_vertex("person", HashMap::new());
let edge_id = GraphAccess::add_edge(&graph, a, b, "knows", HashMap::new()).unwrap();
assert!(graph.get_edge(edge_id).is_some());
let out_edges = graph.out_edge_ids(a);
assert_eq!(out_edges.len(), 1);
assert_eq!(out_edges[0], edge_id);
let in_edges = graph.in_edge_ids(b);
assert_eq!(in_edges.len(), 1);
assert_eq!(in_edges[0], edge_id);
graph
.set_edge_property(edge_id, "since", Value::Int(2020))
.unwrap();
let e = graph.get_edge(edge_id).unwrap();
assert_eq!(e.properties.get("since"), Some(&Value::Int(2020)));
graph.remove_edge(edge_id).unwrap();
assert!(graph.get_edge(edge_id).is_none());
}
#[test]
fn arc_graph_vertex_removal() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex("person", HashMap::new());
let b = graph.add_vertex("person", HashMap::new());
let _edge_id = GraphAccess::add_edge(&graph, a, b, "knows", HashMap::new()).unwrap();
graph.remove_vertex(a).unwrap();
assert!(graph.get_vertex(a).is_none());
assert_eq!(graph.out_edge_ids(a).len(), 0);
}
#[test]
fn arc_graph_error_on_nonexistent_vertex() {
let graph = Arc::new(Graph::new());
let result = graph.set_vertex_property(VertexId(999), "name", Value::from("Bob"));
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
let result = graph.remove_vertex(VertexId(999));
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
}
#[test]
fn arc_graph_error_on_nonexistent_edge() {
let graph = Arc::new(Graph::new());
let result = graph.set_edge_property(EdgeId(999), "since", Value::from(2020i64));
assert!(matches!(result, Err(StorageError::EdgeNotFound(_))));
let result = graph.remove_edge(EdgeId(999));
assert!(matches!(result, Err(StorageError::EdgeNotFound(_))));
}
#[test]
fn arc_graph_add_edge_error_on_nonexistent_vertices() {
let graph = Arc::new(Graph::new());
let a = graph.add_vertex("person", HashMap::new());
let result = GraphAccess::add_edge(&graph, a, VertexId(999), "knows", HashMap::new());
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
let result = GraphAccess::add_edge(&graph, VertexId(999), a, "knows", HashMap::new());
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
}
#[test]
fn arc_graph_clone_and_thread_safety() {
use std::thread;
let graph = Arc::new(Graph::new());
let id = graph.add_vertex("person", HashMap::new());
let graph2 = Arc::clone(&graph);
let handle = thread::spawn(move || {
let v = graph2.get_vertex(id);
assert!(v.is_some());
});
handle.join().unwrap();
assert!(graph.get_vertex(id).is_some());
}
#[cfg(feature = "mmap")]
mod mmap_tests {
use super::*;
use crate::storage::CowMmapGraph;
use tempfile::tempdir;
fn temp_db_path() -> (tempfile::TempDir, std::path::PathBuf) {
let dir = tempdir().unwrap();
let path = dir.path().join("test.db");
(dir, path)
}
#[test]
fn arc_mmap_graph_implements_graph_access() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let id = graph
.add_vertex(
"person",
HashMap::from([("name".to_string(), "Alice".into())]),
)
.unwrap();
let v = graph.get_vertex(id).unwrap();
assert_eq!(v.label, "person");
assert_eq!(
v.properties.get("name"),
Some(&Value::String("Alice".to_string()))
);
graph
.set_vertex_property(id, "age", Value::Int(30))
.unwrap();
let v = graph.get_vertex(id).unwrap();
assert_eq!(v.properties.get("age"), Some(&Value::Int(30)));
}
#[test]
fn arc_mmap_graph_edge_operations() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let a = graph.add_vertex("person", HashMap::new()).unwrap();
let b = graph.add_vertex("person", HashMap::new()).unwrap();
let edge_id = GraphAccess::add_edge(&graph, a, b, "knows", HashMap::new()).unwrap();
assert!(graph.get_edge(edge_id).is_some());
let out_edges = graph.out_edge_ids(a);
assert_eq!(out_edges.len(), 1);
assert_eq!(out_edges[0], edge_id);
let in_edges = graph.in_edge_ids(b);
assert_eq!(in_edges.len(), 1);
assert_eq!(in_edges[0], edge_id);
graph
.set_edge_property(edge_id, "since", Value::Int(2020))
.unwrap();
let e = graph.get_edge(edge_id).unwrap();
assert_eq!(e.properties.get("since"), Some(&Value::Int(2020)));
graph.remove_edge(edge_id).unwrap();
assert!(graph.get_edge(edge_id).is_none());
}
#[test]
fn arc_mmap_graph_vertex_removal() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let a = graph.add_vertex("person", HashMap::new()).unwrap();
let b = graph.add_vertex("person", HashMap::new()).unwrap();
let _edge_id = GraphAccess::add_edge(&graph, a, b, "knows", HashMap::new()).unwrap();
graph.remove_vertex(a).unwrap();
assert!(graph.get_vertex(a).is_none());
assert_eq!(graph.out_edge_ids(a).len(), 0);
}
#[test]
fn arc_mmap_graph_error_on_nonexistent_vertex() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let result = graph.set_vertex_property(VertexId(999), "name", Value::from("Bob"));
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
let result = graph.remove_vertex(VertexId(999));
assert!(matches!(result, Err(StorageError::VertexNotFound(_))));
}
#[test]
fn arc_mmap_graph_error_on_nonexistent_edge() {
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let result = graph.set_edge_property(EdgeId(999), "since", Value::from(2020i64));
assert!(matches!(result, Err(StorageError::EdgeNotFound(_))));
let result = graph.remove_edge(EdgeId(999));
assert!(matches!(result, Err(StorageError::EdgeNotFound(_))));
}
#[test]
fn arc_mmap_graph_clone_and_thread_safety() {
use std::thread;
let (_dir, path) = temp_db_path();
let graph = Arc::new(CowMmapGraph::open(&path).unwrap());
let id = graph.add_vertex("person", HashMap::new()).unwrap();
let graph2 = Arc::clone(&graph);
let handle = thread::spawn(move || {
let v = graph2.get_vertex(id);
assert!(v.is_some());
});
handle.join().unwrap();
assert!(graph.get_vertex(id).is_some());
}
}
}