From dab2deb81e4327d1e74a4d9be0c373495dd26361 Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 1 Sep 2025 15:20:18 +0400 Subject: [PATCH 1/4] add multiple boolean flag to `doc_refs_check` --- rust/signed_doc/src/validator/mod.rs | 2 + .../signed_doc/src/validator/rules/content.rs | 1 + .../signed_doc/src/validator/rules/doc_ref.rs | 125 +++++++++++++++++- .../src/validator/rules/parameters.rs | 1 + rust/signed_doc/src/validator/rules/reply.rs | 1 + 5 files changed, 129 insertions(+), 1 deletion(-) diff --git a/rust/signed_doc/src/validator/mod.rs b/rust/signed_doc/src/validator/mod.rs index 983b851ab03..5feafdd17cf 100644 --- a/rust/signed_doc/src/validator/mod.rs +++ b/rust/signed_doc/src/validator/mod.rs @@ -90,6 +90,7 @@ fn proposal_comment_rule() -> Rules { }, doc_ref: RefRule::Specified { exp_ref_types: vec![PROPOSAL.clone()], + multiple: true, optional: false, }, reply: ReplyRule::Specified { @@ -146,6 +147,7 @@ fn proposal_submission_action_rule() -> Rules { }, doc_ref: RefRule::Specified { exp_ref_types: vec![PROPOSAL.clone()], + multiple: true, optional: false, }, reply: ReplyRule::NotSpecified, diff --git a/rust/signed_doc/src/validator/rules/content.rs b/rust/signed_doc/src/validator/rules/content.rs index 8b9c8ac24fb..ab24b2f864e 100644 --- a/rust/signed_doc/src/validator/rules/content.rs +++ b/rust/signed_doc/src/validator/rules/content.rs @@ -91,6 +91,7 @@ impl ContentRule { return doc_refs_check( template_ref, std::slice::from_ref(exp_template_type), + false, "template", provider, doc.report(), diff --git a/rust/signed_doc/src/validator/rules/doc_ref.rs b/rust/signed_doc/src/validator/rules/doc_ref.rs index 938f19bf02c..6ef3c509819 100644 --- a/rust/signed_doc/src/validator/rules/doc_ref.rs +++ b/rust/signed_doc/src/validator/rules/doc_ref.rs @@ -14,6 +14,8 @@ pub(crate) enum RefRule { Specified { /// expected `type` field of the referenced doc exp_ref_types: Vec, + /// allows multiple document references or only one + multiple: bool, /// optional flag for the `ref` field optional: bool, }, @@ -33,6 +35,7 @@ impl RefRule { let context: &str = "Ref rule check"; if let Self::Specified { exp_ref_types, + multiple, optional, } = self { @@ -40,6 +43,7 @@ impl RefRule { return doc_refs_check( doc_refs, exp_ref_types, + *multiple, "ref", provider, doc.report(), @@ -73,6 +77,7 @@ impl RefRule { pub(crate) async fn doc_refs_check( doc_refs: &DocumentRefs, exp_ref_types: &[DocType], + multiple: bool, field_name: &str, provider: &Provider, report: &ProblemReport, @@ -84,6 +89,18 @@ where { let mut all_valid = true; + if !multiple && doc_refs.len() > 1 { + report.other( + format!( + "Only ONE document reference is allowed, found {} document references", + doc_refs.len() + ) + .as_str(), + &format!("Referenced document validation for the `{field_name}` field"), + ); + return Ok(false); + } + for dr in doc_refs.iter() { if let Some(ref ref_doc) = provider.try_get_doc(dr).await? { let is_valid = referenced_doc_type_check(ref_doc, exp_ref_types, field_name, report) @@ -396,7 +413,109 @@ mod tests { "valid reference to the missing one document" )] #[tokio::test] - async fn ref_specified_test( + async fn ref_multiple_specified_test( + doc_gen: impl FnOnce(&[DocType; 2], &mut TestCatalystProvider) -> CatalystSignedDocument + ) -> bool { + let mut provider = TestCatalystProvider::default(); + + let exp_types: [DocType; 2] = [UuidV4::new().into(), UuidV4::new().into()]; + + let doc = doc_gen(&exp_types, &mut provider); + + let non_optional_res = RefRule::Specified { + exp_ref_types: exp_types.to_vec(), + multiple: true, + optional: false, + } + .check(&doc, &provider) + .await + .unwrap(); + + let optional_res = RefRule::Specified { + exp_ref_types: exp_types.to_vec(), + multiple: true, + optional: true, + } + .check(&doc, &provider) + .await + .unwrap(); + + assert_eq!(non_optional_res, optional_res); + non_optional_res + } + + #[test_case( + |exp_types, provider| { + let ref_doc = Builder::new() + .with_metadata_field(SupportedField::Id(UuidV7::new())) + .with_metadata_field(SupportedField::Ver(UuidV7::new())) + .with_metadata_field(SupportedField::Type(exp_types[0].clone())) + .build(); + provider.add_document(None, &ref_doc).unwrap(); + + Builder::new() + .with_metadata_field(SupportedField::Ref( + vec![DocumentRef::new( + ref_doc.doc_id().unwrap(), + ref_doc.doc_ver().unwrap(), + DocLocator::default(), + )] + .into(), + )) + .build() + } + => true + ; + "valid reference to the one correct document" + )] + #[test_case( + |exp_types, provider| { + let ref_doc_1 = Builder::new() + .with_metadata_field(SupportedField::Id(UuidV7::new())) + .with_metadata_field(SupportedField::Ver(UuidV7::new())) + .with_metadata_field(SupportedField::Type(exp_types[0].clone())) + .build(); + provider.add_document(None, &ref_doc_1).unwrap(); + let ref_doc_2 = Builder::new() + .with_metadata_field(SupportedField::Id(UuidV7::new())) + .with_metadata_field(SupportedField::Ver(UuidV7::new())) + .with_metadata_field(SupportedField::Type(exp_types[1].clone())) + .build(); + provider.add_document(None, &ref_doc_2).unwrap(); + let ref_doc_3 = Builder::new() + .with_metadata_field(SupportedField::Id(UuidV7::new())) + .with_metadata_field(SupportedField::Ver(UuidV7::new())) + .with_metadata_field(SupportedField::Type(exp_types[0].clone())) + .build(); + provider.add_document(None, &ref_doc_3).unwrap(); + + Builder::new() + .with_metadata_field(SupportedField::Ref( + vec![DocumentRef::new( + ref_doc_1.doc_id().unwrap(), + ref_doc_1.doc_ver().unwrap(), + DocLocator::default(), + ), + DocumentRef::new( + ref_doc_2.doc_id().unwrap(), + ref_doc_2.doc_ver().unwrap(), + DocLocator::default(), + ), + DocumentRef::new( + ref_doc_3.doc_id().unwrap(), + ref_doc_3.doc_ver().unwrap(), + DocLocator::default(), + )] + .into(), + )) + .build() + } + => false + ; + "valid reference to the multiple documents" + )] + #[tokio::test] + async fn ref_non_multiple_specified_test( doc_gen: impl FnOnce(&[DocType; 2], &mut TestCatalystProvider) -> CatalystSignedDocument ) -> bool { let mut provider = TestCatalystProvider::default(); @@ -407,6 +526,7 @@ mod tests { let non_optional_res = RefRule::Specified { exp_ref_types: exp_types.to_vec(), + multiple: false, optional: false, } .check(&doc, &provider) @@ -415,6 +535,7 @@ mod tests { let optional_res = RefRule::Specified { exp_ref_types: exp_types.to_vec(), + multiple: false, optional: true, } .check(&doc, &provider) @@ -430,6 +551,7 @@ mod tests { let provider = TestCatalystProvider::default(); let rule = RefRule::Specified { exp_ref_types: vec![UuidV4::new().into()], + multiple: true, optional: true, }; @@ -439,6 +561,7 @@ mod tests { let provider = TestCatalystProvider::default(); let rule = RefRule::Specified { exp_ref_types: vec![UuidV4::new().into()], + multiple: true, optional: false, }; diff --git a/rust/signed_doc/src/validator/rules/parameters.rs b/rust/signed_doc/src/validator/rules/parameters.rs index 19dfc9188ff..af5d2411d36 100644 --- a/rust/signed_doc/src/validator/rules/parameters.rs +++ b/rust/signed_doc/src/validator/rules/parameters.rs @@ -43,6 +43,7 @@ impl ParametersRule { let parameters_check = doc_refs_check( parameters_ref, exp_parameters_type, + false, "parameters", provider, doc.report(), diff --git a/rust/signed_doc/src/validator/rules/reply.rs b/rust/signed_doc/src/validator/rules/reply.rs index edb49a4bbc3..fe634ecc99d 100644 --- a/rust/signed_doc/src/validator/rules/reply.rs +++ b/rust/signed_doc/src/validator/rules/reply.rs @@ -66,6 +66,7 @@ impl ReplyRule { return doc_refs_check( reply_ref, std::slice::from_ref(exp_reply_type), + false, "reply", provider, doc.report(), From 0474cf73088f35a865b949b12f2f35eeee78913b Mon Sep 17 00:00:00 2001 From: Mr-Leshiy Date: Mon, 1 Sep 2025 21:07:48 +0400 Subject: [PATCH 2/4] wip --- rust/signed_doc/src/validator/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rust/signed_doc/src/validator/mod.rs b/rust/signed_doc/src/validator/mod.rs index 5feafdd17cf..df04585402d 100644 --- a/rust/signed_doc/src/validator/mod.rs +++ b/rust/signed_doc/src/validator/mod.rs @@ -90,7 +90,7 @@ fn proposal_comment_rule() -> Rules { }, doc_ref: RefRule::Specified { exp_ref_types: vec![PROPOSAL.clone()], - multiple: true, + multiple: false, optional: false, }, reply: ReplyRule::Specified { @@ -147,7 +147,7 @@ fn proposal_submission_action_rule() -> Rules { }, doc_ref: RefRule::Specified { exp_ref_types: vec![PROPOSAL.clone()], - multiple: true, + multiple: false, optional: false, }, reply: ReplyRule::NotSpecified, From 4bbf806f15b573577c48f7c5794bc0ac86c6da85 Mon Sep 17 00:00:00 2001 From: Alex Pozhylenkov Date: Tue, 2 Sep 2025 10:23:29 +0400 Subject: [PATCH 3/4] Update rust/signed_doc/src/validator/rules/doc_ref.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Joaquín Rosales --- rust/signed_doc/src/validator/rules/doc_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/signed_doc/src/validator/rules/doc_ref.rs b/rust/signed_doc/src/validator/rules/doc_ref.rs index 6ef3c509819..187cfd1fa96 100644 --- a/rust/signed_doc/src/validator/rules/doc_ref.rs +++ b/rust/signed_doc/src/validator/rules/doc_ref.rs @@ -466,7 +466,7 @@ mod tests { } => true ; - "valid reference to the one correct document" + "valid document with a single reference" )] #[test_case( |exp_types, provider| { From 061e6129e020cbc25bd6044107f0f5d6ee19697b Mon Sep 17 00:00:00 2001 From: Alex Pozhylenkov Date: Tue, 2 Sep 2025 10:23:37 +0400 Subject: [PATCH 4/4] Update rust/signed_doc/src/validator/rules/doc_ref.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Joaquín Rosales --- rust/signed_doc/src/validator/rules/doc_ref.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rust/signed_doc/src/validator/rules/doc_ref.rs b/rust/signed_doc/src/validator/rules/doc_ref.rs index 187cfd1fa96..a84859030c5 100644 --- a/rust/signed_doc/src/validator/rules/doc_ref.rs +++ b/rust/signed_doc/src/validator/rules/doc_ref.rs @@ -512,7 +512,7 @@ mod tests { } => false ; - "valid reference to the multiple documents" + "valid document with multiple references" )] #[tokio::test] async fn ref_non_multiple_specified_test(