From 1cb1601fc0c3a7b2add9ae134ebdad50ebbfd9f8 Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 24 Jun 2026 16:54:44 -0400 Subject: [PATCH 1/3] test(drive): cover shared-prefix aggregate index insertion Adds a Drive e2e regression test for the accepted contract shape from #3960: a count+sum prefix index combined with a deeper range-countable index sharing that prefix. The test currently fails with the unsupported NotCountedOrSummed wrapping error, documenting the gap until validation or layout support is fixed. --- .../insert/insert_contract/v0/tests/mod.rs | 4 + .../shared_prefix_aggregation_e2e_tests.rs | 150 ++++++++++++++++++ 2 files changed, 154 insertions(+) create mode 100644 packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/mod.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/mod.rs index 5e9a4da4fb2..a6d29b9e29a 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/mod.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/mod.rs @@ -15,7 +15,11 @@ //! `rangeSummable` / `rangeCountable` 4-way dispatcher //! (`(false, false) | (true, false) | (false, true) | (true, //! true)` corners of the matrix). +//! - [`shared_prefix_aggregation_e2e_tests`] — cross-index shared +//! prefix layouts where a shorter aggregate index and a deeper +//! range index must compose safely during document insertion. mod countable_e2e_tests; mod range_countable_index_e2e_tests; mod range_summable_index_e2e_tests; +mod shared_prefix_aggregation_e2e_tests; diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs new file mode 100644 index 00000000000..ddb7e39663b --- /dev/null +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs @@ -0,0 +1,150 @@ +//! End-to-end coverage for shared-prefix aggregate index layouts. +//! +//! A shorter index can terminate at a prefix while another index +//! continues below that same prefix. When the prefix index uses count +//! + sum aggregation, child continuation trees under the prefix value +//! tree must either be supported by Drive's wrapper logic or the +//! contract must be rejected before publish. + +use crate::drive::Drive; +use crate::util::object_size_info::DocumentInfo::DocumentRefInfo; +use crate::util::object_size_info::{DocumentAndContractInfo, OwnedDocumentInfo}; +use crate::util::storage_flags::StorageFlags; +use crate::util::test_helpers::setup::setup_drive_with_initial_state_structure; +use dpp::block::block_info::BlockInfo; +use dpp::data_contract::accessors::v0::DataContractV0Getters; +use dpp::data_contract::document_type::random_document::CreateRandomDocument; +use dpp::data_contract::DataContractFactory; +use dpp::document::DocumentV0Setters; +use dpp::platform_value::{platform_value, Value}; +use dpp::prelude::DataContract; +use dpp::tests::utils::generate_random_identifier_struct; +use dpp::version::PlatformVersion; +use std::collections::BTreeMap; + +const PROTOCOL_VERSION_V12: u32 = 12; + +fn build_review_contract() -> DataContract { + let factory = + DataContractFactory::new(PROTOCOL_VERSION_V12).expect("expected to create factory"); + + let document_schema = platform_value!({ + "type": "object", + "documentsMutable": true, + "documentsKeepHistory": true, + "canBeDeleted": false, + "properties": { + "resourceId": { + "type": "string", + "minLength": 1, + "maxLength": 63, + "position": 0, + }, + "rating": { + "type": "integer", + "minimum": 1, + "maximum": 5, + "position": 1, + }, + "reviewText": { + "type": "string", + "maxLength": 1000, + "position": 2, + }, + }, + "required": ["$createdAt", "$updatedAt", "resourceId", "rating"], + "additionalProperties": false, + "indices": [ + { + "name": "ownerAndResource", + "unique": true, + "properties": [{"$ownerId": "asc"}, {"resourceId": "asc"}], + }, + { + "name": "ownerReviews", + "properties": [{"$ownerId": "asc"}, {"$updatedAt": "asc"}], + }, + { + "name": "resourceRatingAggregate", + "properties": [{"resourceId": "asc"}], + "countable": "countable", + "summable": "rating", + }, + { + "name": "resourceRatingDistribution", + "properties": [{"resourceId": "asc"}, {"rating": "asc"}], + "countable": "countable", + "rangeCountable": true, + }, + ], + }); + + let schemas = platform_value!({ "review": document_schema }); + let owner_id = generate_random_identifier_struct(); + + factory + .create_with_value_config(owner_id, 0, schemas, None, None) + .expect("expected to create data contract") + .data_contract_owned() +} + +fn apply_contract(drive: &Drive, contract: &DataContract) { + drive + .apply_contract( + contract, + BlockInfo::default(), + true, + StorageFlags::optional_default_as_cow(), + None, + PlatformVersion::latest(), + ) + .expect("expected to apply contract"); +} + +#[test] +fn insert_document_with_shared_prefix_count_sum_and_range_countable_indexes_succeeds() { + let drive = setup_drive_with_initial_state_structure(None); + let pv = PlatformVersion::latest(); + let contract = build_review_contract(); + apply_contract(&drive, &contract); + + let document_type = contract + .document_type_for_name("review") + .expect("review document type exists"); + let mut document = document_type + .random_document(Some(1), pv) + .expect("random review document"); + let mut properties = BTreeMap::new(); + properties.insert( + "resourceId".to_string(), + Value::Text("resource-1".to_string()), + ); + properties.insert("rating".to_string(), Value::U8(5)); + properties.insert( + "reviewText".to_string(), + Value::Text("works as expected".to_string()), + ); + document.set_properties(properties); + + drive + .add_document_for_contract( + DocumentAndContractInfo { + owned_document_info: OwnedDocumentInfo { + document_info: DocumentRefInfo((&document, None)), + owner_id: Some(generate_random_identifier_struct().into()), + }, + contract: &contract, + document_type, + }, + false, + BlockInfo::default(), + true, + None, + pv, + None, + ) + .expect( + "accepted shared-prefix aggregate indexes must not fail document insertion due to \ + unsupported NotCountedOrSummed wrapping", + ); +} From bd2bd25efd2509ad7b245dbe05149bc77a98a5bb Mon Sep 17 00:00:00 2001 From: thephez Date: Wed, 24 Jun 2026 17:05:51 -0400 Subject: [PATCH 2/3] test(drive): expand shared-prefix aggregate index matrix Extends the shared-prefix aggregate index regression coverage to exercise count-only, sum-only, and count+sum parent value trees against plain, range-count, range-sum, and combined range count+sum child continuations. The matrix distinguishes compatible cases that must insert from unsupported layouts that should either be rejected during contract validation or supported by Drive insertion. --- .../shared_prefix_aggregation_e2e_tests.rs | 281 +++++++++++++++--- 1 file changed, 245 insertions(+), 36 deletions(-) diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs index ddb7e39663b..327ad677ed0 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs @@ -24,10 +24,138 @@ use std::collections::BTreeMap; const PROTOCOL_VERSION_V12: u32 = 12; -fn build_review_contract() -> DataContract { +#[derive(Clone, Copy)] +struct IndexFlags { + countable: bool, + range_countable: bool, + summable: bool, + range_summable: bool, +} + +impl IndexFlags { + const PLAIN: Self = Self { + countable: false, + range_countable: false, + summable: false, + range_summable: false, + }; + const COUNT: Self = Self { + countable: true, + range_countable: false, + summable: false, + range_summable: false, + }; + const SUM: Self = Self { + countable: false, + range_countable: false, + summable: true, + range_summable: false, + }; + const COUNT_SUM: Self = Self { + countable: true, + range_countable: false, + summable: true, + range_summable: false, + }; + const RANGE_COUNT: Self = Self { + countable: true, + range_countable: true, + summable: false, + range_summable: false, + }; + const RANGE_SUM: Self = Self { + countable: false, + range_countable: false, + summable: true, + range_summable: true, + }; + const RANGE_COUNT_SUM: Self = Self { + countable: true, + range_countable: true, + summable: true, + range_summable: true, + }; +} + +struct SharedPrefixCase { + name: &'static str, + prefix_flags: IndexFlags, + child_flags: IndexFlags, + must_insert: bool, +} + +enum SharedPrefixOutcome { + Inserted, + ContractRejected(String), + InsertFailed(String), +} + +fn review_index(name: &str, properties: Vec, flags: IndexFlags) -> Value { + let mut index = vec![ + ( + Value::Text("name".to_string()), + Value::Text(name.to_string()), + ), + ( + Value::Text("properties".to_string()), + Value::Array(properties), + ), + ]; + + if flags.countable { + index.push(( + Value::Text("countable".to_string()), + Value::Text("countable".to_string()), + )); + } + if flags.range_countable { + index.push((Value::Text("rangeCountable".to_string()), Value::Bool(true))); + } + if flags.summable { + index.push(( + Value::Text("summable".to_string()), + Value::Text("rating".to_string()), + )); + } + if flags.range_summable { + index.push((Value::Text("rangeSummable".to_string()), Value::Bool(true))); + } + + Value::Map(index) +} + +fn build_review_contract( + prefix_flags: IndexFlags, + child_flags: IndexFlags, +) -> Result { let factory = DataContractFactory::new(PROTOCOL_VERSION_V12).expect("expected to create factory"); + let indices = vec![ + platform_value!({ + "name": "ownerAndResource", + "unique": true, + "properties": [{"$ownerId": "asc"}, {"resourceId": "asc"}], + }), + platform_value!({ + "name": "ownerReviews", + "properties": [{"$ownerId": "asc"}, {"$updatedAt": "asc"}], + }), + review_index( + "resourceRatingAggregate", + vec![platform_value!({"resourceId": "asc"})], + prefix_flags, + ), + review_index( + "resourceRatingDistribution", + vec![ + platform_value!({"resourceId": "asc"}), + platform_value!({"rating": "asc"}), + ], + child_flags, + ), + ]; + let document_schema = platform_value!({ "type": "object", "documentsMutable": true, @@ -54,29 +182,7 @@ fn build_review_contract() -> DataContract { }, "required": ["$createdAt", "$updatedAt", "resourceId", "rating"], "additionalProperties": false, - "indices": [ - { - "name": "ownerAndResource", - "unique": true, - "properties": [{"$ownerId": "asc"}, {"resourceId": "asc"}], - }, - { - "name": "ownerReviews", - "properties": [{"$ownerId": "asc"}, {"$updatedAt": "asc"}], - }, - { - "name": "resourceRatingAggregate", - "properties": [{"resourceId": "asc"}], - "countable": "countable", - "summable": "rating", - }, - { - "name": "resourceRatingDistribution", - "properties": [{"resourceId": "asc"}, {"rating": "asc"}], - "countable": "countable", - "rangeCountable": true, - }, - ], + "indices": Value::Array(indices), }); let schemas = platform_value!({ "review": document_schema }); @@ -84,11 +190,11 @@ fn build_review_contract() -> DataContract { factory .create_with_value_config(owner_id, 0, schemas, None, None) - .expect("expected to create data contract") - .data_contract_owned() + .map(|contract| contract.data_contract_owned()) + .map_err(|error| format!("{error:?}")) } -fn apply_contract(drive: &Drive, contract: &DataContract) { +fn apply_contract(drive: &Drive, contract: &DataContract) -> Result<(), String> { drive .apply_contract( contract, @@ -98,15 +204,20 @@ fn apply_contract(drive: &Drive, contract: &DataContract) { None, PlatformVersion::latest(), ) - .expect("expected to apply contract"); + .map(|_| ()) + .map_err(|error| format!("{error:?}")) } -#[test] -fn insert_document_with_shared_prefix_count_sum_and_range_countable_indexes_succeeds() { +fn insert_review_document_for_case(case: &SharedPrefixCase) -> SharedPrefixOutcome { let drive = setup_drive_with_initial_state_structure(None); let pv = PlatformVersion::latest(); - let contract = build_review_contract(); - apply_contract(&drive, &contract); + let contract = match build_review_contract(case.prefix_flags, case.child_flags) { + Ok(contract) => contract, + Err(error) => return SharedPrefixOutcome::ContractRejected(error), + }; + if let Err(error) = apply_contract(&drive, &contract) { + return SharedPrefixOutcome::ContractRejected(error); + } let document_type = contract .document_type_for_name("review") @@ -143,8 +254,106 @@ fn insert_document_with_shared_prefix_count_sum_and_range_countable_indexes_succ pv, None, ) - .expect( - "accepted shared-prefix aggregate indexes must not fail document insertion due to \ - unsupported NotCountedOrSummed wrapping", - ); + .map_or_else( + |error| SharedPrefixOutcome::InsertFailed(format!("{error:?}")), + |_| SharedPrefixOutcome::Inserted, + ) +} + +#[test] +fn shared_prefix_aggregate_index_combinations_reject_or_insert() { + let cases = [ + SharedPrefixCase { + name: "count_parent_plain_child", + prefix_flags: IndexFlags::COUNT, + child_flags: IndexFlags::PLAIN, + must_insert: true, + }, + SharedPrefixCase { + name: "count_parent_range_count_child", + prefix_flags: IndexFlags::COUNT, + child_flags: IndexFlags::RANGE_COUNT, + must_insert: true, + }, + SharedPrefixCase { + name: "count_parent_range_sum_child", + prefix_flags: IndexFlags::COUNT, + child_flags: IndexFlags::RANGE_SUM, + must_insert: false, + }, + SharedPrefixCase { + name: "count_parent_range_count_sum_child", + prefix_flags: IndexFlags::COUNT, + child_flags: IndexFlags::RANGE_COUNT_SUM, + must_insert: false, + }, + SharedPrefixCase { + name: "sum_parent_plain_child", + prefix_flags: IndexFlags::SUM, + child_flags: IndexFlags::PLAIN, + must_insert: false, + }, + SharedPrefixCase { + name: "sum_parent_range_count_child", + prefix_flags: IndexFlags::SUM, + child_flags: IndexFlags::RANGE_COUNT, + must_insert: false, + }, + SharedPrefixCase { + name: "sum_parent_range_sum_child", + prefix_flags: IndexFlags::SUM, + child_flags: IndexFlags::RANGE_SUM, + must_insert: true, + }, + SharedPrefixCase { + name: "sum_parent_range_count_sum_child", + prefix_flags: IndexFlags::SUM, + child_flags: IndexFlags::RANGE_COUNT_SUM, + must_insert: true, + }, + SharedPrefixCase { + name: "count_sum_parent_plain_child", + prefix_flags: IndexFlags::COUNT_SUM, + child_flags: IndexFlags::PLAIN, + must_insert: false, + }, + SharedPrefixCase { + name: "count_sum_parent_range_count_child", + prefix_flags: IndexFlags::COUNT_SUM, + child_flags: IndexFlags::RANGE_COUNT, + must_insert: false, + }, + SharedPrefixCase { + name: "count_sum_parent_range_sum_child", + prefix_flags: IndexFlags::COUNT_SUM, + child_flags: IndexFlags::RANGE_SUM, + must_insert: true, + }, + SharedPrefixCase { + name: "count_sum_parent_range_count_sum_child", + prefix_flags: IndexFlags::COUNT_SUM, + child_flags: IndexFlags::RANGE_COUNT_SUM, + must_insert: true, + }, + ]; + + let failures = cases + .iter() + .filter_map(|case| match insert_review_document_for_case(case) { + SharedPrefixOutcome::Inserted => None, + SharedPrefixOutcome::ContractRejected(_) if !case.must_insert => None, + SharedPrefixOutcome::ContractRejected(error) => Some(format!( + "{}: compatible shared-prefix aggregate indexes were rejected: {}", + case.name, error + )), + SharedPrefixOutcome::InsertFailed(error) => Some(format!("{}: {}", case.name, error)), + }) + .collect::>(); + + assert!( + failures.is_empty(), + "shared-prefix aggregate index contracts must either reject unsupported layouts at \ + contract creation or allow document insertion; incompatible accepted layouts failed for:\n{}", + failures.join("\n") + ); } From 3ab8552b2389fa030d275301f1d98c9fd3a1c4dc Mon Sep 17 00:00:00 2001 From: thephez Date: Thu, 25 Jun 2026 09:44:48 -0400 Subject: [PATCH 3/3] test(drive): keep shared-prefix regression ignored --- .../v0/tests/shared_prefix_aggregation_e2e_tests.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs index 327ad677ed0..4bc16c14986 100644 --- a/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs +++ b/packages/rs-drive/src/drive/contract/insert/insert_contract/v0/tests/shared_prefix_aggregation_e2e_tests.rs @@ -87,6 +87,7 @@ struct SharedPrefixCase { enum SharedPrefixOutcome { Inserted, ContractRejected(String), + ApplyFailed(String), InsertFailed(String), } @@ -216,7 +217,7 @@ fn insert_review_document_for_case(case: &SharedPrefixCase) -> SharedPrefixOutco Err(error) => return SharedPrefixOutcome::ContractRejected(error), }; if let Err(error) = apply_contract(&drive, &contract) { - return SharedPrefixOutcome::ContractRejected(error); + return SharedPrefixOutcome::ApplyFailed(error); } let document_type = contract @@ -261,6 +262,7 @@ fn insert_review_document_for_case(case: &SharedPrefixCase) -> SharedPrefixOutco } #[test] +#[ignore = "tracks #3960; unsupported shared-prefix aggregate layouts are accepted but fail insertion today"] fn shared_prefix_aggregate_index_combinations_reject_or_insert() { let cases = [ SharedPrefixCase { @@ -346,6 +348,10 @@ fn shared_prefix_aggregate_index_combinations_reject_or_insert() { "{}: compatible shared-prefix aggregate indexes were rejected: {}", case.name, error )), + SharedPrefixOutcome::ApplyFailed(error) => Some(format!( + "{}: contract application failed: {}", + case.name, error + )), SharedPrefixOutcome::InsertFailed(error) => Some(format!("{}: {}", case.name, error)), }) .collect::>();