use crdt_graph::{
TwoPTwoPAddEdge, TwoPTwoPAddVertex, TwoPTwoPGraph, TwoPTwoPGraphError, TwoPTwoPId,
TwoPTwoPRemoveEdge, TwoPTwoPRemoveVertex, UpdateOperation,
};
type Id = u64;
#[derive(Clone, Debug)]
struct VA {
id: Id,
}
impl TwoPTwoPId<Id> for VA {
fn id(&self) -> &Id {
&self.id
}
}
impl TwoPTwoPAddVertex<Id> for VA {}
#[derive(Clone, Debug)]
struct VR {
id: Id,
add_vertex_id: Id,
}
impl TwoPTwoPId<Id> for VR {
fn id(&self) -> &Id {
&self.id
}
}
impl TwoPTwoPRemoveVertex<Id> for VR {
fn add_vertex_id(&self) -> &Id {
&self.add_vertex_id
}
}
#[derive(Clone, Debug)]
struct EA {
id: Id,
source: Id,
target: Id,
}
impl TwoPTwoPId<Id> for EA {
fn id(&self) -> &Id {
&self.id
}
}
impl TwoPTwoPAddEdge<Id> for EA {
fn source(&self) -> &Id {
&self.source
}
fn target(&self) -> &Id {
&self.target
}
}
#[derive(Clone, Debug)]
struct ER {
id: Id,
add_edge_id: Id,
}
impl TwoPTwoPId<Id> for ER {
fn id(&self) -> &Id {
&self.id
}
}
impl TwoPTwoPRemoveEdge<Id> for ER {
fn add_edge_id(&self) -> &Id {
&self.add_edge_id
}
}
type TestGraph = TwoPTwoPGraph<VA, VR, EA, ER, Id>;
fn new_graph() -> TestGraph {
TwoPTwoPGraph::new()
}
#[test]
fn add_vertex_and_lookup() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
assert!(g.lookup_vertex(&1));
assert!(!g.lookup_vertex(&99));
}
#[test]
fn add_edge_and_lookup() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
let pg = g.generate_petgraph();
assert_eq!(pg.node_count(), 2);
assert_eq!(pg.edge_count(), 1);
}
#[test]
fn remove_vertex_and_lookup() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
assert!(g.lookup_vertex(&1));
g.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap();
assert!(!g.lookup_vertex(&1));
}
#[test]
fn remove_edge() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
g.prepare(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap();
let pg = g.generate_petgraph();
assert_eq!(pg.node_count(), 2);
assert_eq!(pg.edge_count(), 0);
}
#[test]
fn add_duplicate_vertex_fails() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
let err = g
.prepare(UpdateOperation::AddVertex(VA { id: 1 }))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexAlreadyExists(1)));
}
#[test]
fn add_duplicate_edge_fails() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
let err = g
.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::EdgeAlreadyExists(10)));
}
#[test]
fn remove_vertex_twice_fails() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap();
let err = g
.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexDoesNotExists(1)));
}
#[test]
fn remove_edge_twice_fails() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
g.prepare(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap();
let err = g
.prepare(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::EdgeDoesNotExists(10)));
}
#[test]
fn add_edge_source_missing() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
let err = g
.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexDoesNotExists(1)));
}
#[test]
fn add_edge_target_missing() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
let err = g
.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexDoesNotExists(2)));
}
#[test]
fn remove_nonexistent_vertex() {
let mut g = new_graph();
let err = g
.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexDoesNotExists(1)));
}
#[test]
fn remove_vertex_with_active_edge_fails() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
let err = g
.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexHasEdge(1, 10)));
}
#[test]
fn remove_vertex_after_edge_removed_succeeds() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
g.prepare(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap();
g.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap();
assert!(!g.lookup_vertex(&1));
}
#[test]
fn remove_nonexistent_edge() {
let mut g = new_graph();
let err = g
.prepare(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::EdgeDoesNotExists(10)));
}
#[test]
fn downstream_add_vertex() {
let mut g = new_graph();
g.apply_downstream(UpdateOperation::AddVertex(VA { id: 1 }))
.unwrap();
assert!(g.lookup_vertex(&1));
}
#[test]
fn downstream_add_edge_skips_vertex_check() {
let mut g = new_graph();
g.apply_downstream(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
let pg = g.generate_petgraph();
assert_eq!(pg.edge_count(), 0);
}
#[test]
fn downstream_remove_vertex_requires_add_delivered() {
let mut g = new_graph();
let err = g
.apply_downstream(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::AddVertexNotDelivered(1)));
}
#[test]
fn downstream_remove_vertex_after_add_delivered() {
let mut g = new_graph();
g.apply_downstream(UpdateOperation::AddVertex(VA { id: 1 }))
.unwrap();
g.apply_downstream(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap();
assert!(!g.lookup_vertex(&1));
}
#[test]
fn downstream_remove_edge_requires_add_delivered() {
let mut g = new_graph();
let err = g
.apply_downstream(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::AddEdgeNotDelivered(10)));
}
#[test]
fn downstream_remove_edge_after_add_delivered() {
let mut g = new_graph();
g.apply_downstream(UpdateOperation::AddVertex(VA { id: 1 }))
.unwrap();
g.apply_downstream(UpdateOperation::AddVertex(VA { id: 2 }))
.unwrap();
g.apply_downstream(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
g.apply_downstream(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap();
let pg = g.generate_petgraph();
assert_eq!(pg.edge_count(), 0);
}
#[test]
fn two_replica_add_vertex_sync() {
let mut replica_a = new_graph();
let mut replica_b = new_graph();
let op = replica_a
.prepare(UpdateOperation::AddVertex(VA { id: 1 }))
.unwrap();
replica_b.apply_downstream(op).unwrap();
assert!(replica_a.lookup_vertex(&1));
assert!(replica_b.lookup_vertex(&1));
}
#[test]
fn two_replica_full_lifecycle() {
let mut ra = new_graph();
let mut rb = new_graph();
let op1 = ra
.prepare(UpdateOperation::AddVertex(VA { id: 1 }))
.unwrap();
let op2 = ra
.prepare(UpdateOperation::AddVertex(VA { id: 2 }))
.unwrap();
rb.apply_downstream(op1).unwrap();
rb.apply_downstream(op2).unwrap();
let op3 = rb
.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
ra.apply_downstream(op3).unwrap();
assert_eq!(ra.generate_petgraph().edge_count(), 1);
assert_eq!(rb.generate_petgraph().edge_count(), 1);
let op4 = ra
.prepare(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap();
let op5 = ra
.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap();
rb.apply_downstream(op4).unwrap();
rb.apply_downstream(op5).unwrap();
assert!(!ra.lookup_vertex(&1));
assert!(!rb.lookup_vertex(&1));
assert_eq!(ra.generate_petgraph().edge_count(), 0);
assert_eq!(rb.generate_petgraph().edge_count(), 0);
}
#[test]
fn self_loop_edge() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 1,
}))
.unwrap();
let pg = g.generate_petgraph();
assert_eq!(pg.edge_count(), 1);
}
#[test]
fn self_loop_prevents_vertex_removal() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 1,
}))
.unwrap();
let err = g
.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexHasEdge(1, 10)));
}
#[test]
fn remove_vertex_target_side_of_edge_blocked() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
let err = g
.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 2,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexHasEdge(2, 10)));
}
#[test]
fn generate_petgraph_empty() {
let g = new_graph();
let pg = g.generate_petgraph();
assert_eq!(pg.node_count(), 0);
assert_eq!(pg.edge_count(), 0);
}
#[test]
fn generate_petgraph_excludes_removed() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 3 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 11,
source: 2,
target: 3,
}))
.unwrap();
g.prepare(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap();
g.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap();
let pg = g.generate_petgraph();
assert_eq!(pg.node_count(), 2); assert_eq!(pg.edge_count(), 1); }
#[test]
fn update_operation_backward_compat() {
let mut g = new_graph();
g.update_operation(UpdateOperation::AddVertex(VA { id: 1 }))
.unwrap();
assert!(g.lookup_vertex(&1));
}
#[test]
fn downstream_out_of_causal_order_remove_before_add() {
let mut remote = new_graph();
let err = remote
.apply_downstream(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::AddVertexNotDelivered(1)));
remote
.apply_downstream(UpdateOperation::AddVertex(VA { id: 1 }))
.unwrap();
remote
.apply_downstream(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap();
assert!(!remote.lookup_vertex(&1));
}
#[test]
fn downstream_out_of_causal_order_remove_edge_before_add_edge() {
let mut remote = new_graph();
let err = remote
.apply_downstream(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::AddEdgeNotDelivered(10)));
remote
.apply_downstream(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
remote
.apply_downstream(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap();
}
#[test]
fn multiple_edges_between_same_vertices() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 11,
source: 1,
target: 2,
}))
.unwrap();
let pg = g.generate_petgraph();
assert_eq!(pg.edge_count(), 2);
}
#[test]
fn remove_one_of_multiple_edges() {
let mut g = new_graph();
g.prepare(UpdateOperation::AddVertex(VA { id: 1 })).unwrap();
g.prepare(UpdateOperation::AddVertex(VA { id: 2 })).unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 10,
source: 1,
target: 2,
}))
.unwrap();
g.prepare(UpdateOperation::AddEdge(EA {
id: 11,
source: 1,
target: 2,
}))
.unwrap();
g.prepare(UpdateOperation::RemoveEdge(ER {
id: 200,
add_edge_id: 10,
}))
.unwrap();
let pg = g.generate_petgraph();
assert_eq!(pg.edge_count(), 1);
let err = g
.prepare(UpdateOperation::RemoveVertex(VR {
id: 100,
add_vertex_id: 1,
}))
.unwrap_err();
assert!(matches!(err, TwoPTwoPGraphError::VertexHasEdge(..)));
}
#[test]
fn prepare_returns_cloned_operation() {
let mut g = new_graph();
let op = UpdateOperation::AddVertex(VA { id: 42 });
let returned = g.prepare(op).unwrap();
match returned {
UpdateOperation::AddVertex(va) => assert_eq!(*va.id(), 42),
_ => panic!("expected AddVertex"),
}
}