use selene_core::{EdgeId, GraphId, LabelSet, NodeId, PropertyMap, db_string};
use crate::store::RowIndex;
use crate::{SeleneGraph, SharedGraph};
#[test]
fn id_row_maps_round_trip_for_all_alive() {
let shared = SharedGraph::new(GraphId::new(1));
let a = selene_core::db_string("inc2.a").unwrap();
let b = selene_core::db_string("inc2.b").unwrap();
let mut txn = shared.begin_write();
{
let mut m = txn.mutator();
let n0 = m
.create_node(LabelSet::single(a.clone()), PropertyMap::new())
.unwrap();
let n1 = m
.create_node(LabelSet::single(a.clone()), PropertyMap::new())
.unwrap();
let n2 = m
.create_node(LabelSet::single(b), PropertyMap::new())
.unwrap();
m.create_edge(a.clone(), n0, n1, PropertyMap::new())
.unwrap();
m.create_edge(a.clone(), n1, n2, PropertyMap::new())
.unwrap();
m.create_edge(a, n0, n2, PropertyMap::new()).unwrap();
m.delete_node(n1).unwrap();
}
txn.commit().unwrap();
let g = shared.read();
for row in g.node_store.alive.iter() {
let id = *g
.node_store
.row_to_id
.get(row as usize)
.expect("row_to_id in-bounds");
assert_ne!(
id,
NodeId::TOMBSTONE,
"alive node row {row} has tombstone id"
);
assert_eq!(
g.node_id_to_row.get(&id).copied(),
Some(RowIndex::new(row)),
"id->row map disagrees with row_to_id for alive {id}"
);
}
assert!(!g.node_store.alive.contains(1));
assert_eq!(
g.node_id_to_row.get(&NodeId::new(2)).copied(),
Some(RowIndex::new(1)),
"deleted node id must stay mapped (NotAlive, not NotFound)"
);
assert_eq!(
*g.node_store.row_to_id.get(1).unwrap(),
NodeId::new(2),
"deleted row keeps its real external id in row_to_id"
);
for row in g.edge_store.alive.iter() {
let id = *g
.edge_store
.row_to_id
.get(row as usize)
.expect("row_to_id in-bounds");
assert_ne!(
id,
selene_core::EdgeId::TOMBSTONE,
"alive edge row {row} has tombstone id"
);
assert_eq!(
g.edge_id_to_row.get(&id).copied(),
Some(RowIndex::new(row)),
"id->row map disagrees with row_to_id for alive {id}"
);
}
assert!(g.edge_store.alive.contains(2));
assert_eq!(
g.edge_id_to_row.get(&selene_core::EdgeId::new(1)).copied(),
Some(RowIndex::new(0))
);
assert_eq!(
g.edge_id_to_row.get(&selene_core::EdgeId::new(2)).copied(),
Some(RowIndex::new(1))
);
assert_eq!(
*g.edge_store.row_to_id.get(0).unwrap(),
selene_core::EdgeId::new(1)
);
assert_eq!(
*g.edge_store.row_to_id.get(1).unwrap(),
selene_core::EdgeId::new(2)
);
assert_eq!(g.node_store.row_to_id.len(), g.node_store.len());
assert_eq!(g.edge_store.row_to_id.len(), g.edge_store.len());
}
#[test]
fn non_identity_map_read_paths_resolve_by_map() {
let label = selene_core::db_string("ni.node").unwrap();
let elabel = selene_core::db_string("ni.edge").unwrap();
let mut built = SeleneGraph::new(GraphId::new(1));
built
.node_store
.labels
.push(LabelSet::single(label.clone()));
built.node_store.properties.push(PropertyMap::new());
built.node_store.row_to_id.push(NodeId::new(5));
built
.node_store
.labels
.push(LabelSet::single(label.clone()));
built.node_store.properties.push(PropertyMap::new());
built.node_store.row_to_id.push(NodeId::new(8));
built.node_store.alive_mut().insert(0);
built.node_store.alive_mut().insert(1);
built.edge_store.label.push(elabel.clone());
built.edge_store.source.push(NodeId::new(5));
built.edge_store.target.push(NodeId::new(8));
built.edge_store.properties.push(PropertyMap::new());
built.edge_store.row_to_id.push(EdgeId::new(3));
built.edge_store.alive_mut().insert(0);
built.meta.next_node_id = 9;
built.meta.next_edge_id = 4;
let shared = SharedGraph::from_graph(built);
let g = shared.read();
assert_eq!(g.row_for_node_id(NodeId::new(5)), Some(RowIndex::new(0)));
assert_eq!(g.row_for_node_id(NodeId::new(8)), Some(RowIndex::new(1)));
assert_eq!(g.node_id_for_row(RowIndex::new(0)), Some(NodeId::new(5)));
assert_eq!(g.node_id_for_row(RowIndex::new(1)), Some(NodeId::new(8)));
assert_ne!(g.row_for_node_id(NodeId::new(5)), Some(RowIndex::new(4)));
assert!(g.is_node_alive(NodeId::new(5)));
assert!(g.is_node_alive(NodeId::new(8)));
assert!(!g.is_node_alive(NodeId::new(1)));
assert!(g.node_labels(NodeId::new(1)).is_none());
assert_eq!(
g.node_labels(NodeId::new(5))
.unwrap()
.iter()
.cloned()
.collect::<Vec<_>>(),
vec![label.clone()]
);
let labelled = g.nodes_with_label(&label).unwrap();
assert!(labelled.contains(0) && labelled.contains(1));
assert_eq!(g.row_for_edge_id(EdgeId::new(3)), Some(RowIndex::new(0)));
assert_eq!(g.edge_id_for_row(RowIndex::new(0)), Some(EdgeId::new(3)));
assert!(g.is_edge_alive(EdgeId::new(3)));
assert!(!g.is_edge_alive(EdgeId::new(1)));
assert_eq!(
g.edge_endpoints(EdgeId::new(3)),
Some((NodeId::new(5), NodeId::new(8)))
);
assert_eq!(g.edge_label(EdgeId::new(3)).unwrap(), &elabel);
assert!(
g.outgoing_edges(NodeId::new(5))
.unwrap()
.iter()
.any(|e| e.neighbor == NodeId::new(8) && e.edge_id == EdgeId::new(3))
);
assert!(
g.incoming_edges(NodeId::new(8))
.unwrap()
.iter()
.any(|e| e.neighbor == NodeId::new(5) && e.edge_id == EdgeId::new(3))
);
}
#[test]
fn create_node_past_u32_id_space_succeeds_with_append_rows() {
let mut graph = SeleneGraph::new(GraphId::new(1));
graph.meta.next_node_id = u32::MAX as u64 + 2;
let shared = SharedGraph::from_graph(graph);
let id = {
let mut txn = shared.begin_write();
let id = txn
.mutator()
.create_node(LabelSet::new(), PropertyMap::new())
.expect("create past u32 id-space succeeds with append rows");
txn.commit().unwrap();
id
};
assert_eq!(id.get(), u32::MAX as u64 + 2);
let g = shared.read();
assert_eq!(g.row_for_node_id(id).map(|r| r.get()), Some(0));
assert!(g.is_node_alive(id));
}
#[test]
fn create_edge_past_u32_id_space_succeeds_with_append_rows() {
let mut graph = SeleneGraph::new(GraphId::new(1));
graph.meta.next_edge_id = u32::MAX as u64 + 2;
let shared = SharedGraph::from_graph(graph);
let edge_id = {
let mut txn = shared.begin_write();
let id = {
let mut mutator = txn.mutator();
let source = mutator
.create_node(LabelSet::new(), PropertyMap::new())
.expect("create_node ok");
let target = mutator
.create_node(LabelSet::new(), PropertyMap::new())
.expect("create_node ok");
mutator
.create_edge(
db_string("edge.overflow").unwrap(),
source,
target,
PropertyMap::new(),
)
.expect("create edge past u32 id-space succeeds with append rows")
};
txn.commit().unwrap();
id
};
assert_eq!(edge_id.get(), u32::MAX as u64 + 2);
let g = shared.read();
assert_eq!(g.row_for_edge_id(edge_id).map(|r| r.get()), Some(0));
assert!(g.is_edge_alive(edge_id));
}