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