diff --git a/include/up-cpp/client/usubscription/v3/RequestBuilder.h b/include/up-cpp/client/usubscription/v3/RequestBuilder.h index c51c82652..ad16392d8 100644 --- a/include/up-cpp/client/usubscription/v3/RequestBuilder.h +++ b/include/up-cpp/client/usubscription/v3/RequestBuilder.h @@ -18,12 +18,10 @@ namespace uprotocol::core::usubscription::v3 { -/** - * @struct USubscriptionOptions - * @brief Additional details for uSubscription service. - * - * Each member represents an optional parameter for the uSubscription service. - */ +/// @struct USubscriptionOptions +/// @brief Additional details for uSubscription service. +/// +/// Each member represents an optional parameter for the uSubscription service. struct USubscriptionOptions { /// Permission level of the subscription request std::optional permission_level; @@ -39,84 +37,75 @@ struct USubscriptionOptions { std::optional subscription_details; }; -/** - * @brief Builds different requests using specified options. - * - * This struct facilitates the construction of requests based on - * `USubscriptionOptions`, providing methods to build different requests. - */ +/// @brief Builds different requests using specified options. +/// +/// This struct facilitates the construction of requests based on +/// `USubscriptionOptions`, providing methods to build different requests. struct RequestBuilder { - /** - * @brief Constructs a RequestBuilder with the given subscription options. - * - * @param options Subscription options to configure the requests. Defaults - * to empty options. - */ + /// @brief Constructs a RequestBuilder with the given subscription options. + /// + /// @param options Subscription options to configure the requests. Defaults + /// to empty options. explicit RequestBuilder(USubscriptionOptions options = {}) : options_(std::move(options)) {} - /** - * @brief Builds a subscription request for a given topic. - * - * @param topic The `v1::UUri` representing the topic for the subscription. - * - * @return A `SubscriptionRequest` configured for the specified topic. - */ + /// @brief Sets the permission level for the requests build by the builder + /// + /// @param permission_level to be set + /// @return returns a reference to the builder struct to allow chaining + RequestBuilder& setPremissionLevel(uint32_t permission_level); + + /// @brief Builds a subscription request for a given topic. + /// + /// @param topic The `v1::UUri` representing the topic for the subscription. + /// + /// @return A `SubscriptionRequest` configured for the specified topic. SubscriptionRequest buildSubscriptionRequest(const v1::UUri& topic) const; - /** - * @brief Builds an unsubscription request for a given topic. - * - * @param topic The `v1::UUri` representing the topic to unsubscribe from. - * - * @return An `UnsubscribeRequest` configured for the specified topic. - */ - static UnsubscribeRequest buildUnsubscribeRequest(const v1::UUri& topic); + /// @brief Builds an unsubscription request for a given topic. + /// + /// @param topic The `v1::UUri` representing the topic to unsubscribe from. + /// + /// @return An `UnsubscribeRequest` configured for the specified topic. + UnsubscribeRequest buildUnsubscribeRequest(const v1::UUri& topic) const; - /** - * @brief Build fetch subscritions request for a given topic. - * - * @param topic The `v1::UUri` representing the topic to fetch. - * - * @return An `FetchSubscriptionsRequest` configured for the specified - * topic. - */ - static FetchSubscriptionsRequest buildFetchSubscriptionsRequest( - const v1::UUri& topic); + /// @brief Build fetch subscritions request for a given topic. + /// + /// @param topic The `v1::UUri` representing the topic to fetch. + /// + /// @return An `FetchSubscriptionsRequest` configured for the specified + /// topic. + FetchSubscriptionsRequest buildFetchSubscriptionsRequest( + const v1::UUri& topic) const; - /** - * @brief Build fetch subscritions request for a given subscriber. - * - * @param subscriber The `SubscriberInfo` representing the subscriber to - * fetch. - * - * @return An `FetchSubscriptionsRequest` configured for the specified - * subscriber. - */ - static FetchSubscriptionsRequest buildFetchSubscriptionsRequest( - const SubscriberInfo& subscriber); + /// @brief Build fetch subscritions request for a given subscriber. + /// + /// @param subscriber The `SubscriberInfo` representing the subscriber to + /// fetch. + /// + /// @return An `FetchSubscriptionsRequest` configured for the specified + /// subscriber. + FetchSubscriptionsRequest buildFetchSubscriptionsRequest( + const SubscriberInfo& subscriber) const; - /** - * @brief Build fetch subscribers request for a given topic. - * - * @param topic The `v1::UUri` representing the topic to fetch. - * - * @return An `FetchSubscribersRequest` configured for the specified topic. - */ - static FetchSubscribersRequest buildFetchSubscribersRequest( - const v1::UUri& topic); + /// @brief Build fetch subscribers request for a given topic. + /// + /// @param topic The `v1::UUri` representing the topic to fetch. + /// + /// @return An `FetchSubscribersRequest` configured for the specified topic. + FetchSubscribersRequest buildFetchSubscribersRequest( + const v1::UUri& topic) const; - /** - * @brief Build notifications request for a given topic. Subscription change - *notifications MUST use topic SubscriptionsChange with resource id 0x8000, - *as per the protobuf definition. - * - * @param topic The `v1::UUri` representing the topic to (un)register - *for/from. - * - * @return An `NotificationsRequest` configured for the specified topic. - */ - static NotificationsRequest buildNotificationsRequest(const v1::UUri& topic); + /// @brief Build notifications request for a given topic. Subscription + /// change + /// notifications MUST use topic SubscriptionsChange with resource id + /// 0x8000, as per the protobuf definition. + /// + /// @param topic The `v1::UUri` representing the topic to (un)register + /// for/from. + /// + /// @return An `NotificationsRequest` configured for the specified topic. + NotificationsRequest buildNotificationsRequest(const v1::UUri& topic) const; private: USubscriptionOptions options_; ///< Options used to configure the requests. diff --git a/include/up-cpp/client/usubscription/v3/RpcClientUSubscription.h b/include/up-cpp/client/usubscription/v3/RpcClientUSubscription.h index aa7b4c003..70a34d697 100644 --- a/include/up-cpp/client/usubscription/v3/RpcClientUSubscription.h +++ b/include/up-cpp/client/usubscription/v3/RpcClientUSubscription.h @@ -46,10 +46,7 @@ namespace uprotocol::core::usubscription::v3 { using v3::SubscriptionRequest; using v3::UnsubscribeRequest; -/// @brief Interface for uEntities to create subscriptions. -/// -/// Like all L3 client APIs, the RpcClientUSubscription is a wrapper on top of -/// the L2 Communication APIs and USubscription service. +/// @brief Client which implements the USubscription interface struct RpcClientUSubscription : USubscription { using RpcClientUSubscriptionOrStatus = utils::Expected, v1::UStatus>; @@ -59,54 +56,69 @@ struct RpcClientUSubscription : USubscription { template Response invokeResponse(communication::RpcClient rpc_client); - /// @brief Subscribe to the topic + /// @brief Subscribes from a given topic /// + /// @param subscription_request The request object containing the topic to + /// subscribe to + /// @return Returns a SubscriptionResponse on success and a UStatus else utils::Expected subscribe( const SubscriptionRequest& subscription_request) override; - /// @brief Unsubscribe from the topic + /// @brief Unsubscribes from a given topic /// + /// @param unsubscribe_request The request object containing the topic to + /// unsubscribe from + /// @return Returns an UnsubscribeResponse on success and a UStatus else utils::Expected unsubscribe( const UnsubscribeRequest& unsubscribe_request) override; - /// @brief Fetch all subscriptions either by topic or subscriber + /// @brief Fetches the list of topics the client is subscribed to /// + /// @param fetch_subscriptions_request The request object + /// @return Returns a FetchSubscriptionsResponse on success and a UStatus + /// else utils::Expected fetch_subscriptions( const FetchSubscriptionsRequest& fetch_subscriptions_request) override; - /// @brief Fetch all subscribers + /// @brief Fetches the list of subscribers for a given topic /// + /// @param fetch_subscribers_request The request object containing the topic + /// for which the subscribers are to be fetched + /// @return Returns a FetchSubscribersResponse on success and a UStatus else utils::Expected fetch_subscribers( const FetchSubscribersRequest& fetch_subscribers_request) override; - /// @brief Register for notifications + /// @brief Registers to receive notifications /// + /// @param register_notifications_request The request object containing + /// the details to register for notifications + /// @return Returns a NotificationResponse on success and a UStatus else utils::Expected register_for_notifications( const NotificationsRequest& register_notifications_request) override; - /// @brief Unregister for notifications + /// @brief Unregisters from receiving notifications. /// + /// @param unregister_notifications_request The request object containing + /// the details needed to stop receiving notifications. + /// @return Returns a NotificationResponse on success and a UStatus else utils::Expected unregister_for_notifications( const NotificationsRequest& unregister_notifications_request) override; /// @brief Constructor /// - /// @param transport Transport to register with. + /// @param transport Transport used to send messages explicit RpcClientUSubscription( std::shared_ptr transport) : transport_(std::move(transport)) {} - /// @brief Destructor ~RpcClientUSubscription() override = default; private: - // Transport std::shared_ptr transport_; - // URI info about the uSubscription service USubscriptionUUriBuilder uuri_builder_; }; diff --git a/include/up-cpp/client/usubscription/v3/USubscription.h b/include/up-cpp/client/usubscription/v3/USubscription.h index 7ad69e8d4..8d0e4a092 100644 --- a/include/up-cpp/client/usubscription/v3/USubscription.h +++ b/include/up-cpp/client/usubscription/v3/USubscription.h @@ -19,28 +19,59 @@ namespace uprotocol::core::usubscription::v3 { +/// @brief Interface for uEntities to create subscriptions. +/// +/// Like all L3 client APIs, the RpcClientUSubscription is a wrapper on top of +/// the L2 Communication APIs and USubscription service. struct USubscription { template using ResponseOrStatus = utils::Expected; virtual ~USubscription() = default; + /// @brief sends a subscription request to a USubscription backend and a + /// response on success or else a status code + /// + /// @param subscription_request containing a topic to subscribe to + /// @return SubscriptionReponse on success and UStatus else virtual ResponseOrStatus subscribe( const SubscriptionRequest& subscription_request) = 0; + /// @brief sends an unsubscribe request to a USubscription backend and a + /// response on success or else a status code + /// + /// @param unsubscribe_request containing a topic to unsubscribe + /// @return UnsubscribeResponse on success and UStatus else virtual ResponseOrStatus unsubscribe( const UnsubscribeRequest& unsubscribe_request) = 0; + /// @brief fetches all topics the client is subscribed to from the backend + /// + /// @param fetch_subscriptions_request + /// @return FetchSubscriptionsResponse on success and UStatus else virtual ResponseOrStatus fetch_subscriptions( const FetchSubscriptionsRequest& fetch_subscriptions_request) = 0; + /// @brief registers for notifications to a USubscription backend + /// + /// @param register_notifications_request + /// @return NotificationResponse on success and UStatus else virtual ResponseOrStatus register_for_notifications( const NotificationsRequest& register_notifications_request) = 0; + /// @brief unregisters for notifications to a USubscription backend + /// + /// @param unregister_notifications_request + /// @return NotificationResponse on success and UStatus else virtual ResponseOrStatus unregister_for_notifications( const NotificationsRequest& unregister_notifications_request) = 0; + /// @brief fetches all subscribers for a given topic from the backend + /// + /// @param fetch_subscriptions_request containing the topic for which the + /// subscribers are fetched + /// @return FetchSubscriptionsResponse on success and UStatus else virtual ResponseOrStatus fetch_subscribers( const FetchSubscribersRequest& fetch_subscribers_request) = 0; }; diff --git a/include/up-cpp/communication/RpcClient.h b/include/up-cpp/communication/RpcClient.h index d36f0c77a..ef218e04e 100644 --- a/include/up-cpp/communication/RpcClient.h +++ b/include/up-cpp/communication/RpcClient.h @@ -177,15 +177,17 @@ struct RpcClient { return ResponseOrStatus( UnexpectedStatus(payload_or_status.error())); } - datamodel::builder::Payload payload(payload_or_status.value()); - auto message_or_status = this->invokeMethod(std::move(payload)).get(); + datamodel::builder::Payload tmp_payload(payload_or_status.value()); + + auto message_or_status = + this->invokeMethod(std::move(tmp_payload)).get(); if (!message_or_status.has_value()) { return ResponseOrStatus( UnexpectedStatus(message_or_status.error())); } - + auto response_or_status = utils::ProtoConverter::extractFromProtobuf( message_or_status.value()); @@ -193,12 +195,10 @@ struct RpcClient { spdlog::error( "invokeProtoMethod: Error when extracting response from " "protobuf."); - return ResponseOrStatus( - UnexpectedStatus(response_or_status.error())); + return response_or_status; } - T response_message = response_or_status.value(); - return ResponseOrStatus(std::move(response_message)); + return ResponseOrStatus(response_or_status.value()); } /// @brief Default move constructor (defined in RpcClient.cpp) diff --git a/include/up-cpp/utils/ProtoConverter.h b/include/up-cpp/utils/ProtoConverter.h index 93a4471db..8f5c3deaa 100644 --- a/include/up-cpp/utils/ProtoConverter.h +++ b/include/up-cpp/utils/ProtoConverter.h @@ -18,13 +18,13 @@ using TOrStatus = utils::Expected; using UnexpectedStatus = utils::Unexpected; using PayloadOrStatus = utils::Expected; +using core::usubscription::v3::FetchSubscribersRequest; +using core::usubscription::v3::FetchSubscriptionsRequest; +using core::usubscription::v3::NotificationsRequest; using uprotocol::core::usubscription::v3::SubscribeAttributes; using uprotocol::core::usubscription::v3::SubscriberInfo; using uprotocol::core::usubscription::v3::SubscriptionRequest; using uprotocol::core::usubscription::v3::UnsubscribeRequest; -using core::usubscription::v3::FetchSubscriptionsRequest; -using core::usubscription::v3::FetchSubscribersRequest; -using core::usubscription::v3::NotificationsRequest; struct ProtoConverter { /// @brief Converts std::chrono::time_point to google::protobuf::Timestamp @@ -66,43 +66,42 @@ struct ProtoConverter { /// @return the built UnsubscribeRequest static UnsubscribeRequest BuildUnSubscribeRequest( const v1::UUri& subscription_topic); - + /// @brief Builds a FetchSubscriptionsRequest from the given topic /// /// @param topic the UUri of the topic to fetch subscriptions for /// @return the built FetchSubscriptionsRequest - static FetchSubscriptionsRequest BuildFetchSubscriptionsRequest(const v1::UUri& topic); + static FetchSubscriptionsRequest BuildFetchSubscriptionsRequest( + const v1::UUri& topic); - /// @brief Builds a FetchSubscriptionsRequest from the given subscriber information + /// @brief Builds a FetchSubscriptionsRequest from the given subscriber + /// information /// - /// @param subscriber the SubscriberInfo containing details of the subscriber + /// @param subscriber the SubscriberInfo containing details of the + /// subscriber /// @return the built FetchSubscriptionsRequest - static FetchSubscriptionsRequest BuildFetchSubscriptionsRequest(const SubscriberInfo& subscriber); + static FetchSubscriptionsRequest BuildFetchSubscriptionsRequest( + const SubscriberInfo& subscriber); /// @brief Builds a FetchSubscribersRequest from the given topic /// /// @param topic the UUri of the topic to fetch subscribers for /// @return the built FetchSubscribersRequest - static FetchSubscribersRequest BuildFetchSubscribersRequest(const v1::UUri& topic); + static FetchSubscribersRequest BuildFetchSubscribersRequest( + const v1::UUri& topic); /// @brief Builds a NotificationsRequest from the given topic /// /// @param topic the UUri of the topic to build a notification request for /// @return the built NotificationsRequest - static NotificationsRequest BuildNotificationsRequest(const v1::UUri& topic); - - /** - * @brief Deserializes a protobuf message from a given payload. - * - * Parses the payload in `v1::UMessage` using `google::protobuf::Any`, - * returning a deserialized object of type `T` or an error if parsing fails. - * - * @tparam T The type to deserialize the message into. - * - * @param message The `v1::UMessage` containing the payload. - * - * @return `TOrStatus` with the deserialized object or an error status. - */ + static NotificationsRequest BuildNotificationsRequest( + const v1::UUri& topic); + + /// @brief Deserializes a protobuf message from a given payload. + /// + /// @tparam T The type to deserialize the message into. + /// @param message The `v1::UMessage` containing the payload. + /// @return `TOrStatus` with the deserialized object or an error status. template static TOrStatus extractFromProtobuf(const v1::UMessage& message) { switch (message.attributes().payload_format()) { @@ -151,8 +150,7 @@ struct ProtoConverter { UPayloadFormat_INT_MAX_SENTINEL_DO_NOT_USE_: { v1::UStatus status; status.set_code(v1::UCode::UNIMPLEMENTED); - status.set_message( - "Unimplemented payload format."); + status.set_message("Unimplemented payload format."); return TOrStatus(UnexpectedStatus(status)); } default: { @@ -165,19 +163,11 @@ struct ProtoConverter { } } - /** - * @brief Serializes a protobuf object into a payload. - * - * Converts the given `proto` object to a payload using - * `google::protobuf::Any`. Returns the payload or an error status if - * serialization fails. - * - * @tparam T The type of the protobuf object to serialize. - * - * @param proto The protobuf object to be converted into a payload. - * - * @return `PayloadOrStatus` containing the payload or an error status. - */ + /// @brief Serializes a protobuf object into a payload. + /// + /// @tparam T The type of the protobuf object to serialize. + /// @param proto The protobuf object to be converted into a payload. + /// @return `PayloadOrStatus` containing the payload or an error status. template static PayloadOrStatus protoToPayload(const T& proto) { google::protobuf::Any any; diff --git a/src/client/usubscription/v3/RequestBuilder.cpp b/src/client/usubscription/v3/RequestBuilder.cpp index cbdfb4af7..7e30b4cbc 100644 --- a/src/client/usubscription/v3/RequestBuilder.cpp +++ b/src/client/usubscription/v3/RequestBuilder.cpp @@ -24,28 +24,33 @@ SubscriptionRequest RequestBuilder::buildSubscriptionRequest( return utils::ProtoConverter::BuildSubscriptionRequest(topic, attributes); } +RequestBuilder& RequestBuilder::setPremissionLevel(uint32_t permission_level) { + options_.permission_level = permission_level; + return *this; +} + UnsubscribeRequest RequestBuilder::buildUnsubscribeRequest( - const v1::UUri& topic) { + const v1::UUri& topic) const { return utils::ProtoConverter::BuildUnSubscribeRequest(topic); } FetchSubscriptionsRequest RequestBuilder::buildFetchSubscriptionsRequest( - const v1::UUri& topic) { + const v1::UUri& topic) const { return utils::ProtoConverter::BuildFetchSubscriptionsRequest(topic); } FetchSubscriptionsRequest RequestBuilder::buildFetchSubscriptionsRequest( - const SubscriberInfo& subscriber) { + const SubscriberInfo& subscriber) const { return utils::ProtoConverter::BuildFetchSubscriptionsRequest(subscriber); } FetchSubscribersRequest RequestBuilder::buildFetchSubscribersRequest( - const v1::UUri& topic) { + const v1::UUri& topic) const { return utils::ProtoConverter::BuildFetchSubscribersRequest(topic); } NotificationsRequest RequestBuilder::buildNotificationsRequest( - const v1::UUri& topic) { + const v1::UUri& topic) const { return utils::ProtoConverter::BuildNotificationsRequest(topic); } diff --git a/src/utils/ProtoConverter.cpp b/src/utils/ProtoConverter.cpp index 2f0cc378f..b6ee5403a 100644 --- a/src/utils/ProtoConverter.cpp +++ b/src/utils/ProtoConverter.cpp @@ -84,32 +84,36 @@ UnsubscribeRequest ProtoConverter::BuildUnSubscribeRequest( return unsubscribe_request; } -FetchSubscriptionsRequest ProtoConverter::BuildFetchSubscriptionsRequest(const v1::UUri& topic) { - FetchSubscriptionsRequest fetch_subscriptions_request; - *fetch_subscriptions_request.mutable_topic() = topic; +FetchSubscriptionsRequest ProtoConverter::BuildFetchSubscriptionsRequest( + const v1::UUri& topic) { + FetchSubscriptionsRequest fetch_subscriptions_request; + *fetch_subscriptions_request.mutable_topic() = topic; - return fetch_subscriptions_request; + return fetch_subscriptions_request; } -FetchSubscriptionsRequest ProtoConverter::BuildFetchSubscriptionsRequest(const SubscriberInfo& subscriber) { - FetchSubscriptionsRequest fetch_subscriptions_request; - *fetch_subscriptions_request.mutable_subscriber() = subscriber; +FetchSubscriptionsRequest ProtoConverter::BuildFetchSubscriptionsRequest( + const SubscriberInfo& subscriber) { + FetchSubscriptionsRequest fetch_subscriptions_request; + *fetch_subscriptions_request.mutable_subscriber() = subscriber; - return fetch_subscriptions_request; + return fetch_subscriptions_request; } -FetchSubscribersRequest ProtoConverter::BuildFetchSubscribersRequest(const v1::UUri& topic) { - FetchSubscribersRequest fetch_subscribers_request; - *fetch_subscribers_request.mutable_topic() = topic; +FetchSubscribersRequest ProtoConverter::BuildFetchSubscribersRequest( + const v1::UUri& topic) { + FetchSubscribersRequest fetch_subscribers_request; + *fetch_subscribers_request.mutable_topic() = topic; - return fetch_subscribers_request; + return fetch_subscribers_request; } -NotificationsRequest ProtoConverter::BuildNotificationsRequest(const v1::UUri& topic) { - NotificationsRequest notifications_request; - *notifications_request.mutable_topic() = topic; +NotificationsRequest ProtoConverter::BuildNotificationsRequest( + const v1::UUri& topic) { + NotificationsRequest notifications_request; + *notifications_request.mutable_topic() = topic; - return notifications_request; + return notifications_request; } } // namespace uprotocol::utils diff --git a/test/coverage/client/usubscription/v3/RequestBuilderTest.cpp b/test/coverage/client/usubscription/v3/RequestBuilderTest.cpp index 2de6e0a82..6b5c0d50e 100644 --- a/test/coverage/client/usubscription/v3/RequestBuilderTest.cpp +++ b/test/coverage/client/usubscription/v3/RequestBuilderTest.cpp @@ -29,12 +29,10 @@ namespace uprotocol::core::usubscription::v3 { class RequestBuilderTest : public ::testing::Test { private: v1::UUri source_; - v1::UUri wrong_topic_; protected: USubscriptionOptions options_; const v1::UUri& getSource() const { return source_; } - const v1::UUri& getWrongTopic() const { return wrong_topic_; } void SetUp() override { // Create a UUri object for testing @@ -43,16 +41,11 @@ class RequestBuilderTest : public ::testing::Test { source_.set_ue_version_major(SOURCE_UE_VERSION_MAJOR); source_.set_resource_id(SOURCE_RESOURCE_ID); - wrong_topic_.set_authority_name("10.0.0.2"); // random different authority - wrong_topic_.set_ue_id(SOURCE_UE_ID); - wrong_topic_.set_ue_version_major(SOURCE_UE_VERSION_MAJOR); - wrong_topic_.set_resource_id(SOURCE_RESOURCE_ID); - options_.permission_level = 2; options_.token = "sample_token"; options_.when_expire = - std::chrono::system_clock::now() + std::chrono::milliseconds(1000); - options_.sample_period_ms = std::chrono::milliseconds(1000); + std::chrono::system_clock::now() + std::chrono::milliseconds(1); + options_.sample_period_ms = std::chrono::seconds(1); options_.subscriber_details = google::protobuf::Any(); options_.subscription_details = google::protobuf::Any(); } @@ -72,81 +65,90 @@ class RequestBuilderTest : public ::testing::Test { }; TEST_F(RequestBuilderTest, BuildSubscriptionRequestWithOptions) { - v1::UUri topic = getSource(); - RequestBuilder builder(options_); + const v1::UUri topic = getSource(); + const RequestBuilder builder(options_); - SubscriptionRequest request = builder.buildSubscriptionRequest(topic); + SubscriptionRequest request; + ASSERT_NO_THROW(request = builder.buildSubscriptionRequest(topic)); // Verify the attributes in the request + // TODO(max) there should probably be some test that explicitely checks data + // from the options EXPECT_TRUE(request.has_topic()); EXPECT_TRUE(request.has_attributes()); EXPECT_EQ(request.topic().SerializeAsString(), topic.SerializeAsString()); - EXPECT_NE(request.topic().SerializeAsString(), - getWrongTopic().SerializeAsString()); - EXPECT_EQ(request.GetTypeName(), "uprotocol.core.usubscription.v3.SubscriptionRequest"); + EXPECT_EQ(request.GetTypeName(), + "uprotocol.core.usubscription.v3.SubscriptionRequest"); } TEST_F(RequestBuilderTest, BuildUnsubscribeRequest) { - v1::UUri topic = getSource(); - RequestBuilder builder(options_); + const v1::UUri topic = getSource(); + const RequestBuilder builder(options_); - UnsubscribeRequest request = builder.buildUnsubscribeRequest(topic); + UnsubscribeRequest request; + ASSERT_NO_THROW(request = builder.buildUnsubscribeRequest(topic)); - // Verify the attributes in the request - EXPECT_TRUE(request.has_topic()); - EXPECT_EQ(request.topic().SerializeAsString(), topic.SerializeAsString()); - EXPECT_NE(request.topic().SerializeAsString(), getWrongTopic().SerializeAsString()); - EXPECT_EQ(request.GetTypeName(), "uprotocol.core.usubscription.v3.UnsubscribeRequest"); + // Verify the attributes in the request + EXPECT_TRUE(request.has_topic()); + EXPECT_EQ(request.topic().SerializeAsString(), topic.SerializeAsString()); + EXPECT_EQ(request.GetTypeName(), + "uprotocol.core.usubscription.v3.UnsubscribeRequest"); } TEST_F(RequestBuilderTest, BuildFetchSubscriptionsRequestWithTopic) { - v1::UUri topic = getSource(); - RequestBuilder builder(options_); + const v1::UUri topic = getSource(); + const RequestBuilder builder(options_); - FetchSubscriptionsRequest request = builder.buildFetchSubscriptionsRequest(topic); + FetchSubscriptionsRequest request; + ASSERT_NO_THROW(request = builder.buildFetchSubscriptionsRequest(topic)); - // Verify the attributes in the request - EXPECT_TRUE(request.has_topic()); + // Verify the attributes in the request + EXPECT_TRUE(request.has_topic()); EXPECT_EQ(request.topic().SerializeAsString(), topic.SerializeAsString()); - EXPECT_NE(request.topic().SerializeAsString(), getWrongTopic().SerializeAsString()); - EXPECT_EQ(request.GetTypeName(), "uprotocol.core.usubscription.v3.FetchSubscriptionsRequest"); + EXPECT_EQ(request.GetTypeName(), + "uprotocol.core.usubscription.v3.FetchSubscriptionsRequest"); } TEST_F(RequestBuilderTest, BuildFetchSubscriptionsRequestWithSubscriberInfo) { - SubscriberInfo subscriber; - RequestBuilder builder(options_); + const SubscriberInfo subscriber; + const RequestBuilder builder(options_); - FetchSubscriptionsRequest request = builder.buildFetchSubscriptionsRequest(subscriber); + FetchSubscriptionsRequest request; + ASSERT_NO_THROW(request = + builder.buildFetchSubscriptionsRequest(subscriber)); - // Verify the attributes in the request - EXPECT_FALSE(request.has_topic()); - EXPECT_EQ(request.GetTypeName(), "uprotocol.core.usubscription.v3.FetchSubscriptionsRequest"); + // Verify the attributes in the request + EXPECT_FALSE(request.has_topic()); + EXPECT_EQ(request.GetTypeName(), + "uprotocol.core.usubscription.v3.FetchSubscriptionsRequest"); } TEST_F(RequestBuilderTest, BuildFetchSubscribersRequest) { - v1::UUri topic = getSource(); - RequestBuilder builder(options_); + const v1::UUri topic = getSource(); + const RequestBuilder builder(options_); - FetchSubscribersRequest request = builder.buildFetchSubscribersRequest(topic); + FetchSubscribersRequest request; + ASSERT_NO_THROW(request = builder.buildFetchSubscribersRequest(topic)); - // Verify the attributes in the request - EXPECT_TRUE(request.has_topic()); - EXPECT_EQ(request.topic().SerializeAsString(), topic.SerializeAsString()); - EXPECT_NE(request.topic().SerializeAsString(), getWrongTopic().SerializeAsString()); - EXPECT_EQ(request.GetTypeName(), "uprotocol.core.usubscription.v3.FetchSubscribersRequest"); + // Verify the attributes in the request + EXPECT_TRUE(request.has_topic()); + EXPECT_EQ(request.topic().SerializeAsString(), topic.SerializeAsString()); + EXPECT_EQ(request.GetTypeName(), + "uprotocol.core.usubscription.v3.FetchSubscribersRequest"); } TEST_F(RequestBuilderTest, BuildNotificationsRequest) { - v1::UUri topic = getSource(); - RequestBuilder builder(options_); + const v1::UUri topic = getSource(); + const RequestBuilder builder(options_); - NotificationsRequest request = builder.buildNotificationsRequest(topic); + NotificationsRequest request; + ASSERT_NO_THROW(request = builder.buildNotificationsRequest(topic)); - // Verify the attributes in the request - EXPECT_TRUE(request.has_topic()); - EXPECT_EQ(request.topic().SerializeAsString(), topic.SerializeAsString()); - EXPECT_NE(request.topic().SerializeAsString(), getWrongTopic().SerializeAsString()); - EXPECT_EQ(request.GetTypeName(), "uprotocol.core.usubscription.v3.NotificationsRequest"); + // Verify the attributes in the request + EXPECT_TRUE(request.has_topic()); + EXPECT_EQ(request.topic().SerializeAsString(), topic.SerializeAsString()); + EXPECT_EQ(request.GetTypeName(), + "uprotocol.core.usubscription.v3.NotificationsRequest"); } } // namespace uprotocol::core::usubscription::v3 diff --git a/test/coverage/client/usubscription/v3/RpcClientUSubscriptionTest.cpp b/test/coverage/client/usubscription/v3/RpcClientUSubscriptionTest.cpp index 2eb6f9bf9..2703f459a 100644 --- a/test/coverage/client/usubscription/v3/RpcClientUSubscriptionTest.cpp +++ b/test/coverage/client/usubscription/v3/RpcClientUSubscriptionTest.cpp @@ -11,14 +11,20 @@ using UMessage = uprotocol::v1::UMessage; using Payload = uprotocol::datamodel::builder::Payload; using ProtoConverter = uprotocol::utils::ProtoConverter; -using SubscriptionRequest = uprotocol::core::usubscription::v3::SubscriptionRequest; -using SubscriptionResponse = uprotocol::core::usubscription::v3::SubscriptionResponse; +using SubscriptionRequest = + uprotocol::core::usubscription::v3::SubscriptionRequest; +using SubscriptionResponse = + uprotocol::core::usubscription::v3::SubscriptionResponse; namespace { constexpr uint32_t UE_VERSION_MAJOR = 3; constexpr uint32_t CLIENT_UE_ID = 23492; +constexpr int ITERATIONS_TILL_TIMEOUT = 10; +constexpr std::chrono::milliseconds MILLISECONDS_PER_ITERATION = + std::chrono::milliseconds(50); + class RpcClientUSubscriptionTest : public testing::Test { protected: // Run once per TEST_F.s @@ -94,6 +100,10 @@ class RpcClientUSubscriptionTest : public testing::Test { ~RpcClientUSubscriptionTest() override = default; }; +// +// Tests for subscribe method +// + TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoPayload) { bool server_callback_executed = false; SubscriptionRequest server_capture; @@ -104,9 +114,9 @@ TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoPayload) { [&server_callback_executed, &server_capture, &server_response](const UMessage& message) -> std::optional { server_callback_executed = true; - auto request_or_status = ProtoConverter::extractFromProtobuf< - SubscriptionRequest>( - message); + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); if (!request_or_status.has_value()) { return std::nullopt; } @@ -126,20 +136,16 @@ TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoPayload) { const auto subscription_request = getRequestBuilder().buildSubscriptionRequest(getSubscriptionTopic()); - auto response_or_status_future = std::async( - std::launch::async, - [&client, &subscription_request]() - -> uprotocol::utils::Expected< - SubscriptionResponse, - uprotocol::v1::UStatus> { - return client.subscribe(subscription_request); - }); + auto response_or_status_future = + std::async(std::launch::async, + [&client, &subscription_request]() + -> uprotocol::utils::Expected { + return client.subscribe(subscription_request); + }); // wait to give the client time to send the request. Otherwise this would // cause a race condition - constexpr int ITERATIONS_TILL_TIMEOUT = 10; - constexpr std::chrono::milliseconds MILLISECONDS_PER_ITERATION = - std::chrono::milliseconds(100); int counter = ITERATIONS_TILL_TIMEOUT; while (counter > 0 && getClientTransport()->getSendCount() == 0) { counter--; @@ -172,19 +178,19 @@ TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoAnyPayload) { [&server_callback_executed, &server_capture, &server_response](const UMessage& message) -> std::optional { server_callback_executed = true; - auto request_or_status = ProtoConverter::extractFromProtobuf< - SubscriptionRequest>( - message); + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); if (!request_or_status.has_value()) { return std::nullopt; } server_capture = request_or_status.value(); - google::protobuf::Any any; - if(!any.PackFrom(server_response)) { - return std::nullopt; - } - Payload response_payload(any); - return response_payload; + google::protobuf::Any any; + if (!any.PackFrom(server_response)) { + return std::nullopt; + } + Payload response_payload(any); + return response_payload; }, uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY); @@ -198,20 +204,16 @@ TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoAnyPayload) { const auto subscription_request = getRequestBuilder().buildSubscriptionRequest(getSubscriptionTopic()); - auto response_or_status_future = std::async( - std::launch::async, - [&client, &subscription_request]() - -> uprotocol::utils::Expected< - SubscriptionResponse, - uprotocol::v1::UStatus> { - return client.subscribe(subscription_request); - }); + auto response_or_status_future = + std::async(std::launch::async, + [&client, &subscription_request]() + -> uprotocol::utils::Expected { + return client.subscribe(subscription_request); + }); // wait to give the client time to send the request. Otherwise this would // cause a race condition - constexpr int ITERATIONS_TILL_TIMEOUT = 10; - constexpr std::chrono::milliseconds MILLISECONDS_PER_ITERATION = - std::chrono::milliseconds(100); int counter = ITERATIONS_TILL_TIMEOUT; while (counter > 0 && getClientTransport()->getSendCount() == 0) { counter--; @@ -234,11 +236,12 @@ TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoAnyPayload) { server_response.SerializeAsString()); } -TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoPayloadDifferentTopic) { +TEST_F(RpcClientUSubscriptionTest, + SubscribeRoundtripWithValidProtoPayloadDifferentTopic) { bool server_callback_executed = false; SubscriptionRequest server_capture; SubscriptionResponse server_response; - + constexpr uint32_t TOPIC_UE = 4321; constexpr uint32_t TOPIC_RESOURCE_ID = 54321; uprotocol::v1::UUri wrong_subscription_topic; @@ -253,9 +256,9 @@ TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoPayloadDiffer [&server_callback_executed, &server_capture, &server_response](const UMessage& message) -> std::optional { server_callback_executed = true; - auto request_or_status = ProtoConverter::extractFromProtobuf< - SubscriptionRequest>( - message); + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); if (!request_or_status.has_value()) { return std::nullopt; } @@ -275,20 +278,228 @@ TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoPayloadDiffer const auto subscription_request = getRequestBuilder().buildSubscriptionRequest(getSubscriptionTopic()); + auto response_or_status_future = + std::async(std::launch::async, + [&client, &subscription_request]() + -> uprotocol::utils::Expected { + return client.subscribe(subscription_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + subscription_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_FALSE( + response_or_status + .has_value()); // Should fail because the topics do not match +} + +//////////////////////////////// +// Tests for unsubscribe method// +//////////////////////////////// + +using UnsubscibeRequest = + uprotocol::core::usubscription::v3::UnsubscribeRequest; +using UnsubscribeResponse = + uprotocol::core::usubscription::v3::UnsubscribeResponse; + +TEST_F(RpcClientUSubscriptionTest, UnsubscribeRoundtripWithValidProtoPayload) { + bool server_callback_executed = false; + UnsubscibeRequest server_capture; + UnsubscribeResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf(message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + Payload response_payload(server_response); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const auto unsubscribe_request = + getRequestBuilder().buildUnsubscribeRequest(getSubscriptionTopic()); + + auto response_or_status_future = + std::async(std::launch::async, + [&client, &unsubscribe_request]() + -> uprotocol::utils::Expected { + return client.unsubscribe(unsubscribe_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + unsubscribe_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +TEST_F(RpcClientUSubscriptionTest, + UnsubscribeRoundtripWithValidProtoAnyPayload) { + bool server_callback_executed = false; + UnsubscibeRequest server_capture; + UnsubscribeResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf(message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + google::protobuf::Any any; + if (!any.PackFrom(server_response)) { + return std::nullopt; + } + Payload response_payload(any); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const auto unsubscribe_request = + getRequestBuilder().buildUnsubscribeRequest(getSubscriptionTopic()); + + auto response_or_status_future = + std::async(std::launch::async, + [&client, &unsubscribe_request]() + -> uprotocol::utils::Expected { + return client.unsubscribe(unsubscribe_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + unsubscribe_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +//////////////////////////////// +// Tests for fetch_subscribers method// +//////////////////////////////// + +using FetchSubscribersRequest = + uprotocol::core::usubscription::v3::FetchSubscribersRequest; +using FetchSubscribersResponse = + uprotocol::core::usubscription::v3::FetchSubscribersResponse; + +TEST_F(RpcClientUSubscriptionTest, + fetchSubscriberRoundtripWithValidProtoPayload) { + bool server_callback_executed = false; + FetchSubscribersRequest server_capture; + FetchSubscribersResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + Payload response_payload(server_response); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const auto fetch_subscribers_request = + getRequestBuilder().buildFetchSubscribersRequest( + getSubscriptionTopic()); + auto response_or_status_future = std::async( std::launch::async, - [&client, &subscription_request]() - -> uprotocol::utils::Expected< - SubscriptionResponse, - uprotocol::v1::UStatus> { - return client.subscribe(subscription_request); + [&client, &fetch_subscribers_request]() + -> uprotocol::utils::Expected { + return client.fetch_subscribers(fetch_subscribers_request); }); // wait to give the client time to send the request. Otherwise this would // cause a race condition - constexpr int ITERATIONS_TILL_TIMEOUT = 10; - constexpr std::chrono::milliseconds MILLISECONDS_PER_ITERATION = - std::chrono::milliseconds(100); int counter = ITERATIONS_TILL_TIMEOUT; while (counter > 0 && getClientTransport()->getSendCount() == 0) { counter--; @@ -300,13 +511,509 @@ TEST_F(RpcClientUSubscriptionTest, SubscribeRoundtripWithValidProtoPayloadDiffer (*getServerTransport()->getListener())(getClientTransport()->getMessage()); EXPECT_TRUE(server_callback_executed); EXPECT_EQ(server_capture.SerializeAsString(), - subscription_request.SerializeAsString()); + fetch_subscribers_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +TEST_F(RpcClientUSubscriptionTest, + FetchSubscriberRoundtripWithValidProtoAnyPayload) { + bool server_callback_executed = false; + FetchSubscribersRequest server_capture; + FetchSubscribersResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + google::protobuf::Any any; + if (!any.PackFrom(server_response)) { + return std::nullopt; + } + Payload response_payload(any); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const auto fetch_subscribers_request = + getRequestBuilder().buildFetchSubscribersRequest( + getSubscriptionTopic()); + + auto response_or_status_future = std::async( + std::launch::async, + [&client, &fetch_subscribers_request]() + -> uprotocol::utils::Expected { + return client.fetch_subscribers(fetch_subscribers_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + fetch_subscribers_request.SerializeAsString()); getClientTransport()->mockMessage(getServerTransport()->getMessage()); EXPECT_TRUE(getClientTransport()->getListener()); EXPECT_EQ(getClientTransport()->getSendCount(), 1); auto response_or_status = response_or_status_future.get(); - ASSERT_FALSE(response_or_status.has_value()); // Should fail because the topics do not match + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +//////////////////////////////// +// Tests for fetch_subscriptions method// +//////////////////////////////// + +using FetchSubscriptionsRequest = + uprotocol::core::usubscription::v3::FetchSubscriptionsRequest; +using FetchSubscriptionsResponse = + uprotocol::core::usubscription::v3::FetchSubscriptionsResponse; + +TEST_F(RpcClientUSubscriptionTest, + fetchSubscriptionsRoundtripWithValidProtoPayload) { + bool server_callback_executed = false; + FetchSubscriptionsRequest server_capture; + FetchSubscriptionsResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + Payload response_payload(server_response); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const uprotocol::core::usubscription::v3::SubscriberInfo subscriber_info; + const auto fetch_subscriptions_request = + getRequestBuilder().buildFetchSubscriptionsRequest(subscriber_info); + + auto response_or_status_future = std::async( + std::launch::async, + [&client, &fetch_subscriptions_request]() + -> uprotocol::utils::Expected { + return client.fetch_subscriptions(fetch_subscriptions_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + fetch_subscriptions_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +TEST_F(RpcClientUSubscriptionTest, + fetchSubscriptionRoundtripWithValidProtoAnyPayload) { + bool server_callback_executed = false; + FetchSubscriptionsRequest server_capture; + FetchSubscriptionsResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + google::protobuf::Any any; + if (!any.PackFrom(server_response)) { + return std::nullopt; + } + Payload response_payload(any); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const uprotocol::core::usubscription::v3::SubscriberInfo subscriber_info; + const auto fetch_subscribers_request = + getRequestBuilder().buildFetchSubscriptionsRequest(subscriber_info); + + auto response_or_status_future = std::async( + std::launch::async, + [&client, &fetch_subscribers_request]() + -> uprotocol::utils::Expected { + return client.fetch_subscriptions(fetch_subscribers_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + fetch_subscribers_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +//////////////////////////////// +// Tests for register_for_notification method// +//////////////////////////////// + +using NotificationsRequest = + uprotocol::core::usubscription::v3::NotificationsRequest; +using NotificationsResponse = + uprotocol::core::usubscription::v3::NotificationsResponse; + +TEST_F(RpcClientUSubscriptionTest, + registerNotificationRoundtripWithValidProtoPayload) { + bool server_callback_executed = false; + NotificationsRequest server_capture; + NotificationsResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + Payload response_payload(server_response); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const auto notifications_request = + getRequestBuilder().buildNotificationsRequest(getSubscriptionTopic()); + + auto response_or_status_future = std::async( + std::launch::async, + [&client, ¬ifications_request]() + -> uprotocol::utils::Expected { + return client.register_for_notifications(notifications_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + notifications_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +TEST_F(RpcClientUSubscriptionTest, + registerNotificationRoundtripWithValidProtoAnyPayload) { + bool server_callback_executed = false; + NotificationsRequest server_capture; + NotificationsResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + google::protobuf::Any any; + if (!any.PackFrom(server_response)) { + return std::nullopt; + } + Payload response_payload(any); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const auto notifications_request = + getRequestBuilder().buildNotificationsRequest(getSubscriptionTopic()); + + auto response_or_status_future = std::async( + std::launch::async, + [&client, ¬ifications_request]() + -> uprotocol::utils::Expected { + return client.register_for_notifications(notifications_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + notifications_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +//////////////////////////////// +// Tests for unregister_for_notification method// +//////////////////////////////// + +using NotificationsRequest = + uprotocol::core::usubscription::v3::NotificationsRequest; +using NotificationsResponse = + uprotocol::core::usubscription::v3::NotificationsResponse; + +TEST_F(RpcClientUSubscriptionTest, + unregisterNotificationRoundtripWithValidProtoPayload) { + bool server_callback_executed = false; + NotificationsRequest server_capture; + NotificationsResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + Payload response_payload(server_response); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const auto notifications_request = + getRequestBuilder().buildNotificationsRequest(getSubscriptionTopic()); + + auto response_or_status_future = std::async( + std::launch::async, + [&client, ¬ifications_request]() + -> uprotocol::utils::Expected { + return client.unregister_for_notifications(notifications_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + notifications_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); +} + +TEST_F(RpcClientUSubscriptionTest, + unregisterNotificationRoundtripWithValidProtoAnyPayload) { + bool server_callback_executed = false; + NotificationsRequest server_capture; + NotificationsResponse server_response; + auto server_or_status = uprotocol::communication::RpcServer::create( + getServerTransport(), getServerMethodUuri(), + [&server_callback_executed, &server_capture, + &server_response](const UMessage& message) -> std::optional { + server_callback_executed = true; + auto request_or_status = + ProtoConverter::extractFromProtobuf( + message); + if (!request_or_status.has_value()) { + return std::nullopt; + } + server_capture = request_or_status.value(); + google::protobuf::Any any; + if (!any.PackFrom(server_response)) { + return std::nullopt; + } + Payload response_payload(any); + return response_payload; + }, + uprotocol::v1::UPayloadFormat::UPAYLOAD_FORMAT_PROTOBUF_WRAPPED_IN_ANY); + + ASSERT_TRUE(server_or_status.has_value()); + ASSERT_NE(server_or_status.value(), nullptr); + EXPECT_TRUE(getServerTransport()->getListener()); + + auto client = uprotocol::core::usubscription::v3::RpcClientUSubscription( + getClientTransport()); + + const auto notifications_request = + getRequestBuilder().buildNotificationsRequest(getSubscriptionTopic()); + + auto response_or_status_future = std::async( + std::launch::async, + [&client, ¬ifications_request]() + -> uprotocol::utils::Expected { + return client.unregister_for_notifications(notifications_request); + }); + + // wait to give the client time to send the request. Otherwise this would + // cause a race condition + int counter = ITERATIONS_TILL_TIMEOUT; + while (counter > 0 && getClientTransport()->getSendCount() == 0) { + counter--; + std::this_thread::sleep_for(MILLISECONDS_PER_ITERATION); + } + ASSERT_EQ(getClientTransport()->getSendCount(), 1); + EXPECT_TRUE(getClientTransport()->getListener()); + + (*getServerTransport()->getListener())(getClientTransport()->getMessage()); + EXPECT_TRUE(server_callback_executed); + EXPECT_EQ(server_capture.SerializeAsString(), + notifications_request.SerializeAsString()); + + getClientTransport()->mockMessage(getServerTransport()->getMessage()); + EXPECT_TRUE(getClientTransport()->getListener()); + EXPECT_EQ(getClientTransport()->getSendCount(), 1); + auto response_or_status = response_or_status_future.get(); + ASSERT_TRUE(response_or_status.has_value()); + EXPECT_EQ(response_or_status.value().SerializeAsString(), + server_response.SerializeAsString()); } }; // namespace diff --git a/test/coverage/client/usubscription/v3/USubscriptionUUriBuilderTest.cpp b/test/coverage/client/usubscription/v3/USubscriptionUUriBuilderTest.cpp index b62ef5a85..c27a62cbb 100644 --- a/test/coverage/client/usubscription/v3/USubscriptionUUriBuilderTest.cpp +++ b/test/coverage/client/usubscription/v3/USubscriptionUUriBuilderTest.cpp @@ -11,6 +11,7 @@ #include #include + #include "up-cpp/client/usubscription/v3/USubscriptionUUriBuilder.h" constexpr uint16_t RESOURCE_ID_TEST = 0x0001; @@ -19,52 +20,41 @@ constexpr uint16_t RESOURCE_ID_NOTIFICATION_ID = 0x8000; namespace uprotocol::core::usubscription::v3 { class USubscriptionUUriBuilderTest : public ::testing::Test { private: - v1::UUri expected_uri_; - v1::UUri wrong_uri_; -protected: - v1::UUri getExpectedUri() const { - return expected_uri_; - } - v1::UUri getWrongUri() const { - return wrong_uri_; - } - void SetUp() override { - expected_uri_.set_authority_name("core.usubscription"); - expected_uri_.set_ue_id(0); - expected_uri_.set_ue_version_major(3); + v1::UUri expected_uri_; - wrong_uri_.set_authority_name("core.usubscription_wrong"); - wrong_uri_.set_ue_id(1); - wrong_uri_.set_ue_version_major(1); - wrong_uri_.set_resource_id(1); - } +protected: + v1::UUri getExpectedUri() const { return expected_uri_; } - void TearDown() override {} + void SetUp() override { + expected_uri_.set_authority_name("core.usubscription"); + expected_uri_.set_ue_id(0); + expected_uri_.set_ue_version_major(3); + } + void TearDown() override {} }; TEST_F(USubscriptionUUriBuilderTest, GetServiceUriWithResourceId) { - // Example test case for building a subscription UUri - auto expected_uri = getExpectedUri(); - expected_uri.set_resource_id(RESOURCE_ID_TEST); - USubscriptionUUriBuilder builder; - v1::UUri actual_uri = builder.getServiceUriWithResourceId(RESOURCE_ID_TEST); - - EXPECT_TRUE(actual_uri.IsInitialized()); - EXPECT_EQ(actual_uri.GetTypeName(), "uprotocol.v1.UUri"); - EXPECT_EQ(actual_uri.SerializeAsString(), expected_uri.SerializeAsString()); - EXPECT_NE(actual_uri.SerializeAsString(), getWrongUri().SerializeAsString()); + // Example test case for building a subscription UUri + auto expected_uri = getExpectedUri(); + expected_uri.set_resource_id(RESOURCE_ID_TEST); + const USubscriptionUUriBuilder builder; + const v1::UUri actual_uri = + builder.getServiceUriWithResourceId(RESOURCE_ID_TEST); + + EXPECT_TRUE(actual_uri.IsInitialized()); + EXPECT_EQ(actual_uri.GetTypeName(), "uprotocol.v1.UUri"); + EXPECT_EQ(actual_uri.SerializeAsString(), expected_uri.SerializeAsString()); } -TEST_F(USubscriptionUUriBuilderTest, GetNotificationUri){ - auto expected_uri = getExpectedUri(); - expected_uri.set_resource_id(RESOURCE_ID_NOTIFICATION_ID); - USubscriptionUUriBuilder builder; - v1::UUri actual_uri = builder.getNotificationUri(); - EXPECT_TRUE(actual_uri.IsInitialized()); - EXPECT_EQ(actual_uri.GetTypeName(), "uprotocol.v1.UUri"); - EXPECT_EQ(actual_uri.SerializeAsString(), expected_uri.SerializeAsString()); - EXPECT_NE(actual_uri.SerializeAsString(), getWrongUri().SerializeAsString()); +TEST_F(USubscriptionUUriBuilderTest, GetNotificationUri) { + auto expected_uri = getExpectedUri(); + expected_uri.set_resource_id(RESOURCE_ID_NOTIFICATION_ID); + USubscriptionUUriBuilder builder; + v1::UUri actual_uri = builder.getNotificationUri(); + EXPECT_TRUE(actual_uri.IsInitialized()); + EXPECT_EQ(actual_uri.GetTypeName(), "uprotocol.v1.UUri"); + EXPECT_EQ(actual_uri.SerializeAsString(), expected_uri.SerializeAsString()); } -} // namespace uprotocol::core::usubscription::v3 +} // namespace uprotocol::core::usubscription::v3