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
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 [{}]",
101 unknown_code
102 ))
103 })
104 .and_then(|prio| {
105 if prio.value() < UPriority::UPRIORITY_CS4.value() {
106 Err(UAttributesError::ValidationError(
107 "RPC message must have a priority of at least CS4".to_string(),
108 ))
109 } else {
110 Ok(())
111 }
112 })
113}
114
115pub enum UAttributesValidators {
117 Publish,
118 Notification,
119 Request,
120 Response,
121}
122
123impl UAttributesValidators {
124 pub fn validator(&self) -> Box<dyn UAttributesValidator> {
145 match self {
146 UAttributesValidators::Publish => Box::new(PublishValidator),
147 UAttributesValidators::Notification => Box::new(NotificationValidator),
148 UAttributesValidators::Request => Box::new(RequestValidator),
149 UAttributesValidators::Response => Box::new(ResponseValidator),
150 }
151 }
152
153 pub fn get_validator_for_attributes(attributes: &UAttributes) -> Box<dyn UAttributesValidator> {
174 Self::get_validator(attributes.type_.enum_value_or_default())
175 }
176
177 pub fn get_validator(message_type: UMessageType) -> Box<dyn UAttributesValidator> {
198 match message_type {
199 UMessageType::UMESSAGE_TYPE_REQUEST => Box::new(RequestValidator),
200 UMessageType::UMESSAGE_TYPE_RESPONSE => Box::new(ResponseValidator),
201 UMessageType::UMESSAGE_TYPE_NOTIFICATION => Box::new(NotificationValidator),
202 _ => Box::new(PublishValidator),
203 }
204 }
205}
206
207pub struct PublishValidator;
209
210impl UAttributesValidator for PublishValidator {
211 fn message_type(&self) -> UMessageType {
212 UMessageType::UMESSAGE_TYPE_PUBLISH
213 }
214
215 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
227 let error_message = vec![
228 self.validate_type(attributes),
229 self.validate_id(attributes),
230 self.validate_source(attributes),
231 self.validate_sink(attributes),
232 ]
233 .into_iter()
234 .filter_map(Result::err)
235 .map(|e| e.to_string())
236 .collect::<Vec<_>>()
237 .join("; ");
238
239 if error_message.is_empty() {
240 Ok(())
241 } else {
242 Err(UAttributesError::validation_error(error_message))
243 }
244 }
245
246 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
256 if let Some(source) = attributes.source.as_ref() {
257 source.verify_event().map_err(|e| {
258 UAttributesError::validation_error(format!("Invalid source URI: {}", e))
259 })
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() {
274 Err(UAttributesError::validation_error(
275 "Attributes for a publish message must not contain a sink URI",
276 ))
277 } else {
278 Ok(())
279 }
280 }
281}
282
283pub struct NotificationValidator;
285
286impl UAttributesValidator for NotificationValidator {
287 fn message_type(&self) -> UMessageType {
288 UMessageType::UMESSAGE_TYPE_NOTIFICATION
289 }
290
291 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
303 let error_message = vec![
304 self.validate_type(attributes),
305 self.validate_id(attributes),
306 self.validate_source(attributes),
307 self.validate_sink(attributes),
308 ]
309 .into_iter()
310 .filter_map(Result::err)
311 .map(|e| e.to_string())
312 .collect::<Vec<_>>()
313 .join("; ");
314
315 if error_message.is_empty() {
316 Ok(())
317 } else {
318 Err(UAttributesError::validation_error(error_message))
319 }
320 }
321
322 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
332 if let Some(source) = attributes.source.as_ref() {
333 if source.is_rpc_response() {
334 Err(UAttributesError::validation_error(
335 "Origin must not be an RPC response URI",
336 ))
337 } else {
338 source.verify_no_wildcards().map_err(|e| {
339 UAttributesError::validation_error(format!("Invalid source URI: {}", e))
340 })
341 }
342 } else {
343 Err(UAttributesError::validation_error(
344 "Attributes must contain a source URI",
345 ))
346 }
347 }
348
349 fn validate_sink(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
359 if let Some(sink) = attributes.sink.as_ref() {
360 if !sink.is_notification_destination() {
361 Err(UAttributesError::validation_error(
362 "Destination's resource ID must be 0",
363 ))
364 } else {
365 sink.verify_no_wildcards().map_err(|e| {
366 UAttributesError::validation_error(format!("Invalid sink URI: {}", e))
367 })
368 }
369 } else {
370 Err(UAttributesError::validation_error(
371 "Attributes for a notification message must contain a sink URI",
372 ))
373 }
374 }
375}
376
377pub struct RequestValidator;
379
380impl RequestValidator {
381 pub fn validate_ttl(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
387 match attributes.ttl {
388 Some(ttl) if ttl > 0 => Ok(()),
389 Some(invalid_ttl) => Err(UAttributesError::validation_error(format!(
390 "RPC request message's TTL must be a positive integer [{invalid_ttl}]"
391 ))),
392 None => Err(UAttributesError::validation_error(
393 "RPC request message must contain a TTL",
394 )),
395 }
396 }
397}
398
399impl UAttributesValidator for RequestValidator {
400 fn message_type(&self) -> UMessageType {
401 UMessageType::UMESSAGE_TYPE_REQUEST
402 }
403
404 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
418 let error_message = vec![
419 self.validate_type(attributes),
420 self.validate_id(attributes),
421 self.validate_ttl(attributes),
422 self.validate_source(attributes),
423 self.validate_sink(attributes),
424 validate_rpc_priority(attributes),
425 ]
426 .into_iter()
427 .filter_map(Result::err)
428 .map(|e| e.to_string())
429 .collect::<Vec<_>>()
430 .join("; ");
431
432 if error_message.is_empty() {
433 Ok(())
434 } else {
435 Err(UAttributesError::validation_error(error_message))
436 }
437 }
438
439 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
446 if let Some(source) = attributes.source.as_ref() {
447 UUri::verify_rpc_response(source).map_err(|e| {
448 UAttributesError::validation_error(format!("Invalid source URI: {}", e))
449 })
450 } else {
451 Err(UAttributesError::validation_error("Attributes for a request message must contain a reply-to address in the source property"))
452 }
453 }
454
455 fn validate_sink(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
462 if let Some(sink) = attributes.sink.as_ref() {
463 UUri::verify_rpc_method(sink)
464 .map_err(|e| UAttributesError::validation_error(format!("Invalid sink URI: {}", e)))
465 } else {
466 Err(UAttributesError::validation_error("Attributes for a request message must contain a method-to-invoke in the sink property"))
467 }
468 }
469}
470
471pub struct ResponseValidator;
473
474impl ResponseValidator {
475 pub fn validate_reqid(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
482 if !attributes
483 .reqid
484 .as_ref()
485 .is_some_and(|id| id.is_uprotocol_uuid())
486 {
487 Err(UAttributesError::validation_error(
488 "Request ID is not a valid uProtocol UUID",
489 ))
490 } else {
491 Ok(())
492 }
493 }
494
495 pub fn validate_commstatus(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
501 if let Some(status) = attributes.commstatus {
502 match status.enum_value() {
503 Ok(_) => {
504 return Ok(());
505 }
506 Err(e) => {
507 return Err(UAttributesError::validation_error(format!(
508 "Invalid Communication Status code: {e}"
509 )));
510 }
511 }
512 }
513 Ok(())
514 }
515}
516
517impl UAttributesValidator for ResponseValidator {
518 fn message_type(&self) -> UMessageType {
519 UMessageType::UMESSAGE_TYPE_RESPONSE
520 }
521
522 fn validate(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
537 let error_message = vec![
538 self.validate_type(attributes),
539 self.validate_id(attributes),
540 self.validate_source(attributes),
541 self.validate_sink(attributes),
542 self.validate_reqid(attributes),
543 self.validate_commstatus(attributes),
544 validate_rpc_priority(attributes),
545 ]
546 .into_iter()
547 .filter_map(Result::err)
548 .map(|e| e.to_string())
549 .collect::<Vec<_>>()
550 .join("; ");
551
552 if error_message.is_empty() {
553 Ok(())
554 } else {
555 Err(UAttributesError::validation_error(error_message))
556 }
557 }
558
559 fn validate_source(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
567 if let Some(source) = attributes.source.as_ref() {
568 UUri::verify_rpc_method(source).map_err(|e| {
569 UAttributesError::validation_error(format!("Invalid source URI: {}", e))
570 })
571 } else {
572 Err(UAttributesError::validation_error("Missing Source"))
573 }
574 }
575
576 fn validate_sink(&self, attributes: &UAttributes) -> Result<(), UAttributesError> {
584 if let Some(sink) = &attributes.sink.as_ref() {
585 UUri::verify_rpc_response(sink)
586 .map_err(|e| UAttributesError::validation_error(format!("Invalid sink URI: {}", e)))
587 } else {
588 Err(UAttributesError::validation_error("Missing Sink"))
589 }
590 }
591}
592
593#[cfg(test)]
594mod tests {
595 use protobuf::EnumOrUnknown;
596 use test_case::test_case;
597
598 use super::*;
599 use crate::{UCode, UPriority, UUri, UUID};
600
601 #[test]
602 fn test_validate_type_fails_for_unknown_type_code() {
603 let attributes = UAttributes {
604 type_: EnumOrUnknown::from_i32(20),
605 ..Default::default()
606 };
607 assert!(UAttributesValidators::Publish
608 .validator()
609 .validate_type(&attributes)
610 .is_err());
611 assert!(UAttributesValidators::Notification
612 .validator()
613 .validate_type(&attributes)
614 .is_err());
615 assert!(UAttributesValidators::Request
616 .validator()
617 .validate_type(&attributes)
618 .is_err());
619 assert!(UAttributesValidators::Response
620 .validator()
621 .validate_type(&attributes)
622 .is_err());
623 }
624
625 #[test_case(UMessageType::UMESSAGE_TYPE_UNSPECIFIED, UMessageType::UMESSAGE_TYPE_PUBLISH; "succeeds for Unspecified message")]
626 #[test_case(UMessageType::UMESSAGE_TYPE_PUBLISH, UMessageType::UMESSAGE_TYPE_PUBLISH; "succeeds for Publish message")]
627 #[test_case(UMessageType::UMESSAGE_TYPE_NOTIFICATION, UMessageType::UMESSAGE_TYPE_NOTIFICATION; "succeeds for Notification message")]
628 #[test_case(UMessageType::UMESSAGE_TYPE_REQUEST, UMessageType::UMESSAGE_TYPE_REQUEST; "succeeds for Request message")]
629 #[test_case(UMessageType::UMESSAGE_TYPE_RESPONSE, UMessageType::UMESSAGE_TYPE_RESPONSE; "succeeds for Response message")]
630 fn test_get_validator_returns_matching_validator(
631 message_type: UMessageType,
632 expected_validator_type: UMessageType,
633 ) {
634 let validator: Box<dyn UAttributesValidator> =
635 UAttributesValidators::get_validator(message_type);
636 assert_eq!(validator.message_type(), expected_validator_type);
637 }
638
639 #[test_case(Some(UUID::build()), Some(publish_topic()), None, None, true; "succeeds for topic only")]
640 #[test_case(Some(UUID::build()), Some(publish_topic()), Some(destination()), None, false; "fails for message containing destination")]
641 #[test_case(Some(UUID::build()), Some(publish_topic()), None, Some(100), true; "succeeds for valid attributes")]
642 #[test_case(Some(UUID::build()), None, None, None, false; "fails for missing topic")]
643 #[test_case(Some(UUID::build()), Some(UUri { resource_id: 0x54, ..Default::default()}), None, None, false; "fails for invalid topic")]
644 #[test_case(None, Some(publish_topic()), None, None, false; "fails for missing message ID")]
645 #[test_case(
646 Some(UUID {
647 msb: 0x000000000001C000u64,
649 lsb: 0x8000000000000000u64,
650 ..Default::default()
651 }),
652 Some(publish_topic()),
653 None,
654 None,
655 false;
656 "fails for invalid message id")]
657 fn test_validate_attributes_for_publish_message(
658 id: Option<UUID>,
659 source: Option<UUri>,
660 sink: Option<UUri>,
661 ttl: Option<u32>,
662 expected_result: bool,
663 ) {
664 let attributes = UAttributes {
665 type_: UMessageType::UMESSAGE_TYPE_PUBLISH.into(),
666 id: id.into(),
667 priority: UPriority::UPRIORITY_CS1.into(),
668 source: source.into(),
669 sink: sink.into(),
670 ttl,
671 ..Default::default()
672 };
673 let validator = UAttributesValidators::Publish.validator();
674 let status = validator.validate(&attributes);
675 assert!(status.is_ok() == expected_result);
676 if status.is_ok() {
677 assert!(UAttributesValidators::Notification
678 .validator()
679 .validate(&attributes)
680 .is_err());
681 assert!(UAttributesValidators::Request
682 .validator()
683 .validate(&attributes)
684 .is_err());
685 assert!(UAttributesValidators::Response
686 .validator()
687 .validate(&attributes)
688 .is_err());
689 }
690 }
691
692 #[test_case(Some(UUID::build()), Some(origin()), None, None, false; "fails for missing destination")]
693 #[test_case(Some(UUID::build()), Some(origin()), Some(destination()), None, true; "succeeds for both origin and destination")]
694 #[test_case(Some(UUID::build()), Some(origin()), Some(destination()), Some(100), true; "succeeds for valid attributes")]
695 #[test_case(Some(UUID::build()), None, Some(destination()), None, false; "fails for missing origin")]
696 #[test_case(Some(UUID::build()), Some(UUri::default()), Some(destination()), None, false; "fails for invalid origin")]
697 #[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")]
698 #[test_case(Some(UUID::build()), None, None, None, false; "fails for neither origin nor destination")]
699 #[test_case(None, Some(origin()), Some(destination()), None, false; "fails for missing message ID")]
700 #[test_case(
701 Some(UUID {
702 msb: 0x000000000001C000u64,
704 lsb: 0x8000000000000000u64,
705 ..Default::default()
706 }),
707 Some(origin()),
708 Some(destination()),
709 None,
710 false;
711 "fails for invalid message id")]
712 fn test_validate_attributes_for_notification_message(
713 id: Option<UUID>,
714 source: Option<UUri>,
715 sink: Option<UUri>,
716 ttl: Option<u32>,
717 expected_result: bool,
718 ) {
719 let attributes = UAttributes {
720 type_: UMessageType::UMESSAGE_TYPE_NOTIFICATION.into(),
721 id: id.into(),
722 priority: UPriority::UPRIORITY_CS1.into(),
723 source: source.into(),
724 sink: sink.into(),
725 ttl,
726 ..Default::default()
727 };
728 let validator = UAttributesValidators::Notification.validator();
729 let status = validator.validate(&attributes);
730 assert!(status.is_ok() == expected_result);
731 if status.is_ok() {
732 assert!(UAttributesValidators::Publish
733 .validator()
734 .validate(&attributes)
735 .is_err());
736 assert!(UAttributesValidators::Request
737 .validator()
738 .validate(&attributes)
739 .is_err());
740 assert!(UAttributesValidators::Response
741 .validator()
742 .validate(&attributes)
743 .is_err());
744 }
745 }
746
747 #[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")]
748 #[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")]
749 #[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")]
750 #[test_case(
751 Some(UUID {
752 msb: 0x000000000001C000u64,
754 lsb: 0x8000000000000000u64,
755 ..Default::default()
756 }),
757 Some(method_to_invoke()),
758 Some(reply_to_address()),
759 None,
760 Some(2000),
761 Some(UPriority::UPRIORITY_CS4),
762 None,
763 false;
764 "fails for invalid message id")]
765 #[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")]
766 #[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")]
767 #[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")]
768 #[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")]
769 #[test_case(Some(UUID::build()), Some(method_to_invoke()), Some(reply_to_address()), Some(1), Some(2000), None, None, false; "fails for missing priority")]
770 #[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")]
771 #[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")]
772 #[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 < 1")]
773 #[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")]
774 #[allow(clippy::too_many_arguments)]
775 fn test_validate_attributes_for_rpc_request_message(
776 id: Option<UUID>,
777 method_to_invoke: Option<UUri>,
778 reply_to_address: Option<UUri>,
779 perm_level: Option<u32>,
780 ttl: Option<u32>,
781 priority: Option<UPriority>,
782 token: Option<String>,
783 expected_result: bool,
784 ) {
785 let attributes = UAttributes {
786 type_: UMessageType::UMESSAGE_TYPE_REQUEST.into(),
787 id: id.into(),
788 priority: priority.unwrap_or(UPriority::UPRIORITY_UNSPECIFIED).into(),
789 source: reply_to_address.into(),
790 sink: method_to_invoke.into(),
791 permission_level: perm_level,
792 ttl,
793 token,
794 ..Default::default()
795 };
796 let status = UAttributesValidators::Request
797 .validator()
798 .validate(&attributes);
799 assert!(status.is_ok() == expected_result);
800 if status.is_ok() {
801 assert!(UAttributesValidators::Publish
802 .validator()
803 .validate(&attributes)
804 .is_err());
805 assert!(UAttributesValidators::Notification
806 .validator()
807 .validate(&attributes)
808 .is_err());
809 assert!(UAttributesValidators::Response
810 .validator()
811 .validate(&attributes)
812 .is_err());
813 }
814 }
815
816 #[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")]
817 #[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")]
818 #[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")]
819 #[test_case(
820 Some(UUID {
821 msb: 0x000000000001C000u64,
823 lsb: 0x8000000000000000u64,
824 ..Default::default()
825 }),
826 Some(reply_to_address()),
827 Some(method_to_invoke()),
828 Some(UUID::build()),
829 None,
830 None,
831 Some(UPriority::UPRIORITY_CS4),
832 false;
833 "fails for invalid message id")]
834 #[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")]
835 #[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")]
836 #[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")]
837 #[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")]
838 #[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")]
839 #[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")]
840 #[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)")]
841 #[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")]
842 #[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")]
843 #[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")]
844 #[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")]
845 #[test_case(
846 Some(UUID::build()),
847 Some(reply_to_address()),
848 Some(method_to_invoke()),
849 Some(UUID {
850 msb: 0x000000000001C000u64,
852 lsb: 0x8000000000000000u64,
853 ..Default::default()
854 }),
855 None,
856 None,
857 Some(UPriority::UPRIORITY_CS4),
858 false;
859 "fails for invalid request id")]
860 #[allow(clippy::too_many_arguments)]
861 fn test_validate_attributes_for_rpc_response_message(
862 id: Option<UUID>,
863 reply_to_address: Option<UUri>,
864 invoked_method: Option<UUri>,
865 reqid: Option<UUID>,
866 commstatus: Option<EnumOrUnknown<UCode>>,
867 ttl: Option<u32>,
868 priority: Option<UPriority>,
869 expected_result: bool,
870 ) {
871 let attributes = UAttributes {
872 type_: UMessageType::UMESSAGE_TYPE_RESPONSE.into(),
873 id: id.into(),
874 priority: priority.unwrap_or(UPriority::UPRIORITY_UNSPECIFIED).into(),
875 reqid: reqid.into(),
876 source: invoked_method.into(),
877 sink: reply_to_address.into(),
878 commstatus,
879 ttl,
880 ..Default::default()
881 };
882 let status = UAttributesValidators::Response
883 .validator()
884 .validate(&attributes);
885 assert!(status.is_ok() == expected_result);
886 if status.is_ok() {
887 assert!(UAttributesValidators::Publish
888 .validator()
889 .validate(&attributes)
890 .is_err());
891 assert!(UAttributesValidators::Notification
892 .validator()
893 .validate(&attributes)
894 .is_err());
895 assert!(UAttributesValidators::Request
896 .validator()
897 .validate(&attributes)
898 .is_err());
899 }
900 }
901
902 fn publish_topic() -> UUri {
903 UUri {
904 authority_name: String::from("vcu.someVin"),
905 ue_id: 0x0000_5410,
906 ue_version_major: 0x01,
907 resource_id: 0xa010,
908 ..Default::default()
909 }
910 }
911
912 fn origin() -> UUri {
913 UUri {
914 authority_name: String::from("vcu.someVin"),
915 ue_id: 0x0000_3c00,
916 ue_version_major: 0x02,
917 resource_id: 0x9a00,
918 ..Default::default()
919 }
920 }
921
922 fn destination() -> UUri {
923 UUri {
924 authority_name: String::from("vcu.someVin"),
925 ue_id: 0x0000_3d07,
926 ue_version_major: 0x01,
927 resource_id: 0x0000,
928 ..Default::default()
929 }
930 }
931
932 fn reply_to_address() -> UUri {
933 UUri {
934 authority_name: String::from("vcu.someVin"),
935 ue_id: 0x0000_010b,
936 ue_version_major: 0x01,
937 resource_id: 0x0000,
938 ..Default::default()
939 }
940 }
941
942 fn method_to_invoke() -> UUri {
943 UUri {
944 authority_name: String::from("vcu.someVin"),
945 ue_id: 0x0000_03ae,
946 ue_version_major: 0x01,
947 resource_id: 0x00e2,
948 ..Default::default()
949 }
950 }
951}