use graph_d::graph::{Node, Relationship};
use graph_d::transaction::{IsolationLevel, TransactionManager};
use std::collections::HashMap;
use std::sync::Arc;
use std::thread;
fn make_node(id: u64, label: &str) -> Node {
let mut props = HashMap::new();
props.insert("label".to_string(), serde_json::json!(label));
Node::new(id, props)
}
fn make_relationship(id: u64, from: u64, to: u64, rel_type: &str) -> Relationship {
let mut props = HashMap::new();
props.insert("type".to_string(), serde_json::json!(rel_type));
Relationship::new(id, from, to, rel_type.to_string(), props)
}
#[test]
fn test_basic_transaction_lifecycle() {
let tm = TransactionManager::new(IsolationLevel::ReadCommitted);
let mut tx = tm.begin();
assert!(tx.id() > 0);
let result = tx.commit();
assert!(result.is_ok());
}
#[test]
fn test_transaction_rollback() {
let tm = TransactionManager::new(IsolationLevel::ReadCommitted);
let mut tx = tm.begin();
let result = tx.rollback();
assert!(result.is_ok());
}
#[test]
fn test_mvcc_enabled_transaction_manager() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::RepeatableRead);
assert!(tm.has_mvcc());
assert!(tm.mvcc().is_some());
let tx_id = tm.begin_mvcc().unwrap();
assert!(tx_id > 0);
let node = make_node(1, "Person");
let result = tm.mvcc_write_node(tx_id, node);
assert!(result.is_ok());
let commit_result = tm.mvcc_commit(tx_id);
assert!(commit_result.is_ok());
let read_tx = tm.begin_mvcc().unwrap();
let read_result = tm.mvcc_read_node(read_tx, 1);
assert!(read_result.is_ok());
assert!(read_result.unwrap().is_some());
tm.mvcc_commit(read_tx).unwrap();
}
#[test]
fn test_mvcc_isolation() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::RepeatableRead);
let tx1 = tm.begin_mvcc().unwrap();
tm.mvcc_write_node(tx1, make_node(1, "Original")).unwrap();
tm.mvcc_commit(tx1).unwrap();
let tx2 = tm.begin_mvcc().unwrap();
let node_v1 = tm.mvcc_read_node(tx2, 1).unwrap().unwrap();
assert_eq!(node_v1.get_property("label").unwrap(), "Original");
let tx3 = tm.begin_mvcc().unwrap();
tm.mvcc_write_node(tx3, make_node(1, "Modified")).unwrap();
tm.mvcc_commit(tx3).unwrap();
let node_still_v1 = tm.mvcc_read_node(tx2, 1).unwrap().unwrap();
assert_eq!(node_still_v1.get_property("label").unwrap(), "Original");
tm.mvcc_commit(tx2).unwrap();
}
#[test]
fn test_mvcc_write_conflict() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::RepeatableRead);
let setup = tm.begin_mvcc().unwrap();
tm.mvcc_write_node(setup, make_node(1, "Initial")).unwrap();
tm.mvcc_commit(setup).unwrap();
let tx1 = tm.begin_mvcc().unwrap();
let tx2 = tm.begin_mvcc().unwrap();
tm.mvcc_read_node(tx1, 1).unwrap();
tm.mvcc_read_node(tx2, 1).unwrap();
tm.mvcc_write_node(tx1, make_node(1, "TX1")).unwrap();
tm.mvcc_commit(tx1).unwrap();
tm.mvcc_write_node(tx2, make_node(1, "TX2")).unwrap();
let result = tm.mvcc_commit(tx2);
assert!(result.is_err());
assert!(result.unwrap_err().to_string().contains("conflict"));
}
#[test]
fn test_mvcc_rollback_isolation() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::RepeatableRead);
let tx1 = tm.begin_mvcc().unwrap();
tm.mvcc_write_node(tx1, make_node(1, "ShouldNotExist"))
.unwrap();
tm.mvcc_rollback(tx1).unwrap();
let tx2 = tm.begin_mvcc().unwrap();
let node = tm.mvcc_read_node(tx2, 1).unwrap();
assert!(node.is_none());
tm.mvcc_commit(tx2).unwrap();
}
#[test]
fn test_mvcc_relationship_transaction() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::RepeatableRead);
let tx = tm.begin_mvcc().unwrap();
tm.mvcc_write_node(tx, make_node(1, "Person")).unwrap();
tm.mvcc_write_node(tx, make_node(2, "Company")).unwrap();
tm.mvcc_write_relationship(tx, make_relationship(1, 1, 2, "WORKS_AT"))
.unwrap();
tm.mvcc_commit(tx).unwrap();
let verify_tx = tm.begin_mvcc().unwrap();
let node1 = tm.mvcc_read_node(verify_tx, 1).unwrap();
let node2 = tm.mvcc_read_node(verify_tx, 2).unwrap();
let rel = tm.mvcc_read_relationship(verify_tx, 1).unwrap();
assert!(node1.is_some());
assert!(node2.is_some());
assert!(rel.is_some());
let rel = rel.unwrap();
assert_eq!(rel.from_id, 1);
assert_eq!(rel.to_id, 2);
tm.mvcc_commit(verify_tx).unwrap();
}
#[test]
fn test_mvcc_stats() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::ReadCommitted);
let tx1 = tm.begin_mvcc().unwrap();
let tx2 = tm.begin_mvcc().unwrap();
let stats = tm.mvcc_stats();
assert!(stats.is_some());
let stats = stats.unwrap();
assert!(stats.active_transactions >= 2);
tm.mvcc_commit(tx1).unwrap();
tm.mvcc_commit(tx2).unwrap();
let stats_after = tm.mvcc_stats().unwrap();
assert_eq!(stats_after.active_transactions, 0);
}
#[test]
fn test_mvcc_garbage_collection() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::ReadCommitted);
for i in 0..10 {
let tx = tm.begin_mvcc().unwrap();
tm.mvcc_write_node(tx, make_node(1, &format!("Version{i}")))
.unwrap();
tm.mvcc_commit(tx).unwrap();
}
tm.mvcc_gc();
let tx = tm.begin_mvcc().unwrap();
let node = tm.mvcc_read_node(tx, 1).unwrap();
assert!(node.is_some());
tm.mvcc_commit(tx).unwrap();
}
#[test]
fn test_non_mvcc_transaction_manager() {
let tm = TransactionManager::new(IsolationLevel::ReadCommitted);
assert!(!tm.has_mvcc());
assert!(tm.mvcc().is_none());
let mut tx = tm.begin();
tx.commit().unwrap();
}
#[test]
fn test_concurrent_transactions() {
let tm = Arc::new(TransactionManager::new_with_mvcc(
IsolationLevel::RepeatableRead,
));
let setup = tm.begin_mvcc().unwrap();
for i in 0..10 {
tm.mvcc_write_node(setup, make_node(i, "Initial")).unwrap();
}
tm.mvcc_commit(setup).unwrap();
let mut handles = vec![];
for _thread_id in 0..4 {
let tm_clone = Arc::clone(&tm);
let handle = thread::spawn(move || {
for _ in 0..10 {
let tx = tm_clone.begin_mvcc().unwrap();
for i in 0..10 {
let node = tm_clone.mvcc_read_node(tx, i).unwrap();
assert!(node.is_some());
}
tm_clone.mvcc_commit(tx).unwrap();
}
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
}
#[test]
fn test_serializable_isolation() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::Serializable);
let setup = tm
.begin_mvcc_with_isolation(IsolationLevel::Serializable)
.unwrap();
tm.mvcc_write_node(setup, make_node(1, "Node1")).unwrap();
tm.mvcc_commit(setup).unwrap();
let tx1 = tm
.begin_mvcc_with_isolation(IsolationLevel::Serializable)
.unwrap();
tm.mvcc_read_node(tx1, 1).unwrap();
let tx2 = tm
.begin_mvcc_with_isolation(IsolationLevel::Serializable)
.unwrap();
tm.mvcc_write_node(tx2, make_node(1, "Modified")).unwrap();
tm.mvcc_commit(tx2).unwrap();
tm.mvcc_write_node(tx1, make_node(2, "NewNode")).unwrap();
let result = tm.mvcc_commit(tx1);
assert!(result.is_err());
}
#[test]
fn test_mixed_isolation_levels() {
let tm = TransactionManager::new_with_mvcc(IsolationLevel::ReadCommitted);
let setup = tm.begin_mvcc().unwrap();
tm.mvcc_write_node(setup, make_node(1, "Initial")).unwrap();
tm.mvcc_commit(setup).unwrap();
let tx1 = tm
.begin_mvcc_with_isolation(IsolationLevel::RepeatableRead)
.unwrap();
let v1 = tm.mvcc_read_node(tx1, 1).unwrap().unwrap();
let tx2 = tm
.begin_mvcc_with_isolation(IsolationLevel::ReadCommitted)
.unwrap();
tm.mvcc_write_node(tx2, make_node(1, "Changed")).unwrap();
tm.mvcc_commit(tx2).unwrap();
let v1_again = tm.mvcc_read_node(tx1, 1).unwrap().unwrap();
assert_eq!(v1.get_property("label"), v1_again.get_property("label"));
tm.mvcc_commit(tx1).unwrap();
}