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}