Version 1.17.0 alexa-client-sdk
Changes in this update:
**Enhancements**
- Added support for [captions for TTS](https://developer.amazon.com/docs/avs-device-sdk/features.html#captions). This enhancement allows you to print onscreen captions for Alexa voice responses.
- Added support for [SpeechSynthesizer Interface 1.3](https://developer.amazon.com/docs/alexa-voice-service/speechsynthesizer.html). This interface supports the new `captions` parameter.
- Added support for [AudioPlayer Interface 1.3](https://developer.amazon.com/docs/alexa-voice-service/audioplayer.html). This interface supports the new `captions` parameter.
- Added support for [Interaction Model 1.2](https://developer.amazon.com/docs/alexa-voice-service/interactionmodel-interface.html).
- Added support for [System 2.0](https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/system.html).
- Added support for Alarm Volume Ramp. This feature lets you to fade in alarms for a more pleasant experience. You enable alarm volume ramp in the sample app through the settings menu.
- Added support for using certified senders for URI path extensions. This change allows you to specify the URI path extension when sending messages with [`CertifiedSender::sendJSONMessage`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1certified_sender_1_1_certified_sender.html#a4c0706d79717b226ba77d1a9c3280fe6).
- Added new [`Metrics`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1avs_common_1_1utils_1_1_metrics.html) interfaces and helper classes. These additions help you create and consume [`Metrics`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1avs_common_1_1utils_1_1_metrics.html) events.
- **Interfaces** - `MetricRecorderInterface`, `MetricSinkInterface`.
- **Helper Classes** - `DataPointStringBuilder`, `DataPointCounterBuilder`, `DataPointDurationBuilder`, `MetricEventBuilder`.
- Added support for the following AVS [endpoint](../avs-device-sdk/endpoints.html) controller capabilities:
- [Alexa.ModeController](https://developer.amazon.com/docs/alexa-voice-service/alexa-modecontroller.html)
- [Alexa.RangeController](https://developer.amazon.com/docs/alexa-voice-service/alexa-rangecontroller.html)
- [Alexa.PowerController](https://developer.amazon.com/docs/alexa-voice-service/alexa-powercontroller.html)
- [Alexa.ToggleController](https://developer.amazon.com/docs/alexa-voice-service/alexa-togglecontroller.html)
- Added `PowerResourceManagerInterface`. This interface allows the SDK to control power resource levels for components such as the `AudioInputProcessor` and `SpeechSynthesizer`.
- Added `AlexaInterfaceCapabilityAgent`. This Capability Agent handles common directives and endpoint controller capabilities support by [`Alexa.AlexaInterface`](../alexa-voice-service/alexa.html).
- Added `AlexaInterfaceMessageSenderInterface`. This interface is required to send common events defined by the `Alexa.AlexaInterface` interface.
- Added `BufferingComplete` to [`MediaPlayerObserverInterface`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1avs_common_1_1utils_1_1media_player_1_1_media_player_observer_interface.html). This method helps improve performance in poor networking conditions by making sure `MediaPlayer` pre-buffers correctly.
- Added `SendDTMF` to `CallManagerInterface`. This method allows you to send DTMF tones during calls.
**New build options**
- CAPTIONS
- **ADDED** [`CAPTIONS`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#captions)
- **ADDED** [`LIBWEBVTT_LIB_PATH`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#captions)
- **ADDED** [`LIBWEBVTT_INCLUDE_DIR`](https://developer.amazon.com/docs//avs-device-sdk/cmake-parameters.html#captions)
- METRICS
- **ADDED** [`METRICS`](https://developer.amazon.com/docs//avs-device-sdk/cmake-parameters.html#metrics)
- ENDPONTS
- **ADDED** [`ENABLE_ALL_ENDPOINT_CONTROLLERS`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
- **ADDED** [`ENABLE_POWER_CONTROLLER`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
- **ADDED** [`ENABLE_TOGGLE_CONTROLLER`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
- **ADDED** [`ENABLE_RANGE_CONTROLLER`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
- **ADDED** [`ENABLE_MODE_CONTROLLER`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
**New dependencies**
- To use captions, you must install a [new dependency](https://developer.amazon.com/docs/avs-device-sdk/dependencies) – the [libwebvtt parsing library](https://github.com/alexa/webvtt). Webvtt is a C/C++ library for interpreting and authoring conformant WebVTT content. WebVTT is a caption and subtitle format designed for use with HTML5 audio and video elements.
**Bug fixes**
- Fixed [`MimeResponseSink::onReceiveNonMimeData`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1acl_1_1_mime_response_sink.html) [data issue](https://github.com/alexa/avs-device-sdk/issues/1519) that returned invalid data.
- Fixed [data type issue](https://github.com/alexa/avs-device-sdk/issues/1519) that incorrectly used `finalResponseCode` instead of [`FinalResponseCodeId`](https://github.com/alexa/avs-device-sdk/blob/master/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp#L370).
- Fixed [`UrlContentToAttachmentConverter`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1playlist_parser_1_1_url_content_to_attachment_converter.html) issue that used the incorrect range parameter.
- Fixed `FinallyGuard` [linking issue](https://github.com/alexa/avs-device-sdk/issues/1517) that caused problems compiling the SDK on iOS.
- Fixed a [Bluetooth Capability Agent](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1capability_agents_1_1bluetooth_1_1_bluetooth.html) bug that prevented devices from initializing.
**Known Issues**
* The WebVTT dependency required for `captions` isn't supported for Windows/Android.
* Music playback history isn't displayed in the Alexa app for certain account and device types.
* When using Gnu Compiler Collection 8+ (GCC 8+), `-Wclass-memaccess` triggers warnings. You can ignore these, they don't cause the build to fail.
* Android error `libDefaultClient.so not found` might occur. Resolve this by upgrading to ADB version 1.0.40.
* If a device loses a network connection, the lost connection status isn't returned via local TTS.
* ACL encounters issues if it receives audio attachments but doesn't consume them.
* `SpeechSynthesizerState` uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate states.
* Media steamed through Bluetooth might abruptly stop. To restart playback, resume the media in the source application or toggle next/previous.
* If a connected Bluetooth device is inactive, the Alexa app might indicates that audio is playing.
* The Bluetooth agent assumes that the Bluetooth adapter is always connected to a power source. Disconnecting from a power source during operation isn't yet supported.
* When using some products, interrupted Bluetooth playback might not resume if other content is locally streamed.
* `make integration` isn't available for Android. To run Android integration tests, manually upload the test binary and input file and run ADB.
* Alexa might truncate the beginning of speech when responding to text-to-speech (TTS) user events. This only impacts Raspberry Pi devices running Android Things with HDMI output audio.
* A reminder TTS message doesn't play if the sample app restarts and loses a network connection. Instead, the default alarm tone plays twice.
* `ServerDisconnectIntegratonTest` tests are disabled until they are updated to reflect new service behavior.
* Bluetooth initialization must complete before connecting devices, otherwise devices are ignored.
* The `DirectiveSequencerTest.test_handleBlockingThenImmediatelyThenNonBockingOnSameDialogId` test fails intermittently.
* On some devices, Alexa gets stuck in a permanent listening state. Pressing `t` and `h` in the Sample App doesn't exit the listening state.
* Exiting the settings menu doesn't provide a message to indicate that you're back in the main menu.
2019-12-10 21:02:09 +00:00
|
|
|
/*
|
2020-04-13 22:56:35 +00:00
|
|
|
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
|
Version 1.17.0 alexa-client-sdk
Changes in this update:
**Enhancements**
- Added support for [captions for TTS](https://developer.amazon.com/docs/avs-device-sdk/features.html#captions). This enhancement allows you to print onscreen captions for Alexa voice responses.
- Added support for [SpeechSynthesizer Interface 1.3](https://developer.amazon.com/docs/alexa-voice-service/speechsynthesizer.html). This interface supports the new `captions` parameter.
- Added support for [AudioPlayer Interface 1.3](https://developer.amazon.com/docs/alexa-voice-service/audioplayer.html). This interface supports the new `captions` parameter.
- Added support for [Interaction Model 1.2](https://developer.amazon.com/docs/alexa-voice-service/interactionmodel-interface.html).
- Added support for [System 2.0](https://developer.amazon.com/en-US/docs/alexa/alexa-voice-service/system.html).
- Added support for Alarm Volume Ramp. This feature lets you to fade in alarms for a more pleasant experience. You enable alarm volume ramp in the sample app through the settings menu.
- Added support for using certified senders for URI path extensions. This change allows you to specify the URI path extension when sending messages with [`CertifiedSender::sendJSONMessage`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1certified_sender_1_1_certified_sender.html#a4c0706d79717b226ba77d1a9c3280fe6).
- Added new [`Metrics`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1avs_common_1_1utils_1_1_metrics.html) interfaces and helper classes. These additions help you create and consume [`Metrics`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1avs_common_1_1utils_1_1_metrics.html) events.
- **Interfaces** - `MetricRecorderInterface`, `MetricSinkInterface`.
- **Helper Classes** - `DataPointStringBuilder`, `DataPointCounterBuilder`, `DataPointDurationBuilder`, `MetricEventBuilder`.
- Added support for the following AVS [endpoint](../avs-device-sdk/endpoints.html) controller capabilities:
- [Alexa.ModeController](https://developer.amazon.com/docs/alexa-voice-service/alexa-modecontroller.html)
- [Alexa.RangeController](https://developer.amazon.com/docs/alexa-voice-service/alexa-rangecontroller.html)
- [Alexa.PowerController](https://developer.amazon.com/docs/alexa-voice-service/alexa-powercontroller.html)
- [Alexa.ToggleController](https://developer.amazon.com/docs/alexa-voice-service/alexa-togglecontroller.html)
- Added `PowerResourceManagerInterface`. This interface allows the SDK to control power resource levels for components such as the `AudioInputProcessor` and `SpeechSynthesizer`.
- Added `AlexaInterfaceCapabilityAgent`. This Capability Agent handles common directives and endpoint controller capabilities support by [`Alexa.AlexaInterface`](../alexa-voice-service/alexa.html).
- Added `AlexaInterfaceMessageSenderInterface`. This interface is required to send common events defined by the `Alexa.AlexaInterface` interface.
- Added `BufferingComplete` to [`MediaPlayerObserverInterface`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1avs_common_1_1utils_1_1media_player_1_1_media_player_observer_interface.html). This method helps improve performance in poor networking conditions by making sure `MediaPlayer` pre-buffers correctly.
- Added `SendDTMF` to `CallManagerInterface`. This method allows you to send DTMF tones during calls.
**New build options**
- CAPTIONS
- **ADDED** [`CAPTIONS`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#captions)
- **ADDED** [`LIBWEBVTT_LIB_PATH`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#captions)
- **ADDED** [`LIBWEBVTT_INCLUDE_DIR`](https://developer.amazon.com/docs//avs-device-sdk/cmake-parameters.html#captions)
- METRICS
- **ADDED** [`METRICS`](https://developer.amazon.com/docs//avs-device-sdk/cmake-parameters.html#metrics)
- ENDPONTS
- **ADDED** [`ENABLE_ALL_ENDPOINT_CONTROLLERS`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
- **ADDED** [`ENABLE_POWER_CONTROLLER`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
- **ADDED** [`ENABLE_TOGGLE_CONTROLLER`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
- **ADDED** [`ENABLE_RANGE_CONTROLLER`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
- **ADDED** [`ENABLE_MODE_CONTROLLER`](https://developer.amazon.com/docs/avs-device-sdk/cmake-parameters.html#endpoints)
**New dependencies**
- To use captions, you must install a [new dependency](https://developer.amazon.com/docs/avs-device-sdk/dependencies) – the [libwebvtt parsing library](https://github.com/alexa/webvtt). Webvtt is a C/C++ library for interpreting and authoring conformant WebVTT content. WebVTT is a caption and subtitle format designed for use with HTML5 audio and video elements.
**Bug fixes**
- Fixed [`MimeResponseSink::onReceiveNonMimeData`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1acl_1_1_mime_response_sink.html) [data issue](https://github.com/alexa/avs-device-sdk/issues/1519) that returned invalid data.
- Fixed [data type issue](https://github.com/alexa/avs-device-sdk/issues/1519) that incorrectly used `finalResponseCode` instead of [`FinalResponseCodeId`](https://github.com/alexa/avs-device-sdk/blob/master/AVSCommon/Utils/src/LibcurlUtils/LibCurlHttpContentFetcher.cpp#L370).
- Fixed [`UrlContentToAttachmentConverter`](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1playlist_parser_1_1_url_content_to_attachment_converter.html) issue that used the incorrect range parameter.
- Fixed `FinallyGuard` [linking issue](https://github.com/alexa/avs-device-sdk/issues/1517) that caused problems compiling the SDK on iOS.
- Fixed a [Bluetooth Capability Agent](https://alexa.github.io/avs-device-sdk/classalexa_client_s_d_k_1_1capability_agents_1_1bluetooth_1_1_bluetooth.html) bug that prevented devices from initializing.
**Known Issues**
* The WebVTT dependency required for `captions` isn't supported for Windows/Android.
* Music playback history isn't displayed in the Alexa app for certain account and device types.
* When using Gnu Compiler Collection 8+ (GCC 8+), `-Wclass-memaccess` triggers warnings. You can ignore these, they don't cause the build to fail.
* Android error `libDefaultClient.so not found` might occur. Resolve this by upgrading to ADB version 1.0.40.
* If a device loses a network connection, the lost connection status isn't returned via local TTS.
* ACL encounters issues if it receives audio attachments but doesn't consume them.
* `SpeechSynthesizerState` uses `GAINING_FOCUS` and `LOSING_FOCUS` as a workaround for handling intermediate states.
* Media steamed through Bluetooth might abruptly stop. To restart playback, resume the media in the source application or toggle next/previous.
* If a connected Bluetooth device is inactive, the Alexa app might indicates that audio is playing.
* The Bluetooth agent assumes that the Bluetooth adapter is always connected to a power source. Disconnecting from a power source during operation isn't yet supported.
* When using some products, interrupted Bluetooth playback might not resume if other content is locally streamed.
* `make integration` isn't available for Android. To run Android integration tests, manually upload the test binary and input file and run ADB.
* Alexa might truncate the beginning of speech when responding to text-to-speech (TTS) user events. This only impacts Raspberry Pi devices running Android Things with HDMI output audio.
* A reminder TTS message doesn't play if the sample app restarts and loses a network connection. Instead, the default alarm tone plays twice.
* `ServerDisconnectIntegratonTest` tests are disabled until they are updated to reflect new service behavior.
* Bluetooth initialization must complete before connecting devices, otherwise devices are ignored.
* The `DirectiveSequencerTest.test_handleBlockingThenImmediatelyThenNonBockingOnSameDialogId` test fails intermittently.
* On some devices, Alexa gets stuck in a permanent listening state. Pressing `t` and `h` in the Sample App doesn't exit the listening state.
* Exiting the settings menu doesn't provide a message to indicate that you're back in the main menu.
2019-12-10 21:02:09 +00:00
|
|
|
*
|
|
|
|
* 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 <thread>
|
|
|
|
#include <gmock/gmock.h>
|
|
|
|
|
|
|
|
#include <AVSCommon/SDKInterfaces/MockPostConnectSendMessage.h>
|
|
|
|
#include <AVSCommon/SDKInterfaces/PostConnectOperationInterface.h>
|
|
|
|
#include <AVSCommon/Utils/JSON/JSONUtils.h>
|
|
|
|
#include <AVSCommon/Utils/PromiseFuturePair.h>
|
|
|
|
#include <CapabilitiesDelegate/PostConnectCapabilitiesPublisher.h>
|
|
|
|
|
|
|
|
#include "MockAuthDelegate.h"
|
|
|
|
|
|
|
|
namespace alexaClientSDK {
|
|
|
|
namespace capabilitiesDelegate {
|
|
|
|
namespace test {
|
|
|
|
|
|
|
|
using namespace ::testing;
|
|
|
|
using namespace avsCommon::avs;
|
|
|
|
using namespace avsCommon::sdkInterfaces;
|
|
|
|
using namespace avsCommon::sdkInterfaces::test;
|
|
|
|
using namespace avsCommon::utils;
|
|
|
|
using namespace avsCommon::utils::json::jsonUtils;
|
|
|
|
|
|
|
|
/// The Discovery event Namsepace.
|
|
|
|
static const std::string DISCOVERY_NAMESPACE = "Alexa.Discovery";
|
|
|
|
/// The AddOrUpdateReport Namespace.
|
|
|
|
static const std::string ADD_OR_UPDATE_REPORT_NAME = "AddOrUpdateReport";
|
|
|
|
/// The DeleteReport Namespace.
|
|
|
|
static const std::string DELETE_REPORT_NAME = "DeleteReport";
|
|
|
|
/// The payload version of the discovery event.
|
|
|
|
static const std::string DISCOVERY_PAYLOAD_VERSION = "3";
|
|
|
|
/// The scope key in the discovery event.
|
|
|
|
static const std::string SCOPE_KEY = "scope";
|
|
|
|
/// The scope key in the discovery event.
|
|
|
|
static const std::string TYPE_KEY = "type";
|
|
|
|
/// The scope key in the discovery event.
|
|
|
|
static const std::string BEARER_TOKEN_KEY = "bearerToken";
|
|
|
|
/// The scope key in the discovery event.
|
|
|
|
static const std::string TOKEN_KEY = "token";
|
|
|
|
/// The scope key in the discovery event.
|
|
|
|
static const std::string ENDPOINTS_KEY = "endpoints";
|
|
|
|
/// The scope key in the discovery event.
|
|
|
|
static const std::string ENDPOINTID_KEY = "endpointId";
|
|
|
|
/// 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 namespace key in the discovery event.
|
|
|
|
static const std::string NAMESPACE_KEY = "namespace";
|
|
|
|
/// The name key in the discovery event.
|
|
|
|
static const std::string NAME_KEY = "name";
|
|
|
|
/// The payload version key in the discovery event.
|
|
|
|
static const std::string PAYLOAD_VERSION_KEY = "payloadVersion";
|
|
|
|
/// The eventCorrelationToken key in the discovery event.
|
|
|
|
static const std::string EVENT_CORRELATION_TOKEN_KEY = "eventCorrelationToken";
|
|
|
|
/// The payload key in the discovery event.
|
|
|
|
static const std::string PAYLOAD_KEY = "payload";
|
|
|
|
/// Test string for auth token.
|
|
|
|
static const std::string TEST_AUTH_TOKEN = "TEST_AUTH_TOKEN";
|
|
|
|
/// Test string for endpointID 1
|
|
|
|
static const std::string TEST_ENDPOINT_ID_1 = "1";
|
|
|
|
/// Test string for endpointId 2
|
|
|
|
static const std::string TEST_ENDPOINT_ID_2 = "2";
|
|
|
|
/// The test endpointId to endpointConfig map AddOrUpdateReport event endpoints.
|
|
|
|
static const std::unordered_map<std::string, std::string> TEST_ADD_OR_UPDATE_ENDPOINTS = {
|
|
|
|
{TEST_ENDPOINT_ID_1, R"({"endpointId":")" + TEST_ENDPOINT_ID_1 + R"("})"}};
|
|
|
|
/// The test endpointId to endpointConfig map DeleteReport event endpoints.
|
|
|
|
static const std::unordered_map<std::string, std::string> TEST_DELETE_ENDPOINTS = {
|
|
|
|
{TEST_ENDPOINT_ID_2, R"({"endpointId":")" + TEST_ENDPOINT_ID_2 + R"("})"}};
|
|
|
|
/// The expected AddOrUpdateReport endpoint Ids.
|
|
|
|
static const std::vector<std::string> EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS = {TEST_ENDPOINT_ID_1};
|
|
|
|
/// The expected DeleteReport endpoint Ids.
|
|
|
|
static const std::vector<std::string> EXPECTED_DELETE_ENDPOINT_IDS = {TEST_ENDPOINT_ID_2};
|
|
|
|
/// The test string for eventCorrelationToken sent in headers.
|
|
|
|
static const std::string TEST_EVENT_CORRELATION_TOKEN = "TEST_EVENT_CORRELATION_TOKEN";
|
|
|
|
/// The test retry count.
|
|
|
|
static const int TEST_RETRY_COUNT = 2;
|
|
|
|
/// Maximum number of endpoints that can be sent in a Discovery event.
|
|
|
|
static const int MAX_ENDPOINTS_PER_EVENT = 300;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* 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;
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the auth token sent in the Discovery event and stores the data in the @c EventData structure.
|
|
|
|
*
|
|
|
|
* @param payloadString The payload of the discovery event.
|
|
|
|
* @param eventData The pointer to the @c EventData structure.
|
|
|
|
* @return True if parsing is successful, else false.
|
|
|
|
*/
|
|
|
|
bool parseAuthToken(const std::string& payloadString, EventData* eventData) {
|
|
|
|
std::string scopeString;
|
|
|
|
if (!retrieveValue(payloadString, "scope", &scopeString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string bearerToken;
|
|
|
|
if (!retrieveValue(scopeString, "type", &bearerToken)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (bearerToken != "BearerToken") {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!retrieveValue(scopeString, "token", &eventData->authToken)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the event JSON and stores the data in the @c EventData structure.
|
|
|
|
*
|
|
|
|
* @param payloadString The payload string.
|
|
|
|
* @param eventData The pointer to the @c EventData structure.
|
|
|
|
* @return True if parsing is successful, else false.
|
|
|
|
*/
|
|
|
|
bool parseEndpointsIds(const std::string& payloadString, EventData* eventData) {
|
|
|
|
rapidjson::Document document;
|
|
|
|
document.Parse(payloadString);
|
|
|
|
|
|
|
|
if (document.HasParseError()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!jsonArrayExists(document, ENDPOINTS_KEY)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
auto endpointsJsonArray = document.FindMember(ENDPOINTS_KEY)->value.GetArray();
|
|
|
|
for (rapidjson::SizeType i = 0; i < endpointsJsonArray.Size(); ++i) {
|
|
|
|
std::string endpointId;
|
|
|
|
if (!retrieveValue(endpointsJsonArray[i], ENDPOINTID_KEY, &endpointId)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
eventData->endpointIdsInPayload.push_back(endpointId);
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Parses the event JSON and stores the data in the @c EventData structure.
|
|
|
|
*
|
|
|
|
* @param eventJson The event data.
|
|
|
|
* @param eventData The pointer to the @c EventData structure.
|
|
|
|
* @return True if the parsing is successful, else false.
|
|
|
|
*/
|
|
|
|
bool parseEventJson(const std::string& eventJson, EventData* eventData) {
|
|
|
|
std::string eventString;
|
|
|
|
if (!retrieveValue(eventJson, EVENT_KEY, &eventString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string headerString;
|
|
|
|
if (!retrieveValue(eventString, HEADER_KEY, &headerString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!retrieveValue(headerString, NAMESPACE_KEY, &eventData->namespaceString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!retrieveValue(headerString, NAME_KEY, &eventData->nameString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!retrieveValue(headerString, PAYLOAD_VERSION_KEY, &eventData->payloadVersionString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!retrieveValue(headerString, EVENT_CORRELATION_TOKEN_KEY, &eventData->eventCorrelationTokenString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::string payloadString;
|
|
|
|
if (!retrieveValue(eventString, PAYLOAD_KEY, &payloadString)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!parseAuthToken(payloadString, eventData)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!parseEndpointsIds(payloadString, eventData)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Validates if the event data structure contains expected discovery event fields.
|
|
|
|
* @param eventData The @c EventData containing the parsed discovery fields
|
|
|
|
* @param eventName The name of the discovery event to validate against.
|
|
|
|
* @param endpointIds The optional endpointIds list to be validated against the payload.
|
|
|
|
*/
|
|
|
|
void validateDiscoveryEvent(
|
|
|
|
const EventData& eventData,
|
|
|
|
const std::string& eventName,
|
|
|
|
const std::vector<std::string>& endpointIds = std::vector<std::string>()) {
|
|
|
|
ASSERT_EQ(eventData.namespaceString, DISCOVERY_NAMESPACE);
|
|
|
|
ASSERT_EQ(eventData.nameString, eventName);
|
|
|
|
ASSERT_EQ(eventData.payloadVersionString, DISCOVERY_PAYLOAD_VERSION);
|
|
|
|
ASSERT_EQ(eventData.authToken, TEST_AUTH_TOKEN);
|
|
|
|
if (!endpointIds.empty()) {
|
|
|
|
ASSERT_EQ(eventData.endpointIdsInPayload, endpointIds);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mock class for the @c DiscoveryStatusObserverInterface.
|
|
|
|
*/
|
|
|
|
class MockDiscoveryStatusObserver : public DiscoveryStatusObserverInterface {
|
|
|
|
public:
|
|
|
|
MOCK_METHOD2(
|
|
|
|
onDiscoveryCompleted,
|
|
|
|
void(const std::unordered_map<std::string, std::string>&, const std::unordered_map<std::string, std::string>&));
|
|
|
|
MOCK_METHOD1(onDiscoveryFailure, void(MessageRequestObserverInterface::Status));
|
|
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test harness for @c PostConnectCapabilitiesPublisher class.
|
|
|
|
*/
|
|
|
|
class PostConnectCapabilitiesPublisherTest : public Test {
|
|
|
|
public:
|
|
|
|
/// Setup the test harness for running the test.
|
|
|
|
void SetUp() override;
|
|
|
|
|
|
|
|
/// Clean up the test harness after running the test.
|
|
|
|
void TearDown() override;
|
|
|
|
|
|
|
|
protected:
|
|
|
|
/**
|
|
|
|
* Validates if the expected methods get called on the auth delegate.
|
|
|
|
*
|
|
|
|
* @param expectAuthFailure True if a call to onAuthFailure() is expected, else false.
|
|
|
|
*/
|
|
|
|
void validateCallsToAuthDelegate(bool expectAuthFailure = false);
|
|
|
|
|
|
|
|
/// The mock @c PostConnectSendMessage used in the tests.
|
|
|
|
std::shared_ptr<MockPostConnectSendMessage> m_mockPostConnectSendMessage;
|
|
|
|
|
|
|
|
/// The instance of the @c AuthDelegate used in the tests.
|
|
|
|
std::shared_ptr<MockAuthDelegate> m_mockAuthDelegate;
|
|
|
|
|
|
|
|
/// The instance of the @c DiscoveryObserverStatusInterface used in the tests.
|
|
|
|
std::shared_ptr<MockDiscoveryStatusObserver> m_mockDiscoveryStatusObserver;
|
|
|
|
|
|
|
|
/// The instance of the @c PostConnectCapabilitiesPublisher to be tested.
|
|
|
|
std::shared_ptr<PostConnectCapabilitiesPublisher> m_postConnectCapabilitiesPublisher;
|
|
|
|
};
|
|
|
|
|
|
|
|
void PostConnectCapabilitiesPublisherTest::SetUp() {
|
|
|
|
m_mockPostConnectSendMessage = std::make_shared<StrictMock<MockPostConnectSendMessage>>();
|
|
|
|
m_mockAuthDelegate = std::make_shared<StrictMock<MockAuthDelegate>>();
|
|
|
|
m_mockDiscoveryStatusObserver = std::make_shared<StrictMock<MockDiscoveryStatusObserver>>();
|
|
|
|
|
|
|
|
m_postConnectCapabilitiesPublisher = PostConnectCapabilitiesPublisher::create(
|
|
|
|
TEST_ADD_OR_UPDATE_ENDPOINTS, TEST_DELETE_ENDPOINTS, m_mockAuthDelegate);
|
|
|
|
m_postConnectCapabilitiesPublisher->addDiscoveryStatusObserver(m_mockDiscoveryStatusObserver);
|
|
|
|
}
|
|
|
|
|
|
|
|
void PostConnectCapabilitiesPublisherTest::TearDown() {
|
|
|
|
}
|
|
|
|
|
|
|
|
void PostConnectCapabilitiesPublisherTest::validateCallsToAuthDelegate(bool expectAuthFailure) {
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, addAuthObserver(_)).WillOnce(InvokeWithoutArgs([this]() {
|
|
|
|
m_postConnectCapabilitiesPublisher->onAuthStateChange(
|
|
|
|
AuthObserverInterface::State::REFRESHED, AuthObserverInterface::Error::SUCCESS);
|
|
|
|
}));
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, getAuthToken()).WillRepeatedly(Return(TEST_AUTH_TOKEN));
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, removeAuthObserver(_)).WillOnce(Return());
|
|
|
|
if (expectAuthFailure) {
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, onAuthFailure(TEST_AUTH_TOKEN));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test create fails with invalid parameters
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_createWithInvalidParams) {
|
|
|
|
auto instance =
|
|
|
|
PostConnectCapabilitiesPublisher::create(TEST_ADD_OR_UPDATE_ENDPOINTS, TEST_DELETE_ENDPOINTS, nullptr);
|
|
|
|
ASSERT_EQ(instance, nullptr);
|
|
|
|
|
|
|
|
std::unordered_map<std::string, std::string> addOrUpdateEndpoints, deleteEndpoints;
|
|
|
|
instance = PostConnectCapabilitiesPublisher::create(addOrUpdateEndpoints, deleteEndpoints, m_mockAuthDelegate);
|
|
|
|
ASSERT_EQ(instance, nullptr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test getPriority method returns expected value.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_getPostConnectOperationPriority) {
|
|
|
|
ASSERT_EQ(
|
|
|
|
static_cast<unsigned int>(PostConnectOperationInterface::ENDPOINT_DISCOVERY_PRIORITY),
|
|
|
|
m_postConnectCapabilitiesPublisher->getOperationPriority());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that performOperation() fails if the PostConnectSendMessage is invalid.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_performOperationWithInvalidPostConnectSender) {
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(nullptr));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test Happy Path, if AddOrUpdateReport event and DeleteReport event get sent out.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_performOperationSendsDiscoveryEvents) {
|
|
|
|
validateCallsToAuthDelegate();
|
|
|
|
|
|
|
|
auto sendAddOrUpdateReport = [this](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
|
|
|
|
m_postConnectCapabilitiesPublisher->onAlexaEventProcessedReceived(eventData.eventCorrelationTokenString);
|
|
|
|
};
|
|
|
|
|
|
|
|
auto sendDeleteReport = [](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, DELETE_REPORT_NAME, EXPECTED_DELETE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.WillOnce(Invoke(sendAddOrUpdateReport))
|
|
|
|
.WillOnce(Invoke(sendDeleteReport));
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver, onDiscoveryCompleted(TEST_ADD_OR_UPDATE_ENDPOINTS, TEST_DELETE_ENDPOINTS))
|
|
|
|
.WillOnce(Return());
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test second call to performOperation fails when executed twice.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_performOperationFailsWhenCalledTwice) {
|
|
|
|
validateCallsToAuthDelegate();
|
|
|
|
auto handleAddOrUpdateReport = [this](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
|
|
|
|
m_postConnectCapabilitiesPublisher->onAlexaEventProcessedReceived(eventData.eventCorrelationTokenString);
|
|
|
|
};
|
|
|
|
|
|
|
|
auto handleDeleteReport = [](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, DELETE_REPORT_NAME, EXPECTED_DELETE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.WillOnce(Invoke(handleAddOrUpdateReport))
|
|
|
|
.WillOnce(Invoke(handleDeleteReport));
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver, onDiscoveryCompleted(TEST_ADD_OR_UPDATE_ENDPOINTS, TEST_DELETE_ENDPOINTS))
|
|
|
|
.WillOnce(Return());
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This method returns the number of Discovery Events the given endpoints will be split into.
|
|
|
|
*
|
|
|
|
* @param numEndpoints The totol number of endpoints
|
|
|
|
* @return The number of discovery events these endpoints will be split into.
|
|
|
|
*/
|
|
|
|
static int getExpecetedNumberOfDiscoveryEvents(int numEndpoints) {
|
|
|
|
return numEndpoints / MAX_ENDPOINTS_PER_EVENT + ((numEndpoints % MAX_ENDPOINTS_PER_EVENT) != 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test if multiple discovery events are sent if the number of endpoints is greater than MAX endpoints.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_performOperationSplitsEventsWithMaxEndpoints) {
|
|
|
|
std::unordered_map<std::string, std::string> addOrUpdateReportEndpoints, deleteReportEndpoints;
|
|
|
|
std::string endpointIdPrefix = "ENDPOINT_ID_";
|
|
|
|
int testNumAddOrUpdateReportEndpoints = 1400;
|
|
|
|
int testNumDeleteReportEndpoints = 299;
|
|
|
|
|
|
|
|
for (int i = 1; i <= testNumAddOrUpdateReportEndpoints; ++i) {
|
|
|
|
std::string endpointId = endpointIdPrefix + std::to_string(i);
|
|
|
|
std::string endpointIdConfig = "{\"endpointId\":\"" + std::to_string(i) + "\"}";
|
|
|
|
addOrUpdateReportEndpoints.insert({endpointId, endpointIdConfig});
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int i = 1; i <= testNumDeleteReportEndpoints; ++i) {
|
|
|
|
std::string endpointId = endpointIdPrefix + std::to_string(i);
|
|
|
|
std::string endpointIdConfig = "{\"endpointId\":\"" + std::to_string(i) + "\"}";
|
|
|
|
deleteReportEndpoints.insert({endpointId, endpointIdConfig});
|
|
|
|
}
|
|
|
|
|
|
|
|
auto postConnectCapabilitiesPublisher =
|
|
|
|
PostConnectCapabilitiesPublisher::create(addOrUpdateReportEndpoints, deleteReportEndpoints, m_mockAuthDelegate);
|
|
|
|
|
|
|
|
postConnectCapabilitiesPublisher->addDiscoveryStatusObserver(m_mockDiscoveryStatusObserver);
|
|
|
|
|
|
|
|
auto handleAddOrUpdateReport = [postConnectCapabilitiesPublisher](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME);
|
|
|
|
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
postConnectCapabilitiesPublisher->onAlexaEventProcessedReceived(eventData.eventCorrelationTokenString);
|
|
|
|
};
|
|
|
|
|
|
|
|
auto handleDeleteReport = [](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, DELETE_REPORT_NAME);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
};
|
|
|
|
|
|
|
|
int expectedNumOfAddOrUpdateReportEvents = getExpecetedNumberOfDiscoveryEvents(testNumAddOrUpdateReportEndpoints);
|
|
|
|
int expectedNumOfDeleteReportEvents = getExpecetedNumberOfDiscoveryEvents(testNumDeleteReportEndpoints);
|
|
|
|
{
|
|
|
|
InSequence s;
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.Times(expectedNumOfAddOrUpdateReportEvents)
|
|
|
|
.WillRepeatedly(Invoke(handleAddOrUpdateReport));
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.Times(expectedNumOfDeleteReportEvents)
|
|
|
|
.WillRepeatedly(Invoke(handleDeleteReport));
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockDiscoveryStatusObserver, onDiscoveryCompleted(addOrUpdateReportEndpoints, deleteReportEndpoints))
|
|
|
|
.WillOnce(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, addAuthObserver(_))
|
|
|
|
.WillOnce(InvokeWithoutArgs([&postConnectCapabilitiesPublisher]() {
|
|
|
|
postConnectCapabilitiesPublisher->onAuthStateChange(
|
|
|
|
AuthObserverInterface::State::REFRESHED, AuthObserverInterface::Error::SUCCESS);
|
|
|
|
}));
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, getAuthToken()).WillRepeatedly(Return(TEST_AUTH_TOKEN));
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, removeAuthObserver(_)).WillRepeatedly(Return());
|
|
|
|
|
|
|
|
ASSERT_TRUE(postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
|
|
|
|
/// cleanup
|
|
|
|
postConnectCapabilitiesPublisher->removeDiscoveryStatusObserver(m_mockDiscoveryStatusObserver);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test when AddOrUpdateReport response is 202 and DeleteReport response is 4xx.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_deleteReportEventReceives4xxResponse) {
|
|
|
|
validateCallsToAuthDelegate(true);
|
|
|
|
auto handleAddOrUpdateReport = [this](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
m_postConnectCapabilitiesPublisher->onAlexaEventProcessedReceived(eventData.eventCorrelationTokenString);
|
|
|
|
};
|
|
|
|
|
|
|
|
auto handleDeleteReport = [](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, DELETE_REPORT_NAME, EXPECTED_DELETE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::INVALID_AUTH);
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.WillOnce(Invoke(handleAddOrUpdateReport))
|
|
|
|
.WillOnce(Invoke(handleDeleteReport));
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver, onDiscoveryFailure(MessageRequestObserverInterface::Status::INVALID_AUTH))
|
|
|
|
.WillOnce(Return());
|
|
|
|
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test when AddOrUpdateReport response is 4xx, DeleteReport event is not sent.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_addOrUpdateReportReceives4xxResponse) {
|
|
|
|
validateCallsToAuthDelegate(true);
|
|
|
|
auto handleAddOrUpdateReport = [this](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::INVALID_AUTH);
|
|
|
|
m_postConnectCapabilitiesPublisher->onAlexaEventProcessedReceived(eventData.eventCorrelationTokenString);
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_)).WillOnce(Invoke(handleAddOrUpdateReport));
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver, onDiscoveryFailure(MessageRequestObserverInterface::Status::INVALID_AUTH))
|
|
|
|
.WillOnce(Return());
|
|
|
|
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test when AddOrUpdateReport response is 5xx, there are retries and deleteReport is not sent.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_retriesWhenAddOrUpdateReportReceives5xxResponse) {
|
|
|
|
validateCallsToAuthDelegate();
|
|
|
|
int retryCount = 0;
|
|
|
|
auto handleAddOrUpdateReport = [this, &retryCount](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2);
|
|
|
|
|
|
|
|
/// wait until 3 retries and exit.
|
|
|
|
static int count = 0;
|
|
|
|
count++;
|
|
|
|
if (TEST_RETRY_COUNT == count) {
|
|
|
|
retryCount = TEST_RETRY_COUNT;
|
|
|
|
/// spin a thread to initiate abort.
|
|
|
|
std::thread localThread([this] { m_postConnectCapabilitiesPublisher->abortOperation(); });
|
|
|
|
|
|
|
|
if (localThread.joinable()) {
|
|
|
|
localThread.join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver,
|
|
|
|
onDiscoveryFailure(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2))
|
|
|
|
.WillRepeatedly(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.WillRepeatedly(Invoke(handleAddOrUpdateReport));
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
ASSERT_EQ(retryCount, TEST_RETRY_COUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test when AddOrUpdateReport response is 5xx, there are retries. If its successful after two retries, deleteReport
|
|
|
|
* event also gets sent out.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_addOrUpdateRetriesThenSuccessfulResponse) {
|
|
|
|
validateCallsToAuthDelegate();
|
|
|
|
int retryCount = 0;
|
|
|
|
auto handleAddOrUpdateReport = [this, &retryCount](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
if (retryCount < 2) {
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2);
|
|
|
|
retryCount++;
|
|
|
|
} else {
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
m_postConnectCapabilitiesPublisher->onAlexaEventProcessedReceived(eventData.eventCorrelationTokenString);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
auto handleDeleteReport = [](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, DELETE_REPORT_NAME, EXPECTED_DELETE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver,
|
|
|
|
onDiscoveryFailure(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2))
|
|
|
|
.WillRepeatedly(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver, onDiscoveryCompleted(TEST_ADD_OR_UPDATE_ENDPOINTS, TEST_DELETE_ENDPOINTS))
|
|
|
|
.WillOnce(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.WillOnce(Invoke(handleAddOrUpdateReport))
|
|
|
|
.WillOnce(Invoke(handleAddOrUpdateReport))
|
|
|
|
.WillOnce(Invoke(handleAddOrUpdateReport))
|
|
|
|
.WillOnce(Invoke(handleDeleteReport));
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test when DeleteReport response is 5xx, there are retries. If its successful after two retries, Discovery is
|
|
|
|
* completed.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_deleteReportRetriesThenSuccessfulResponse) {
|
|
|
|
validateCallsToAuthDelegate();
|
|
|
|
|
|
|
|
auto handleAddOrUpdateReport = [this](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
m_postConnectCapabilitiesPublisher->onAlexaEventProcessedReceived(eventData.eventCorrelationTokenString);
|
|
|
|
};
|
|
|
|
|
|
|
|
int retryCount = 0;
|
|
|
|
auto handleDeleteReport = [&retryCount](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, DELETE_REPORT_NAME, EXPECTED_DELETE_ENDPOINT_IDS);
|
|
|
|
if (retryCount < 2) {
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2);
|
|
|
|
retryCount++;
|
|
|
|
} else {
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver,
|
|
|
|
onDiscoveryFailure(MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2))
|
|
|
|
.WillRepeatedly(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(
|
|
|
|
*m_mockDiscoveryStatusObserver, onDiscoveryCompleted(TEST_ADD_OR_UPDATE_ENDPOINTS, TEST_DELETE_ENDPOINTS))
|
|
|
|
.WillOnce(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.WillOnce(Invoke(handleAddOrUpdateReport))
|
|
|
|
.WillOnce(Invoke(handleDeleteReport))
|
|
|
|
.WillOnce(Invoke(handleDeleteReport))
|
|
|
|
.WillOnce(Invoke(handleDeleteReport));
|
|
|
|
|
|
|
|
ASSERT_TRUE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test when EventProcessed directive is not received, the AddOrUpdateReport event is retried and deleteReport event
|
|
|
|
* does not get sent.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_retriesWhenEventProcessedDirectiveIsNotReceived) {
|
|
|
|
validateCallsToAuthDelegate();
|
|
|
|
int retryCount = 0;
|
|
|
|
auto handleAddOrUpdateReport = [&retryCount, this](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
|
|
|
|
/// wait until 3 retries and exit.
|
|
|
|
static int count = 0;
|
|
|
|
count++;
|
|
|
|
if (TEST_RETRY_COUNT == count) {
|
|
|
|
retryCount = TEST_RETRY_COUNT;
|
|
|
|
/// spin a thread to initiate abort.
|
|
|
|
std::thread localThread([this] { m_postConnectCapabilitiesPublisher->abortOperation(); });
|
|
|
|
|
|
|
|
if (localThread.joinable()) {
|
|
|
|
localThread.join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockDiscoveryStatusObserver, onDiscoveryFailure(MessageRequestObserverInterface::Status::TIMEDOUT))
|
|
|
|
.WillRepeatedly(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.WillRepeatedly(Invoke(handleAddOrUpdateReport));
|
|
|
|
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test when the abortOperation() gets called while waiting on the EventProcessed directive.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_abortWhenWaitingOnEventProcessedDirective) {
|
|
|
|
validateCallsToAuthDelegate();
|
|
|
|
auto handleAddOrUpdateReport = [this](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
|
|
|
|
/// spin a thread to initiate abort.
|
|
|
|
std::thread localThread([this] { m_postConnectCapabilitiesPublisher->abortOperation(); });
|
|
|
|
|
|
|
|
if (localThread.joinable()) {
|
|
|
|
localThread.join();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockDiscoveryStatusObserver, onDiscoveryFailure(MessageRequestObserverInterface::Status::CANCELED))
|
|
|
|
.WillRepeatedly(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_)).WillOnce(Invoke(handleAddOrUpdateReport));
|
|
|
|
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test when EventProcessed directive is received with a wrong event correlation token, the AddOrUpdateReport event is
|
|
|
|
* retried and deleteReport event does not get sent.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_retriesWhenInvalidEventProcessedDirectiveIsReceived) {
|
|
|
|
validateCallsToAuthDelegate();
|
|
|
|
int retryCount = 0;
|
|
|
|
auto handleAddOrUpdateReport = [&retryCount, this](std::shared_ptr<MessageRequest> request) {
|
|
|
|
EventData eventData;
|
|
|
|
ASSERT_TRUE(parseEventJson(request->getJsonContent(), &eventData));
|
|
|
|
validateDiscoveryEvent(eventData, ADD_OR_UPDATE_REPORT_NAME, EXPECTED_ADD_OR_UPDATE_ENDPOINT_IDS);
|
|
|
|
request->sendCompleted(MessageRequestObserverInterface::Status::SUCCESS_ACCEPTED);
|
|
|
|
m_postConnectCapabilitiesPublisher->onAlexaEventProcessedReceived(TEST_EVENT_CORRELATION_TOKEN);
|
|
|
|
|
|
|
|
/// wait until 3 retries and exit.
|
|
|
|
static int count = 0;
|
|
|
|
count++;
|
|
|
|
if (TEST_RETRY_COUNT == count) {
|
|
|
|
retryCount = TEST_RETRY_COUNT;
|
|
|
|
/// spin a thread to initiate abort.
|
|
|
|
std::thread localThread([this] { m_postConnectCapabilitiesPublisher->abortOperation(); });
|
|
|
|
|
|
|
|
if (localThread.joinable()) {
|
|
|
|
localThread.join();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockDiscoveryStatusObserver, onDiscoveryFailure(MessageRequestObserverInterface::Status::TIMEDOUT))
|
|
|
|
.WillRepeatedly(Return());
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockPostConnectSendMessage, sendPostConnectMessage(_))
|
|
|
|
.WillRepeatedly(Invoke(handleAddOrUpdateReport));
|
|
|
|
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
ASSERT_EQ(retryCount, TEST_RETRY_COUNT);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test auth token is empty.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_performOperationWhenAuthTokenIsEmpty) {
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, addAuthObserver(_)).WillOnce(InvokeWithoutArgs([this]() {
|
|
|
|
m_postConnectCapabilitiesPublisher->onAuthStateChange(
|
|
|
|
AuthObserverInterface::State::REFRESHED, AuthObserverInterface::Error::SUCCESS);
|
|
|
|
}));
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, getAuthToken()).WillRepeatedly(Return(""));
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, removeAuthObserver(_)).WillOnce(Return());
|
|
|
|
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test abortOperation when requesting auth token.
|
|
|
|
*/
|
|
|
|
TEST_F(PostConnectCapabilitiesPublisherTest, test_abortOperationWhenAuthTokenIsRequested) {
|
|
|
|
/// Since the state is uninitialized performOperation() will be waitin in getAuthToken() method.
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, addAuthObserver(_)).WillOnce(InvokeWithoutArgs([this]() {
|
|
|
|
m_postConnectCapabilitiesPublisher->onAuthStateChange(
|
|
|
|
AuthObserverInterface::State::UNINITIALIZED, AuthObserverInterface::Error::SUCCESS);
|
|
|
|
|
|
|
|
EXPECT_CALL(*m_mockAuthDelegate, removeAuthObserver(_)).WillOnce(Return());
|
|
|
|
|
|
|
|
/// give a short time
|
|
|
|
std::this_thread::sleep_for(std::chrono::milliseconds(200));
|
|
|
|
|
|
|
|
/// spin a thread to initiate abort.
|
|
|
|
std::thread localThread([this] { m_postConnectCapabilitiesPublisher->abortOperation(); });
|
|
|
|
|
|
|
|
if (localThread.joinable()) {
|
|
|
|
localThread.join();
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
ASSERT_FALSE(m_postConnectCapabilitiesPublisher->performOperation(m_mockPostConnectSendMessage));
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace test
|
|
|
|
} // namespace capabilitiesDelegate
|
|
|
|
} // namespace alexaClientSDK
|