From 086af42d09ab28c0976b1c79e22a77b025ecfb6e Mon Sep 17 00:00:00 2001 From: Kai Hudalla Date: Tue, 17 Jun 2025 10:04:37 +0200 Subject: [PATCH 1/3] Fix covered Transport Layer OFT specitem IDs --- src/utransport.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utransport.rs b/src/utransport.rs index a656b7f..185559c 100644 --- a/src/utransport.rs +++ b/src/utransport.rs @@ -66,7 +66,7 @@ pub fn verify_filter_criteria( /// /// Implementations may use arbitrary mechanisms to determine the information that /// is necessary for creating URIs, e.g. environment variables, configuration files etc. -// [impl->req~up-language-transport-api~1] +// [impl->dsn~localuriprovider-declaration~1] #[cfg_attr(any(test, feature = "test-util"), mockall::automock)] pub trait LocalUriProvider: Send + Sync { /// Gets the _authority_ used for URIs representing this uEntity's resources. @@ -187,7 +187,7 @@ impl TryFrom<&UUri> for StaticUriProvider { /// /// Please refer to the [uProtocol Transport Layer specification](https://github.com/eclipse-uprotocol/up-spec/blob/v1.6.0-alpha.5/up-l1/README.adoc) /// for details. -// [impl->req~up-language-transport-api~1] +// [impl->dsn~ulistener-declaration~1] #[cfg_attr(any(test, feature = "test-util"), mockall::automock)] #[async_trait] pub trait UListener: Send + Sync { @@ -213,7 +213,7 @@ pub trait UListener: Send + Sync { /// /// Please refer to the [uProtocol Transport Layer specification](https://github.com/eclipse-uprotocol/up-spec/blob/v1.6.0-alpha.5/up-l1/README.adoc) /// for details. -// [impl->req~up-language-transport-api~1] +// [impl->dsn~utransport-declaration~1] #[async_trait] pub trait UTransport: Send + Sync { /// Sends a message using this transport's message exchange mechanism. From 169f0c1690e3a3aaa649b5cae3ee56062c76cfc4 Mon Sep 17 00:00:00 2001 From: Kai Hudalla Date: Tue, 17 Jun 2025 10:14:55 +0200 Subject: [PATCH 2/3] Fix covered Communication Layer OFT specitem IDs --- src/communication/default_notifier.rs | 4 ++-- src/communication/default_pubsub.rs | 4 ++-- src/communication/in_memory_rpc_client.rs | 4 ++-- src/communication/in_memory_rpc_server.rs | 4 ++-- src/communication/notification.rs | 4 ++-- src/communication/pubsub.rs | 8 ++++---- src/communication/rpc.rs | 8 ++++---- src/lib.rs | 1 + 8 files changed, 19 insertions(+), 18 deletions(-) diff --git a/src/communication/default_notifier.rs b/src/communication/default_notifier.rs index 1319e61..844aaae 100644 --- a/src/communication/default_notifier.rs +++ b/src/communication/default_notifier.rs @@ -11,7 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -// [impl->req~up-language-comm-api-default-impl~1] +// [impl->dsn~communication-layer-impl-default~1] use std::sync::Arc; @@ -100,7 +100,7 @@ impl Notifier for SimpleNotifier { #[cfg(test)] mod tests { - // [utest->req~up-language-comm-api-default-impl~1] + // [utest->dsn~communication-layer-impl-default~1] use super::*; diff --git a/src/communication/default_pubsub.rs b/src/communication/default_pubsub.rs index 1bc4b14..146bc1b 100644 --- a/src/communication/default_pubsub.rs +++ b/src/communication/default_pubsub.rs @@ -11,7 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -// [impl->req~up-language-comm-api-default-impl~1] +// [impl->dsn~communication-layer-impl-default~1] use std::{ collections::{hash_map::Entry, HashMap}, @@ -446,7 +446,7 @@ impl Subscriber for InMemorySubscriber { #[cfg(test)] mod tests { - // [utest->req~up-language-comm-api-default-impl~1] + // [utest->dsn~communication-layer-impl-default~1] use super::*; diff --git a/src/communication/in_memory_rpc_client.rs b/src/communication/in_memory_rpc_client.rs index 7070f9a..f3d8e7a 100644 --- a/src/communication/in_memory_rpc_client.rs +++ b/src/communication/in_memory_rpc_client.rs @@ -11,7 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -// [impl->req~up-language-comm-api-default-impl~1] +// [impl->dsn~communication-layer-impl-default~1] use std::collections::hash_map::Entry; use std::collections::HashMap; @@ -288,7 +288,7 @@ impl RpcClient for InMemoryRpcClient { #[cfg(test)] mod tests { - // [utest->req~up-language-comm-api-default-impl~1] + // [utest->dsn~communication-layer-impl-default~1] use super::*; diff --git a/src/communication/in_memory_rpc_server.rs b/src/communication/in_memory_rpc_server.rs index 9866389..31ef8e9 100644 --- a/src/communication/in_memory_rpc_server.rs +++ b/src/communication/in_memory_rpc_server.rs @@ -11,7 +11,7 @@ * SPDX-License-Identifier: Apache-2.0 ********************************************************************************/ -// [impl->req~up-language-comm-api-default-impl~1] +// [impl->dsn~communication-layer-impl-default~1] use std::collections::hash_map::Entry; use std::collections::HashMap; @@ -285,7 +285,7 @@ impl RpcServer for InMemoryRpcServer { #[cfg(test)] mod tests { - // [utest->req~up-language-comm-api-default-impl~1] + // [utest->dsn~communication-layer-impl-default~1] use super::*; diff --git a/src/communication/notification.rs b/src/communication/notification.rs index 82f15c8..6faca74 100644 --- a/src/communication/notification.rs +++ b/src/communication/notification.rs @@ -21,7 +21,7 @@ use crate::{UListener, UStatus, UUri}; use super::{CallOptions, UPayload}; /// An error indicating a problem with sending a notification to another uEntity. -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[derive(Debug)] pub enum NotificationError { /// Indicates that the given message cannot be sent because it is not a [valid Notification message](crate::NotificationValidator). @@ -48,7 +48,7 @@ impl Error for NotificationError {} /// /// Please refer to the /// [Communication Layer API Specifications](https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l2/api.adoc). -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[cfg_attr(any(test, feature = "test-util"), mockall::automock)] #[async_trait] pub trait Notifier: Send + Sync { diff --git a/src/communication/pubsub.rs b/src/communication/pubsub.rs index e4d2487..86ab70c 100644 --- a/src/communication/pubsub.rs +++ b/src/communication/pubsub.rs @@ -22,7 +22,7 @@ use crate::{UListener, UStatus, UUri}; use super::{CallOptions, UPayload}; /// An error indicating a problem with publishing a message to a topic. -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[derive(Debug)] pub enum PubSubError { /// Indicates that the given message cannot be sent because it is not a [valid Publish message](crate::PublishValidator). @@ -49,7 +49,7 @@ impl Error for PubSubError {} /// /// Please refer to the /// [Communication Layer API Specifications](https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l2/api.adoc). -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[async_trait] pub trait Publisher: Send + Sync { /// Publishes a message to a topic. @@ -71,7 +71,7 @@ pub trait Publisher: Send + Sync { ) -> Result<(), PubSubError>; } -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[cfg_attr(any(test, feature = "test-util"), mockall::automock)] pub trait SubscriptionChangeHandler: Send + Sync { /// Invoked for each update to the subscription status for a given topic. @@ -89,7 +89,7 @@ pub trait SubscriptionChangeHandler: Send + Sync { /// /// Please refer to the /// [Communication Layer API Specifications](https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l2/api.adoc). -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[async_trait] pub trait Subscriber: Send + Sync { /// Registers a handler to invoke for messages that have been published to a given topic. diff --git a/src/communication/rpc.rs b/src/communication/rpc.rs index 344174b..ab3f467 100644 --- a/src/communication/rpc.rs +++ b/src/communication/rpc.rs @@ -23,7 +23,7 @@ use crate::{UAttributes, UCode, UStatus, UUri}; use super::{CallOptions, UPayload}; /// An error indicating a problem with invoking a (remote) service operation. -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[derive(Clone, Error, Debug)] pub enum ServiceInvocationError { /// Indicates that the calling uE requested to add/create something that already exists. @@ -136,7 +136,7 @@ impl From for UStatus { /// Please refer to the /// [Communication Layer API specification](https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l2/api.adoc) /// for details. -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[cfg_attr(any(test, feature = "test-util"), mockall::automock)] #[async_trait] pub trait RpcClient: Send + Sync { @@ -210,7 +210,7 @@ impl dyn RpcClient { /// A handler for processing incoming RPC requests. /// -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[cfg_attr(any(test, feature = "test-util"), mockall::automock)] #[async_trait] pub trait RequestHandler: Send + Sync { @@ -246,7 +246,7 @@ pub trait RequestHandler: Send + Sync { /// Please refer to the /// [Communication Layer API specification](https://github.com/eclipse-uprotocol/up-spec/blob/main/up-l2/api.adoc) /// for details. -// [impl->req~up-language-comm-api~1] +// [impl->dsn~communication-layer-api-declaration~1] #[async_trait] pub trait RpcServer { /// Registers an endpoint for RPC requests. diff --git a/src/lib.rs b/src/lib.rs index 0d96819..d3026b8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -64,6 +64,7 @@ mod cloudevents; #[cfg(feature = "cloudevents")] pub use cloudevents::{CloudEvent, CONTENT_TYPE_CLOUDEVENTS_PROTOBUF}; +// [impl->dsn~communication-layer-api-namespace~1] #[cfg(feature = "communication")] pub mod communication; From 7a41883421bc7e20210ce7cc433a2d6961439605 Mon Sep 17 00:00:00 2001 From: Kai Hudalla Date: Tue, 17 Jun 2025 13:12:27 +0200 Subject: [PATCH 3/3] Fix covered OFT specitem IDs Also added missing tests for UMessage and UAttributes. --- build.rs | 4 ++ src/uattributes.rs | 11 +---- src/umessage.rs | 3 +- src/umessage/umessagebuilder.rs | 74 +++++++++++++++++++++++---------- 4 files changed, 60 insertions(+), 32 deletions(-) diff --git a/build.rs b/build.rs index cd37237..fa32784 100644 --- a/build.rs +++ b/build.rs @@ -24,8 +24,12 @@ fn main() -> Result<(), Box> { format!("{}uprotocol/v1/uuid.proto", UPROTOCOL_BASE_URI), // [impl->req~uri-data-model-proto~1] format!("{}uprotocol/v1/uri.proto", UPROTOCOL_BASE_URI), + // [impl->req~uattributes-data-model-impl~1] + // [impl->req~uattributes-data-model-proto~1] format!("{}uprotocol/v1/uattributes.proto", UPROTOCOL_BASE_URI), format!("{}uprotocol/v1/ucode.proto", UPROTOCOL_BASE_URI), + // [impl->req~umessage-data-model-impl~1] + // [impl->req~umessage-data-model-proto~1] format!("{}uprotocol/v1/umessage.proto", UPROTOCOL_BASE_URI), // [impl->req~ustatus-data-model-impl~1] // [impl->req~ustatus-data-model-proto~1] diff --git a/src/uattributes.rs b/src/uattributes.rs index b33e74b..6db2087 100644 --- a/src/uattributes.rs +++ b/src/uattributes.rs @@ -23,7 +23,7 @@ pub use upriority::*; pub use crate::up_core_api::uattributes::*; use crate::UUID; -const UPRIORITY_DEFAULT: UPriority = UPriority::UPRIORITY_CS1; +pub(crate) const UPRIORITY_DEFAULT: UPriority = UPriority::UPRIORITY_CS1; #[derive(Debug)] pub enum UAttributesError { @@ -62,9 +62,8 @@ impl UAttributes { /// Checks if a given priority class is the default priority class. /// /// Messages that do not have a priority class set explicity, are assigned to - /// the default prioritiy class. + /// the default priority class. pub(crate) fn is_default_priority(prio: UPriority) -> bool { - // [impl->dsn~up-attributes-priority~1] prio == UPRIORITY_DEFAULT } @@ -146,11 +145,9 @@ impl UAttributes { pub fn check_expired(&self) -> Result<(), UAttributesError> { let ttl = match self.ttl { Some(t) if t > 0 => u64::from(t), - // [impl->dsn~up-attributes-ttl~1] _ => return Ok(()), }; - // [impl->dsn~up-attributes-ttl-timeout~1] if let Some(creation_time) = self.id.as_ref().and_then(UUID::get_time) { let delta = match SystemTime::now().duration_since(SystemTime::UNIX_EPOCH) { Ok(duration) => { @@ -195,15 +192,11 @@ mod tests { } #[test_case(None, None, false; "for message without ID nor TTL")] - // [utest->dsn~up-attributes-ttl~1] #[test_case(None, Some(0), false; "for message without ID with TTL 0")] #[test_case(None, Some(500), false; "for message without ID with TTL")] #[test_case(Some(build_n_ms_in_past(1000)), None, false; "for message with ID without TTL")] - // [utest->dsn~up-attributes-ttl~1] #[test_case(Some(build_n_ms_in_past(1000)), Some(0), false; "for message with ID and TTL 0")] - // [utest->dsn~up-attributes-ttl-timeout~1] #[test_case(Some(build_n_ms_in_past(1000)), Some(500), true; "for message with ID and expired TTL")] - // [utest->dsn~up-attributes-ttl-timeout~1] #[test_case(Some(build_n_ms_in_past(1000)), Some(2000), false; "for message with ID and non-expired TTL")] fn test_is_expired(id: Option, ttl: Option, should_be_expired: bool) { let attributes = UAttributes { diff --git a/src/umessage.rs b/src/umessage.rs index 4bfca62..303a171 100644 --- a/src/umessage.rs +++ b/src/umessage.rs @@ -141,10 +141,9 @@ impl UMessage { self.attributes .as_ref() .and_then(|attribs| attribs.priority.enum_value().ok()) - // [impl->dsn~up-attributes-priority~1] .map(|prio| { if prio == UPriority::UPRIORITY_UNSPECIFIED { - UPriority::UPRIORITY_CS1 + crate::uattributes::UPRIORITY_DEFAULT } else { prio } diff --git a/src/umessage/umessagebuilder.rs b/src/umessage/umessagebuilder.rs index 1dca59f..2e189ed 100644 --- a/src/umessage/umessagebuilder.rs +++ b/src/umessage/umessagebuilder.rs @@ -279,7 +279,6 @@ impl UMessageBuilder { priority: request_attributes .priority .enum_value_or(UPriority::UPRIORITY_CS4), - // [impl->dsn~up-attributes-response-ttl~1] ttl: request_attributes.ttl, ..Default::default() } @@ -372,9 +371,6 @@ impl UMessageBuilder { pub fn with_priority(&mut self, priority: UPriority) -> &mut UMessageBuilder { // [impl->dsn~up-attributes-request-priority~1] if self.message_type == UMessageType::UMESSAGE_TYPE_REQUEST - // up-spec erroneously uses this name to define a requirement - // for the response's priority class ... - // [impl->dsn~up-attributes-response-reqid~1] || self.message_type == UMessageType::UMESSAGE_TYPE_RESPONSE { assert!(priority.value() >= UPriority::UPRIORITY_CS4.value()) @@ -530,7 +526,6 @@ impl UMessageBuilder { /// # } /// ``` pub fn with_comm_status(&mut self, comm_status: UCode) -> &mut UMessageBuilder { - // [impl->dsn~up-attributes-response-commstatus~1] assert!(self.message_type == UMessageType::UMESSAGE_TYPE_RESPONSE); self.comm_status = Some(comm_status.into()); self @@ -800,6 +795,7 @@ mod tests { use super::*; + use protobuf::Message; use test_case::test_case; const METHOD_TO_INVOKE: &str = "//my-vehicle/4D123/2/6FA3"; @@ -820,7 +816,6 @@ mod tests { // [utest->dsn~up-attributes-permission-level~1] #[test_case(Some(5), None, None; "with permission level")] - // [utest->dsn~up-attributes-response-commstatus~1] #[test_case(None, Some(UCode::NOT_FOUND), None; "with commstatus")] // [utest->dsn~up-attributes-request-token~1] #[test_case(None, None, Some(String::from("my-token")); "with token")] @@ -843,7 +838,6 @@ mod tests { // [utest->dsn~up-attributes-permission-level~1] #[test_case(Some(5), None, None; "with permission level")] - // [utest->dsn~up-attributes-response-commstatus~1] #[test_case(None, Some(UCode::NOT_FOUND), None; "with commstatus")] // [utest->dsn~up-attributes-request-token~1] #[test_case(None, None, Some(String::from("my-token")); "with token")] @@ -870,17 +864,15 @@ mod tests { #[test_case(Some(5), None, None; "with permission level")] // [utest->dsn~up-attributes-request-token~1] #[test_case(None, Some(String::from("my-token")), None; "with token")] - // up-spec erroneously uses this name to define a requirement - // for the response's priority class ... - // [utest->dsn~up-attributes-response-reqid~1] + // [utest->dsn~up-attributes-request-priority~1] #[test_case(None, None, Some(UPriority::UPRIORITY_UNSPECIFIED); "with priority UNSPECIFIED")] - // [utest->dsn~up-attributes-response-reqid~1] + // [utest->dsn~up-attributes-request-priority~1] #[test_case(None, None, Some(UPriority::UPRIORITY_CS0); "with priority CS0")] - // [utest->dsn~up-attributes-response-reqid~1] + // [utest->dsn~up-attributes-request-priority~1] #[test_case(None, None, Some(UPriority::UPRIORITY_CS1); "with priority CS1")] - // [utest->dsn~up-attributes-response-reqid~1] + // [utest->dsn~up-attributes-request-priority~1] #[test_case(None, None, Some(UPriority::UPRIORITY_CS2); "with priority CS2")] - // [utest->dsn~up-attributes-response-reqid~1] + // [utest->dsn~up-attributes-request-priority~1] #[test_case(None, None, Some(UPriority::UPRIORITY_CS3); "with priority CS3")] #[should_panic] fn test_response_message_builder_panics( @@ -904,7 +896,6 @@ mod tests { } } - // [utest->dsn~up-attributes-response-commstatus~1] #[test_case(Some(UCode::NOT_FOUND), None; "for comm status")] // [utest->dsn~up-attributes-request-priority~1] #[test_case(None, Some(UPriority::UPRIORITY_UNSPECIFIED); "for priority class unspecified")] @@ -953,6 +944,8 @@ mod tests { } #[test] + // [utest->req~uattributes-data-model-impl~1] + // [utest->req~umessage-data-model-impl~1] fn test_build_retains_all_publish_attributes() { let message_id = UUID::build(); let traceparent = String::from("traceparent"); @@ -963,9 +956,9 @@ mod tests { .with_traceparent(&traceparent) .build_with_payload("locked", UPayloadFormat::UPAYLOAD_FORMAT_TEXT) .expect("should have been able to create message"); + // [utest->dsn~up-attributes-id~1] assert_eq!(message.id_unchecked(), &message_id); - // [utest->dsn~up-attributes-priority~1] assert!(UAttributes::is_default_priority( message.priority_unchecked() )); @@ -985,9 +978,20 @@ mod tests { message.payload_format_unchecked(), UPayloadFormat::UPAYLOAD_FORMAT_TEXT ); + + // [utest->req~uattributes-data-model-proto~1] + // [utest->req~umessage-data-model-proto~1] + let proto = message + .write_to_bytes() + .expect("failed to serialize to protobuf"); + let deserialized_message = + UMessage::parse_from_bytes(proto.as_slice()).expect("failed to deserialize protobuf"); + assert_eq!(message, deserialized_message); } #[test] + // [utest->req~uattributes-data-model-impl~1] + // [utest->req~umessage-data-model-impl~1] fn test_build_retains_all_notification_attributes() { let message_id = UUID::build(); let traceparent = String::from("traceparent"); @@ -1001,6 +1005,7 @@ mod tests { .with_traceparent(&traceparent) .build_with_payload("locked", UPayloadFormat::UPAYLOAD_FORMAT_TEXT) .expect("should have been able to create message"); + // [utest->dsn~up-attributes-id~1] assert_eq!(message.id_unchecked(), &message_id); assert_eq!(message.priority_unchecked(), UPriority::UPRIORITY_CS2); @@ -1019,9 +1024,20 @@ mod tests { message.payload_format_unchecked(), UPayloadFormat::UPAYLOAD_FORMAT_TEXT ); + + // [utest->req~uattributes-data-model-proto~1] + // [utest->req~umessage-data-model-proto~1] + let proto = message + .write_to_bytes() + .expect("failed to serialize to protobuf"); + let deserialized_message = + UMessage::parse_from_bytes(proto.as_slice()).expect("failed to deserialize protobuf"); + assert_eq!(message, deserialized_message); } #[test] + // [utest->req~uattributes-data-model-impl~1] + // [utest->req~umessage-data-model-impl~1] fn test_build_retains_all_request_attributes() { let message_id = UUID::build(); let token = String::from("token"); @@ -1062,6 +1078,15 @@ mod tests { message.payload_format_unchecked(), UPayloadFormat::UPAYLOAD_FORMAT_TEXT ); + + // [utest->req~uattributes-data-model-proto~1] + // [utest->req~umessage-data-model-proto~1] + let proto = message + .write_to_bytes() + .expect("failed to serialize to protobuf"); + let deserialized_message = + UMessage::parse_from_bytes(proto.as_slice()).expect("failed to deserialize protobuf"); + assert_eq!(message, deserialized_message); } #[test] @@ -1086,14 +1111,10 @@ mod tests { // [utest->dsn~up-attributes-id~1] assert_eq!(message.id_unchecked(), &response_message_id); assert_eq!(message.commstatus_unchecked(), UCode::DEADLINE_EXCEEDED); - // up-spec erroneously uses this name to define a requirement - // for the response's priority class ... - // [utest->dsn~up-attributes-response-reqid~1] assert_eq!(message.priority_unchecked(), UPriority::UPRIORITY_CS5); assert_eq!(message.request_id_unchecked(), &request_message_id); assert_eq!(message.sink_unchecked(), &reply_to_address); assert_eq!(message.source_unchecked(), &method_to_invoke); - // [utest->dsn~up-attributes-response-ttl~1] assert_eq!(message.ttl_unchecked(), 5000); // [utest->dsn~up-attributes-response-type~1] assert_eq!( @@ -1108,6 +1129,8 @@ mod tests { } #[test] + // [utest->req~uattributes-data-model-impl~1] + // [utest->req~umessage-data-model-impl~1] fn test_build_retains_all_response_attributes() { let message_id = UUID::build(); let request_id = UUID::build(); @@ -1128,9 +1151,9 @@ mod tests { .with_traceparent(&traceparent) .build() .expect("should have been able to create message"); + // [utest->dsn~up-attributes-id~1] assert_eq!(message.id_unchecked(), &message_id); - // [utest->dsn~up-attributes-response-commstatus~1] assert_eq!(message.commstatus_unchecked(), UCode::DEADLINE_EXCEEDED); assert_eq!(message.priority_unchecked(), UPriority::UPRIORITY_CS5); assert_eq!(message.request_id_unchecked(), &request_id); @@ -1149,5 +1172,14 @@ mod tests { UPayloadFormat::UPAYLOAD_FORMAT_UNSPECIFIED ); assert!(message.payload.is_none()); + + // [utest->req~uattributes-data-model-proto~1] + // [utest->req~umessage-data-model-proto~1] + let proto = message + .write_to_bytes() + .expect("failed to serialize to protobuf"); + let deserialized_message = + UMessage::parse_from_bytes(proto.as_slice()).expect("failed to deserialize protobuf"); + assert_eq!(message, deserialized_message); } }