diff --git a/packages/rs-drive/src/cache/data_contract.rs b/packages/rs-drive/src/cache/data_contract.rs index cdedae6b39f..f3dbbf3479d 100644 --- a/packages/rs-drive/src/cache/data_contract.rs +++ b/packages/rs-drive/src/cache/data_contract.rs @@ -77,11 +77,11 @@ impl DataContractCache { #[cfg(test)] mod tests { use super::*; + use dpp::version::PlatformVersion; mod get { use super::*; use dpp::data_contract::accessors::v0::{DataContractV0Getters, DataContractV0Setters}; - use dpp::version::PlatformVersion; #[test] fn test_get_from_global_cache_when_block_cache_is_not_requested() { @@ -165,4 +165,102 @@ mod tests { assert_eq!(fetch_info_from_cache, fetch_info_block) } } + + mod remove { + use super::*; + + #[test] + fn test_remove_clears_global_cache_entry() { + let data_contract_cache = DataContractCache::new(10, 10); + + let protocol_version = PlatformVersion::latest().protocol_version; + let fetch_info = Arc::new(DataContractFetchInfo::dpns_contract_fixture( + protocol_version, + )); + let contract_id = fetch_info.contract.id().to_buffer(); + + data_contract_cache.insert(fetch_info, false); + data_contract_cache.remove(contract_id); + + assert!(data_contract_cache.get(contract_id, false).is_none()); + } + + #[test] + fn test_remove_clears_entry_from_both_caches() { + let data_contract_cache = DataContractCache::new(10, 10); + + let protocol_version = PlatformVersion::latest().protocol_version; + let fetch_info_global = Arc::new(DataContractFetchInfo::dpns_contract_fixture( + protocol_version, + )); + let contract_id = fetch_info_global.contract.id().to_buffer(); + let fetch_info_block = Arc::clone(&fetch_info_global); + + data_contract_cache.insert(fetch_info_global, false); + data_contract_cache.insert(fetch_info_block, true); + data_contract_cache.remove(contract_id); + + assert!(data_contract_cache.block_cache.get(&contract_id).is_none()); + assert!(data_contract_cache.global_cache.get(&contract_id).is_none()); + } + } + + mod merge_and_clear_block_cache { + use super::*; + + #[test] + fn test_merge_moves_block_items_to_global_cache() { + let data_contract_cache = DataContractCache::new(10, 10); + + let protocol_version = PlatformVersion::latest().protocol_version; + let fetch_info = Arc::new(DataContractFetchInfo::dpns_contract_fixture( + protocol_version, + )); + let contract_id = fetch_info.contract.id().to_buffer(); + + data_contract_cache.insert(fetch_info, true); + data_contract_cache.merge_and_clear_block_cache(); + + assert!(data_contract_cache.global_cache.get(&contract_id).is_some()); + } + + #[test] + fn test_merge_clears_block_cache() { + let data_contract_cache = DataContractCache::new(10, 10); + + let protocol_version = PlatformVersion::latest().protocol_version; + let fetch_info = Arc::new(DataContractFetchInfo::dpns_contract_fixture( + protocol_version, + )); + let contract_id = fetch_info.contract.id().to_buffer(); + + data_contract_cache.insert(fetch_info, true); + data_contract_cache.merge_and_clear_block_cache(); + + assert!(data_contract_cache.block_cache.get(&contract_id).is_none()); + } + } + + mod clear { + use super::*; + + #[test] + fn test_clear_empties_global_and_block_caches() { + let data_contract_cache = DataContractCache::new(10, 10); + + let protocol_version = PlatformVersion::latest().protocol_version; + let fetch_info_global = Arc::new(DataContractFetchInfo::dpns_contract_fixture( + protocol_version, + )); + let contract_id = fetch_info_global.contract.id().to_buffer(); + let fetch_info_block = Arc::clone(&fetch_info_global); + + data_contract_cache.insert(fetch_info_global, false); + data_contract_cache.insert(fetch_info_block, true); + data_contract_cache.clear(); + + assert!(data_contract_cache.get(contract_id, false).is_none()); + assert!(data_contract_cache.block_cache.get(&contract_id).is_none()); + } + } } diff --git a/packages/rs-drive/src/drive/document/delete/mod.rs b/packages/rs-drive/src/drive/document/delete/mod.rs index 159cdd41a70..2e658677a1d 100644 --- a/packages/rs-drive/src/drive/document/delete/mod.rs +++ b/packages/rs-drive/src/drive/document/delete/mod.rs @@ -61,6 +61,8 @@ mod tests { use crate::config::DriveConfig; use crate::drive::document::tests::setup_dashpay; use crate::drive::Drive; + use crate::error::drive::DriveError; + use crate::error::Error; use crate::util::object_size_info::DocumentInfo::DocumentRefInfo; use crate::util::object_size_info::{DocumentAndContractInfo, OwnedDocumentInfo}; use crate::util::storage_flags::StorageFlags; @@ -74,6 +76,7 @@ mod tests { use dpp::document::Document; use dpp::fee::default_costs::KnownCostItem::StorageDiskUsageCreditPerByte; use dpp::fee::default_costs::{CachedEpochIndexFeeVersions, EpochCosts}; + use dpp::identifier::Identifier; use dpp::tests::json_document::{json_document_to_contract, json_document_to_document}; use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure; @@ -184,6 +187,39 @@ mod tests { assert_eq!(results_on_transaction.len(), 0); } + #[test] + fn test_delete_nonexistent_document_returns_error() { + let (drive, contract) = setup_dashpay("delete-nonexistent", true); + + let platform_version = PlatformVersion::latest(); + + let document_id = Identifier::random(); + + let err = drive + .delete_document_for_contract( + document_id, + &contract, + "profile", + BlockInfo::default(), + true, + None, + platform_version, + Some(&EPOCH_CHANGE_FEE_VERSION_TEST), + ) + .expect_err("expected deleting a nonexistent document to fail"); + + assert!( + matches!( + err, + Error::Drive(DriveError::DeletingDocumentThatDoesNotExist(_)) + ) || matches!( + err, + Error::GroveDB(ref grovedb_error) + if matches!(grovedb_error.as_ref(), grovedb::Error::PathKeyNotFound(_)) + ) + ); + } + #[test] fn test_add_and_remove_family_one_document() { let drive = setup_drive_with_initial_state_structure(None); diff --git a/packages/rs-drive/src/drive/document/insert/mod.rs b/packages/rs-drive/src/drive/document/insert/mod.rs index 9b430e8a85b..144af1a1d44 100644 --- a/packages/rs-drive/src/drive/document/insert/mod.rs +++ b/packages/rs-drive/src/drive/document/insert/mod.rs @@ -51,6 +51,8 @@ mod tests { use once_cell::sync::Lazy; use std::collections::BTreeMap; + use crate::error::drive::DriveError; + use crate::error::Error; use crate::util::object_size_info::DocumentInfo::DocumentRefInfo; use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure; use dpp::block::epoch::Epoch; @@ -153,6 +155,76 @@ mod tests { .expect("expected to override a document successfully"); } + #[test] + fn test_add_dashpay_document_duplicate_insert_returns_error() { + let (drive, dashpay) = setup_dashpay("add-duplicate-error", true); + + let random_owner_id = rand::thread_rng().gen::<[u8; 32]>(); + + let platform_version = PlatformVersion::first(); + + let document_type = dashpay + .document_type_for_name("contactRequest") + .expect("expected to get document type"); + + let dashpay_cr_document = json_document_to_document( + "tests/supporting_files/contract/dashpay/contact-request0.json", + Some(random_owner_id.into()), + document_type, + platform_version, + ) + .expect("expected to get cbor document"); + + drive + .add_document_for_contract( + DocumentAndContractInfo { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentRefInfo(( + &dashpay_cr_document, + StorageFlags::optional_default_as_cow(), + )), + owner_id: Some(random_owner_id), + }, + contract: &dashpay, + document_type, + }, + false, + BlockInfo::default(), + true, + None, + platform_version, + None, + ) + .expect("expected to insert a document successfully"); + + let err = drive + .add_document_for_contract( + DocumentAndContractInfo { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentRefInfo(( + &dashpay_cr_document, + StorageFlags::optional_default_as_cow(), + )), + owner_id: Some(random_owner_id), + }, + contract: &dashpay, + document_type, + }, + false, + BlockInfo::default(), + true, + None, + platform_version, + None, + ) + .expect_err("expected duplicate insert to return an error"); + + assert!(matches!( + err, + Error::Drive(DriveError::CorruptedDocumentAlreadyExists(_)) + )); + } + #[test] fn test_add_dashpay_documents() { let drive = setup_drive_with_initial_state_structure(None); diff --git a/packages/rs-drive/src/drive/document/update/mod.rs b/packages/rs-drive/src/drive/document/update/mod.rs index 2d52d89c1ee..522e40effbd 100644 --- a/packages/rs-drive/src/drive/document/update/mod.rs +++ b/packages/rs-drive/src/drive/document/update/mod.rs @@ -43,6 +43,8 @@ mod tests { use crate::config::DriveConfig; use crate::drive::Drive; + use crate::error::drive::DriveError; + use crate::error::Error; use crate::util::object_size_info::DocumentInfo::{DocumentOwnedInfo, DocumentRefInfo}; use crate::util::object_size_info::{DocumentAndContractInfo, OwnedDocumentInfo}; use crate::util::storage_flags::StorageFlags; @@ -249,6 +251,53 @@ mod tests { assert_eq!(results_no_transaction.len(), 1); } + #[test] + fn test_update_nonexistent_document_returns_error() { + let (drive, contract) = setup_dashpay("update-nonexistent", true); + + let platform_version = PlatformVersion::latest(); + + let document_type = contract + .document_type_for_name("contactRequest") + .expect("contactRequest document exists"); + + let owner_id = random::<[u8; 32]>(); + + let document = json_document_to_document( + "tests/supporting_files/contract/dashpay/contact-request0.json", + Some(owner_id.into()), + document_type, + platform_version, + ) + .expect("expected to get cbor document"); + + let err = drive + .update_document_for_contract( + &document, + &contract, + document_type, + Some(owner_id), + BlockInfo::default(), + true, + StorageFlags::optional_default_as_cow(), + None, + platform_version, + Some(&EPOCH_CHANGE_FEE_VERSION_TEST), + ) + .expect_err("expected updating a nonexistent document to fail"); + + assert!( + matches!( + err, + Error::Drive(DriveError::UpdatingDocumentThatDoesNotExist(_)) + ) || matches!( + err, + Error::GroveDB(ref grovedb_error) + if matches!(grovedb_error.as_ref(), grovedb::Error::PathKeyNotFound(_)) + ) + ); + } + #[test] fn test_create_and_update_document_in_different_transactions() { let (drive, contract) = setup_dashpay("", true);