/* * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. * * Licensed under the Apache License, Version 2.0 (the "License"). * You may not use this file except in compliance with the License. * A copy of the License is located at * * http://aws.amazon.com/apache2.0/ * * or in the "license" file accompanying this file. This file is distributed * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either * express or implied. See the License for the specific language governing * permissions and limitations under the License. */ #include #include #include #include #include #include #include #include #include "MockAuthDelegate.h" #include "MockCapabilitiesObserver.h" #include "MockCapabilitiesStorage.h" namespace alexaClientSDK { namespace capabilitiesDelegate { namespace test { using namespace avsCommon::sdkInterfaces; using namespace avsCommon::sdkInterfaces::endpoints; using namespace avsCommon::avs; using namespace ::testing; /** * Mock discovery event sender class. */ class MockDiscoveryEventSender : public DiscoveryEventSenderInterface { public: MOCK_METHOD1(addDiscoveryStatusObserver, void(const std::shared_ptr&)); MOCK_METHOD1(removeDiscoveryStatusObserver, void(const std::shared_ptr&)); MOCK_METHOD1(onAlexaEventProcessedReceived, void(const std::string&)); }; /** * Create a test @c AVSDiscoveryEndpointAttributes. * @return a @c AVSDiscoveryEndpointAttributes structure. */ AVSDiscoveryEndpointAttributes createEndpointAttributes() { AVSDiscoveryEndpointAttributes attributes; attributes.endpointId = "TEST_ENDPOINT_ID"; attributes.description = "TEST_DESCRIPTION"; attributes.manufacturerName = "TEST_MANUFACTURER_NAME"; attributes.displayCategories = {"TEST_DISPLAY_CATEGORY"}; return attributes; } /** * Creates a test @c CapabilityConfiguration. * @return a @c CapabilityConfiguration structure. */ CapabilityConfiguration createCapabilityConfiguration() { return CapabilityConfiguration("TEST_TYPE", "TEST_INTERFACE", "TEST_VERSION"); } /** * Test harness for @c CapabilitiesDelegate class. */ class CapabilitiesDelegateTest : public Test { public: /** * Set up the test harness for running a test. */ void SetUp() override; /** * Tear down the test harness after running a test. */ void TearDown() override; protected: /// The mock Auth Delegate instance. std::shared_ptr m_mockAuthDelegate; /// The mock Capabilities Storage instance. std::shared_ptr m_mockCapabilitiesStorage; /// The mock Capabilities observer instance. std::shared_ptr m_mockCapabilitiesObserver; /// The data manager required to build the base object std::shared_ptr m_dataManager; /// The instance of the capabilitiesDelegate used in the tests. std::shared_ptr m_capabilitiesDelegate; }; void CapabilitiesDelegateTest::SetUp() { m_mockCapabilitiesStorage = std::make_shared>(); m_mockAuthDelegate = std::make_shared>(); m_mockCapabilitiesObserver = std::make_shared>(); m_dataManager = std::make_shared(); /// Expect calls to storage. EXPECT_CALL(*m_mockCapabilitiesStorage, open()).WillOnce(Return(true)); m_capabilitiesDelegate = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); ASSERT_NE(m_capabilitiesDelegate, nullptr); /// Add a new observer and it receives notifications of the current capabilities state. m_mockCapabilitiesObserver = std::make_shared>(); EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _)) .WillOnce( Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) { EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); })); m_capabilitiesDelegate->addCapabilitiesObserver(m_mockCapabilitiesObserver); } void CapabilitiesDelegateTest::TearDown() { m_capabilitiesDelegate->shutdown(); } /** * Tests the create method with various configurations. */ TEST_F(CapabilitiesDelegateTest, test_createMethodWithInvalidParameters) { auto instance = CapabilitiesDelegate::create(nullptr, m_mockCapabilitiesStorage, m_dataManager); ASSERT_EQ(instance, nullptr); instance = CapabilitiesDelegate::create(m_mockAuthDelegate, nullptr, m_dataManager); ASSERT_EQ(instance, nullptr); instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, nullptr); ASSERT_EQ(instance, nullptr); EXPECT_CALL(*m_mockCapabilitiesStorage, open()).WillOnce(Return(true)); instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); ASSERT_NE(instance, nullptr); instance->shutdown(); } /** * Tests the init method and if the open(), createDatabase() and load() methods get called on storage. */ TEST_F(CapabilitiesDelegateTest, test_init) { /// Test if creteDatabase fails create method return nullptr. EXPECT_CALL(*m_mockCapabilitiesStorage, open()).WillOnce(Return(false)); EXPECT_CALL(*m_mockCapabilitiesStorage, createDatabase()).Times(1).WillOnce(Return(false)); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); ASSERT_EQ(instance, nullptr); /// Happy path. EXPECT_CALL(*m_mockCapabilitiesStorage, open()).WillOnce(Return(false)); EXPECT_CALL(*m_mockCapabilitiesStorage, createDatabase()).WillOnce(Return(true)); instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); ASSERT_NE(instance, nullptr); instance->shutdown(); } /** * Tests if the invalidateCapabilities method triggers a database cleanup. */ TEST_F(CapabilitiesDelegateTest, test_invalidateCapabilities) { EXPECT_CALL(*m_mockCapabilitiesStorage, clearDatabase()).WillOnce(Return(true)); m_capabilitiesDelegate->invalidateCapabilities(); } /** * Tests if the clearData method triggers a database cleanup. */ TEST_F(CapabilitiesDelegateTest, test_clearData) { EXPECT_CALL(*m_mockCapabilitiesStorage, clearDatabase()).WillOnce(Return(true)); m_capabilitiesDelegate->clearData(); } /** * Tests if the addDiscoveryObserver method gets triggered when the addDiscoveryEventSender method is called. * Test if the removeDisoveryObserver method gets triggered when the shutdown method is called. */ TEST_F(CapabilitiesDelegateTest, test_shutdownTriggeresRemoveDiscoveryObserver) { auto discoveryEventSender = std::make_shared>(); EXPECT_CALL(*discoveryEventSender, addDiscoveryStatusObserver(_)) .WillOnce(Invoke([this](const std::shared_ptr& observer) { EXPECT_EQ(observer, m_capabilitiesDelegate); })); m_capabilitiesDelegate->addDiscoveryEventSender(discoveryEventSender); EXPECT_CALL(*discoveryEventSender, removeDiscoveryStatusObserver(_)) .WillOnce(Invoke([this](const std::shared_ptr& observer) { EXPECT_EQ(observer, m_capabilitiesDelegate); })); m_capabilitiesDelegate->shutdown(); } /** * Tests the addCapabilitiesObserver() method. */ TEST_F(CapabilitiesDelegateTest, test_addCapabilitiesObserver) { /// Adding null observer doesn't fail. m_capabilitiesDelegate->addCapabilitiesObserver(nullptr); /// Add a new observer and it receives notifications of the current capabilities state. auto mockObserver = std::make_shared>(); EXPECT_CALL(*mockObserver, onCapabilitiesStateChange(_, _)) .WillOnce( Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) { EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED); EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED); })); m_capabilitiesDelegate->addCapabilitiesObserver(mockObserver); /// Add existing observer and it does not get any notifications (strict mock would catch any extra notifications) m_capabilitiesDelegate->addCapabilitiesObserver(mockObserver); } /** * Tests for onDiscoveryCompleted() method. */ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryCompleted) { std::unordered_map addOrUpdateReportEndpoints = {{"add_1", "1"}, {"update_1", "2"}}; std::unordered_map deleteReportEndpoints = {{"delete_1", "1"}}; EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(true)); EXPECT_CALL(*m_mockCapabilitiesStorage, erase(deleteReportEndpoints)).WillOnce(Return(true)); EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _)) .WillOnce( Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) { EXPECT_EQ(newState, CapabilitiesObserverInterface::State::SUCCESS); EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::SUCCESS); })); /// Check if store and erase is triggered and if observer gets notified. m_capabilitiesDelegate->onDiscoveryCompleted(addOrUpdateReportEndpoints, deleteReportEndpoints); /// Check removing observer does not send notifications to the observer. m_capabilitiesDelegate->removeCapabilitiesObserver(m_mockCapabilitiesObserver); EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(true)); EXPECT_CALL(*m_mockCapabilitiesStorage, erase(deleteReportEndpoints)).WillOnce(Return(true)); /// Only store and erase is triggered, observer does not get notified (should fail as we use strict mock for /// observer). m_capabilitiesDelegate->onDiscoveryCompleted(addOrUpdateReportEndpoints, deleteReportEndpoints); } /** * Check onDiscoveryCompleted() but storage to device fails. */ TEST_F(CapabilitiesDelegateTest, test_onDiscoveryCompletedButStorageFails) { std::unordered_map addOrUpdateReportEndpoints = {{"add_1", "1"}, {"update_1", "2"}}; std::unordered_map deleteReportEndpoints = {{"delete_1", "1"}}; EXPECT_CALL(*m_mockCapabilitiesStorage, store(addOrUpdateReportEndpoints)).WillOnce(Return(false)); EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _)) .WillOnce( Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) { EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR); EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNKNOWN_ERROR); })); /// Check if store and erase is triggered and if observer gets notified. m_capabilitiesDelegate->onDiscoveryCompleted(addOrUpdateReportEndpoints, deleteReportEndpoints); } /** * Check notifications when onDiscoveryFailure() method is called. */ TEST_F(CapabilitiesDelegateTest, test_onDicoveryFailure) { /// validate retriable error response EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _)) .WillOnce( Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) { EXPECT_EQ(newState, CapabilitiesObserverInterface::State::RETRIABLE_ERROR); EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::SERVER_INTERNAL_ERROR); })); m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2); /// validate invalid auth error response EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _)) .WillOnce( Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) { EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR); EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::FORBIDDEN); })); m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::INVALID_AUTH); /// validate bad request error response EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _)) .WillOnce( Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) { EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR); EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::BAD_REQUEST); })); m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::BAD_REQUEST); /// other responses EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _)) .WillOnce( Invoke([](CapabilitiesObserverInterface::State newState, CapabilitiesObserverInterface::Error newError) { EXPECT_EQ(newState, CapabilitiesObserverInterface::State::RETRIABLE_ERROR); EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNKNOWN_ERROR); })); m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::THROTTLED); } /** * Tests if the registerEndpoint returns true for new endpoints, and false for invalid input or existing endpoint. */ TEST_F(CapabilitiesDelegateTest, test_registerEndpoint) { /// Invalid AVSDiscoveryEndpointAttributes. auto attributes = createEndpointAttributes(); auto capabilityConfig = createCapabilityConfiguration(); /// Empty Capabilities. ASSERT_FALSE(m_capabilitiesDelegate->registerEndpoint(attributes, std::vector())); /// Invalid CapabilityConfiguration. capabilityConfig.version = ""; ASSERT_FALSE(m_capabilitiesDelegate->registerEndpoint(attributes, {capabilityConfig})); /// Registering with same endpointAttributes fails. ASSERT_TRUE(m_capabilitiesDelegate->registerEndpoint(attributes, {createCapabilityConfiguration()})); ASSERT_FALSE(m_capabilitiesDelegate->registerEndpoint(attributes, {createCapabilityConfiguration()})); /// EndpointAttributes does not have endpointID which is required. attributes.endpointId = ""; ASSERT_FALSE(m_capabilitiesDelegate->registerEndpoint(attributes, {capabilityConfig})); } /*** * Tests if the createPostConnectOperation() creates the @c PostConnectCapabilitiesPublisher when registered endpoint * configurations are different from the ones in storage. */ TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithDifferentEndpointConfigs) { auto endpointAttributes = createEndpointAttributes(); auto capabilityConfig = createCapabilityConfiguration(); std::string endpointConfig = "TEST_CONFIG"; EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true)); EXPECT_CALL(*m_mockCapabilitiesStorage, load(_)) .Times(1) .WillOnce( Invoke([endpointAttributes, endpointConfig](std::unordered_map* storedEndpoints) { storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig}); return true; })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); instance->registerEndpoint(endpointAttributes, {capabilityConfig}); /// Endpoint config is different from the endpoint config created with the test endpoint attributes so a /// post connect operation is created. auto publisher = instance->createPostConnectOperation(); instance->shutdown(); ASSERT_NE(publisher, nullptr); } /** * Tests if the createPostConnectOperation() does not create a new @c PostConnectCapabilitiesPublisher when registered * endpoint configurations are same as the ones in storage. */ TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithSameEndpointConfigs) { auto endpointAttributes = createEndpointAttributes(); auto capabilityConfig = createCapabilityConfiguration(); std::vector capabilityConfigs = {capabilityConfig}; std::string endpointConfig = utils::getEndpointConfigJson(endpointAttributes, capabilityConfigs); EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true)); EXPECT_CALL(*m_mockCapabilitiesStorage, load(_)) .Times(1) .WillOnce( Invoke([endpointAttributes, endpointConfig](std::unordered_map* storedEndpoints) { storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig}); return true; })); auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager); instance->registerEndpoint(endpointAttributes, capabilityConfigs); /// Endpoint config is same as the endpoint config created with the test endpoint attributes so a /// post connect operation is not created. auto publisher = instance->createPostConnectOperation(); ASSERT_EQ(publisher, nullptr); } /** * Test if the CapabilitiesDelegate calls the clearDatabase() method when the onAVSGatewayChanged() method is called. */ TEST_F(CapabilitiesDelegateTest, test_onAVSGatewayChangedNotification) { EXPECT_CALL(*m_mockCapabilitiesStorage, clearDatabase()).WillOnce(Return(true)); m_capabilitiesDelegate->onAVSGatewayChanged("TEST_GATEWAY"); } } // namespace test } // namespace capabilitiesDelegate } // namespace alexaClientSDK