use aletheiadb::AletheiaDB;
use aletheiadb::Error;
use aletheiadb::WriteOps;
use aletheiadb::core::graph::{Edge, Node};
use aletheiadb::core::id::{EdgeId, NodeId};
use aletheiadb::core::property::PropertyMapBuilder;
#[test]
fn test_time_travel_query() {
let db = AletheiaDB::new().unwrap();
let props_v1 = PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build();
let node_id = db.create_node("Person", props_v1).unwrap();
std::thread::sleep(std::time::Duration::from_micros(100));
let t1 = aletheiadb::core::temporal::time::now();
let historical_node = db.get_node_at_time(node_id, t1, t1).unwrap();
assert_eq!(
historical_node.get_property("age").and_then(|v| v.as_int()),
Some(30)
);
let current_node = db.get_node(node_id).unwrap();
assert_eq!(
current_node.get_property("age").and_then(|v| v.as_int()),
Some(30)
);
}
#[test]
fn test_time_travel_after_deletion() {
let db = AletheiaDB::new().unwrap();
let props = PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build();
let node_id = db.create_node("Person", props).unwrap();
std::thread::sleep(std::time::Duration::from_micros(100));
let t_after_create = aletheiadb::core::temporal::time::now();
std::thread::sleep(std::time::Duration::from_micros(100));
db.write(|tx| {
tx.delete_node(node_id)?;
Ok::<_, Error>(())
})
.unwrap();
std::thread::sleep(std::time::Duration::from_micros(100));
let t_after_delete = aletheiadb::core::temporal::time::now();
let result = db.get_node_at_time(node_id, t_after_delete, t_after_delete);
assert!(
result.is_err(),
"Expected NodeNotFound after deletion, but got: {:?}",
result
);
let result = db.get_node_at_time(node_id, t_after_create, t_after_create);
assert!(
result.is_ok(),
"Expected to find node before deletion, but got: {:?}",
result
);
let node = result.unwrap();
assert_eq!(
node.get_property("name").and_then(|v| v.as_str()),
Some("Alice")
);
}
#[test]
fn test_temporal_index_deletion_integration() {
let db = AletheiaDB::new().unwrap();
let props = PropertyMapBuilder::new().insert("name", "TestNode").build();
let node_id = db.create_node("TestLabel", props).unwrap();
std::thread::sleep(std::time::Duration::from_micros(100));
let t_after_create = aletheiadb::core::temporal::time::now();
let result = db.get_node_at_time(node_id, t_after_create, t_after_create);
assert!(
result.is_ok(),
"Node should be queryable after creation: {:?}",
result
);
let version_ids = db.__test_temporal_indexes().find_node_version_at_point(
node_id,
t_after_create,
t_after_create,
);
assert!(
!version_ids.is_empty(),
"Temporal index should return candidates for existing node"
);
std::thread::sleep(std::time::Duration::from_micros(100));
db.write(|tx| {
tx.delete_node(node_id)?;
Ok::<_, Error>(())
})
.unwrap();
std::thread::sleep(std::time::Duration::from_micros(100));
let t_after_delete = aletheiadb::core::temporal::time::now();
let version_ids_after = db.__test_temporal_indexes().find_node_version_at_point(
node_id,
t_after_delete,
t_after_delete,
);
let result = db.get_node_at_time(node_id, t_after_delete, t_after_delete);
assert!(
result.is_err(),
"Query after deletion should fail. Temporal index returned {:?} candidates, \
but historical visibility check should reject them. Got: {:?}",
version_ids_after.len(),
result
);
let result = db.get_node_at_time(node_id, t_after_create, t_after_create);
assert!(
result.is_ok(),
"Query before deletion should succeed: {:?}",
result
);
}
#[test]
fn test_get_nodes_at_time_basic() {
let db = AletheiaDB::new().unwrap();
let props1 = PropertyMapBuilder::new()
.insert("name", "Alice")
.insert("age", 30i64)
.build();
let props2 = PropertyMapBuilder::new()
.insert("name", "Bob")
.insert("age", 25i64)
.build();
let props3 = PropertyMapBuilder::new()
.insert("name", "Charlie")
.insert("age", 35i64)
.build();
let node1 = db.create_node("Person", props1).unwrap();
let node2 = db.create_node("Person", props2).unwrap();
let node3 = db.create_node("Person", props3).unwrap();
let t1 = db.__test_current_timestamp();
let node_ids = vec![node1, node2, node3];
let results = db.get_nodes_at_time(&node_ids, t1, t1).unwrap();
assert_eq!(results.len(), 3);
let results_map: std::collections::HashMap<NodeId, Node> = results
.into_iter()
.map(|(id, node_opt)| (id, node_opt.expect("Node should exist")))
.collect();
assert_eq!(results_map.len(), 3);
let n1 = results_map.get(&node1).unwrap();
assert_eq!(n1.id, node1);
assert_eq!(
n1.get_property("name").and_then(|v| v.as_str()),
Some("Alice")
);
let n2 = results_map.get(&node2).unwrap();
assert_eq!(n2.id, node2);
assert_eq!(
n2.get_property("name").and_then(|v| v.as_str()),
Some("Bob")
);
let n3 = results_map.get(&node3).unwrap();
assert_eq!(n3.id, node3);
assert_eq!(
n3.get_property("name").and_then(|v| v.as_str()),
Some("Charlie")
);
}
#[test]
fn test_get_nodes_at_time_mixed_results() {
let db = AletheiaDB::new().unwrap();
let node1 = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let node2 = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Bob").build(),
)
.unwrap();
let t1 = db.__test_current_timestamp();
let non_existent = NodeId::new(9999).unwrap();
let node_ids = vec![node1, non_existent, node2];
let results = db.get_nodes_at_time(&node_ids, t1, t1).unwrap();
assert_eq!(results.len(), 3);
assert!(results[0].1.is_some());
assert_eq!(results[0].0, node1);
assert!(results[1].1.is_none());
assert_eq!(results[1].0, non_existent);
assert!(results[2].1.is_some());
assert_eq!(results[2].0, node2);
}
#[test]
fn test_get_nodes_at_time_empty_batch() {
let db = AletheiaDB::new().unwrap();
let t1 = db.__test_current_timestamp();
let results = db.get_nodes_at_time(&[], t1, t1).unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_get_nodes_at_time_after_deletion() {
let db = AletheiaDB::new().unwrap();
let node1 = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let node2 = db
.create_node(
"Person",
PropertyMapBuilder::new().insert("name", "Bob").build(),
)
.unwrap();
let t_after_create = db.__test_current_timestamp();
db.write(|tx| {
tx.delete_node(node1)?;
Ok::<_, Error>(())
})
.unwrap();
let t_after_delete = db.__test_current_timestamp();
let node_ids = vec![node1, node2];
let results = db
.get_nodes_at_time(&node_ids, t_after_delete, t_after_delete)
.unwrap();
assert_eq!(results.len(), 2);
assert!(results[0].1.is_none()); assert!(results[1].1.is_some());
let results = db
.get_nodes_at_time(&node_ids, t_after_create, t_after_create)
.unwrap();
assert_eq!(results.len(), 2);
assert!(results[0].1.is_some()); assert!(results[1].1.is_some()); }
#[test]
fn test_get_edges_at_time_basic() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let charlie = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge1 = db
.create_edge(
alice,
bob,
"KNOWS",
PropertyMapBuilder::new().insert("since", 2020i64).build(),
)
.unwrap();
let edge2 = db
.create_edge(
bob,
charlie,
"KNOWS",
PropertyMapBuilder::new().insert("since", 2021i64).build(),
)
.unwrap();
let edge3 = db
.create_edge(
alice,
charlie,
"WORKS_WITH",
PropertyMapBuilder::new().insert("since", 2022i64).build(),
)
.unwrap();
let t1 = db.__test_current_timestamp();
let edge_ids = vec![edge1, edge2, edge3];
let results = db.get_edges_at_time(&edge_ids, t1, t1).unwrap();
assert_eq!(results.len(), 3);
let results_map: std::collections::HashMap<EdgeId, Edge> = results
.into_iter()
.map(|(id, edge_opt)| (id, edge_opt.expect("Edge should exist")))
.collect();
assert_eq!(results_map.len(), 3);
let e1 = results_map.get(&edge1).unwrap();
assert_eq!(e1.id, edge1);
assert_eq!(e1.source, alice);
assert_eq!(e1.target, bob);
assert_eq!(
e1.get_property("since").and_then(|v| v.as_int()),
Some(2020)
);
let e2 = results_map.get(&edge2).unwrap();
assert_eq!(e2.id, edge2);
assert_eq!(e2.source, bob);
assert_eq!(e2.target, charlie);
assert_eq!(
e2.get_property("since").and_then(|v| v.as_int()),
Some(2021)
);
let e3 = results_map.get(&edge3).unwrap();
assert_eq!(e3.id, edge3);
assert_eq!(e3.source, alice);
assert_eq!(e3.target, charlie);
assert_eq!(
e3.get_property("since").and_then(|v| v.as_int()),
Some(2022)
);
}
#[test]
fn test_get_edges_at_time_mixed_results() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge1 = db
.create_edge(
alice,
bob,
"KNOWS",
PropertyMapBuilder::new().insert("since", 2020i64).build(),
)
.unwrap();
let t1 = db.__test_current_timestamp();
let non_existent = EdgeId::new(9999).unwrap();
let edge_ids = vec![edge1, non_existent];
let results = db.get_edges_at_time(&edge_ids, t1, t1).unwrap();
assert_eq!(results.len(), 2);
assert!(results[0].1.is_some());
assert_eq!(results[0].0, edge1);
assert!(results[1].1.is_none());
assert_eq!(results[1].0, non_existent);
}
#[test]
fn test_get_edges_at_time_empty_batch() {
let db = AletheiaDB::new().unwrap();
let t1 = db.__test_current_timestamp();
let results = db.get_edges_at_time(&[], t1, t1).unwrap();
assert_eq!(results.len(), 0);
}
#[test]
fn test_get_edges_at_time_after_deletion() {
let db = AletheiaDB::new().unwrap();
let alice = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let bob = db
.create_node("Person", PropertyMapBuilder::new().build())
.unwrap();
let edge1 = db
.create_edge(alice, bob, "KNOWS", PropertyMapBuilder::new().build())
.unwrap();
let edge2 = db
.create_edge(bob, alice, "WORKS_WITH", PropertyMapBuilder::new().build())
.unwrap();
let t_after_create = db.__test_current_timestamp();
db.write(|tx| {
tx.delete_edge(edge1)?;
Ok::<_, Error>(())
})
.unwrap();
let t_after_delete = db.__test_current_timestamp();
let edge_ids = vec![edge1, edge2];
let results = db
.get_edges_at_time(&edge_ids, t_after_delete, t_after_delete)
.unwrap();
assert_eq!(results.len(), 2);
assert!(results[0].1.is_none()); assert!(results[1].1.is_some());
let results = db
.get_edges_at_time(&edge_ids, t_after_create, t_after_create)
.unwrap();
assert_eq!(results.len(), 2);
assert!(results[0].1.is_some()); assert!(results[1].1.is_some()); }
#[test]
fn test_get_nodes_at_time_large_batch() {
let db = AletheiaDB::new().unwrap();
let node_ids: Vec<_> = (0..100)
.map(|i| {
db.create_node(
"Test",
PropertyMapBuilder::new().insert("index", i as i64).build(),
)
.unwrap()
})
.collect();
let t1 = db.__test_current_timestamp();
let results = db.get_nodes_at_time(&node_ids, t1, t1).unwrap();
assert_eq!(results.len(), 100);
assert!(results.iter().all(|(_, node)| node.is_some()));
for (i, (id, _)) in results.iter().enumerate() {
assert_eq!(*id, node_ids[i]);
}
}
#[test]
fn test_get_nodes_at_time_duplicate_ids() {
let db = AletheiaDB::new().unwrap();
let node1 = db
.create_node(
"Test",
PropertyMapBuilder::new().insert("name", "Alice").build(),
)
.unwrap();
let t1 = db.__test_current_timestamp();
let node_ids = vec![node1, node1, node1];
let results = db.get_nodes_at_time(&node_ids, t1, t1).unwrap();
assert_eq!(results.len(), 3);
assert!(
results
.iter()
.all(|(id, node)| { *id == node1 && node.is_some() })
);
}
#[test]
fn test_get_edges_at_time_large_batch() {
let db = AletheiaDB::new().unwrap();
let source = db
.create_node("Node", PropertyMapBuilder::new().build())
.unwrap();
let target = db
.create_node("Node", PropertyMapBuilder::new().build())
.unwrap();
let edge_ids: Vec<_> = (0..100)
.map(|i| {
db.create_edge(
source,
target,
"LINK",
PropertyMapBuilder::new().insert("index", i as i64).build(),
)
.unwrap()
})
.collect();
let t1 = db.__test_current_timestamp();
let results = db.get_edges_at_time(&edge_ids, t1, t1).unwrap();
assert_eq!(results.len(), 100);
assert!(results.iter().all(|(_, edge)| edge.is_some()));
for (i, (id, _)) in results.iter().enumerate() {
assert_eq!(*id, edge_ids[i]);
}
}
#[test]
fn test_get_edges_at_time_duplicate_ids() {
let db = AletheiaDB::new().unwrap();
let source = db
.create_node("Node", PropertyMapBuilder::new().build())
.unwrap();
let target = db
.create_node("Node", PropertyMapBuilder::new().build())
.unwrap();
let edge1 = db
.create_edge(source, target, "LINK", PropertyMapBuilder::new().build())
.unwrap();
let t1 = db.__test_current_timestamp();
let edge_ids = vec![edge1, edge1, edge1];
let results = db.get_edges_at_time(&edge_ids, t1, t1).unwrap();
assert_eq!(results.len(), 3);
assert!(
results
.iter()
.all(|(id, edge)| { *id == edge1 && edge.is_some() })
);
}