From 374b2d90e9e235d9e5ffd77bf83f781f85f029b1 Mon Sep 17 00:00:00 2001 From: Bill Pittman Date: Wed, 5 Jun 2024 11:24:53 -0500 Subject: [PATCH] UMessage: validator and unit test Signed-off-by: Bill Pittman --- include/up-cpp/datamodel/validator/UMessage.h | 6 +- src/datamodel/validator/UMessage.cpp | 342 ++++++++ .../datamodel/UMessageValidatorTest.cpp | 748 +++++++++++++++++- 3 files changed, 1087 insertions(+), 9 deletions(-) diff --git a/include/up-cpp/datamodel/validator/UMessage.h b/include/up-cpp/datamodel/validator/UMessage.h index 6dec5558c..bab3ec678 100644 --- a/include/up-cpp/datamodel/validator/UMessage.h +++ b/include/up-cpp/datamodel/validator/UMessage.h @@ -131,9 +131,9 @@ using ValidationResult = std::tuple>; /// @brief Checks if UMessage is valid for sending a notification /// /// In addition to all common attributes being valid, these checks must pass: -/// * Message type must be UMESSAGE_TYPE_PUBLISH -/// * Message source must pass uri::isValidTopic() -/// * Message sink must pass uri::isValidTopic() +/// * Message type must be UMESSAGE_TYPE_NOTIFICATION +/// * Message source must pass uri::isValidNotification() +/// * Message sink must pass uri::isValidNotification() /// * Message must not set commstatus /// * Message must not set reqid /// * Message must not set permission_level diff --git a/src/datamodel/validator/UMessage.cpp b/src/datamodel/validator/UMessage.cpp index 292b7768a..58dfaa6e7 100644 --- a/src/datamodel/validator/UMessage.cpp +++ b/src/datamodel/validator/UMessage.cpp @@ -10,3 +10,345 @@ // SPDX-License-Identifier: Apache-2.0 #include "up-cpp/datamodel/validator/UMessage.h" + +#include + +#include "up-cpp/datamodel/validator/UUri.h" +#include "up-cpp/datamodel/validator/Uuid.h" + +namespace uprotocol::datamodel::validator::message { + +using namespace uprotocol::v1; +using namespace uprotocol::datamodel::validator; + +std::string_view message(Reason reason) { + switch (reason) { + case Reason::BAD_ID: + return "The ID does not pass UUID validity checks"; + case Reason::ID_EXPIRED: + return "The TTL, if present, indicates the ID has expired"; + case Reason::PRIORITY_OUT_OF_RANGE: + return "The Priority, if set, is not within the allowable range"; + case Reason::PAYLOAD_FORMAT_OUT_OF_RANGE: + return "The Payload Format is not within the allowable range"; + case Reason::WRONG_MESSAGE_TYPE: + return "The type set in the message is incorrect for the validated " + "mode"; + case Reason::BAD_SOURCE_URI: + return "Source URI did not pass validity checks"; + case Reason::BAD_SINK_URI: + return "Sink URI did not pass validity checks"; + case Reason::INVALID_TTL: + return "TTL is set to an invalid value (e.g. 0)"; + case Reason::DISALLOWED_FIELD_SET: + return "A field was set that is not allowed for the validated mode"; + case Reason::REQID_MISMATCH: + return "The Request ID did not match the ID of the request message"; + case Reason::PRIORITY_MISMATCH: + return "The Priority did not match the Priority of the request " + "message"; + default: + return "Unknown reason."; + } +} + +ValidationResult isValid(const v1::UMessage& umessage) { + { + auto [valid, reason] = isValidRpcRequest(umessage); + if (valid) { + return {true, std::nullopt}; + } + } + + { + auto [valid, reason] = isValidRpcResponse(umessage); + if (valid) { + return {true, std::nullopt}; + } + } + + { + auto [valid, reason] = isValidPublish(umessage); + if (valid) { + return {true, std::nullopt}; + } + } + + return isValidNotification(umessage); +} + +ValidationResult areCommonAttributesValid(const v1::UMessage& umessage) { + auto [valid, reason] = uuid::isUuid(umessage.attributes().id()); + if (!valid) { + return {false, Reason::BAD_ID}; + } + + if (umessage.attributes().has_ttl() && (umessage.attributes().ttl() > 0)) { + auto [expired, reason] = uuid::isExpired( + umessage.attributes().id(), + std::chrono::milliseconds(umessage.attributes().ttl())); + if (expired) { + return {false, Reason::ID_EXPIRED}; + } + } + + if (!UPriority_IsValid(umessage.attributes().priority())) { + return {false, Reason::PRIORITY_OUT_OF_RANGE}; + } + + if (!UPayloadFormat_IsValid(umessage.attributes().payload_format())) { + return {false, Reason::PAYLOAD_FORMAT_OUT_OF_RANGE}; + } + + return {true, std::nullopt}; +} + +ValidationResult isValidRpcRequest(const v1::UMessage& umessage) { + auto [valid, reason] = areCommonAttributesValid(umessage); + if (!valid) { + return {false, reason}; + } + + if (umessage.attributes().type() != + v1::UMessageType::UMESSAGE_TYPE_REQUEST) { + return {false, Reason::WRONG_MESSAGE_TYPE}; + } + + if (!umessage.attributes().has_source()) { + return {false, Reason::BAD_SOURCE_URI}; + } + + if (!umessage.attributes().has_sink()) { + return {false, Reason::BAD_SINK_URI}; + } + + { + auto [valid, reason] = + uri::isValidRpcResponse(umessage.attributes().source()); + if (!valid) { + return {false, Reason::BAD_SOURCE_URI}; + } + } + + { + auto [valid, reason] = + uri::isValidRpcMethod(umessage.attributes().sink()); + if (!valid) { + return {false, Reason::BAD_SINK_URI}; + } + } + + if (umessage.attributes().priority() < UPRIORITY_CS4) { + return {false, Reason::PRIORITY_OUT_OF_RANGE}; + } + + if ((!umessage.attributes().has_ttl()) || + (umessage.attributes().ttl() == 0)) { + return {false, Reason::INVALID_TTL}; + } + + if (umessage.attributes().has_commstatus()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_reqid()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + return {true, std::nullopt}; +} + +ValidationResult isValidRpcResponse(const v1::UMessage& umessage) { + auto [valid, reason] = areCommonAttributesValid(umessage); + if (!valid) { + return {false, reason}; + } + + if (umessage.attributes().type() != + v1::UMessageType::UMESSAGE_TYPE_RESPONSE) { + return {false, Reason::WRONG_MESSAGE_TYPE}; + } + + if (!umessage.attributes().has_source()) { + return {false, Reason::BAD_SOURCE_URI}; + } + + if (!umessage.attributes().has_sink()) { + return {false, Reason::BAD_SINK_URI}; + } + + { + auto [valid, reason] = + uri::isValidRpcMethod(umessage.attributes().source()); + if (!valid) { + return {false, Reason::BAD_SOURCE_URI}; + } + } + + { + auto [valid, reason] = + uri::isValidRpcResponse(umessage.attributes().sink()); + if (!valid) { + return {false, Reason::BAD_SINK_URI}; + } + } + + if (!umessage.attributes().has_reqid()) { + return {false, Reason::REQID_MISMATCH}; + } + + { + auto [valid, reason] = uuid::isUuid(umessage.attributes().reqid()); + if (!valid) { + return {false, Reason::REQID_MISMATCH}; + } + } + + if (umessage.attributes().has_ttl() && (umessage.attributes().ttl() > 0)) { + auto [expired, reason] = uuid::isExpired( + umessage.attributes().reqid(), + std::chrono::milliseconds(umessage.attributes().ttl())); + if (expired) { + return {false, Reason::ID_EXPIRED}; + } + } + + if (umessage.attributes().priority() < UPRIORITY_CS4) { + return {false, Reason::PRIORITY_OUT_OF_RANGE}; + } + + if (umessage.attributes().has_permission_level()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_token()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + return {true, std::nullopt}; +} + +ValidationResult isValidRpcResponseFor(const v1::UMessage& request, + const v1::UMessage& response) { + auto [valid, reason] = isValidRpcResponse(response); + if (!valid) { + return {false, reason}; + } + + if (!response.attributes().has_reqid()) { + return {false, Reason::REQID_MISMATCH}; + } + + if (!google::protobuf::util::MessageDifferencer::Equals( + response.attributes().reqid(), request.attributes().id())) { + return {false, Reason::REQID_MISMATCH}; + } + + if (request.attributes().priority() != response.attributes().priority()) { + return {false, Reason::PRIORITY_MISMATCH}; + } + + return {true, std::nullopt}; +} + +ValidationResult isValidPublish(const v1::UMessage& umessage) { + auto [valid, reason] = areCommonAttributesValid(umessage); + if (!valid) { + return {false, reason}; + } + + if (umessage.attributes().type() != + v1::UMessageType::UMESSAGE_TYPE_PUBLISH) { + return {false, Reason::WRONG_MESSAGE_TYPE}; + } + + if (!umessage.attributes().has_source()) { + return {false, Reason::BAD_SOURCE_URI}; + } + + { + auto [valid, reason] = + uri::isValidPublishTopic(umessage.attributes().source()); + if (!valid) { + return {false, Reason::BAD_SOURCE_URI}; + } + } + + if (umessage.attributes().has_sink()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_commstatus()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_reqid()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_permission_level()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_token()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + return {true, std::nullopt}; +} + +ValidationResult isValidNotification(const v1::UMessage& umessage) { + auto [valid, reason] = areCommonAttributesValid(umessage); + if (!valid) { + return {false, reason}; + } + + if (umessage.attributes().type() != + v1::UMessageType::UMESSAGE_TYPE_NOTIFICATION) { + return {false, Reason::WRONG_MESSAGE_TYPE}; + } + + if (!umessage.attributes().has_source()) { + return {false, Reason::BAD_SOURCE_URI}; + } + + if (!umessage.attributes().has_sink()) { + return {false, Reason::BAD_SINK_URI}; + } + + { + auto [valid, reason] = + uri::isValidNotification(umessage.attributes().source()); + if (!valid) { + return {false, Reason::BAD_SOURCE_URI}; + } + } + + { + auto [valid, reason] = + uri::isValidNotification(umessage.attributes().sink()); + if (!valid) { + return {false, Reason::BAD_SINK_URI}; + } + } + + if (umessage.attributes().has_commstatus()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_reqid()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_permission_level()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + if (umessage.attributes().has_token()) { + return {false, Reason::DISALLOWED_FIELD_SET}; + } + + return {true, std::nullopt}; +} + +} // namespace uprotocol::datamodel::validator::message diff --git a/test/coverage/datamodel/UMessageValidatorTest.cpp b/test/coverage/datamodel/UMessageValidatorTest.cpp index d8fcd230b..6367987be 100644 --- a/test/coverage/datamodel/UMessageValidatorTest.cpp +++ b/test/coverage/datamodel/UMessageValidatorTest.cpp @@ -11,28 +11,764 @@ #include #include +#include namespace { -class TestFixture : public testing::Test { +using namespace uprotocol::v1; +using namespace uprotocol::datamodel::validator::message; + +class TestUMessageValidator : public testing::Test { protected: // Run once per TEST_F. // Used to set up clean environments per test. - void SetUp() override {} + void SetUp() override { + source_.set_authority_name("10.0.0.1"); + source_.set_ue_id(0x00010001); + source_.set_ue_version_major(1); + source_.set_resource_id(1); + + sink_.set_authority_name("10.0.0.2"); + sink_.set_ue_id(0x00010002); + sink_.set_ue_version_major(2); + sink_.set_resource_id(2); + + reqId_.set_msb(0x1234); + reqId_.set_lsb(0x5678); + } + void TearDown() override {} // Run once per execution of the test application. // Used for setup of all tests. Has access to this instance. - TestFixture() = default; - ~TestFixture() = default; + TestUMessageValidator() = default; + ~TestUMessageValidator() = default; // Run once per execution of the test application. // Used only for global setup outside of tests. static void SetUpTestSuite() {} static void TearDownTestSuite() {} + + UUri source_; + UUri sink_; + UUID reqId_; }; -// TODO replace -TEST_F(TestFixture, SomeTestName) {} +UAttributes fakeRequest(UUri&& source, UUri&& sink) { + UUID id; + UAttributes attributes; + + uint64_t timestamp = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + id.set_msb((timestamp << 16) | (8ULL << 12) | + (0x123ULL)); // version 8 ; counter = 0x123 + id.set_lsb((2ULL << 62) | (0xFFFFFFFFFFFFULL)); // variant 10 + + attributes.set_type(UMESSAGE_TYPE_REQUEST); + attributes.set_allocated_id(new UUID(id)); + attributes.set_allocated_source(new UUri(source)); + attributes.set_allocated_sink(new UUri(sink)); + attributes.set_priority(UPRIORITY_CS4); + attributes.set_payload_format(UPAYLOAD_FORMAT_PROTOBUF); + attributes.set_ttl(1000); + + return attributes; +} + +UAttributes fakeResponse(UUri&& sink, UUri&& source) { + UUID id; + UAttributes attributes; + + uint64_t timestamp = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + id.set_msb((timestamp << 16) | (8ULL << 12) | + (0x123ULL)); // version 8 ; counter = 0x123 + id.set_lsb((2ULL << 62) | (0xFFFFFFFFFFFFULL)); // variant 10 + + attributes.set_type(UMESSAGE_TYPE_RESPONSE); + attributes.set_allocated_id(new UUID(id)); + attributes.set_allocated_source(new UUri(source)); + attributes.set_allocated_sink(new UUri(sink)); + attributes.set_priority(UPRIORITY_CS4); + attributes.set_payload_format(UPAYLOAD_FORMAT_PROTOBUF); + attributes.set_allocated_reqid(new UUID(id)); + + return attributes; +} + +UAttributes fakePublish(UUri&& source) { + UUID id; + UAttributes attributes; + + uint64_t timestamp = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + id.set_msb((timestamp << 16) | (8ULL << 12) | + (0x123ULL)); // version 8 ; counter = 0x123 + id.set_lsb((2ULL << 62) | (0xFFFFFFFFFFFFULL)); // variant 10 + + attributes.set_type(UMESSAGE_TYPE_PUBLISH); + attributes.set_allocated_id(new UUID(id)); + attributes.set_allocated_source(new UUri(source)); + attributes.set_payload_format(UPAYLOAD_FORMAT_PROTOBUF); + attributes.set_ttl(1000); + + return attributes; +} + +UAttributes fakeNotification(UUri&& source, UUri&& sink) { + UUID id; + UAttributes attributes; + + uint64_t timestamp = + std::chrono::duration_cast( + std::chrono::system_clock::now().time_since_epoch()) + .count(); + + id.set_msb((timestamp << 16) | (8ULL << 12) | + (0x123ULL)); // version 8 ; counter = 0x123 + id.set_lsb((2ULL << 62) | (0xFFFFFFFFFFFFULL)); // variant 10 + + attributes.set_type(UMESSAGE_TYPE_NOTIFICATION); + attributes.set_allocated_id(new UUID(id)); + attributes.set_allocated_source(new UUri(source)); + attributes.set_allocated_sink(new UUri(sink)); + attributes.set_payload_format(UPAYLOAD_FORMAT_PROTOBUF); + + return attributes; +} + +UMessage build(UAttributes& attributes) { + UMessage umessage; + umessage.set_allocated_attributes(new UAttributes(attributes)); + return umessage; +} + +void testCommonAttributes(UAttributes& attributesIn) { + { + // valid w/ttl + auto attributes = UAttributes(attributesIn); + auto umessage = build(attributes); + auto [valid, reason] = areCommonAttributesValid(umessage); + EXPECT_TRUE(valid); + EXPECT_FALSE(reason.has_value()); + } + + { + // valid w/o ttl + auto attributes = UAttributes(attributesIn); + attributes.clear_ttl(); + auto umessage = build(attributes); + auto [valid, reason] = areCommonAttributesValid(umessage); + EXPECT_TRUE(valid); + EXPECT_FALSE(reason.has_value()); + } + + { + // missing id + auto attributes = UAttributes(attributesIn); + attributes.clear_id(); + auto umessage = build(attributes); + auto [valid, reason] = areCommonAttributesValid(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_ID); + } + + { + // invalid id + auto attributes = UAttributes(attributesIn); + UUID* local_id = new UUID(attributes.id()); + local_id->set_lsb(0); + attributes.set_allocated_id(local_id); + auto umessage = build(attributes); + auto [valid, reason] = areCommonAttributesValid(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_ID); + } + + { + // expired ttl + auto attributes = UAttributes(attributesIn); + attributes.set_ttl(10); + usleep(20000); // sleep (id should be expired by now) + auto umessage = build(attributes); + auto [valid, reason] = areCommonAttributesValid(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::ID_EXPIRED); + } + + { + // out of range priority + auto attributes = UAttributes(attributesIn); + attributes.set_priority(UPriority(UPriority_MAX + 10)); + auto umessage = build(attributes); + auto [valid, reason] = areCommonAttributesValid(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::PRIORITY_OUT_OF_RANGE); + } + + { + // out of range payload format + auto attributes = UAttributes(attributesIn); + attributes.set_payload_format(UPayloadFormat(UPayloadFormat_MAX + 10)); + auto umessage = build(attributes); + auto [valid, reason] = areCommonAttributesValid(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::PAYLOAD_FORMAT_OUT_OF_RANGE); + } +} + +TEST_F(TestUMessageValidator, ValidRpcRequest) { + source_.set_resource_id(0); // must be 0 for valid requests + + { + // test common attributes for any message + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + testCommonAttributes(attributes); + } + + { + // valid + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_TRUE(valid); + EXPECT_FALSE(reason.has_value()); + } + + { + // set wrong type + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + attributes.set_type(UMESSAGE_TYPE_RESPONSE); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::WRONG_MESSAGE_TYPE); + } + + { + // missing source + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + attributes.clear_source(); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SOURCE_URI); + } + + { + // missing sink + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + attributes.clear_sink(); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SINK_URI); + } + + { + // invalid source + UUri source = source_; + source.set_resource_id(1); // should be zero + auto attributes = fakeRequest(std::move(source), std::move(sink_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SOURCE_URI); + } + + { + // invalid sink + UUri sink = sink_; + sink.set_resource_id(0); // should NOT be zero + auto attributes = fakeRequest(std::move(source_), std::move(sink)); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SINK_URI); + } + + { + // wrong priority + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + attributes.set_priority(UPRIORITY_CS3); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::PRIORITY_OUT_OF_RANGE); + } + + { + // Missing TTL + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + attributes.clear_ttl(); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::INVALID_TTL); + } + + { + // Invalid TTL (zero) + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + attributes.set_ttl(0); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::INVALID_TTL); + } + + { + // incorrectly set commstatus + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + attributes.set_commstatus(OK); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // incorrectly set reqid + auto attributes = fakeRequest(std::move(source_), std::move(sink_)); + attributes.set_allocated_reqid(new UUID(reqId_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcRequest(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } +} + +TEST_F(TestUMessageValidator, ValidRpcResponse) { + UPriority priority = UPRIORITY_CS4; + source_.set_resource_id(0); + + { + // test common attributes for any message + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + testCommonAttributes(attributes); + } + + { + // valid + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_TRUE(valid); + EXPECT_FALSE(reason.has_value()); + } + + { + // set wrong type + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.set_type(UMESSAGE_TYPE_REQUEST); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::WRONG_MESSAGE_TYPE); + } + + { + // missing source + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.clear_source(); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SOURCE_URI); + } + + { + // missing sink + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.clear_sink(); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SINK_URI); + } + + { + // invalid source + UUri source = source_; + source.set_resource_id(1); // should be zero + auto attributes = fakeResponse(std::move(source), std::move(sink_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SINK_URI); + } + + { + // invalid sink + UUri sink = sink_; + sink.set_resource_id(0); // should NOT be zero + auto attributes = fakeResponse(std::move(source_), std::move(sink)); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SOURCE_URI); + } + + { + // missing reqid + UUri sink = sink_; + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.clear_reqid(); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::REQID_MISMATCH); + } + + { + // invalid reqid + UUID local_id; + local_id.set_lsb(0); + local_id.set_msb(0); + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.set_allocated_reqid(new UUID(local_id)); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::REQID_MISMATCH); + } + + { + // no ttl set + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.clear_ttl(); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_TRUE(valid); + EXPECT_FALSE(reason.has_value()); + } + + { + // expired ttl + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.set_ttl(1); + usleep(20000); // sleep (id should be expired by now) + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::ID_EXPIRED); + } + + { + // invalid priority + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.set_priority(UPRIORITY_CS3); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::PRIORITY_OUT_OF_RANGE); + } + + { + // set permission level (shouldn't be set) + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.set_permission_level(7); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // set token (shouldn't be set) + auto attributes = fakeResponse(std::move(source_), std::move(sink_)); + attributes.set_token("token"); + auto umessage = build(attributes); + auto [valid, reason] = isValidRpcResponse(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } +} + +TEST_F(TestUMessageValidator, ValidRpcResponseFor) { + source_.set_resource_id(0); + + { + // valid + auto req_attr = fakeRequest(std::move(source_), std::move(sink_)); + auto res_attr = fakeResponse(std::move(source_), std::move(sink_)); + auto request = build(req_attr); + auto response = build(res_attr); + auto [valid, reason] = isValidRpcResponseFor(request, response); + EXPECT_TRUE(valid); + EXPECT_FALSE(reason.has_value()); + } + + { + // missing reqId + auto req_attr = fakeRequest(std::move(source_), std::move(sink_)); + auto res_attr = fakeResponse(std::move(source_), std::move(sink_)); + res_attr.clear_reqid(); + auto request = build(req_attr); + auto response = build(res_attr); + auto [valid, reason] = isValidRpcResponseFor(request, response); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::REQID_MISMATCH); + } + + { + // invalid reqId (does NOT match the request's id) + UUID local_id; + local_id.set_lsb(0); + local_id.set_msb(0); + auto req_attr = fakeRequest(std::move(source_), std::move(sink_)); + auto res_attr = fakeResponse(std::move(source_), std::move(sink_)); + res_attr.set_allocated_reqid(new UUID(local_id)); + auto request = build(req_attr); + auto response = build(res_attr); + auto [valid, reason] = isValidRpcResponseFor(request, response); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::REQID_MISMATCH); + } + + { + // mismatch the priority + auto req_attr = fakeRequest(std::move(source_), std::move(sink_)); + auto res_attr = fakeResponse(std::move(source_), std::move(sink_)); + res_attr.set_priority(UPRIORITY_CS6); + auto request = build(req_attr); + auto response = build(res_attr); + auto [valid, reason] = isValidRpcResponseFor(request, response); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::PRIORITY_MISMATCH); + } +} + +TEST_F(TestUMessageValidator, ValidPublish) { + source_.set_resource_id(0x8000); + + { + // test common attributes for any message + auto attributes = fakePublish(std::move(source_)); + testCommonAttributes(attributes); + } + + { + // valid + auto attributes = fakePublish(std::move(source_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_TRUE(valid); + EXPECT_FALSE(reason.has_value()); + } + + { + // wrong type + auto attributes = fakePublish(std::move(source_)); + attributes.set_type(UMESSAGE_TYPE_REQUEST); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::WRONG_MESSAGE_TYPE); + } + + { + // missing source + auto attributes = fakePublish(std::move(source_)); + attributes.clear_source(); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SOURCE_URI); + } + + { + // invalid source + UUri source = source_; + source.set_resource_id(0x7FFF); // should greater than 0x8000 + auto attributes = fakePublish(std::move(source)); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SOURCE_URI); + } + + { + // set a sink (unexpected) + auto attributes = fakePublish(std::move(source_)); + attributes.set_allocated_sink(new UUri(sink_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // set commstat (unexpected) + auto attributes = fakePublish(std::move(source_)); + attributes.set_commstatus(OK); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // set reqid (unexpected) + auto attributes = fakePublish(std::move(source_)); + attributes.set_allocated_reqid(new UUID(reqId_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // set permission level (unexpected) + auto attributes = fakePublish(std::move(source_)); + attributes.set_permission_level(7); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // set token (unexpected) + auto attributes = fakePublish(std::move(source_)); + attributes.set_token("token"); + auto umessage = build(attributes); + auto [valid, reason] = isValidPublish(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } +} + +TEST_F(TestUMessageValidator, ValidNotification) { + source_.set_resource_id(0x8001); + sink_.set_resource_id(0xFFFE); + + { + // test common attributes for any message + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + testCommonAttributes(attributes); + } + + { + // valid + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_TRUE(valid); + EXPECT_FALSE(reason.has_value()); + } + + { + // incorrect type + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + attributes.set_type(UMESSAGE_TYPE_REQUEST); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::WRONG_MESSAGE_TYPE); + } + + { + // missing source + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + attributes.clear_source(); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SOURCE_URI); + } + + { + // missing sink + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + attributes.clear_sink(); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SINK_URI); + } + + { + // invalid source + UUri local_source = source_; + local_source.set_resource_id(0x7FFF); // should be greater than 0x8000 + auto attributes = + fakeNotification(std::move(local_source), std::move(sink_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SOURCE_URI); + } + + { + // invalid sink + UUri local_sink = sink_; + local_sink.set_resource_id(0x7FFF); // should be greater than 0x8000 + auto attributes = + fakeNotification(std::move(source_), std::move(local_sink)); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::BAD_SINK_URI); + } + + { + // set commstatus (unexpected) + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + attributes.set_commstatus(OK); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // set reqid (unexpected) + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + attributes.set_allocated_reqid(new UUID(reqId_)); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // set permission level (unexpected) + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + attributes.set_permission_level(7); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } + + { + // set token (unexpected) + auto attributes = + fakeNotification(std::move(source_), std::move(sink_)); + attributes.set_token("token"); + auto umessage = build(attributes); + auto [valid, reason] = isValidNotification(umessage); + EXPECT_FALSE(valid); + EXPECT_EQ(reason, Reason::DISALLOWED_FIELD_SET); + } +} } // namespace