1use protobuf::Enum;
15
16use crate::{UAttributes, UMessageType, UPriority, UUri};
17
18use crate::UAttributesError;
19
20pub trait UAttributesValidator: Send {
28 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError>;
35
36 fn validate_type(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
42 let expected_type = self.message_type();
43 match attributes.type_.enum_value() {
44 Ok(mt) if mt == expected_type => Ok(()),
45 Ok(mt) => Err(UAttributesError::validation_error(format!(
46 "Wrong Message Type [{}]",
47 mt.to_cloudevent_type()
48 ))),
49 Err(unknown_code) => Err(UAttributesError::validation_error(format!(
50 "Unknown Message Type code [{unknown_code}]"
51 ))),
52 }
53 }
54
55 fn validate_id(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
61 if attributes
63 .id
64 .as_ref()
65 .is_some_and(|id| id.is_uprotocol_uuid())
66 {
67 Ok(())
68 } else {
69 Err(UAttributesError::validation_error(
70 "Attributes must contain valid uProtocol UUID in id property",
71 ))
72 }
73 }
74
75 fn message_type(&self) -> UMessageType;
77
78 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError>;
84
85 fn validate_sink(&self, attributes: &UAttributes) -> Result<(), UAttributesError>;
87}
88
89pub fn validate_rpc_priority(attributes: &UAttributes) -> Result<(), UAttributesError> {
95 attributes
96 .priority
97 .enum_value()
98 .map_err(|unknown_code| {
99 UAttributesError::ValidationError(format!(
100 "RPC message must have a valid priority [{unknown_code}]"
101 ))
102 })
103 .and_then(|prio| {
104 if prio.value() < UPriority::UPRIORITY_CS4.value() {
105 Err(UAttributesError::ValidationError(
106 "RPC message must have a priority of at least CS4".to_string(),
107 ))
108 } else {
109 Ok(())
110 }
111 })
112}
113
114pub enum UAttributesValidators {
116 Publish,
117 Notification,
118 Request,
119 Response,
120}
121
122impl UAttributesValidators {
123 pub fn validator(&self) -> Box<dyn UAttributesValidator> {
144 match self {
145 UAttributesValidators::Publish => Box::new(PublishValidator),
146 UAttributesValidators::Notification => Box::new(NotificationValidator),
147 UAttributesValidators::Request => Box::new(RequestValidator),
148 UAttributesValidators::Response => Box::new(ResponseValidator),
149 }
150 }
151
152 pub fn get_validator_for_attributes(attributes: &UAttributes) -> Box<dyn UAttributesValidator> {
173 Self::get_validator(attributes.type_.enum_value_or_default())
174 }
175
176 pub fn get_validator(message_type: UMessageType) -> Box<dyn UAttributesValidator> {
197 match message_type {
198 UMessageType::UMESSAGE_TYPE_REQUEST => Box::new(RequestValidator),
199 UMessageType::UMESSAGE_TYPE_RESPONSE => Box::new(ResponseValidator),
200 UMessageType::UMESSAGE_TYPE_NOTIFICATION => Box::new(NotificationValidator),
201 _ => Box::new(PublishValidator),
202 }
203 }
204}
205
206pub struct PublishValidator;
208
209impl UAttributesValidator for PublishValidator {
210 fn message_type(&self) -> UMessageType {
211 UMessageType::UMESSAGE_TYPE_PUBLISH
212 }
213
214 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
226 let error_message = vec![
227 self.validate_type(attributes),
228 self.validate_id(attributes),
229 self.validate_source(attributes),
230 self.validate_sink(attributes),
231 ]
232 .into_iter()
233 .filter_map(Result::err)
234 .map(|e| e.to_string())
235 .collect::<Vec<_>>()
236 .join("; ");
237
238 if error_message.is_empty() {
239 Ok(())
240 } else {
241 Err(UAttributesError::validation_error(error_message))
242 }
243 }
244
245 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
255 if let Some(source) = attributes.source.as_ref() {
257 source
258 .verify_event()
259 .map_err(|e| UAttributesError::validation_error(format!("Invalid source URI: {e}")))
260 } else {
261 Err(UAttributesError::validation_error(
262 "Attributes for a publish message must contain a source URI",
263 ))
264 }
265 }
266
267 fn validate_sink(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
273 if attributes.sink.as_ref().is_some() {
275 Err(UAttributesError::validation_error(
276 "Attributes for a publish message must not contain a sink URI",
277 ))
278 } else {
279 Ok(())
280 }
281 }
282}
283
284pub struct NotificationValidator;
286
287impl UAttributesValidator for NotificationValidator {
288 fn message_type(&self) -> UMessageType {
289 UMessageType::UMESSAGE_TYPE_NOTIFICATION
290 }
291
292 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
304 let error_message = vec![
305 self.validate_type(attributes),
306 self.validate_id(attributes),
307 self.validate_source(attributes),
308 self.validate_sink(attributes),
309 ]
310 .into_iter()
311 .filter_map(Result::err)
312 .map(|e| e.to_string())
313 .collect::<Vec<_>>()
314 .join("; ");
315
316 if error_message.is_empty() {
317 Ok(())
318 } else {
319 Err(UAttributesError::validation_error(error_message))
320 }
321 }
322
323 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
333 if let Some(source) = attributes.source.as_ref() {
335 if source.is_rpc_response() {
336 Err(UAttributesError::validation_error(
337 "Origin must not be an RPC response URI",
338 ))
339 } else {
340 source.verify_no_wildcards().map_err(|e| {
341 UAttributesError::validation_error(format!("Invalid source URI: {e}"))
342 })
343 }
344 } else {
345 Err(UAttributesError::validation_error(
346 "Attributes must contain a source URI",
347 ))
348 }
349 }
350
351 fn validate_sink(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
361 if let Some(sink) = attributes.sink.as_ref() {
363 if !sink.is_notification_destination() {
364 Err(UAttributesError::validation_error(
365 "Destination's resource ID must be 0",
366 ))
367 } else {
368 sink.verify_no_wildcards().map_err(|e| {
369 UAttributesError::validation_error(format!("Invalid sink URI: {e}"))
370 })
371 }
372 } else {
373 Err(UAttributesError::validation_error(
374 "Attributes for a notification message must contain a sink URI",
375 ))
376 }
377 }
378}
379
380pub struct RequestValidator;
382
383impl RequestValidator {
384 pub fn validate_ttl(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
390 match attributes.ttl {
392 Some(ttl) if ttl > 0 => Ok(()),
393 Some(invalid_ttl) => Err(UAttributesError::validation_error(format!(
394 "RPC request message's TTL must be a positive integer [{invalid_ttl}]"
395 ))),
396 None => Err(UAttributesError::validation_error(
397 "RPC request message must contain a TTL",
398 )),
399 }
400 }
401}
402
403impl UAttributesValidator for RequestValidator {
404 fn message_type(&self) -> UMessageType {
405 UMessageType::UMESSAGE_TYPE_REQUEST
406 }
407
408 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
422 let error_message = vec![
423 self.validate_type(attributes),
424 self.validate_id(attributes),
425 self.validate_ttl(attributes),
426 self.validate_source(attributes),
427 self.validate_sink(attributes),
428 validate_rpc_priority(attributes),
429 ]
430 .into_iter()
431 .filter_map(Result::err)
432 .map(|e| e.to_string())
433 .collect::<Vec<_>>()
434 .join("; ");
435
436 if error_message.is_empty() {
437 Ok(())
438 } else {
439 Err(UAttributesError::validation_error(error_message))
440 }
441 }
442
443 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
450 if let Some(source) = attributes.source.as_ref() {
452 UUri::verify_rpc_response(source)
453 .map_err(|e| UAttributesError::validation_error(format!("Invalid source URI: {e}")))
454 } else {
455 Err(UAttributesError::validation_error("Attributes for a request message must contain a reply-to address in the source property"))
456 }
457 }
458
459 fn validate_sink(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
466 if let Some(sink) = attributes.sink.as_ref() {
468 UUri::verify_rpc_method(sink)
469 .map_err(|e| UAttributesError::validation_error(format!("Invalid sink URI: {e}")))
470 } else {
471 Err(UAttributesError::validation_error("Attributes for a request message must contain a method-to-invoke in the sink property"))
472 }
473 }
474}
475
476pub struct ResponseValidator;
478
479impl ResponseValidator {
480 pub fn validate_reqid(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
487 if !attributes
488 .reqid
489 .as_ref()
490 .is_some_and(|id| id.is_uprotocol_uuid())
491 {
492 Err(UAttributesError::validation_error(
493 "Request ID is not a valid uProtocol UUID",
494 ))
495 } else {
496 Ok(())
497 }
498 }
499
500 pub fn validate_commstatus(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
506 if let Some(status) = attributes.commstatus {
507 match status.enum_value() {
508 Ok(_) => {
509 return Ok(());
510 }
511 Err(e) => {
512 return Err(UAttributesError::validation_error(format!(
513 "Invalid Communication Status code: {e}"
514 )));
515 }
516 }
517 }
518 Ok(())
519 }
520}
521
522impl UAttributesValidator for ResponseValidator {
523 fn message_type(&self) -> UMessageType {
524 UMessageType::UMESSAGE_TYPE_RESPONSE
525 }
526
527 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
542 let error_message = vec![
543 self.validate_type(attributes),
544 self.validate_id(attributes),
545 self.validate_source(attributes),
546 self.validate_sink(attributes),
547 self.validate_reqid(attributes),
548 self.validate_commstatus(attributes),
549 validate_rpc_priority(attributes),
550 ]
551 .into_iter()
552 .filter_map(Result::err)
553 .map(|e| e.to_string())
554 .collect::<Vec<_>>()
555 .join("; ");
556
557 if error_message.is_empty() {
558 Ok(())
559 } else {
560 Err(UAttributesError::validation_error(error_message))
561 }
562 }
563
564 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
572 if let Some(source) = attributes.source.as_ref() {
574 UUri::verify_rpc_method(source)
575 .map_err(|e| UAttributesError::validation_error(format!("Invalid source URI: {e}")))
576 } else {
577 Err(UAttributesError::validation_error("Missing Source"))
578 }
579 }
580
581 fn validate_sink(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
589 if let Some(sink) = &attributes.sink.as_ref() {
591 UUri::verify_rpc_response(sink)
592 .map_err(|e| UAttributesError::validation_error(format!("Invalid sink URI: {e}")))
593 } else {
594 Err(UAttributesError::validation_error("Missing Sink"))
595 }
596 }
597}
598
599#[cfg(test)]
600mod tests {
601 use protobuf::EnumOrUnknown;
602 use test_case::test_case;
603
604 use super::*;
605 use crate::{UCode, UPriority, UUri, UUID};
606
607 #[test]
608 fn test_validate_type_fails_for_unknown_type_code() {
609 let attributes = UAttributes {
610 type_: EnumOrUnknown::from_i32(20),
611 ..Default::default()
612 };
613 assert!(UAttributesValidators::Publish
614 .validator()
615 .validate_type(&attributes)
616 .is_err());
617 assert!(UAttributesValidators::Notification
618 .validator()
619 .validate_type(&attributes)
620 .is_err());
621 assert!(UAttributesValidators::Request
622 .validator()
623 .validate_type(&attributes)
624 .is_err());
625 assert!(UAttributesValidators::Response
626 .validator()
627 .validate_type(&attributes)
628 .is_err());
629 }
630
631 #[test_case(UMessageType::UMESSAGE_TYPE_UNSPECIFIED, UMessageType::UMESSAGE_TYPE_PUBLISH; "succeeds for Unspecified message")]
632 #[test_case(UMessageType::UMESSAGE_TYPE_PUBLISH, UMessageType::UMESSAGE_TYPE_PUBLISH; "succeeds for Publish message")]
633 #[test_case(UMessageType::UMESSAGE_TYPE_NOTIFICATION, UMessageType::UMESSAGE_TYPE_NOTIFICATION; "succeeds for Notification message")]
634 #[test_case(UMessageType::UMESSAGE_TYPE_REQUEST, UMessageType::UMESSAGE_TYPE_REQUEST; "succeeds for Request message")]
635 #[test_case(UMessageType::UMESSAGE_TYPE_RESPONSE, UMessageType::UMESSAGE_TYPE_RESPONSE; "succeeds for Response message")]
636 fn test_get_validator_returns_matching_validator(
637 message_type: UMessageType,
638 expected_validator_type: UMessageType,
639 ) {
640 let validator: Box<dyn UAttributesValidator> =
641 UAttributesValidators::get_validator(message_type);
642 assert_eq!(validator.message_type(), expected_validator_type);
643 }
644
645 #[test_case(Some(UUID::build()), Some(publish_topic()), None, None, true; "succeeds for topic only")]
646 #[test_case(Some(UUID::build()), Some(publish_topic()), Some(destination()), None, false; "fails for message containing destination")]
648 #[test_case(Some(UUID::build()), Some(publish_topic()), None, Some(100), true; "succeeds for valid attributes")]
649 #[test_case(Some(UUID::build()), None, None, None, false; "fails for missing topic")]
651 #[test_case(Some(UUID::build()), Some(UUri { resource_id: 0x54, ..Default::default()}), None, None, false; "fails for invalid topic")]
653 #[test_case(None, Some(publish_topic()), None, None, false; "fails for missing message ID")]
655 #[test_case(
657 Some(UUID {
658 msb: 0x000000000001C000u64,
660 lsb: 0x8000000000000000u64,
661 ..Default::default()
662 }),
663 Some(publish_topic()),
664 None,
665 None,
666 false;
667 "fails for invalid message id")]
668 fn test_validate_attributes_for_publish_message(
669 id: Option<UUID>,
670 source: Option<UUri>,
671 sink: Option<UUri>,
672 ttl: Option<u32>,
673 expected_result: bool,
674 ) {
675 let attributes = UAttributes {
676 type_: UMessageType::UMESSAGE_TYPE_PUBLISH.into(),
677 id: id.into(),
678 priority: UPriority::UPRIORITY_CS1.into(),
679 source: source.into(),
680 sink: sink.into(),
681 ttl,
682 ..Default::default()
683 };
684 let validator = UAttributesValidators::Publish.validator();
685 let status = validator.validate(&attributes);
686 assert!(status.is_ok() == expected_result);
687 if status.is_ok() {
688 assert!(UAttributesValidators::Notification
689 .validator()
690 .validate(&attributes)
691 .is_err());
692 assert!(UAttributesValidators::Request
693 .validator()
694 .validate(&attributes)
695 .is_err());
696 assert!(UAttributesValidators::Response
697 .validator()
698 .validate(&attributes)
699 .is_err());
700 }
701 }
702
703 #[test_case(Some(UUID::build()), Some(origin()), None, None, false; "fails for missing destination")]
705 #[test_case(Some(UUID::build()), Some(origin()), Some(destination()), None, true; "succeeds for both origin and destination")]
706 #[test_case(Some(UUID::build()), Some(origin()), Some(destination()), Some(100), true; "succeeds for valid attributes")]
707 #[test_case(Some(UUID::build()), None, Some(destination()), None, false; "fails for missing origin")]
709 #[test_case(Some(UUID::build()), Some(UUri::default()), Some(destination()), None, false; "fails for invalid origin")]
711 #[test_case(Some(UUID::build()), Some(origin()), Some(UUri { ue_id: 0xabcd, ue_version_major: 0x01, resource_id: 0x0011, ..Default::default() }), None, false; "fails for invalid destination")]
713 #[test_case(Some(UUID::build()), None, None, None, false; "fails for neither origin nor destination")]
714 #[test_case(None, Some(origin()), Some(destination()), None, false; "fails for missing message ID")]
716 #[test_case(
718 Some(UUID {
719 msb: 0x000000000001C000u64,
721 lsb: 0x8000000000000000u64,
722 ..Default::default()
723 }),
724 Some(origin()),
725 Some(destination()),
726 None,
727 false;
728 "fails for invalid message id")]
729 fn test_validate_attributes_for_notification_message(
730 id: Option<UUID>,
731 source: Option<UUri>,
732 sink: Option<UUri>,
733 ttl: Option<u32>,
734 expected_result: bool,
735 ) {
736 let attributes = UAttributes {
737 type_: UMessageType::UMESSAGE_TYPE_NOTIFICATION.into(),
738 id: id.into(),
739 priority: UPriority::UPRIORITY_CS1.into(),
740 source: source.into(),
741 sink: sink.into(),
742 ttl,
743 ..Default::default()
744 };
745 let validator = UAttributesValidators::Notification.validator();
746 let status = validator.validate(&attributes);
747 assert!(status.is_ok() == expected_result);
748 if status.is_ok() {
749 assert!(UAttributesValidators::Publish
750 .validator()
751 .validate(&attributes)
752 .is_err());
753 assert!(UAttributesValidators::Request
754 .validator()
755 .validate(&attributes)
756 .is_err());
757 assert!(UAttributesValidators::Response
758 .validator()
759 .validate(&attributes)
760 .is_err());
761 }
762 }
763
764 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(reply_to_address()), None, Some(2000), Some(UPriority::UPRIORITY_CS4), None, true; "succeeds for mandatory attributes")]
765 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(reply_to_address()), Some(1), Some(2000), Some(UPriority::UPRIORITY_CS4), Some(String::from("token")), true; "succeeds for valid attributes")]
766 #[test_case(None, Some(method_to_invoke()), Some(reply_to_address()), Some(1), Some(2000), Some(UPriority::UPRIORITY_CS4), Some(String::from("token")), false; "fails for missing message ID")]
768 #[test_case(
770 Some(UUID {
771 msb: 0x000000000001C000u64,
773 lsb: 0x8000000000000000u64,
774 ..Default::default()
775 }),
776 Some(method_to_invoke()),
777 Some(reply_to_address()),
778 None,
779 Some(2000),
780 Some(UPriority::UPRIORITY_CS4),
781 None,
782 false;
783 "fails for invalid message id")]
784 #[test_case(Some(UUID::build()), Some(method_to_invoke()), None, None, Some(2000), Some(UPriority::UPRIORITY_CS4), None, false; "fails for missing reply-to-address")]
786 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(UUri { resource_id: 0x0001, ..Default::default()}), None, Some(2000), Some(UPriority::UPRIORITY_CS4), None, false; "fails for invalid reply-to-address")]
788 #[test_case(Some(UUID::build()), None, Some(reply_to_address()), None, Some(2000), Some(UPriority::UPRIORITY_CS4), None, false; "fails for missing method-to-invoke")]
790 #[test_case(Some(UUID::build()), Some(UUri::default()), Some(reply_to_address()), None, Some(2000), Some(UPriority::UPRIORITY_CS4), None, false; "fails for invalid method-to-invoke")]
792 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(reply_to_address()), Some(1), Some(2000), None, None, false; "fails for missing priority")]
793 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(reply_to_address()), Some(1), Some(2000), Some(UPriority::UPRIORITY_CS3), None, false; "fails for invalid priority")]
794 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(reply_to_address()), None, None, Some(UPriority::UPRIORITY_CS4), None, false; "fails for missing ttl")]
796 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(reply_to_address()), None, Some(0), Some(UPriority::UPRIORITY_CS4), None, false; "fails for ttl = 0")]
798 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(reply_to_address()), Some(1), Some(2000), Some(UPriority::UPRIORITY_CS4), None, true; "succeeds for valid permission level")]
799 #[allow(clippy::too_many_arguments)]
800 fn test_validate_attributes_for_rpc_request_message(
801 id: Option<UUID>,
802 method_to_invoke: Option<UUri>,
803 reply_to_address: Option<UUri>,
804 perm_level: Option<u32>,
805 ttl: Option<u32>,
806 priority: Option<UPriority>,
807 token: Option<String>,
808 expected_result: bool,
809 ) {
810 let attributes = UAttributes {
811 type_: UMessageType::UMESSAGE_TYPE_REQUEST.into(),
812 id: id.into(),
813 priority: priority.unwrap_or(UPriority::UPRIORITY_UNSPECIFIED).into(),
814 source: reply_to_address.into(),
815 sink: method_to_invoke.into(),
816 permission_level: perm_level,
817 ttl,
818 token,
819 ..Default::default()
820 };
821 let status = UAttributesValidators::Request
822 .validator()
823 .validate(&attributes);
824 assert!(status.is_ok() == expected_result);
825 if status.is_ok() {
826 assert!(UAttributesValidators::Publish
827 .validator()
828 .validate(&attributes)
829 .is_err());
830 assert!(UAttributesValidators::Notification
831 .validator()
832 .validate(&attributes)
833 .is_err());
834 assert!(UAttributesValidators::Response
835 .validator()
836 .validate(&attributes)
837 .is_err());
838 }
839 }
840
841 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), None, None, Some(UPriority::UPRIORITY_CS4), true; "succeeds for mandatory attributes")]
842 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), Some(EnumOrUnknown::from(UCode::CANCELLED)), Some(100), Some(UPriority::UPRIORITY_CS4), true; "succeeds for valid attributes")]
843 #[test_case(None, Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), Some(EnumOrUnknown::from(UCode::CANCELLED)), Some(100), Some(UPriority::UPRIORITY_CS4), false; "fails for missing message ID")]
845 #[test_case(
847 Some(UUID {
848 msb: 0x000000000001C000u64,
850 lsb: 0x8000000000000000u64,
851 ..Default::default()
852 }),
853 Some(reply_to_address()),
854 Some(method_to_invoke()),
855 Some(UUID::build()),
856 None,
857 None,
858 Some(UPriority::UPRIORITY_CS4),
859 false;
860 "fails for invalid message id")]
861 #[test_case(Some(UUID::build()), None, Some(method_to_invoke()), Some(UUID::build()), None, None, Some(UPriority::UPRIORITY_CS4), false; "fails for missing reply-to-address")]
863 #[test_case(Some(UUID::build()), Some(UUri { resource_id: 0x0001, ..Default::default()}), Some(method_to_invoke()), Some(UUID::build()), None, None, Some(UPriority::UPRIORITY_CS4), false; "fails for invalid reply-to-address")]
865 #[test_case(Some(UUID::build()), Some(reply_to_address()), None, Some(UUID::build()), None, None, Some(UPriority::UPRIORITY_CS4), false; "fails for missing invoked-method")]
867 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(UUri::default()), Some(UUID::build()), None, None, Some(UPriority::UPRIORITY_CS4), false; "fails for invalid invoked-method")]
869 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), Some(EnumOrUnknown::from(UCode::CANCELLED)), None, Some(UPriority::UPRIORITY_CS4), true; "succeeds for valid commstatus")]
870 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), Some(EnumOrUnknown::from_i32(-42)), None, Some(UPriority::UPRIORITY_CS4), false; "fails for invalid commstatus")]
871 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), None, Some(100), Some(UPriority::UPRIORITY_CS4), true; "succeeds for ttl > 0)")]
872 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), None, Some(0), Some(UPriority::UPRIORITY_CS4), true; "succeeds for ttl = 0")]
873 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), Some(EnumOrUnknown::from(UCode::CANCELLED)), Some(100), None, false; "fails for missing priority")]
874 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), Some(UUID::build()), Some(EnumOrUnknown::from(UCode::CANCELLED)), Some(100), Some(UPriority::UPRIORITY_CS3), false; "fails for invalid priority")]
875 #[test_case(Some(UUID::build()), Some(reply_to_address()), Some(method_to_invoke()), None, None, None, Some(UPriority::UPRIORITY_CS4), false; "fails for missing request id")]
876 #[test_case(
877 Some(UUID::build()),
878 Some(reply_to_address()),
879 Some(method_to_invoke()),
880 Some(UUID {
881 msb: 0x000000000001C000u64,
883 lsb: 0x8000000000000000u64,
884 ..Default::default()
885 }),
886 None,
887 None,
888 Some(UPriority::UPRIORITY_CS4),
889 false;
890 "fails for invalid request id")]
891 #[allow(clippy::too_many_arguments)]
892 fn test_validate_attributes_for_rpc_response_message(
893 id: Option<UUID>,
894 reply_to_address: Option<UUri>,
895 invoked_method: Option<UUri>,
896 reqid: Option<UUID>,
897 commstatus: Option<EnumOrUnknown<UCode>>,
898 ttl: Option<u32>,
899 priority: Option<UPriority>,
900 expected_result: bool,
901 ) {
902 let attributes = UAttributes {
903 type_: UMessageType::UMESSAGE_TYPE_RESPONSE.into(),
904 id: id.into(),
905 priority: priority.unwrap_or(UPriority::UPRIORITY_UNSPECIFIED).into(),
906 reqid: reqid.into(),
907 source: invoked_method.into(),
908 sink: reply_to_address.into(),
909 commstatus,
910 ttl,
911 ..Default::default()
912 };
913 let status = UAttributesValidators::Response
914 .validator()
915 .validate(&attributes);
916 assert!(status.is_ok() == expected_result);
917 if status.is_ok() {
918 assert!(UAttributesValidators::Publish
919 .validator()
920 .validate(&attributes)
921 .is_err());
922 assert!(UAttributesValidators::Notification
923 .validator()
924 .validate(&attributes)
925 .is_err());
926 assert!(UAttributesValidators::Request
927 .validator()
928 .validate(&attributes)
929 .is_err());
930 }
931 }
932
933 fn publish_topic() -> UUri {
934 UUri {
935 authority_name: String::from("vcu.someVin"),
936 ue_id: 0x0000_5410,
937 ue_version_major: 0x01,
938 resource_id: 0xa010,
939 ..Default::default()
940 }
941 }
942
943 fn origin() -> UUri {
944 UUri {
945 authority_name: String::from("vcu.someVin"),
946 ue_id: 0x0000_3c00,
947 ue_version_major: 0x02,
948 resource_id: 0x9a00,
949 ..Default::default()
950 }
951 }
952
953 fn destination() -> UUri {
954 UUri {
955 authority_name: String::from("vcu.someVin"),
956 ue_id: 0x0000_3d07,
957 ue_version_major: 0x01,
958 resource_id: 0x0000,
959 ..Default::default()
960 }
961 }
962
963 fn reply_to_address() -> UUri {
964 UUri {
965 authority_name: String::from("vcu.someVin"),
966 ue_id: 0x0000_010b,
967 ue_version_major: 0x01,
968 resource_id: 0x0000,
969 ..Default::default()
970 }
971 }
972
973 fn method_to_invoke() -> UUri {
974 UUri {
975 authority_name: String::from("vcu.someVin"),
976 ue_id: 0x0000_03ae,
977 ue_version_major: 0x01,
978 resource_id: 0x00e2,
979 ..Default::default()
980 }
981 }
982}