From c313e2506c72eaaabb4cc7914d73c898fb007664 Mon Sep 17 00:00:00 2001 From: Evgeny Fomin Date: Thu, 13 Feb 2025 15:00:20 +0100 Subject: [PATCH] add insertion tests --- grovedb/src/operations/delete/mod.rs | 126 +-------------------------- grovedb/src/operations/insert/mod.rs | 88 ++++++++++++++++++- grovedb/src/tests/common.rs | 124 ++++++++++++++++++++++++++ 3 files changed, 213 insertions(+), 125 deletions(-) diff --git a/grovedb/src/operations/delete/mod.rs b/grovedb/src/operations/delete/mod.rs index 5813902a..d6c1aa13 100644 --- a/grovedb/src/operations/delete/mod.rs +++ b/grovedb/src/operations/delete/mod.rs @@ -589,12 +589,10 @@ mod tests { use pretty_assertions::assert_eq; use crate::{ - bidirectional_references::BidirectionalReference, operations::delete::{delete_up_tree::DeleteUpTreeOptions, ClearOptions, DeleteOptions}, - reference_path::ReferencePathType, tests::{ - common::EMPTY_PATH, make_deep_tree, make_test_grovedb, TempGroveDb, ANOTHER_TEST_LEAF, - TEST_LEAF, + common::{make_tree_with_bidi_references, EMPTY_PATH}, + make_test_grovedb, ANOTHER_TEST_LEAF, TEST_LEAF, }, Element, Error, }; @@ -1316,126 +1314,6 @@ mod tests { assert_ne!(root_hash_before_clear, root_hash_after_clear); } - fn make_tree_with_bidi_references(version: &GroveVersion) -> TempGroveDb { - let db = make_deep_tree(&version); - - let transaction = db.start_transaction(); - - // Let's say we're deleting `deep_leaf` with an existing references chain - // that goes like - // test_leaf/innertree:ref -> another_test_leaf/innertree2:ref2 -> - // -> deep_leaf/deep_node_1/deeper_1:ref3 -> - // -> deep_leaf/deep_node_2/deeper_3:ref4 -> - // -> deep_leaf/deep_node_1/deeper_2:key5 - // - - db.insert( - &[b"deep_leaf".as_ref(), b"deep_node_1", b"deeper_2"], - b"key5", - Element::new_item_allowing_bidirectional_references(b"hello".to_vec()), - None, - Some(&transaction), - version, - ) - .unwrap() - .unwrap(); - - db.insert( - &[b"deep_leaf".as_ref(), b"deep_node_2", b"deeper_3"], - b"ref4", - Element::BidirectionalReference(BidirectionalReference { - forward_reference_path: ReferencePathType::UpstreamRootHeightReference( - 1, - vec![ - b"deep_node_1".to_vec(), - b"deeper_2".to_vec(), - b"key5".to_vec(), - ], - ), - backward_reference_slot: 0, - cascade_on_update: true, - max_hop: None, - flags: None, - }), - None, - Some(&transaction), - version, - ) - .unwrap() - .unwrap(); - - db.insert( - &[b"deep_leaf".as_ref(), b"deep_node_1", b"deeper_1"], - b"ref3", - Element::BidirectionalReference(BidirectionalReference { - forward_reference_path: ReferencePathType::UpstreamRootHeightReference( - 1, - vec![ - b"deep_node_2".to_vec(), - b"deeper_3".to_vec(), - b"ref4".to_vec(), - ], - ), - backward_reference_slot: 0, - cascade_on_update: true, - max_hop: None, - flags: None, - }), - None, - Some(&transaction), - version, - ) - .unwrap() - .unwrap(); - - db.insert( - &[ANOTHER_TEST_LEAF, b"innertree2"], - b"ref2", - Element::BidirectionalReference(BidirectionalReference { - forward_reference_path: ReferencePathType::AbsolutePathReference(vec![ - b"deep_leaf".to_vec(), - b"deep_node_1".to_vec(), - b"deeper_1".to_vec(), - b"ref3".to_vec(), - ]), - backward_reference_slot: 0, - cascade_on_update: true, - max_hop: None, - flags: None, - }), - None, - Some(&transaction), - version, - ) - .unwrap() - .unwrap(); - - db.insert( - &[TEST_LEAF, b"innertree"], - b"ref", - Element::BidirectionalReference(BidirectionalReference { - forward_reference_path: ReferencePathType::AbsolutePathReference(vec![ - ANOTHER_TEST_LEAF.to_vec(), - b"innertree2".to_vec(), - b"ref2".to_vec(), - ]), - backward_reference_slot: 0, - cascade_on_update: true, - max_hop: None, - flags: None, - }), - None, - Some(&transaction), - version, - ) - .unwrap() - .unwrap(); - - db.commit_transaction(transaction).unwrap().unwrap(); - - db - } - #[test] fn delete_item_with_backward_references() { // Deletion of an item with backward references shall trigger cascade diff --git a/grovedb/src/operations/insert/mod.rs b/grovedb/src/operations/insert/mod.rs index 369ee2b9..b18af8f7 100644 --- a/grovedb/src/operations/insert/mod.rs +++ b/grovedb/src/operations/insert/mod.rs @@ -466,12 +466,16 @@ mod tests { storage_cost::{removal::StorageRemovedBytes::NoStorageRemoval, StorageCost}, OperationCost, }; + use grovedb_path::SubtreePath; use grovedb_version::version::GroveVersion; use pretty_assertions::assert_eq; use crate::{ operations::insert::InsertOptions, - tests::{common::EMPTY_PATH, make_empty_grovedb, make_test_grovedb, TEST_LEAF}, + tests::{ + common::{make_tree_with_bidi_references, EMPTY_PATH}, + make_empty_grovedb, make_test_grovedb, TEST_LEAF, + }, Element, Error, }; @@ -2187,4 +2191,86 @@ mod tests { } ); } + + #[test] + fn update_item_with_backward_references() { + let version = GroveVersion::latest(); + + let db = make_tree_with_bidi_references(version); + + let transaction = db.start_transaction(); + + let get_hash = || { + Element::get_value_hash( + &db.open_transactional_merk_at_path( + SubtreePath::from(&[TEST_LEAF, b"innertree"]), + &transaction, + None, + version, + ) + .unwrap() + .unwrap(), + b"ref", + true, + version, + ) + .unwrap() + .unwrap() + .unwrap() + }; + + let hash_before = get_hash(); + + db.insert( + &[b"deep_leaf".as_ref(), b"deep_node_1", b"deeper_2"], + b"key5", + Element::new_item_allowing_bidirectional_references(b"certainly new value".to_vec()), + Some(InsertOptions { + propagate_backward_references: true, + ..Default::default() + }), + None, + version, + ) + .unwrap() + .unwrap(); + + let hash_after = get_hash(); + + assert_ne!(hash_before, hash_after); + } + + #[test] + fn update_item_with_backward_references_with_no_support() { + let version = GroveVersion::latest(); + + let db = make_tree_with_bidi_references(version); + + let transaction = db.start_transaction(); + + db.insert( + &[b"deep_leaf".as_ref(), b"deep_node_1", b"deeper_2"], + b"key5", + Element::new_item(b"hello".to_vec()), + Some(InsertOptions { + propagate_backward_references: true, + ..Default::default() + }), + None, + version, + ) + .unwrap() + .unwrap(); + + assert!(matches!( + db.get( + &[TEST_LEAF, b"innertree"], + b"ref", + Some(&transaction), + version + ) + .unwrap(), + Err(Error::PathKeyNotFound(_)) + )); + } } diff --git a/grovedb/src/tests/common.rs b/grovedb/src/tests/common.rs index a02ef9c6..a1079b37 100644 --- a/grovedb/src/tests/common.rs +++ b/grovedb/src/tests/common.rs @@ -3,6 +3,10 @@ use grovedb_path::SubtreePath; use grovedb_version::version::GroveVersion; +use super::{ + make_deep_tree, reference_path::ReferencePathType, BidirectionalReference, TempGroveDb, + ANOTHER_TEST_LEAF, TEST_LEAF, +}; use crate::{operations::proof::util::ProvedPathKeyValues, Element, Error}; /// Compare result tuples @@ -35,4 +39,124 @@ pub fn compare_result_sets(elements: &Vec>, result_set: &ProvedPathKeyVa } } +pub(crate) fn make_tree_with_bidi_references(version: &GroveVersion) -> TempGroveDb { + let db = make_deep_tree(&version); + + let transaction = db.start_transaction(); + + // Let's say we're deleting `deep_leaf` with an existing references chain + // that goes like + // test_leaf/innertree:ref -> another_test_leaf/innertree2:ref2 -> + // -> deep_leaf/deep_node_1/deeper_1:ref3 -> + // -> deep_leaf/deep_node_2/deeper_3:ref4 -> + // -> deep_leaf/deep_node_1/deeper_2:key5 + // + + db.insert( + &[b"deep_leaf".as_ref(), b"deep_node_1", b"deeper_2"], + b"key5", + Element::new_item_allowing_bidirectional_references(b"hello".to_vec()), + None, + Some(&transaction), + version, + ) + .unwrap() + .unwrap(); + + db.insert( + &[b"deep_leaf".as_ref(), b"deep_node_2", b"deeper_3"], + b"ref4", + Element::BidirectionalReference(BidirectionalReference { + forward_reference_path: ReferencePathType::UpstreamRootHeightReference( + 1, + vec![ + b"deep_node_1".to_vec(), + b"deeper_2".to_vec(), + b"key5".to_vec(), + ], + ), + backward_reference_slot: 0, + cascade_on_update: true, + max_hop: None, + flags: None, + }), + None, + Some(&transaction), + version, + ) + .unwrap() + .unwrap(); + + db.insert( + &[b"deep_leaf".as_ref(), b"deep_node_1", b"deeper_1"], + b"ref3", + Element::BidirectionalReference(BidirectionalReference { + forward_reference_path: ReferencePathType::UpstreamRootHeightReference( + 1, + vec![ + b"deep_node_2".to_vec(), + b"deeper_3".to_vec(), + b"ref4".to_vec(), + ], + ), + backward_reference_slot: 0, + cascade_on_update: true, + max_hop: None, + flags: None, + }), + None, + Some(&transaction), + version, + ) + .unwrap() + .unwrap(); + + db.insert( + &[ANOTHER_TEST_LEAF, b"innertree2"], + b"ref2", + Element::BidirectionalReference(BidirectionalReference { + forward_reference_path: ReferencePathType::AbsolutePathReference(vec![ + b"deep_leaf".to_vec(), + b"deep_node_1".to_vec(), + b"deeper_1".to_vec(), + b"ref3".to_vec(), + ]), + backward_reference_slot: 0, + cascade_on_update: true, + max_hop: None, + flags: None, + }), + None, + Some(&transaction), + version, + ) + .unwrap() + .unwrap(); + + db.insert( + &[TEST_LEAF, b"innertree"], + b"ref", + Element::BidirectionalReference(BidirectionalReference { + forward_reference_path: ReferencePathType::AbsolutePathReference(vec![ + ANOTHER_TEST_LEAF.to_vec(), + b"innertree2".to_vec(), + b"ref2".to_vec(), + ]), + backward_reference_slot: 0, + cascade_on_update: true, + max_hop: None, + flags: None, + }), + None, + Some(&transaction), + version, + ) + .unwrap() + .unwrap(); + + db.commit_transaction(transaction).unwrap().unwrap(); + + db +} + pub(crate) const EMPTY_PATH: SubtreePath<'static, [u8; 0]> = SubtreePath::empty();