avs-device-sdk/CapabilitiesDelegate/test/CapabilitiesDelegateTest.cpp

1394 lines
68 KiB
C++

/*
* 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 <memory>
#include <gtest/gtest.h>
#include <gmock/gmock.h>
#include <AVSCommon/SDKInterfaces/CapabilitiesObserverInterface.h>
#include <AVSCommon/SDKInterfaces/MockMessageSender.h>
#include <AVSCommon/Utils/JSON/JSONUtils.h>
#include <AVSCommon/Utils/WaitEvent.h>
#include <CapabilitiesDelegate/CapabilitiesDelegate.h>
#include <CapabilitiesDelegate/PostConnectCapabilitiesPublisher.h>
#include <CapabilitiesDelegate/Storage/CapabilitiesDelegateStorageInterface.h>
#include <CapabilitiesDelegate/Utils/DiscoveryUtils.h>
#include "MockAuthDelegate.h"
#include "MockCapabilitiesObserver.h"
#include "MockCapabilitiesStorage.h"
#include "MockDiscoveryEventSender.h"
namespace alexaClientSDK {
namespace capabilitiesDelegate {
namespace test {
using namespace avsCommon::avs;
using namespace avsCommon::sdkInterfaces;
using namespace avsCommon::sdkInterfaces::endpoints;
using namespace avsCommon::utils::json::jsonUtils;
using namespace avsCommon::utils;
using namespace capabilitiesDelegate::utils;
using namespace ::testing;
/// Test string for auth token.
static const std::string TEST_AUTH_TOKEN = "TEST_AUTH_TOKEN";
/// The event key in the discovery event.
static const std::string EVENT_KEY = "event";
/// The header key in the discovery event.
static const std::string HEADER_KEY = "header";
/// The eventCorrelationToken key in the discovery event.
static const std::string EVENT_CORRELATION_TOKEN_KEY = "eventCorrelationToken";
/// Constant representing the timeout for test events.
/// @note Use a large enough value that should not fail even in slower systems.
static const std::chrono::seconds MY_WAIT_TIMEOUT{5};
/**
* Structure to store event data from a Discovery event JSON.
*/
struct EventData {
std::string namespaceString;
std::string nameString;
std::string payloadVersionString;
std::string eventCorrelationTokenString;
std::vector<std::string> endpointIdsInPayload;
std::string authToken;
};
/**
* Create a test @c AVSDiscoveryEndpointAttributes.
* @param endpointId Optional endpointId to use in these attributes. Default "TEST_ENDPOINT_ID".
* @return a @c AVSDiscoveryEndpointAttributes structure.
*/
AVSDiscoveryEndpointAttributes createEndpointAttributes(const std::string endpointId = "TEST_ENDPOINT_ID") {
AVSDiscoveryEndpointAttributes attributes;
attributes.endpointId = endpointId;
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:
/// Validates AuthDelegate calls.
void validateAuthDelegate();
/// Helper that validates dynamically adding an endpoint. Used for testing dynamic delete.
void addEndpoint(AVSDiscoveryEndpointAttributes attributes, CapabilityConfiguration configuration);
/*
* Gets the event correlation token string.
* @param request The message request to parse.
* @param[out] eventCorrelationTokenString The parsed event correlation token string.
*/
void getEventCorrelationTokenString(
std::shared_ptr<MessageRequest> request,
std::string& eventCorrelationTokenString);
/// The mock Auth Delegate instance.
std::shared_ptr<MockAuthDelegate> m_mockAuthDelegate;
/// The mock Capabilities Storage instance.
std::shared_ptr<MockCapabilitiesDelegateStorage> m_mockCapabilitiesStorage;
/// The mock Capabilities observer instance.
std::shared_ptr<MockCapabilitiesObserver> m_mockCapabilitiesObserver;
/// The data manager required to build the base object
std::shared_ptr<registrationManager::CustomerDataManager> m_dataManager;
/// The mock @c MessageSenderInterface used in the tests.
std::shared_ptr<avsCommon::sdkInterfaces::test::MockMessageSender> m_mockMessageSender;
/// The instance of the capabilitiesDelegate used in the tests.
std::shared_ptr<CapabilitiesDelegate> m_capabilitiesDelegate;
};
void CapabilitiesDelegateTest::SetUp() {
m_mockCapabilitiesStorage = std::make_shared<StrictMock<MockCapabilitiesDelegateStorage>>();
m_mockAuthDelegate = std::make_shared<StrictMock<MockAuthDelegate>>();
m_mockCapabilitiesObserver = std::make_shared<StrictMock<MockCapabilitiesObserver>>();
m_dataManager = std::make_shared<registrationManager::CustomerDataManager>();
m_mockMessageSender = std::make_shared<StrictMock<avsCommon::sdkInterfaces::test::MockMessageSender>>();
/// 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<StrictMock<MockCapabilitiesObserver>>();
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
m_capabilitiesDelegate->addCapabilitiesObserver(m_mockCapabilitiesObserver);
m_capabilitiesDelegate->setMessageSender(m_mockMessageSender);
}
void CapabilitiesDelegateTest::TearDown() {
m_capabilitiesDelegate->shutdown();
}
void CapabilitiesDelegateTest::validateAuthDelegate() {
EXPECT_CALL(*m_mockAuthDelegate, addAuthObserver(_))
.WillRepeatedly(Invoke([](const std::shared_ptr<AuthObserverInterface>& observer) {
observer->onAuthStateChange(AuthObserverInterface::State::REFRESHED, AuthObserverInterface::Error::SUCCESS);
}));
EXPECT_CALL(*m_mockAuthDelegate, removeAuthObserver(_)).Times(AtLeast(1));
EXPECT_CALL(*m_mockAuthDelegate, getAuthToken()).WillRepeatedly(Return(TEST_AUTH_TOKEN));
}
void CapabilitiesDelegateTest::getEventCorrelationTokenString(
std::shared_ptr<MessageRequest> request,
std::string& eventCorrelationTokenString) {
auto eventJson = request->getJsonContent();
std::string eventString;
retrieveValue(eventJson, EVENT_KEY, &eventString);
std::string headerString;
retrieveValue(eventString, HEADER_KEY, &headerString);
retrieveValue(headerString, EVENT_CORRELATION_TOKEN_KEY, &eventCorrelationTokenString);
}
void CapabilitiesDelegateTest::addEndpoint(
AVSDiscoveryEndpointAttributes attributes,
CapabilityConfiguration configuration) {
WaitEvent e;
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::CONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SUCCESS);
EXPECT_CALL(*m_mockMessageSender, sendMessage(_)).WillOnce(Invoke([this](std::shared_ptr<MessageRequest> request) {
std::string eventCorrelationTokenString;
getEventCorrelationTokenString(request, eventCorrelationTokenString);
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString);
}));
EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true));
EXPECT_CALL(*m_mockCapabilitiesStorage, erase((std::unordered_map<std::string, std::string>{})))
.WillOnce(Return(true));
EXPECT_CALL(
*m_mockCapabilitiesObserver,
onCapabilitiesStateChange(
CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, _, _))
.Times(1)
.WillOnce(Invoke([&](CapabilitiesObserverInterface::State state,
CapabilitiesObserverInterface::Error error,
std::vector<std::string> addedEndpoints,
std::vector<std::string> deletedEndpoints) { e.wakeUp(); }));
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(attributes, {configuration}));
ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT));
}
/**
* 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();
}
/**
* Test if the addDiscoveryObserver method gets triggered when the addDiscoveryEventSender method is called.
* Test if DiscoveryEventSender is stopped when the shutdown method is called.
* Test if the removeDiscoveryObserver method gets triggered when the shutdown method is called.
*/
TEST_F(CapabilitiesDelegateTest, test_shutdown) {
auto discoveryEventSender = std::make_shared<StrictMock<MockDiscoveryEventSender>>();
EXPECT_CALL(*discoveryEventSender, addDiscoveryStatusObserver(_))
.WillOnce(Invoke([this](const std::shared_ptr<DiscoveryStatusObserverInterface>& observer) {
EXPECT_EQ(observer, m_capabilitiesDelegate);
}));
m_capabilitiesDelegate->addDiscoveryEventSender(discoveryEventSender);
EXPECT_CALL(*discoveryEventSender, removeDiscoveryStatusObserver(_))
.WillOnce(Invoke([this](const std::shared_ptr<DiscoveryStatusObserverInterface>& observer) {
EXPECT_EQ(observer, m_capabilitiesDelegate);
}));
EXPECT_CALL(*discoveryEventSender, stop()).Times(1);
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<StrictMock<MockCapabilitiesObserver>>();
EXPECT_CALL(*mockObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
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<std::string, std::string> addOrUpdateReportEndpoints = {{"add_1", "1"}, {"update_1", "2"}};
std::unordered_map<std::string, std::string> 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,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::SUCCESS);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::SUCCESS);
std::sort(addOrUpdateReportEndpointIdentifiers.begin(), addOrUpdateReportEndpointIdentifiers.end());
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, (std::vector<std::string>{"add_1", "update_1"}));
EXPECT_EQ(deleteReportEndpointIdentifiers, (std::vector<std::string>{"delete_1"}));
}));
/// 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<std::string, std::string> addOrUpdateReportEndpoints = {{"add_1", "1"}, {"update_1", "2"}};
std::unordered_map<std::string, std::string> 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,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNKNOWN_ERROR);
std::sort(addOrUpdateReportEndpointIdentifiers.begin(), addOrUpdateReportEndpointIdentifiers.end());
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, (std::vector<std::string>{"add_1", "update_1"}));
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{"delete_1"});
}));
/// 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_onDiscoveryFailure) {
/// validate retriable error response
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::RETRIABLE_ERROR);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::SERVER_INTERNAL_ERROR);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
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,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::FORBIDDEN);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
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,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::FATAL_ERROR);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::BAD_REQUEST);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::BAD_REQUEST);
/// other responses
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::RETRIABLE_ERROR);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNKNOWN_ERROR);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
m_capabilitiesDelegate->onDiscoveryFailure(MessageRequestObserverInterface::Status::THROTTLED);
}
/**
* Tests if addOrUpdateEndpoint returns false for invalid input.
*/
TEST_F(CapabilitiesDelegateTest, test_addOrUpdateEndpointReturnsFalseWithInvalidInput) {
/// Invalid AVSDiscoveryEndpointAttributes.
auto attributes = createEndpointAttributes("endpointId");
auto capabilityConfig = createCapabilityConfiguration();
/// Empty Capabilities.
ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(attributes, std::vector<CapabilityConfiguration>()));
/// Invalid CapabilityConfiguration.
capabilityConfig.version = "";
ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(attributes, {capabilityConfig}));
/// EndpointAttributes does not have endpointID which is required.
attributes.endpointId = "";
ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(attributes, {capabilityConfig}));
/// Return false if endpoint is a duplicate in pending list.
attributes = createEndpointAttributes("duplicateId");
capabilityConfig.version = "1";
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(attributes, {capabilityConfig}));
ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(attributes, {capabilityConfig}));
}
/**
* Tests dynamic addOrUpdateEndpoint happy path.
*/
TEST_F(CapabilitiesDelegateTest, test_dynamicAddOrUpdateEndpoint) {
/// Set-up.
WaitEvent e;
validateAuthDelegate();
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::CONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SUCCESS);
auto attributes = createEndpointAttributes("endpointId");
auto capabilityConfig = createCapabilityConfiguration();
/// Expect calls to MessageSender.
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.Times(AtLeast(1))
.WillOnce(Invoke([this](std::shared_ptr<MessageRequest> request) {
std::string eventCorrelationTokenString;
getEventCorrelationTokenString(request, eventCorrelationTokenString);
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString);
}));
/// Expect call to storage.
EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true));
EXPECT_CALL(*m_mockCapabilitiesStorage, erase((std::unordered_map<std::string, std::string>{})))
.WillOnce(Return(true));
/// Expect callback to CapabilitiesObserver.
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([&](CapabilitiesObserverInterface::State state,
CapabilitiesObserverInterface::Error error,
std::vector<std::string> addedEndpoints,
std::vector<std::string> deletedEndpoints) {
EXPECT_EQ(state, CapabilitiesObserverInterface::State::SUCCESS);
EXPECT_EQ(error, CapabilitiesObserverInterface::Error::SUCCESS);
EXPECT_EQ(addedEndpoints, (std::vector<std::string>{"endpointId"}));
EXPECT_EQ(deletedEndpoints, (std::vector<std::string>{}));
e.wakeUp();
}));
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(attributes, {capabilityConfig}));
ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT));
}
/**
* Tests if deleteEndpoint returns false for invalid input.
*/
TEST_F(CapabilitiesDelegateTest, test_deleteEndpointReturnsFalseWithInvalidInput) {
/// Invalid AVSDiscoveryEndpointAttributes.
auto attributes = createEndpointAttributes("endpointId");
auto capabilityConfig = createCapabilityConfiguration();
/// Empty Capabilities.
ASSERT_FALSE(m_capabilitiesDelegate->deleteEndpoint(attributes, std::vector<CapabilityConfiguration>()));
/// Invalid CapabilityConfiguration.
capabilityConfig.version = "";
ASSERT_FALSE(m_capabilitiesDelegate->deleteEndpoint(attributes, {capabilityConfig}));
/// EndpointAttributes does not have endpointID which is required.
attributes.endpointId = "";
ASSERT_FALSE(m_capabilitiesDelegate->deleteEndpoint(attributes, {capabilityConfig}));
}
/**
* Tests dynamic deleteEndpoint happy path.
*/
TEST_F(CapabilitiesDelegateTest, test_dynamicDeleteEndpoint) {
/// Set-up.
WaitEvent e;
validateAuthDelegate();
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::CONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SUCCESS);
auto attributes = createEndpointAttributes("deleteId");
auto capabilityConfig = createCapabilityConfiguration();
auto configJson = getEndpointConfigJson(attributes, std::vector<CapabilityConfiguration>{capabilityConfig});
/// Add endpoint (so we can delete it).
addEndpoint(attributes, capabilityConfig);
/// Expect calls to MessageSender.
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillRepeatedly(Invoke([](std::shared_ptr<MessageRequest> request) {
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
}));
/// Expect call to storage.
EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true));
EXPECT_CALL(
*m_mockCapabilitiesStorage,
erase(std::unordered_map<std::string, std::string>({{attributes.endpointId, configJson}})))
.WillOnce(Return(true));
/// Expect callback to CapabilitiesObserver.
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([&](CapabilitiesObserverInterface::State state,
CapabilitiesObserverInterface::Error error,
std::vector<std::string> addedEndpoints,
std::vector<std::string> deletedEndpoints) {
EXPECT_EQ(state, CapabilitiesObserverInterface::State::SUCCESS);
EXPECT_EQ(error, CapabilitiesObserverInterface::Error::SUCCESS);
EXPECT_EQ(addedEndpoints, (std::vector<std::string>{}));
EXPECT_EQ(deletedEndpoints, (std::vector<std::string>{"deleteId"}));
e.wakeUp();
}));
ASSERT_TRUE(m_capabilitiesDelegate->deleteEndpoint(attributes, {capabilityConfig}));
ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT));
}
/**
* Tests dynamic deleteEndpoint should fail if endpoint is unregistered.
*/
TEST_F(CapabilitiesDelegateTest, test_dynamicDeleteEndpointWhenEndpointNotRegisteredShouldFail) {
/// Set-up.
auto attributes = createEndpointAttributes("deleteId");
auto capabilityConfig = createCapabilityConfiguration();
ASSERT_FALSE(m_capabilitiesDelegate->deleteEndpoint(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("endpointId");
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<std::string, std::string>* storedEndpoints) {
storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig});
return true;
}));
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
instance->addOrUpdateEndpoint(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
* pending endpoint configurations are same as the ones in storage.
* Tests if CapabilitiesDelegate reports this as a success to observers as there are pending endpoints.
*/
TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithPendingEndpointsWithSameEndpointConfigs) {
auto endpointAttributes = createEndpointAttributes("endpointId");
auto capabilityConfig = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> 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<std::string, std::string>* storedEndpoints) {
storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig});
return true;
}));
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
instance->addCapabilitiesObserver(m_mockCapabilitiesObserver);
instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs);
/// Endpoint config is same as the endpoint config created with the test endpoint attributes so a
/// post connect operation is not created. However, we do expect an observer callback as there were pending
/// endpoints.
EXPECT_CALL(
*m_mockCapabilitiesObserver,
onCapabilitiesStateChange(
CapabilitiesObserverInterface::State::SUCCESS,
CapabilitiesObserverInterface::Error::SUCCESS,
std::vector<std::string>{endpointAttributes.endpointId},
_));
auto publisher = instance->createPostConnectOperation();
ASSERT_EQ(publisher, nullptr);
// Clean-up.
instance->shutdown();
}
/**
* Tests if the createPostConnectOperation() does not create a new @c PostConnectCapabilitiesPublisher when registered
* pending endpoint configurations are same as the ones in storage.
* Tests that CapabilitiesDelegate does not notify observers on re-connect, as there were no changes and no pending
* endpoints expecting to be registered. This ensures observers are not notified unnecessarily during re-connects.
*/
TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithoutPendingEndpointsAndSameEndpointConfigs) {
/// Set-up.
auto endpointAttributes = createEndpointAttributes("endpointId");
auto capabilityConfig = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> 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(2)
.WillRepeatedly(
Invoke([endpointAttributes, endpointConfig](std::unordered_map<std::string, std::string>* storedEndpoints) {
storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig});
return true;
}));
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
instance->addCapabilitiesObserver(m_mockCapabilitiesObserver);
/// Add the endpoint here before creating the initial post-connect, which forces the endpoint to be cached in
/// CapabilitiesDelegate.
/// Here we do expect an observer callback because there is a pending endpoint. This is test set-up.s
instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs);
EXPECT_CALL(
*m_mockCapabilitiesObserver,
onCapabilitiesStateChange(
CapabilitiesObserverInterface::State::SUCCESS,
CapabilitiesObserverInterface::Error::SUCCESS,
std::vector<std::string>{endpointAttributes.endpointId},
_));
auto publisher = instance->createPostConnectOperation();
ASSERT_EQ(publisher, nullptr);
/// Test.
/// Create another post-connect operation; this simulates the re-connect.
/// There are no pending endpoints to send in CapabilitiesDelegate, and the cached endpoint in CapabilitiesDelegate
/// matches what is stored in the database, so expect no observer callback as well as a null post connect operation.
/// Strict mocks will catch the observer callback if it happens incorrectly.
publisher = instance->createPostConnectOperation();
/// Endpoint config is same as the endpoint config created with the test endpoint attributes so a
/// post connect operation is not created.
ASSERT_EQ(publisher, nullptr);
// Clean-up.
instance->shutdown();
}
/**
* Tests if the createPostConnectOperation() caches pending endpoints even when it does not create a new post-connect
* operation as capabilities have not change.
*/
TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationCachesEndpoints) {
auto endpointAttributes = createEndpointAttributes("endpointId");
auto capabilityConfig = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> 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(2)
.WillOnce(
Invoke([endpointAttributes, endpointConfig](std::unordered_map<std::string, std::string>* storedEndpoints) {
/// Returning a stored endpoint here is necessary to force CapabilitiesDelegate to cache the endpoint
/// on the first call to createPostConnectOperation().
storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig});
return true;
}))
.WillOnce(
Invoke([endpointAttributes, endpointConfig](std::unordered_map<std::string, std::string>* storedEndpoints) {
/// This represents an empty database, so that when createPostConnectOperation() is called a second
/// time, we can verify that it creates a non-null post-connect publisher to send the cached endpoint.
return true;
}));
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
instance->addCapabilitiesObserver(m_mockCapabilitiesObserver);
/// Endpoint config is same as the endpoint config created with the test endpoint attributes so a
/// post connect operation is not created, but we add the endpoint here so that it is cached in CapabilitiesDelegate
/// for testing later.
/// Expect an observer callback here, since there is a pending endpoint.
instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs);
EXPECT_CALL(
*m_mockCapabilitiesObserver,
onCapabilitiesStateChange(
CapabilitiesObserverInterface::State::SUCCESS,
CapabilitiesObserverInterface::Error::SUCCESS,
std::vector<std::string>{endpointAttributes.endpointId},
_));
auto publisher = instance->createPostConnectOperation();
ASSERT_EQ(publisher, nullptr);
/// Test.
/// Create another post-connect operation; this simulates a re-connect. We can verify whether the endpoint
/// from the first createPostConnectOperation() was cached.
/// The database is empty, but because there should be a cached endpoint, we expect a non-null post-connect
/// publisher to send that endpoint to AVS.
publisher = instance->createPostConnectOperation();
ASSERT_NE(publisher, nullptr);
// Clean-up.
instance->shutdown();
}
/**
* Tests if the createPostConnectOperation() creates a new @c PostConnectCapabilitiesPublisher when registered
* endpoint configurations are same as the ones in storage, but there is one additional stored endpoint that is
* not registered (and needs to be deleted).
* Tests observers are NOT notified of added endpoints since there are no pending endpoints.s
* Tests if CapabilitiesDelegate returns a non-null post connect operation, since there is a stale endpoint to delete.
*/
TEST_F(
CapabilitiesDelegateTest,
test_createPostConnectOperationWithStaleEndpointAndWithoutPendingEndpointsAndSameEndpointConfigs) {
auto endpointAttributes = createEndpointAttributes("endpointId");
auto capabilityConfig = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> capabilityConfigs = {capabilityConfig};
auto staleEndpointAttributes = createEndpointAttributes("staleEndpointId");
auto staleEndpointConfiguration = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> staleCapabilityConfigs = {staleEndpointConfiguration};
std::string endpointConfig = utils::getEndpointConfigJson(endpointAttributes, capabilityConfigs);
std::string staleEndpointConfig = utils::getEndpointConfigJson(staleEndpointAttributes, staleCapabilityConfigs);
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true));
EXPECT_CALL(*m_mockCapabilitiesStorage, load(_))
.Times(2)
.WillOnce(Invoke([endpointAttributes, endpointConfig, staleEndpointAttributes, staleEndpointConfig](
std::unordered_map<std::string, std::string>* storedEndpoints) {
storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig});
return true;
}))
.WillOnce(Invoke([endpointAttributes, endpointConfig, staleEndpointAttributes, staleEndpointConfig](
std::unordered_map<std::string, std::string>* storedEndpoints) {
storedEndpoints->insert({endpointAttributes.endpointId, endpointConfig});
/// Stale endpoint is in the database but not registered.
storedEndpoints->insert({staleEndpointAttributes.endpointId, staleEndpointConfig});
return true;
}));
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
instance->addCapabilitiesObserver(m_mockCapabilitiesObserver);
/// Endpoint config is same as the endpoint config created with the test endpoint attributes so a
/// post connect operation is not created, but add it here so that it is cached in CapabilitiesDelegate.
instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs);
EXPECT_CALL(
*m_mockCapabilitiesObserver,
onCapabilitiesStateChange(
CapabilitiesObserverInterface::State::SUCCESS,
CapabilitiesObserverInterface::Error::SUCCESS,
std::vector<std::string>{endpointAttributes.endpointId},
_));
auto publisher = instance->createPostConnectOperation();
ASSERT_EQ(publisher, nullptr);
/// Create another post-connect operation; this simulates a re-connect.
/// There is a stale endpoint to be sent, so we expect a non-null post connect publisher.
/// However, there is no pending endpoint to register, so we expect no immediate observer notification.
publisher = instance->createPostConnectOperation();
/// Endpoint config is same as the endpoint config created with the test endpoint attributes so a
/// post connect operation is not created.
ASSERT_NE(publisher, nullptr);
// Clean-up.
instance->shutdown();
}
/**
* Tests if the createPostConnectOperation() creates a new @c PostConnectCapabilitiesPublisher when there is an
* endpoint in storage that is not registered (that is, a stale endpoint in the database).
* Tests if CapabilitiesDelegate returns a non-null post connect operation, since there is a stale endpoint to delete.
*/
TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithStaleEndpoint) {
auto staleEndpointAttributes = createEndpointAttributes("staleEndpointId");
auto staleEndpointConfiguration = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> staleCapabilityConfigs = {staleEndpointConfiguration};
std::string staleEndpointConfig = utils::getEndpointConfigJson(staleEndpointAttributes, staleCapabilityConfigs);
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true));
EXPECT_CALL(*m_mockCapabilitiesStorage, load(_))
.WillOnce(Invoke([staleEndpointAttributes,
staleEndpointConfig](std::unordered_map<std::string, std::string>* storedEndpoints) {
storedEndpoints->insert({staleEndpointAttributes.endpointId, staleEndpointConfig});
return true;
}));
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
instance->addCapabilitiesObserver(m_mockCapabilitiesObserver);
/// There is a stale endpoint to be deleted, so we expect a non-null post connect publisher.
auto publisher = instance->createPostConnectOperation();
/// Endpoint config is same as the endpoint config created with the test endpoint attributes so a
/// post connect operation is not created.
ASSERT_NE(publisher, nullptr);
// Clean-up.
instance->shutdown();
}
/**
* Tests if the createPostConnectOperation() creates a new @c PostConnectCapabilitiesPublisher when registered
* endpoint configurations are same as the ones in storage, but there is one additional stored endpoint that is
* not registered (and needs to be deleted).
* Tests CapabilitiesObservers are notified of added endpoints even though they were not published in an event.
* Tests if CapabilitiesDelegate returns a non-null post connect operation, since there is a stale endpoint to delete.
*/
TEST_F(
CapabilitiesDelegateTest,
test_createPostConnectOperationWithStaleEndpointAndPendingEndpointsWithSameEndpointConfigs) {
auto unchangedEndpointAttributes = createEndpointAttributes("endpointId");
auto unchangedEndpointConfiguration = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> unchangedCapabilityConfigs = {unchangedEndpointConfiguration};
auto staleEndpointAttributes = createEndpointAttributes("staleEndpointId");
auto staleEndpointConfiguration = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> staleCapabilityConfigs = {staleEndpointConfiguration};
std::string unchangedEndpointConfig =
utils::getEndpointConfigJson(unchangedEndpointAttributes, unchangedCapabilityConfigs);
std::string staleEndpointConfig = utils::getEndpointConfigJson(staleEndpointAttributes, staleCapabilityConfigs);
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true));
EXPECT_CALL(*m_mockCapabilitiesStorage, load(_))
.Times(1)
.WillOnce(
Invoke([unchangedEndpointAttributes, unchangedEndpointConfig, staleEndpointAttributes, staleEndpointConfig](
std::unordered_map<std::string, std::string>* storedEndpoints) {
storedEndpoints->insert({unchangedEndpointAttributes.endpointId, unchangedEndpointConfig});
storedEndpoints->insert({staleEndpointAttributes.endpointId, staleEndpointConfig});
return true;
}));
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([](CapabilitiesObserverInterface::State newState,
CapabilitiesObserverInterface::Error newError,
std::vector<std::string> addOrUpdateReportEndpointIdentifiers,
std::vector<std::string> deleteReportEndpointIdentifiers) {
EXPECT_EQ(newState, CapabilitiesObserverInterface::State::UNINITIALIZED);
EXPECT_EQ(newError, CapabilitiesObserverInterface::Error::UNINITIALIZED);
EXPECT_EQ(addOrUpdateReportEndpointIdentifiers, std::vector<std::string>{});
EXPECT_EQ(deleteReportEndpointIdentifiers, std::vector<std::string>{});
}));
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
instance->addCapabilitiesObserver(m_mockCapabilitiesObserver);
instance->addOrUpdateEndpoint(unchangedEndpointAttributes, unchangedCapabilityConfigs);
/// Observer callback should only contain the pending endpoint to add (since that is already registered),
/// but not the stale endpoint to delete (since that still needs to be sent to AVS).
EXPECT_CALL(
*m_mockCapabilitiesObserver,
onCapabilitiesStateChange(
CapabilitiesObserverInterface::State::SUCCESS,
CapabilitiesObserverInterface::Error::SUCCESS,
std::vector<std::string>{unchangedEndpointAttributes.endpointId},
std::vector<std::string>{}));
auto publisher = instance->createPostConnectOperation();
ASSERT_NE(publisher, nullptr);
// Clean-up.
instance->shutdown();
}
/**
* Tests if the createPostConnectOperation() creates a new @c PostConnectCapabilitiesPublisher when storage is empty.
* When the capabilities are successfully published, a subsequent call to createPostConnectOperation() results in a
* nullptr.
*
* @note This test simulates a fresh device which sends a successful Discovery event followed by a reconnection.
* Since the discovery event is already sent and the result is stored in the database, a new post connect capabilities
* publisher is not created on reconnection.
*/
TEST_F(CapabilitiesDelegateTest, test_createPostConnectOperationWithReconnects) {
auto endpointAttributes = createEndpointAttributes();
auto capabilityConfig = createCapabilityConfiguration();
std::vector<CapabilityConfiguration> capabilityConfigs = {capabilityConfig};
std::string endpointConfig = utils::getEndpointConfigJson(endpointAttributes, capabilityConfigs);
std::unordered_map<std::string, std::string> addOrUpdateReportEndpoints = {
{endpointAttributes.endpointId, endpointConfig}};
std::unordered_map<std::string, std::string> emptyDeleteReportEndpoints;
EXPECT_CALL(*m_mockCapabilitiesStorage, open()).Times(1).WillOnce(Return(true));
EXPECT_CALL(*m_mockCapabilitiesStorage, load(_)).Times(1).WillOnce(Return(true));
auto instance = CapabilitiesDelegate::create(m_mockAuthDelegate, m_mockCapabilitiesStorage, m_dataManager);
instance->addOrUpdateEndpoint(endpointAttributes, capabilityConfigs);
/// Endpoint config in storage is empty, create a new post connect operation.
auto publisher = instance->createPostConnectOperation();
ASSERT_NE(publisher, nullptr);
/// Expect the successfully published endpoint configuration to be stored and erase.
EXPECT_CALL(*m_mockCapabilitiesStorage, store(_))
.WillOnce(Invoke(
[addOrUpdateReportEndpoints](const std::unordered_map<std::string, std::string>& endpointIdToConfigMap) {
EXPECT_EQ(endpointIdToConfigMap, addOrUpdateReportEndpoints);
return true;
}));
EXPECT_CALL(*m_mockCapabilitiesStorage, erase(emptyDeleteReportEndpoints))
.WillOnce(Invoke(
[emptyDeleteReportEndpoints](const std::unordered_map<std::string, std::string>& endpointIdToConfigMap) {
EXPECT_EQ(endpointIdToConfigMap, emptyDeleteReportEndpoints);
return true;
}));
/// Notify that discovery has successfully completed.
instance->onDiscoveryCompleted(addOrUpdateReportEndpoints, emptyDeleteReportEndpoints);
/// Expect call to load endpoint configuration
EXPECT_CALL(*m_mockCapabilitiesStorage, load(_))
.WillOnce(Invoke([addOrUpdateReportEndpoints](std::unordered_map<std::string, std::string>* storedEndpoints) {
*storedEndpoints = addOrUpdateReportEndpoints;
return true;
}));
/// PostConnectOperation after reconnection
auto postConnectOperationOnReconnection = instance->createPostConnectOperation();
ASSERT_EQ(postConnectOperationOnReconnection, nullptr);
instance->shutdown();
}
/**
* 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");
}
/**
* Test if post-connect operation will send all registered and pending endpoints if database is empty
* (eg after onAVSGatewayChanged).
* Test if pending delete for a registered endpoint will mean that endpoint is not sent in addOrUpdateReport.
*/
TEST_F(CapabilitiesDelegateTest, test_reconnectWhenStorageIsEmpty) {
/// Set-up.
WaitEvent e;
validateAuthDelegate();
auto capabilityConfig = createCapabilityConfiguration();
/// Add a test endpoint.
const std::string firstEndpointId = "add_1";
auto firstEndpointAttributes = createEndpointAttributes(firstEndpointId);
addEndpoint(firstEndpointAttributes, capabilityConfig);
/// Add another test endpoint.
const std::string secondEndpointId = "add_2";
auto secondEndpointAttributes = createEndpointAttributes(secondEndpointId);
addEndpoint(secondEndpointAttributes, capabilityConfig);
/// Add a third test endpoint. Get the capability json to test pending delete.
const std::string thirdEndpointId = "add_3";
auto thirdEndpointAttributes = createEndpointAttributes(thirdEndpointId);
addEndpoint(thirdEndpointAttributes, capabilityConfig);
/// Expect calls to MessageSender.
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.WillOnce(Invoke([this](std::shared_ptr<MessageRequest> request) {
std::string eventCorrelationTokenString;
getEventCorrelationTokenString(request, eventCorrelationTokenString);
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString);
}))
.WillOnce(Invoke([](std::shared_ptr<MessageRequest> request) {
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
}));
/// Expect calls to storage.
EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true));
EXPECT_CALL(
*m_mockCapabilitiesStorage,
erase((std::unordered_map<std::string, std::string>{
{thirdEndpointId, utils::getEndpointConfigJson(thirdEndpointAttributes, {capabilityConfig})}})))
.WillOnce(Return(true));
/// Expect calls to CapabilitiesObserver.
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([&](CapabilitiesObserverInterface::State state,
CapabilitiesObserverInterface::Error error,
std::vector<std::string> addedEndpoints,
std::vector<std::string> deletedEndpoints) {
EXPECT_EQ(state, CapabilitiesObserverInterface::State::SUCCESS);
EXPECT_EQ(error, CapabilitiesObserverInterface::Error::SUCCESS);
std::sort(addedEndpoints.begin(), addedEndpoints.end());
EXPECT_EQ(addedEndpoints, (std::vector<std::string>{firstEndpointId, secondEndpointId}));
EXPECT_EQ(deletedEndpoints, (std::vector<std::string>{thirdEndpointId}));
e.wakeUp();
}));
/// Disconnect to force a pending delete.
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::DISCONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT);
ASSERT_TRUE(m_capabilitiesDelegate->deleteEndpoint(thirdEndpointAttributes, {capabilityConfig}));
/// Test post-connect with an empty database.
EXPECT_CALL(*m_mockCapabilitiesStorage, load(_))
.Times(1)
.WillOnce(Invoke([](std::unordered_map<std::string, std::string>* storedEndpoints) { return true; }));
auto postconnect = m_capabilitiesDelegate->createPostConnectOperation();
ASSERT_NE(postconnect, nullptr);
ASSERT_TRUE(postconnect->performOperation(m_mockMessageSender));
ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT));
}
/**
* Test if the CapabilitiesDelegate defers dynamically adding or deleting endpoints while there is a
* Discovery event in-flight.
*/
TEST_F(CapabilitiesDelegateTest, test_deferSendDiscoveryEventsWhileDiscoveryEventSenderInFlight) {
/// Set-up.
validateAuthDelegate();
auto capabilityConfig = createCapabilityConfiguration();
const std::string firstEndpointId = "add_1";
auto firstEndpointAttributes = createEndpointAttributes(firstEndpointId);
/// Add a second test endpoint. Get the capability json for the last endpoint to test pending delete.
const std::string secondEndpointId = "delete_1";
auto secondEndpointAttributes = createEndpointAttributes(secondEndpointId);
auto deleteCapabilityConfigJson = utils::getEndpointConfigJson(secondEndpointAttributes, {capabilityConfig});
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::CONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SUCCESS);
addEndpoint(secondEndpointAttributes, capabilityConfig);
/// Add a DiscoveryEventSender to simulate Discovery event in-flight.
auto discoveryEventSender = std::make_shared<StrictMock<MockDiscoveryEventSender>>();
EXPECT_CALL(*discoveryEventSender, addDiscoveryStatusObserver(_))
.WillOnce(Invoke([this](const std::shared_ptr<DiscoveryStatusObserverInterface>& observer) {
EXPECT_EQ(observer, m_capabilitiesDelegate);
}));
EXPECT_CALL(*discoveryEventSender, removeDiscoveryStatusObserver(_))
.WillOnce(Invoke([this](const std::shared_ptr<DiscoveryStatusObserverInterface>& observer) {
EXPECT_EQ(observer, m_capabilitiesDelegate);
}));
EXPECT_CALL(*discoveryEventSender, stop());
m_capabilitiesDelegate->addDiscoveryEventSender(discoveryEventSender);
/// Expect no callback to CapabilitiesObserver, since these endpoints remain in pending.
EXPECT_CALL(
*m_mockCapabilitiesObserver,
onCapabilitiesStateChange(
_, _, std::vector<std::string>{firstEndpointId}, std::vector<std::string>{secondEndpointId}))
.Times(0);
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(firstEndpointAttributes, {capabilityConfig}));
ASSERT_TRUE(m_capabilitiesDelegate->deleteEndpoint(secondEndpointAttributes, {capabilityConfig}));
}
/**
* Tests that CapabilitiesDelegate is in the correct state before notifying its observers.
*/
TEST_F(CapabilitiesDelegateTest, test_observerCallingIntoCapabilitiesDelegateOnSuccessNotificationSucceeds) {
WaitEvent e;
/// Set-up.
validateAuthDelegate();
auto capabilityConfig = createCapabilityConfiguration();
/// Add an endpoint.
const std::string endpointId = "delete_1";
auto endpointAttributes = createEndpointAttributes(endpointId);
auto capabilityConfigJson = utils::getEndpointConfigJson(endpointAttributes, {capabilityConfig});
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::CONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SUCCESS);
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.Times(2)
.WillOnce(Invoke([this](std::shared_ptr<MessageRequest> request) {
std::string eventCorrelationTokenString;
getEventCorrelationTokenString(request, eventCorrelationTokenString);
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString);
}))
.WillOnce(Invoke([&](std::shared_ptr<MessageRequest> request) {
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
}));
EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillRepeatedly(Return(true));
EXPECT_CALL(*m_mockCapabilitiesStorage, erase(std::unordered_map<std::string, std::string>{}))
.WillOnce(Return(true));
EXPECT_CALL(
*m_mockCapabilitiesStorage,
erase((std::unordered_map<std::string, std::string>{{endpointId, capabilityConfigJson}})))
.WillOnce(Return(true));
/// On the first successful capabilities change, call from observer back into CapabilitiesDelegate.
EXPECT_CALL(
*m_mockCapabilitiesObserver,
onCapabilitiesStateChange(
CapabilitiesObserverInterface::State::SUCCESS, CapabilitiesObserverInterface::Error::SUCCESS, _, _))
.Times(2)
.WillOnce(Invoke([&, this](
CapabilitiesObserverInterface::State state,
CapabilitiesObserverInterface::Error error,
std::vector<std::string> addedEndpoints,
std::vector<std::string> deletedEndpoints) {
/// If the below is false with "endpoint not registered", this means that the CapabilitiesDelegate
/// state is not correct when it notifies its observers.
ASSERT_TRUE(m_capabilitiesDelegate->deleteEndpoint(endpointAttributes, {capabilityConfig}));
}))
.WillOnce(Invoke([&](CapabilitiesObserverInterface::State state,
CapabilitiesObserverInterface::Error error,
std::vector<std::string> addedEndpoints,
std::vector<std::string> deletedEndpoints) { e.wakeUp(); }));
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(endpointAttributes, {capabilityConfig}));
ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT));
}
/**
* Test if pending endpoints are not sent while disconnected.
*/
TEST_F(CapabilitiesDelegateTest, test_doNotSendEndpointsWhileDisconnected) {
/// Set-up.
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::DISCONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT);
auto attributes = createEndpointAttributes("add_1");
auto capabilityConfig = createCapabilityConfiguration();
/// Strict mock will catch unexpected calls.
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(attributes, {capabilityConfig}));
}
/**
* Test if pending endpoints are sent on re-connect.
*/
TEST_F(CapabilitiesDelegateTest, test_reconnectTriggersSendPendingEndpoints) {
/// Set-up.
WaitEvent e;
validateAuthDelegate();
auto capabilityConfig = createCapabilityConfiguration();
const std::string firstEndpointId = "add_1";
auto firstEndpointAttributes = createEndpointAttributes(firstEndpointId);
const std::string secondEndpointId = "add_2";
auto secondEndpointAttributes = createEndpointAttributes(secondEndpointId);
/// Add a third test endpoint. Get the capability json for the last endpoint to test pending delete.
const std::string thirdEndpointId = "delete_1";
auto thirdEndpointAttributes = createEndpointAttributes(thirdEndpointId);
auto deleteCapabilityConfigJson = utils::getEndpointConfigJson(thirdEndpointAttributes, {capabilityConfig});
addEndpoint(thirdEndpointAttributes, capabilityConfig);
/// Expect calls to MessageSender.
EXPECT_CALL(*m_mockMessageSender, sendMessage(_))
.Times(AtLeast(1))
.WillRepeatedly(Invoke([this](std::shared_ptr<MessageRequest> request) {
std::string eventCorrelationTokenString;
getEventCorrelationTokenString(request, eventCorrelationTokenString);
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
m_capabilitiesDelegate->onAlexaEventProcessedReceived(eventCorrelationTokenString);
}));
/// Expect calls to storage.
EXPECT_CALL(*m_mockCapabilitiesStorage, store(_)).WillOnce(Return(true));
EXPECT_CALL(
*m_mockCapabilitiesStorage,
erase((std::unordered_map<std::string, std::string>{{thirdEndpointId, deleteCapabilityConfigJson}})))
.WillOnce(Return(true));
/// Expect calls to CapabilitiesObserver.
EXPECT_CALL(*m_mockCapabilitiesObserver, onCapabilitiesStateChange(_, _, _, _))
.WillOnce(Invoke([&](CapabilitiesObserverInterface::State state,
CapabilitiesObserverInterface::Error error,
std::vector<std::string> addedEndpoints,
std::vector<std::string> deletedEndpoints) {
EXPECT_EQ(state, CapabilitiesObserverInterface::State::SUCCESS);
EXPECT_EQ(error, CapabilitiesObserverInterface::Error::SUCCESS);
std::sort(addedEndpoints.begin(), addedEndpoints.end());
EXPECT_EQ(addedEndpoints, (std::vector<std::string>{firstEndpointId, secondEndpointId}));
EXPECT_EQ(deletedEndpoints, (std::vector<std::string>{thirdEndpointId}));
e.wakeUp();
}));
/// Test.
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::DISCONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT);
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(firstEndpointAttributes, {capabilityConfig}));
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(secondEndpointAttributes, {capabilityConfig}));
ASSERT_TRUE(m_capabilitiesDelegate->deleteEndpoint(thirdEndpointAttributes, {capabilityConfig}));
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::CONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SUCCESS);
ASSERT_TRUE(e.wait(MY_WAIT_TIMEOUT));
}
/**
* Test if trying to delete an endpoint that is pending in addOrUpdate (and vice versa) results in failure.
*/
TEST_F(CapabilitiesDelegateTest, test_duplicateEndpointInPendingAddOrUpdateAndDeleteShouldFail) {
/// Set-up.
auto deleteEndpointAttributes = createEndpointAttributes("delete_1");
auto addEndpointAttributes = createEndpointAttributes("add_1");
auto capabilityConfig = createCapabilityConfiguration();
/// Add an endpoint now so we can test pending delete.
validateAuthDelegate();
addEndpoint(deleteEndpointAttributes, capabilityConfig);
/// Disconnect to force all endpoints into pending.
m_capabilitiesDelegate->onConnectionStatusChanged(
ConnectionStatusObserverInterface::Status::DISCONNECTED,
ConnectionStatusObserverInterface::ChangedReason::SERVER_SIDE_DISCONNECT);
ASSERT_TRUE(m_capabilitiesDelegate->addOrUpdateEndpoint(addEndpointAttributes, {capabilityConfig}));
ASSERT_FALSE(m_capabilitiesDelegate->deleteEndpoint(addEndpointAttributes, {capabilityConfig}));
ASSERT_TRUE(m_capabilitiesDelegate->deleteEndpoint(deleteEndpointAttributes, {capabilityConfig}));
ASSERT_FALSE(m_capabilitiesDelegate->addOrUpdateEndpoint(deleteEndpointAttributes, {capabilityConfig}));
}
} // namespace test
} // namespace capabilitiesDelegate
} // namespace alexaClientSDK