use grafeo_common::types::Value;
use grafeo_engine::GrafeoDB;
#[test]
fn edge_property_set_then_rollback_removes_property() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Alix'})-[:KNOWS]->(:Person {name: 'Gus'})")
.unwrap();
let before = session
.execute("MATCH (:Person {name: 'Alix'})-[r:KNOWS]->() RETURN r.since")
.unwrap();
assert_eq!(before.rows()[0][0], Value::Null);
session.begin_transaction().unwrap();
session
.execute(
"MATCH (:Person {name: 'Alix'})-[r:KNOWS]->(:Person {name: 'Gus'}) SET r.since = 2020",
)
.unwrap();
let during = session
.execute("MATCH (:Person {name: 'Alix'})-[r:KNOWS]->() RETURN r.since")
.unwrap();
assert_eq!(during.rows()[0][0], Value::Int64(2020));
session.rollback().unwrap();
let after = session
.execute("MATCH (:Person {name: 'Alix'})-[r:KNOWS]->() RETURN r.since")
.unwrap();
assert_eq!(
after.rows()[0][0],
Value::Null,
"edge property should be gone after rollback"
);
}
#[test]
fn edge_property_overwrite_then_rollback_restores_original() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute(
"INSERT (:Person {name: 'Vincent'})-[:KNOWS {since: 2018}]->(:Person {name: 'Jules'})",
)
.unwrap();
session.begin_transaction().unwrap();
session
.execute(
"MATCH (:Person {name: 'Vincent'})-[r:KNOWS]->(:Person {name: 'Jules'}) SET r.since = 2025",
)
.unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (:Person {name: 'Vincent'})-[r:KNOWS]->() RETURN r.since")
.unwrap();
assert_eq!(
result.rows()[0][0],
Value::Int64(2018),
"edge property should revert to 2018 after rollback"
);
}
#[test]
fn edge_multiple_properties_rollback() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute(
"INSERT (:City {name: 'Amsterdam'})-[:ROUTE {distance: 650, toll: 15}]->(:City {name: 'Berlin'})",
)
.unwrap();
session.begin_transaction().unwrap();
session
.execute(
"MATCH (:City {name: 'Amsterdam'})-[r:ROUTE]->(:City {name: 'Berlin'}) SET r.distance = 999, r.toll = 0",
)
.unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (:City {name: 'Amsterdam'})-[r:ROUTE]->() RETURN r.distance, r.toll")
.unwrap();
assert_eq!(
result.rows()[0][0],
Value::Int64(650),
"distance should revert to 650"
);
assert_eq!(
result.rows()[0][1],
Value::Int64(15),
"toll should revert to 15"
);
}
#[test]
fn remove_node_property_rollback_restores_value() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Mia', age: 28})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Mia'}) REMOVE p.age")
.unwrap();
let during = session
.execute("MATCH (p:Person {name: 'Mia'}) RETURN p.age")
.unwrap();
assert_eq!(during.rows()[0][0], Value::Null);
session.rollback().unwrap();
let after = session
.execute("MATCH (p:Person {name: 'Mia'}) RETURN p.age")
.unwrap();
assert_eq!(
after.rows()[0][0],
Value::Int64(28),
"age should be restored to 28 after rollback of REMOVE"
);
}
#[test]
fn set_then_remove_same_property_rollback() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Config {key: 'timeout', value: 30})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (c:Config {key: 'timeout'}) SET c.value = 60")
.unwrap();
session
.execute("MATCH (c:Config {key: 'timeout'}) REMOVE c.value")
.unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (c:Config {key: 'timeout'}) RETURN c.value")
.unwrap();
assert_eq!(
result.rows()[0][0],
Value::Int64(30),
"value should revert to original 30, not null or 60"
);
}
#[test]
fn add_multiple_labels_rollback_removes_all() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session.execute("INSERT (:Person {name: 'Butch'})").unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Butch'}) SET p:Employee")
.unwrap();
session
.execute("MATCH (p:Person {name: 'Butch'}) SET p:Manager")
.unwrap();
session.rollback().unwrap();
let employee = session
.execute("MATCH (p:Employee {name: 'Butch'}) RETURN p.name")
.unwrap();
assert_eq!(
employee.row_count(),
0,
"Employee label should not exist after rollback"
);
let manager = session
.execute("MATCH (p:Manager {name: 'Butch'}) RETURN p.name")
.unwrap();
assert_eq!(
manager.row_count(),
0,
"Manager label should not exist after rollback"
);
let person = session
.execute("MATCH (p:Person {name: 'Butch'}) RETURN p.name")
.unwrap();
assert_eq!(
person.row_count(),
1,
"Person label should survive rollback"
);
}
#[test]
fn add_then_remove_label_same_tx_rollback() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Django'})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Django'}) SET p:Gunslinger")
.unwrap();
session
.execute("MATCH (p:Person {name: 'Django'}) REMOVE p:Gunslinger")
.unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (p:Gunslinger {name: 'Django'}) RETURN p.name")
.unwrap();
assert_eq!(
result.row_count(),
0,
"Gunslinger label should not exist (was never there before tx)"
);
}
#[test]
fn swap_labels_rollback_restores_original() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Active {name: 'Shosanna'})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (n:Active {name: 'Shosanna'}) REMOVE n:Active")
.unwrap();
session
.execute("MATCH (n {name: 'Shosanna'}) SET n:Archived")
.unwrap();
session.rollback().unwrap();
let active = session
.execute("MATCH (n:Active {name: 'Shosanna'}) RETURN n.name")
.unwrap();
assert_eq!(active.row_count(), 1, "Active label should be restored");
let archived = session
.execute("MATCH (n:Archived {name: 'Shosanna'}) RETURN n.name")
.unwrap();
assert_eq!(
archived.row_count(),
0,
"Archived label should not exist after rollback"
);
}
#[test]
fn node_delete_rollback_restores_all_properties_and_labels() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person:VIP {name: 'Hans', age: 55, city: 'Berlin'})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Hans'}) DELETE p")
.unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (p:Person {name: 'Hans'}) RETURN p.age, p.city")
.unwrap();
assert_eq!(result.row_count(), 1, "node should exist after rollback");
assert_eq!(
result.rows()[0][0],
Value::Int64(55),
"age should be restored"
);
assert_eq!(
result.rows()[0][1],
Value::String("Berlin".into()),
"city should be restored"
);
let vip = session
.execute("MATCH (p:VIP {name: 'Hans'}) RETURN p.name")
.unwrap();
assert_eq!(
vip.row_count(),
1,
"VIP label should be restored after rollback"
);
}
#[test]
fn edge_delete_rollback_restores_properties() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute(
"INSERT (:Person {name: 'Beatrix'})-[:KNOWS {since: 2015, trust: 9}]->(:Person {name: 'Alix'})",
)
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (:Person {name: 'Beatrix'})-[r:KNOWS]->(:Person {name: 'Alix'}) DELETE r")
.unwrap();
let during = session
.execute("MATCH (:Person {name: 'Beatrix'})-[r:KNOWS]->() RETURN r.since")
.unwrap();
assert_eq!(during.row_count(), 0);
session.rollback().unwrap();
let after = session
.execute("MATCH (:Person {name: 'Beatrix'})-[r:KNOWS]->() RETURN r.since, r.trust")
.unwrap();
assert_eq!(after.row_count(), 1, "edge should be restored");
assert_eq!(
after.rows()[0][0],
Value::Int64(2015),
"edge 'since' property should be restored"
);
assert_eq!(
after.rows()[0][1],
Value::Int64(9),
"edge 'trust' property should be restored"
);
}
#[test]
fn detach_delete_rollback_restores_node_edges_and_properties() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Vincent', age: 40})")
.unwrap();
session.execute("INSERT (:Person {name: 'Jules'})").unwrap();
session.execute("INSERT (:Person {name: 'Mia'})").unwrap();
session
.execute(
"MATCH (v:Person {name: 'Vincent'}), (j:Person {name: 'Jules'}) \
INSERT (v)-[:KNOWS {since: 2010}]->(j)",
)
.unwrap();
session
.execute(
"MATCH (v:Person {name: 'Vincent'}), (m:Person {name: 'Mia'}) \
INSERT (v)-[:LIKES {intensity: 8}]->(m)",
)
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (v:Person {name: 'Vincent'}) DETACH DELETE v")
.unwrap();
let during_nodes = session
.execute("MATCH (v:Person {name: 'Vincent'}) RETURN v.name")
.unwrap();
assert_eq!(during_nodes.row_count(), 0);
session.rollback().unwrap();
let node = session
.execute("MATCH (v:Person {name: 'Vincent'}) RETURN v.age")
.unwrap();
assert_eq!(node.row_count(), 1, "node should be restored");
assert_eq!(node.rows()[0][0], Value::Int64(40));
let knows = session
.execute("MATCH (:Person {name: 'Vincent'})-[r:KNOWS]->() RETURN r.since")
.unwrap();
assert_eq!(knows.row_count(), 1, "KNOWS edge should be restored");
assert_eq!(knows.rows()[0][0], Value::Int64(2010));
let likes = session
.execute("MATCH (:Person {name: 'Vincent'})-[r:LIKES]->() RETURN r.intensity")
.unwrap();
assert_eq!(likes.row_count(), 1, "LIKES edge should be restored");
assert_eq!(likes.rows()[0][0], Value::Int64(8));
}
#[test]
fn interleaved_property_label_and_delete_rollback() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Alix', age: 30})")
.unwrap();
session
.execute("INSERT (:Temporary {name: 'ephemeral', data: 42})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Alix'}) SET p.age = 99")
.unwrap();
session
.execute("MATCH (p:Person {name: 'Alix'}) SET p:VIP")
.unwrap();
session
.execute("MATCH (t:Temporary {name: 'ephemeral'}) DELETE t")
.unwrap();
session.rollback().unwrap();
let alix = session
.execute("MATCH (p:Person {name: 'Alix'}) RETURN p.age")
.unwrap();
assert_eq!(
alix.rows()[0][0],
Value::Int64(30),
"age should revert to 30"
);
let vip = session
.execute("MATCH (p:VIP {name: 'Alix'}) RETURN p.name")
.unwrap();
assert_eq!(
vip.row_count(),
0,
"VIP label should not exist after rollback"
);
let temp = session
.execute("MATCH (t:Temporary {name: 'ephemeral'}) RETURN t.data")
.unwrap();
assert_eq!(temp.row_count(), 1, "Temporary node should be restored");
assert_eq!(temp.rows()[0][0], Value::Int64(42));
}
#[test]
fn kitchen_sink_operations_rollback() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Gus', score: 10})-[:WORKS_WITH {years: 3}]->(:Person {name: 'Alix'})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Gus'}) SET p.score = 100")
.unwrap();
session
.execute("MATCH (p:Person {name: 'Gus'}) SET p:Senior")
.unwrap();
session
.execute(
"MATCH (:Person {name: 'Gus'})-[r:WORKS_WITH]->(:Person {name: 'Alix'}) SET r.years = 10",
)
.unwrap();
session.rollback().unwrap();
let score = session
.execute("MATCH (p:Person {name: 'Gus'}) RETURN p.score")
.unwrap();
assert_eq!(
score.rows()[0][0],
Value::Int64(10),
"score should revert to 10"
);
let senior = session
.execute("MATCH (p:Senior {name: 'Gus'}) RETURN p.name")
.unwrap();
assert_eq!(
senior.row_count(),
0,
"Senior label should not exist after rollback"
);
let years = session
.execute("MATCH (:Person {name: 'Gus'})-[r:WORKS_WITH]->() RETURN r.years")
.unwrap();
assert_eq!(
years.rows()[0][0],
Value::Int64(3),
"edge years should revert to 3"
);
}
#[test]
fn sequential_commit_then_rollback_different_nodes() {
let db = GrafeoDB::new_in_memory();
let setup = db.session();
setup
.execute("INSERT (:Account {owner: 'Alix', balance: 100})")
.unwrap();
setup
.execute("INSERT (:Account {owner: 'Gus', balance: 200})")
.unwrap();
let mut session1 = db.session();
session1.begin_transaction().unwrap();
session1
.execute("MATCH (a:Account {owner: 'Alix'}) SET a.balance = 500")
.unwrap();
session1.commit().unwrap();
let mut session2 = db.session();
session2.begin_transaction().unwrap();
session2
.execute("MATCH (a:Account {owner: 'Gus'}) SET a.balance = 9999")
.unwrap();
session2.rollback().unwrap();
let reader = db.session();
let alix = reader
.execute("MATCH (a:Account {owner: 'Alix'}) RETURN a.balance")
.unwrap();
assert_eq!(
alix.rows()[0][0],
Value::Int64(500),
"Alix's committed balance should be 500"
);
let gus = reader
.execute("MATCH (a:Account {owner: 'Gus'}) RETURN a.balance")
.unwrap();
assert_eq!(
gus.rows()[0][0],
Value::Int64(200),
"Gus's balance should remain 200 after session2's rollback"
);
}
#[test]
fn sequential_label_commit_then_rollback_different_nodes() {
let db = GrafeoDB::new_in_memory();
let setup = db.session();
setup.execute("INSERT (:Person {name: 'Vincent'})").unwrap();
setup.execute("INSERT (:Person {name: 'Jules'})").unwrap();
let mut session1 = db.session();
session1.begin_transaction().unwrap();
session1
.execute("MATCH (p:Person {name: 'Vincent'}) SET p:Hitman")
.unwrap();
session1.commit().unwrap();
let mut session2 = db.session();
session2.begin_transaction().unwrap();
session2
.execute("MATCH (p:Person {name: 'Jules'}) SET p:Philosopher")
.unwrap();
session2.rollback().unwrap();
let reader = db.session();
let hitman = reader
.execute("MATCH (p:Hitman {name: 'Vincent'}) RETURN p.name")
.unwrap();
assert_eq!(
hitman.row_count(),
1,
"Vincent should have Hitman label (committed)"
);
let philosopher = reader
.execute("MATCH (p:Philosopher {name: 'Jules'}) RETURN p.name")
.unwrap();
assert_eq!(
philosopher.row_count(),
0,
"Jules should NOT have Philosopher label (rolled back)"
);
}
#[test]
fn triple_overwrite_same_property_rollback_restores_original() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Counter {name: 'visits', count: 0})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (c:Counter {name: 'visits'}) SET c.count = 1")
.unwrap();
session
.execute("MATCH (c:Counter {name: 'visits'}) SET c.count = 2")
.unwrap();
session
.execute("MATCH (c:Counter {name: 'visits'}) SET c.count = 3")
.unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (c:Counter {name: 'visits'}) RETURN c.count")
.unwrap();
assert_eq!(
result.rows()[0][0],
Value::Int64(0),
"count should be 0 (original), not 1, 2, or 3"
);
}
#[test]
fn commit_label_then_rollback_different_label() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session.execute("INSERT (:Person {name: 'Alix'})").unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Alix'}) SET p:Employee")
.unwrap();
session.commit().unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Alix'}) SET p:Manager")
.unwrap();
session.rollback().unwrap();
let employee = session
.execute("MATCH (p:Employee {name: 'Alix'}) RETURN p.name")
.unwrap();
assert_eq!(
employee.row_count(),
1,
"Employee label should persist from committed tx"
);
let manager = session
.execute("MATCH (p:Manager {name: 'Alix'}) RETURN p.name")
.unwrap();
assert_eq!(
manager.row_count(),
0,
"Manager label should not exist (rolled back)"
);
}
#[test]
fn create_edge_and_set_property_in_tx_rollback() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session.execute("INSERT (:Person {name: 'Alix'})").unwrap();
session.execute("INSERT (:Person {name: 'Gus'})").unwrap();
session.begin_transaction().unwrap();
session
.execute(
"MATCH (a:Person {name: 'Alix'}), (g:Person {name: 'Gus'}) \
INSERT (a)-[:FRIENDS {level: 5}]->(g)",
)
.unwrap();
let during = session
.execute("MATCH ()-[r:FRIENDS]->() RETURN r.level")
.unwrap();
assert_eq!(during.row_count(), 1);
session.rollback().unwrap();
let after = session
.execute("MATCH ()-[r:FRIENDS]->() RETURN r.level")
.unwrap();
assert_eq!(
after.row_count(),
0,
"edge created in rolled-back tx should not exist"
);
}
#[test]
fn property_type_change_rollback_restores_original_type() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Setting {key: 'mode', value: 1})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (s:Setting {key: 'mode'}) SET s.value = 'debug'")
.unwrap();
let during = session
.execute("MATCH (s:Setting {key: 'mode'}) RETURN s.value")
.unwrap();
assert_eq!(during.rows()[0][0], Value::String("debug".into()));
session.rollback().unwrap();
let after = session
.execute("MATCH (s:Setting {key: 'mode'}) RETURN s.value")
.unwrap();
assert_eq!(
after.rows()[0][0],
Value::Int64(1),
"value should revert to int 1, not string 'debug'"
);
}
#[test]
fn empty_transaction_rollback_leaves_data_intact() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Alix', age: 30})")
.unwrap();
session.begin_transaction().unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (p:Person {name: 'Alix'}) RETURN p.age")
.unwrap();
assert_eq!(result.row_count(), 1);
assert_eq!(result.rows()[0][0], Value::Int64(30));
}
#[test]
fn read_only_transaction_rollback() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Gus', score: 42})")
.unwrap();
session.begin_transaction().unwrap();
let _read = session
.execute("MATCH (p:Person {name: 'Gus'}) RETURN p.score")
.unwrap();
session.rollback().unwrap();
let after = session
.execute("MATCH (p:Person {name: 'Gus'}) RETURN p.score")
.unwrap();
assert_eq!(after.rows()[0][0], Value::Int64(42));
}
#[test]
fn rollback_only_affects_modified_node_shared_property_key() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Alix', score: 10})")
.unwrap();
session
.execute("INSERT (:Person {name: 'Gus', score: 20})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Alix'}) SET p.score = 999")
.unwrap();
session.rollback().unwrap();
let alix = session
.execute("MATCH (p:Person {name: 'Alix'}) RETURN p.score")
.unwrap();
assert_eq!(
alix.rows()[0][0],
Value::Int64(10),
"Alix's score should revert to 10"
);
let gus = session
.execute("MATCH (p:Person {name: 'Gus'}) RETURN p.score")
.unwrap();
assert_eq!(
gus.rows()[0][0],
Value::Int64(20),
"Gus's score should be unaffected (20)"
);
}
#[test]
fn create_node_with_edges_rollback_cleans_adjacency() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session.execute("INSERT (:Person {name: 'Alix'})").unwrap();
session.begin_transaction().unwrap();
session.execute("INSERT (:Person {name: 'Ghost'})").unwrap();
session
.execute(
"MATCH (a:Person {name: 'Alix'}), (g:Person {name: 'Ghost'}) \
INSERT (a)-[:HAUNTS]->(g)",
)
.unwrap();
session.rollback().unwrap();
let ghost = session
.execute("MATCH (g:Person {name: 'Ghost'}) RETURN g.name")
.unwrap();
assert_eq!(
ghost.row_count(),
0,
"Ghost node should not exist after rollback"
);
let edges = session.execute("MATCH ()-[r:HAUNTS]->() RETURN r").unwrap();
assert_eq!(
edges.row_count(),
0,
"HAUNTS edge should not exist after rollback"
);
let alix_edges = session
.execute("MATCH (:Person {name: 'Alix'})-[r]->() RETURN r")
.unwrap();
assert_eq!(
alix_edges.row_count(),
0,
"Alix should have no outgoing edges after rollback"
);
}
#[test]
fn commit_then_rollback_same_property_retains_committed_value() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Person {name: 'Mia', level: 1})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Mia'}) SET p.level = 5")
.unwrap();
session.commit().unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (p:Person {name: 'Mia'}) SET p.level = 99")
.unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (p:Person {name: 'Mia'}) RETURN p.level")
.unwrap();
assert_eq!(
result.rows()[0][0],
Value::Int64(5),
"level should be 5 (committed value), not 99 (rolled back)"
);
}
#[test]
fn rollback_preserves_boolean_and_float_property_types() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Sensor {name: 'thermostat', enabled: true, reading: 21.5})")
.unwrap();
session.begin_transaction().unwrap();
session
.execute("MATCH (s:Sensor {name: 'thermostat'}) SET s.enabled = false, s.reading = 99.9")
.unwrap();
session.rollback().unwrap();
let result = session
.execute("MATCH (s:Sensor {name: 'thermostat'}) RETURN s.enabled, s.reading")
.unwrap();
assert_eq!(
result.rows()[0][0],
Value::Bool(true),
"enabled should revert to true"
);
assert_eq!(
result.rows()[0][1],
Value::Float64(21.5),
"reading should revert to 21.5"
);
}
#[test]
fn rapid_begin_rollback_cycles_no_state_leak() {
let db = GrafeoDB::new_in_memory();
let mut session = db.session();
session
.execute("INSERT (:Counter {name: 'stable', value: 42})")
.unwrap();
for i in 0..10 {
session.begin_transaction().unwrap();
session
.execute(&format!(
"MATCH (c:Counter {{name: 'stable'}}) SET c.value = {}",
i * 100
))
.unwrap();
session.rollback().unwrap();
}
let result = session
.execute("MATCH (c:Counter {name: 'stable'}) RETURN c.value")
.unwrap();
assert_eq!(
result.rows()[0][0],
Value::Int64(42),
"value should remain 42 after 10 begin/rollback cycles"
);
}