text: /*
   * 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 <future>
  #include <iterator>
  #include <memory>
  #include <string>
  #include <tuple>
  #include <vector>
  
  #include <gmock/gmock.h>
  #include <gtest/gtest.h>
  
  #include <ACL/Transport/HTTP2Transport.h>
  #include <AVSCommon/AVS/Attachment/AttachmentManager.h>
  #include <AVSCommon/AVS/Attachment/AttachmentUtils.h>
  #include <AVSCommon/Utils/PromiseFuturePair.h>
  #include <AVSCommon/Utils/HTTP/HttpResponseCode.h>
  #include <AVSCommon/Utils/HTTP2/HTTP2RequestConfig.h>
  #include <AVSCommon/Utils/Metrics/MockMetricRecorder.h>
  
  #include "MockAuthDelegate.h"
  #include "MockEventTracer.h"
  #include "MockHTTP2Connection.h"
  #include "MockHTTP2Request.h"
  #include "MockMessageConsumer.h"
  #include "MockPostConnect.h"
  #include "MockPostConnectFactory.h"
  #include "MockTransportObserver.h"
  
  #include "ACL/Transport/SynchronizedMessageRequestQueue.h"
  
  namespace alexaClientSDK {
  namespace acl {
  namespace transport {
  namespace test {
  
  using namespace acl::test;
  using namespace avsCommon::avs;
  using namespace avsCommon::avs::attachment;
  using namespace avsCommon::sdkInterfaces;
  using namespace avsCommon::utils;
  using namespace avsCommon::utils::http;
  using namespace avsCommon::utils::http2;
  using namespace avsCommon::utils::http2::test;
  using namespace avsCommon::utils::metrics::test;
  using namespace ::testing;
  
  /// Test AVS Gateway.
  static const std::string TEST_AVS_GATEWAY_STRING = "http://avs-alexa-na.amazon.com";
  
  /// Expected Downchannel URL sent on requests.
  static const std::string AVS_DOWNCHANNEL_URL_PATH_EXTENSION = "/v20160207/directives";
  
  /// Expected ping URL sent on requests.
  static const std::string AVS_PING_URL_PATH_EXTENSION = "/ping";
  
  /// Expected Full Downchannel URL sent on requests.
  static const std::string FULL_DOWNCHANNEL_URL = TEST_AVS_GATEWAY_STRING + AVS_DOWNCHANNEL_URL_PATH_EXTENSION;
  
  /// Expected Full ping URL sent on requests.
  static const std::string FULL_PING_URL = TEST_AVS_GATEWAY_STRING + AVS_PING_URL_PATH_EXTENSION;
  
  /// A 100 millisecond delay used in tests.
  static const auto ONE_HUNDRED_MILLISECOND_DELAY = std::chrono::milliseconds(100);
  
  /// A 10 millisecond delay used in tests.
  static const auto TEN_MILLISECOND_DELAY = std::chrono::milliseconds(10);
  
  /// A short delay used in tests.
  static const auto SHORT_DELAY = std::chrono::seconds(1);
  
  /// Typical Timeout used in waiting for responses.
  static const auto RESPONSE_TIMEOUT = std::chrono::seconds(5);
  
  /// A longer timeout used in waiting for responses.
  static const auto LONG_RESPONSE_TIMEOUT = std::chrono::seconds(10);
  
  /// HTTP Authorization header.
  static const std::string HTTP_AUTHORIZATION_HEADER_BEARER = "Authorization: Bearer";
  
  /// Authorization Token.
  static const std::string CBL_AUTHORIZATION_TOKEN = "AUTH_TOKEN";
  
  /// A test AttachmentId string.
  static const std::string TEST_ATTACHMENT_ID_STRING_ONE = "testAttachmentId_1";
  
  // Test message string to be sent.
  static const std::string TEST_MESSAGE = "aaabbccc";
  
  // Test attachment string.
  static const std::string TEST_ATTACHMENT_MESSAGE = "MY_A_T_T_ACHMENT";
  
  // Test attachment field.
  static const std::string TEST_ATTACHMENT_FIELD = "ATTACHMENT";
  
  // A constant that means no call as a response
  static const long NO_CALL = -2;
  
  // Non-MIME payload
  static const std::string NON_MIME_PAYLOAD = "A_NON_MIME_PAYLOAD";
  
  // A test directive.
  static const std::string DIRECTIVE1 =
      "{\"namespace:\"SpeechSynthesizer\",name:\"Speak\",messageId:\"351df0ff-8041-4891-a925-136f52d54da1\","
      "dialogRequestId:\"58352bb2-7d07-4ba2-944b-10e6df25d193\"}";
  
  // Another test directive.
  static const std::string DIRECTIVE2 =
      "{\"namespace:\"Alerts\",name:\"SetAlert\",messageId:\"ccc005b8-ca8f-4c34-aeb5-73a8dbbd8d37\",dialogRequestId:"
      "\"dca0bece-16a7-44f3-b940-e6c4ecc2b1f5\"}";
  
  // Test MIME Boundary.
  static const std::string MIME_BOUNDARY = "thisisaboundary";
  
  // MIME encoded DIRECTIVE1 with start and end MIME boundaries.
  static const std::string MIME_BODY_DIRECTIVE1 = "--" + MIME_BOUNDARY + "\r\nContent-Type: application/json" +
                                                  "\r\n\r\n" + DIRECTIVE1 + "\r\n--" + MIME_BOUNDARY + "--\r\n";
  ;
  
  // MIME encoded DIRECTIVE2 with start MIME boundary but no end MIME boundary.
  static const std::string MIME_BODY_DIRECTIVE2 = "--" + MIME_BOUNDARY + "\r\nContent-Type: application/json" +
                                                  "\r\n\r\n" + DIRECTIVE2 + "\r\n--" + MIME_BOUNDARY + "\r\n";
  
  // HTTP header to specify MIME boundary and content type.
  static const std::string HTTP_BOUNDARY_HEADER =
      "Content-Type: multipart/related; boundary=" + MIME_BOUNDARY + "; type=application/json";
  
  // The maximum dedicated number of ping streams in HTTP2Transport
  static const unsigned MAX_PING_STREAMS = 1;
  
  // The maximum dedicated number of downchannel streams in HTTP2Transport
  static const unsigned MAX_DOWNCHANNEL_STREAMS = 1;
  
  // The maximum number of HTTP2 requests that can be enqueued at a time waiting for response completion
  static const unsigned MAX_AVS_STREAMS = 10;
  
  // Maximum allowed of POST streams
  static const unsigned MAX_POST_STREAMS = MAX_AVS_STREAMS - MAX_DOWNCHANNEL_STREAMS - MAX_PING_STREAMS;
  
  /// Test harness for @c HTTP2Transport class.
  class HTTP2TransportTest : public Test {
  public:
      /// Initial setup for tests.
      void SetUp() override;
  
      /// Cleanup method.
      void TearDown() override;
  
  protected:
      /**
       * Setup the handlers for the mocked methods @c AuthDelegateInterface::addAuthObserver(), @c
       * PostConnectFactoryInterface::createPostConnect() , @c  PostConnectInterface::doPostConnect() , @c
       * TransportObserverInterface::onConnected().
       *
       * @param sendOnPostConnected A boolean to specify whether to send onPostConnected() event when @c
       * PostConnectInterface::doPostConnect() is called.
       * @param expectConnected Specify that a call to TransportObserverInterface::onConnected() is expected.
       */
      void setupHandlers(bool sendOnPostConnected, bool expectConnected);
  
      /**
       * Helper function to send @c Refreshed Auth State to the @c HTTP2Transport observer.
       * It also checks that a proper Auth observer has been registered by @c HTTP2Transport.
       */
      void sendAuthStateRefreshed();
  
      /**
       * Helper function to put the @c HTTP2Transport into connected state.
       */
      void authorizeAndConnect();
  
      /// The HTTP2Transport instance to be tested.
      std::shared_ptr<HTTP2Transport> m_http2Transport;
  
      /// The mock @c AuthDelegateInterface.
      std::shared_ptr<MockAuthDelegate> m_mockAuthDelegate;
  
      /// The mock @c HTTP2ConnectionInterface.
      std::shared_ptr<MockHTTP2Connection> m_mockHttp2Connection;
  
      /// The mock @c MessageConsumerInterface.
      std::shared_ptr<MockMessageConsumer> m_mockMessageConsumer;
  
      /// An instance of the @c AttachmentManager.
      std::shared_ptr<AttachmentManager> m_attachmentManager;
  
      /// The mock @c TransportObserverInterface.
      std::shared_ptr<MockTransportObserver> m_mockTransportObserver;
  
      /// The mock @c PostConnectFactoryInterface
      std::shared_ptr<MockPostConnectFactory> m_mockPostConnectFactory;
  
      /// The mock @c MetricRecorderInterface
      std::shared_ptr<MockMetricRecorder> m_mockMetricRecorder;
  
      /// The mock @c EventTracerInterface
      std::shared_ptr<MockEventTracer> m_mockEventTracer;
  
      /// The mock @c PostConnectInterface.
      std::shared_ptr<MockPostConnect> m_mockPostConnect;
  
      /// The message queue map.
      std::shared_ptr<SynchronizedMessageRequestQueue> m_synchronizedMessageRequestQueue;
  
      /// A promise that the Auth Observer will be set.
      PromiseFuturePair<std::shared_ptr<AuthObserverInterface>> m_authObserverSet;
  
      /// A promise that @c PostConnectFactoryInterface::createPostConnect() will be called).
      PromiseFuturePair<void> m_createPostConnectCalled;
  
      /// A promise that @c PostConnectInterface:doPostConnect() will be called).
      PromiseFuturePair<std::pair<std::shared_ptr<MessageSenderInterface>, std::shared_ptr<PostConnectObserverInterface>>>
          m_doPostConnected;
  
      /// A promise that the @c TransportObserver.onConnected() will be called.
      PromiseFuturePair<void> m_transportConnected;
  };
  
  /**
   *  A @c MessageRequestObserverInterface implementation used in this test.
   */
  class TestMessageRequestObserver : public avsCommon::sdkInterfaces::MessageRequestObserverInterface {
  public:
      /*
       * Called when a message request has been processed by AVS.
       */
      void onSendCompleted(MessageRequestObserverInterface::Status status) {
          m_status.setValue(status);
      }
  
      /*
       * Called when an exception is thrown when trying to send a message to AVS.
       */
      void onExceptionReceived(const std::string& exceptionMessage) {
          m_exception.setValue(exceptionMessage);
      }
  
      /// A promise that @c MessageRequestObserverInterface::onSendCompleted() will be called  with a @c
      /// MessageRequestObserverInterface::Status value
      PromiseFuturePair<MessageRequestObserverInterface::Status> m_status;
  
      /// A promise that @c MessageRequestObserverInterface::onExceptionReceived() will be called  with an exception
      /// message
      PromiseFuturePair<std::string> m_exception;
  };
  
  void HTTP2TransportTest::SetUp() {
      m_mockAuthDelegate = std::make_shared<NiceMock<MockAuthDelegate>>();
      m_mockHttp2Connection = std::make_shared<NiceMock<MockHTTP2Connection>>(FULL_DOWNCHANNEL_URL, FULL_PING_URL);
      m_mockMessageConsumer = std::make_shared<NiceMock<MockMessageConsumer>>();
      m_attachmentManager = std::make_shared<AttachmentManager>(AttachmentManager::AttachmentType::IN_PROCESS);
      m_mockTransportObserver = std::make_shared<NiceMock<MockTransportObserver>>();
      m_mockPostConnectFactory = std::make_shared<NiceMock<MockPostConnectFactory>>();
      m_mockEventTracer = std::make_shared<NiceMock<MockEventTracer>>();
      m_mockPostConnect = std::make_shared<NiceMock<MockPostConnect>>();
      m_mockMetricRecorder = std::make_shared<NiceMock<MockMetricRecorder>>();
      m_mockAuthDelegate->setAuthToken(CBL_AUTHORIZATION_TOKEN);
      HTTP2Transport::Configuration cfg;
      m_synchronizedMessageRequestQueue = std::make_shared<SynchronizedMessageRequestQueue>();
      m_http2Transport = HTTP2Transport::create(
          m_mockAuthDelegate,
          TEST_AVS_GATEWAY_STRING,
          m_mockHttp2Connection,
          m_mockMessageConsumer,
          m_attachmentManager,
          m_mockTransportObserver,
          m_mockPostConnectFactory,
          m_synchronizedMessageRequestQueue,
          cfg,
          m_mockMetricRecorder,
          m_mockEventTracer);
  
      ASSERT_NE(m_http2Transport, nullptr);
  }
  
  void HTTP2TransportTest::TearDown() {
      m_http2Transport->shutdown();
  }
  
  void HTTP2TransportTest::setupHandlers(bool sendOnPostConnected, bool expectConnected) {
      Sequence s1, s2;
  
      // Enforced ordering of mock method calls:
      // addAuthObserver should be before onConnected
      // createPostConnect should be before doPostConnect
  
      // Handle AuthDelegateInterface::addAuthObserver() when called.
      EXPECT_CALL(*m_mockAuthDelegate, addAuthObserver(_))
          .InSequence(s1)
          .WillOnce(Invoke([this](std::shared_ptr<avsCommon::sdkInterfaces::AuthObserverInterface> argAuthObserver) {
              m_authObserverSet.setValue(argAuthObserver);
          }));
  
      {
          InSequence dummy;
  
          // Handle PostConnectFactoryInterface::createPostConnect() when called.
          EXPECT_CALL(*m_mockPostConnectFactory, createPostConnect()).WillOnce(InvokeWithoutArgs([this] {
              m_createPostConnectCalled.setValue();
              return m_mockPostConnect;
          }));
  
          // Handle PostConnectInterface::doPostConnect() when called.
          EXPECT_CALL(*m_mockPostConnect, doPostConnect(_, _))
              .InSequence(s2)
              .WillOnce(Invoke([this, sendOnPostConnected](
                                   std::shared_ptr<MessageSenderInterface> postConnectSender,
                                   std::shared_ptr<PostConnectObserverInterface> postConnectObserver) {
                  m_doPostConnected.setValue(std::make_pair(postConnectSender, postConnectObserver));
                  if (sendOnPostConnected) {
                      postConnectObserver->onPostConnected();
                  }
                  return true;
              }));
      }
  
      if (expectConnected) {
          // Handle TransportObserverInterface::onConnected() when called.
          EXPECT_CALL(*m_mockTransportObserver, onConnected(_))
              .InSequence(s1, s2)
              .WillOnce(Invoke([this](std::shared_ptr<TransportInterface> transport) { m_transportConnected.setValue(); })
  
              );
      }
  }
  
  void HTTP2TransportTest::sendAuthStateRefreshed() {
      std::shared_ptr<AuthObserverInterface> authObserver;
  
      // Wait for HTTP2Transport Auth server registration.
      ASSERT_TRUE(m_authObserverSet.waitFor(RESPONSE_TIMEOUT));
  
      authObserver = m_authObserverSet.getValue();
  
      // Check HTTP2Transport registered itself as the authObserver.
      ASSERT_TRUE(authObserver != nullptr);
      ASSERT_EQ(authObserver.get(), m_http2Transport.get());
  
      // Send REFRESHED Auth State to HTTP2Transport.
      authObserver->onAuthStateChange(AuthObserverInterface::State::REFRESHED, AuthObserverInterface::Error::SUCCESS);
  }
  
  void HTTP2TransportTest::authorizeAndConnect() {
      setupHandlers(true, true);
  
      // Call connect().
      m_http2Transport->connect();
  
      // Deliver a 'REFRESHED' status to observers of AuthDelegateInterface.
      sendAuthStateRefreshed();
  
      // The Mock HTTP2Request replies to any downchannel request with a 200.
      ASSERT_TRUE(m_mockHttp2Connection->respondToDownchannelRequests(
          static_cast<long>(HTTPResponseCode::SUCCESS_OK), false, RESPONSE_TIMEOUT));
  
      // Wait for PostConnectInterface:doPostConnect() call.
      ASSERT_TRUE(m_doPostConnected.waitFor(RESPONSE_TIMEOUT));
  
      // Wait for a long time (up to 5 second), terminating the wait when the mock of
      // TransportObserverInterface::onConnected() is called.
      ASSERT_TRUE(m_transportConnected.waitFor(LONG_RESPONSE_TIMEOUT));
  }
  
  /**
   * Test non-authorization on empty auth token.
   */
  TEST_F(HTTP2TransportTest, testSlow_emptyAuthToken) {
      // Send an empty Auth token.
      m_mockAuthDelegate->setAuthToken("");
  
      setupHandlers(false, false);
  
      m_http2Transport->connect();
  
      // Give m_http2Transport a chance to misbehave and send a request w/o authorization.
      std::this_thread::sleep_for(SHORT_DELAY);
  
      // Check that no HTTP requests created and sent at this point.
      ASSERT_TRUE(m_mockHttp2Connection->isRequestQueueEmpty());
  
      sendAuthStateRefreshed();
  
      // Should not send any HTTP2 Request.
      ASSERT_EQ(m_mockHttp2Connection->waitForRequest(ONE_HUNDRED_MILLISECOND_DELAY), nullptr);
  }
  
  /**
   * Test waiting for AuthDelegateInterface.
   */
  TEST_F(HTTP2TransportTest, testSlow_waitAuthDelegateInterface) {
      setupHandlers(false, false);
  
      m_http2Transport->connect();
  
      // Give m_http2Transport a chance to misbehave and send a request w/o authorization.
      std::this_thread::sleep_for(SHORT_DELAY);
  
      // Check that no HTTP requests created and sent at this point.
      ASSERT_TRUE(m_mockHttp2Connection->isRequestQueueEmpty());
  
      sendAuthStateRefreshed();
  
      // Wait for HTTP2 Request.
      ASSERT_NE(m_mockHttp2Connection->waitForRequest(RESPONSE_TIMEOUT), nullptr);
      auto request = m_mockHttp2Connection->dequeRequest();
      ASSERT_EQ(request->getUrl(), FULL_DOWNCHANNEL_URL);
  }
  
  /**
   * Test verifying the proper inclusion of bearer token in requests.
   */
  TEST_F(HTTP2TransportTest, test_bearerTokenInRequest) {
      setupHandlers(false, false);
  
      m_mockHttp2Connection->setWaitRequestHeader(HTTP_AUTHORIZATION_HEADER_BEARER);
  
      m_http2Transport->connect();
  
      sendAuthStateRefreshed();
  
      // Wait for HTTP2 Request with Authorization: Bearer in header.
      ASSERT_TRUE(m_mockHttp2Connection->waitForRequestWithHeader(RESPONSE_TIMEOUT));
  }
  
  /**
   * Test creation and triggering of post-connect object.
   */
  TEST_F(HTTP2TransportTest, test_triggerPostConnectObject) {
      setupHandlers(false, false);
  
      // Don't expect TransportObserverInterface::onConnected() will be called.
      EXPECT_CALL(*m_mockTransportObserver, onConnected(_)).Times(0);
  
      m_http2Transport->connect();
  
      sendAuthStateRefreshed();
  
      // The Mock HTTP2Request replies to any downchannel request with 200.
      ASSERT_TRUE(m_mockHttp2Connection->respondToDownchannelRequests(
          static_cast<long>(HTTPResponseCode::SUCCESS_OK), false, RESPONSE_TIMEOUT));
  
      // Wait for a long time (up to 5 second), terminating the wait when the mock of
      // PostConnectFactoryInterface::createPostConnect() is called.
      ASSERT_TRUE(m_createPostConnectCalled.waitFor(RESPONSE_TIMEOUT));
  
      // Wait for a long time (up to 5 second), terminating the wait when the mock of
      // PostConnectInterface::doPostConnect() is called.
      ASSERT_TRUE(m_doPostConnected.waitFor(RESPONSE_TIMEOUT));
  }
  
  /**
   * Test delay of connection status until post-connect object created / notifies success.
   */
  TEST_F(HTTP2TransportTest, test_connectionStatusOnPostConnect) {
      setupHandlers(true, true);
  
      // Call connect().
      m_http2Transport->connect();
  
      // Deliver a 'REFRESHED' status to observers of AuthDelegateInterface.
      sendAuthStateRefreshed();
  
      // The Mock HTTP2Request replies to any downchannel request with a 200.
      ASSERT_TRUE(m_mockHttp2Connection->respondToDownchannelRequests(
          static_cast<long>(HTTPResponseCode::SUCCESS_OK), false, RESPONSE_TIMEOUT));
  
      // Wait for PostConnectInterface:doPostConnect() call.
      ASSERT_TRUE(m_doPostConnected.waitFor(RESPONSE_TIMEOUT));
  
      // Wait for a long time (up to 5 second), terminating the wait when the mock of
      // TransportObserverInterface::onConnected() is called.
      ASSERT_TRUE(m_transportConnected.waitFor(LONG_RESPONSE_TIMEOUT));
  }
  
  /**
   * Test retry upon failed downchannel connection.
   */
  TEST_F(HTTP2TransportTest, testSlow_retryOnDownchannelConnectionFailure) {
      setupHandlers(false, false);
  
      EXPECT_CALL(*m_mockTransportObserver, onConnected(_)).Times(0);
  
      // Call connect().
      m_http2Transport->connect();
  
      // Deliver a 'REFRESHED' status to observers of AuthDelegateInterface.
      sendAuthStateRefreshed();
  
      // The Mock HTTP2Request replies to any downchannel request with a 500.
      ASSERT_TRUE(m_mockHttp2Connection->respondToDownchannelRequests(
          static_cast<long>(HTTPResponseCode::SERVER_ERROR_INTERNAL), false, RESPONSE_TIMEOUT));
  
      // Wait for a long time (up to 10 second), terminating the wait when the mock of HTTP2Connection receives a second
      // attempt to create a downchannel request.
      m_mockHttp2Connection->waitForRequest(LONG_RESPONSE_TIMEOUT, 2);
  }
  
  /**
   * Test sending of MessageRequest content.
   */
  TEST_F(HTTP2TransportTest, test_messageRequestContent) {
      setupHandlers(false, false);
  
      // Call connect().
      m_http2Transport->connect();
  
      // Deliver a 'REFRESHED' status to observers of AuthDelegateInterface.
      sendAuthStateRefreshed();
  
      ASSERT_TRUE(m_mockHttp2Connection->respondToDownchannelRequests(
          static_cast<long>(HTTPResponseCode::SUCCESS_OK), false, RESPONSE_TIMEOUT));
  
      // Wait for doPostConnect().
      ASSERT_TRUE(m_doPostConnected.waitFor(RESPONSE_TIMEOUT));
  
      // Send post connect message.
      std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
      m_http2Transport->sendMessage(messageReq);
  
      // Wait for the postConnect message to become HTTP message request and HTTP body to be fully reassembled.
      auto postMessage = m_mockHttp2Connection->waitForPostRequest(LONG_RESPONSE_TIMEOUT);
      ASSERT_NE(postMessage, nullptr);
  
      // The number of MIME parts decoded should just be 1.
      ASSERT_EQ(postMessage->getMimeResponseSink()->getCountOfMimeParts(), 1u);
  
      // Get the first MIME part message.
      auto mimeMessage = postMessage->getMimeResponseSink()->getMimePart(0);
      std::string mimeMessageString(mimeMessage.begin(), mimeMessage.end());
  
      // Check the MIME part is the message sent.
      ASSERT_EQ(TEST_MESSAGE, mimeMessageString);
  }
  
  /**
   * Test sending of MessageRequest with attachment data.
   */
  TEST_F(HTTP2TransportTest, test_messageRequestWithAttachment) {
      // Create an attachment reader.
      std::vector<char> attachment(TEST_ATTACHMENT_MESSAGE.begin(), TEST_ATTACHMENT_MESSAGE.end());
      std::shared_ptr<AttachmentReader> attachmentReader =
          avsCommon::avs::attachment::AttachmentUtils::createAttachmentReader(attachment);
      ASSERT_NE(attachmentReader, nullptr);
  
      setupHandlers(false, false);
  
      // Call connect().
      m_http2Transport->connect();
  
      // Deliver a 'REFRESHED' status to observers of AuthDelegateInterface.
      sendAuthStateRefreshed();
  
      m_mockHttp2Connection->respondToDownchannelRequests(
          static_cast<long>(HTTPResponseCode::SUCCESS_OK), false, RESPONSE_TIMEOUT);
  
      // Wait for doPostConnect().
      ASSERT_TRUE(m_doPostConnected.waitFor(RESPONSE_TIMEOUT));
  
      // Send post connect message with attachment.
      std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
      messageReq->addAttachmentReader(TEST_ATTACHMENT_FIELD, attachmentReader);
      m_http2Transport->sendMessage(messageReq);
  
      // Wait for the postConnect message to become HTTP message request and HTTP body to be fully reassembled.
      auto postMessage = m_mockHttp2Connection->waitForPostRequest(LONG_RESPONSE_TIMEOUT);
      ASSERT_NE(postMessage, nullptr);
  
      // The number of MIME parts decoded should just be 2.
      ASSERT_EQ(postMessage->getMimeResponseSink()->getCountOfMimeParts(), 2u);
  
      // Get the first MIME part message and check it is the message sent.
      auto mimeMessage = postMessage->getMimeResponseSink()->getMimePart(0);
      std::string mimeMessageString(mimeMessage.begin(), mimeMessage.end());
      ASSERT_EQ(TEST_MESSAGE, mimeMessageString);
  
      // Get the second MIME part message and check it is the attachement sent.
      auto mimeAttachment = postMessage->getMimeResponseSink()->getMimePart(1);
      std::string mimeAttachmentString(mimeAttachment.begin(), mimeAttachment.end());
      ASSERT_EQ(TEST_ATTACHMENT_MESSAGE, mimeAttachmentString);
  }
  
  /**
   * Test pause of sending message when attachment buffer (SDS) empty but not closed.
   */
  TEST_F(HTTP2TransportTest, test_pauseSendWhenSDSEmpty) {
      setupHandlers(false, false);
  
      // Call connect().
      m_http2Transport->connect();
  
      // Deliver a 'REFRESHED' status to observers of AuthDelegateInterface.
      sendAuthStateRefreshed();
  
      m_mockHttp2Connection->respondToDownchannelRequests(
          static_cast<long>(HTTPResponseCode::SUCCESS_OK), false, RESPONSE_TIMEOUT);
  
      // Wait for doPostConnect().
      ASSERT_TRUE(m_doPostConnected.waitFor(RESPONSE_TIMEOUT));
  
      // Send post connect message with attachment.
      std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
      AttachmentManager attMgr(AttachmentManager::AttachmentType::IN_PROCESS);
      std::vector<char> attachment(TEST_ATTACHMENT_MESSAGE.begin(), TEST_ATTACHMENT_MESSAGE.end());
      std::shared_ptr<AttachmentReader> attachmentReader =
          attMgr.createReader(TEST_ATTACHMENT_ID_STRING_ONE, avsCommon::utils::sds::ReaderPolicy::NONBLOCKING);
      ASSERT_NE(attachmentReader, nullptr);
      messageReq->addAttachmentReader(TEST_ATTACHMENT_FIELD, attachmentReader);
      m_http2Transport->sendMessage(messageReq);
  
      // Send the attachment in chunks in another thread.
      std::thread writerThread([this, &attMgr, &attachment]() {
          // Number of chunks the attachment will be divided into
          const unsigned chunks = 4;
          // the size of each chunk in bytes, this is the ceiling of (attachment.size / chunks)
          unsigned int chunkSize = (attachment.size() + chunks - 1) / chunks;
          auto writer = attMgr.createWriter(TEST_ATTACHMENT_ID_STRING_ONE, avsCommon::utils::sds::WriterPolicy::BLOCKING);
          AttachmentWriter::WriteStatus writeStatus = AttachmentWriter::WriteStatus::OK;
          unsigned int lastChunkSize = (attachment.size() % chunks == 0) ? chunkSize : attachment.size() % chunks;
          for (unsigned chunk = 0; chunk < chunks; chunk++) {
              writer->write(
                  &attachment[chunk * chunkSize], (chunk == chunks - 1) ? lastChunkSize : chunkSize, &writeStatus);
              ASSERT_EQ(writeStatus, AttachmentWriter::WriteStatus::OK);
              ASSERT_TRUE(m_mockHttp2Connection->isPauseOnSendReceived(ONE_HUNDRED_MILLISECOND_DELAY));
          }
          writer->close();
      });
  
      // Wait for the postConnect message to become HTTP message request and HTTP body to be fully reassembled.
      auto postMessage = m_mockHttp2Connection->waitForPostRequest(LONG_RESPONSE_TIMEOUT);
      ASSERT_NE(postMessage, nullptr);
  
      // The number of MIME parts decoded should just be 2.
      ASSERT_EQ(postMessage->getMimeResponseSink()->getCountOfMimeParts(), 2u);
  
      // Get the first MIME part message and check it is the message sent.
      auto mimeMessage = postMessage->getMimeResponseSink()->getMimePart(0);
      std::string mimeMessageString(mimeMessage.begin(), mimeMessage.end());
      ASSERT_EQ(TEST_MESSAGE, mimeMessageString);
  
      // Get the second MIME part message and check it is the attachment sent.
      auto mimeAttachment = postMessage->getMimeResponseSink()->getMimePart(1);
      std::string mimeAttachmentString(mimeAttachment.begin(), mimeAttachment.end());
      ASSERT_EQ(TEST_ATTACHMENT_MESSAGE, mimeAttachmentString);
  
      writerThread.join();
  }
  
  /**
   * Test queuing MessageRequests until a response code has been received for any outstanding MessageRequest
   */
  TEST_F(HTTP2TransportTest, testSlow_messageRequestsQueuing) {
      authorizeAndConnect();
  
      // Send 5 messages.
      std::vector<std::shared_ptr<TestMessageRequestObserver>> messageObservers;
      const unsigned messagesCount = 5;  // number of test messages to Send
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
          auto messageObserver = std::make_shared<TestMessageRequestObserver>();
          messageObservers.push_back(messageObserver);
          messageReq->addObserver(messageObserver);
          m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
          m_http2Transport->onRequestEnqueued();
      }
  
      // Give m_http2Transport a chance to misbehave and send more than a single request before receiving a response.
      std::this_thread::sleep_for(SHORT_DELAY);
  
      // Check that only 1 out of the 5 POST messages have been in the outgoing send queue.
      ASSERT_EQ(m_mockHttp2Connection->getPostRequestsNum(), 1u);
  
      // Delayed 200 response for each POST request.
      unsigned int postsRequestsCount = 0;
      while (postsRequestsCount < messagesCount) {
          auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
          if (request) {
              postsRequestsCount++;
              // Give m_http2Transport a chance to misbehave and send requests before receiving a response.
              std::this_thread::sleep_for(SHORT_DELAY);
              request->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK);
          } else {
              break;
          }
      }
  
      // Make sure HTTP2Transport sends out the 5 POST requests.
      ASSERT_EQ(postsRequestsCount, messagesCount);
  
      // On disconnect, send CANCELED response for each POST REQUEST.
      EXPECT_CALL(*m_mockHttp2Connection, disconnect()).WillOnce(Invoke([this]() {
          while (true) {
              auto request = m_mockHttp2Connection->dequePostRequest();
              if (!request) break;
  
              request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::CANCELLED);
          };
      }));
  
      m_http2Transport->shutdown();
  
      // Count the number of messages that received CANCELED or NOT_CONNECTED event.
      unsigned messagesCanceled = 0;
      unsigned messagesRemaining = 0;
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          if (messageObservers[messageNum]->m_status.waitFor(RESPONSE_TIMEOUT)) {
              switch (messageObservers[messageNum]->m_status.getValue()) {
                  case MessageRequestObserverInterface::Status::CANCELED:
                  case MessageRequestObserverInterface::Status::NOT_CONNECTED:
                      messagesCanceled++;
                  default:
                      break;
              }
          }
      }
      while (m_synchronizedMessageRequestQueue->dequeueOldestRequest()) {
          ++messagesRemaining;
      }
  
      ASSERT_EQ(messagesCanceled + messagesRemaining, messagesCount);
  }
  
  /**
   * Test MessageRequests are sent sequentially.
   */
  TEST_F(HTTP2TransportTest, messageRequests_SequentialSend) {
      authorizeAndConnect();
  
      // Send 5 messages.
      std::vector<std::shared_ptr<TestMessageRequestObserver>> messageObservers;
      unsigned int messagesCount = 5;  // number of test messages to Send
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
          auto messageObserver = std::make_shared<TestMessageRequestObserver>();
          messageObservers.push_back(messageObserver);
          messageReq->addObserver(messageObserver);
          m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
          m_http2Transport->onRequestEnqueued();
      }
  
      unsigned int postsRequestsCount = 0;
      while (postsRequestsCount < messagesCount) {
          // Delayed 200 response for each POST request.
          std::this_thread::sleep_for(SHORT_DELAY);
          postsRequestsCount++;
  
          ASSERT_EQ((int)m_mockHttp2Connection->getPostRequestsNum(), (int)postsRequestsCount);
          auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
          if (request) {
              request->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK);
          }
      }
  
      // Make sure HTTP2Transport sends out the 5 POST requests.
      ASSERT_EQ(postsRequestsCount, messagesCount);
  
      // On disconnect, send CANCELED response for each POST REQUEST.
      EXPECT_CALL(*m_mockHttp2Connection, disconnect()).WillOnce(Invoke([this]() {
          while (true) {
              auto request = m_mockHttp2Connection->dequePostRequest();
              if (!request) break;
  
              request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::CANCELLED);
          };
      }));
  
      m_http2Transport->shutdown();
  
      // Count the number of messages that received CANCELED or NOT_CONNECTED event.
      unsigned messagesCanceled = 0;
      unsigned messagesRemaining = 0;
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          if (messageObservers[messageNum]->m_status.waitFor(RESPONSE_TIMEOUT)) {
              switch (messageObservers[messageNum]->m_status.getValue()) {
                  case MessageRequestObserverInterface::Status::CANCELED:
                  case MessageRequestObserverInterface::Status::NOT_CONNECTED:
                      messagesCanceled++;
                  default:
                      break;
              }
          }
      }
      while (m_synchronizedMessageRequestQueue->dequeueOldestRequest()) {
          ++messagesRemaining;
      }
  
      ASSERT_EQ(messagesCanceled + messagesRemaining, messagesCount);
  }
  
  /**
   * Test concurrent send of non sequential MessageRequests.
   */
  TEST_F(HTTP2TransportTest, messageRequests_concurrentSend) {
      authorizeAndConnect();
  
      // Send 5 messages.
      std::vector<std::shared_ptr<TestMessageRequestObserver>> messageObservers;
      unsigned int messagesCount = 5;  // number of test messages to Send
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          // 'false' parameter flags the new MessageRequst as not serialized.
          std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, false, "");
          auto messageObserver = std::make_shared<TestMessageRequestObserver>();
          messageObservers.push_back(messageObserver);
          messageReq->addObserver(messageObserver);
          m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
          m_http2Transport->onRequestEnqueued();
      }
  
      unsigned int postsRequestsCount = 0;
      while (postsRequestsCount < messagesCount) {
          auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
          postsRequestsCount++;
          // Note: do not acknowledge the request.
      }
      // Make sure HTTP2Transport sent out the 5 POST requests.
      ASSERT_EQ(postsRequestsCount, messagesCount);
  
      // On disconnect, send CANCELED response for each POST REQUEST.
      EXPECT_CALL(*m_mockHttp2Connection, disconnect()).WillOnce(Invoke([this]() {
          while (true) {
              auto request = m_mockHttp2Connection->dequePostRequest();
              if (!request) {
                  break;
              }
              request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::CANCELLED);
          };
      }));
  
      m_http2Transport->shutdown();
  
      // Count the number of messages that received CANCELED or NOT_CONNECTED event.
      unsigned messagesCanceled = 0;
      unsigned messagesRemaining = 0;
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          if (messageObservers[messageNum]->m_status.waitFor(RESPONSE_TIMEOUT)) {
              switch (messageObservers[messageNum]->m_status.getValue()) {
                  case MessageRequestObserverInterface::Status::CANCELED:
                  case MessageRequestObserverInterface::Status::NOT_CONNECTED:
                      messagesCanceled++;
                  default:
                      break;
              }
          }
      }
      while (m_synchronizedMessageRequestQueue->dequeueOldestRequest()) {
          ++messagesRemaining;
      }
  
      ASSERT_EQ(messagesCanceled + messagesRemaining, messagesCount);
  }
  
  /**
   * Test notification of onSendCompleted (check mapping of all cases and their mapping to
   * MessageRequestObserverInterface::Status).
   */
  TEST_F(HTTP2TransportTest, test_onSendCompletedNotification) {
      // Contains the mapping of HTTPResponseCode, HTTP2ResponseFinishedStatus and the expected
      // MessageRequestObserverInterface::Status.
      const std::vector<std::tuple<long, HTTP2ResponseFinishedStatus, MessageRequestObserverInterface::Status>>
          messageResponseMap = {
              {std::make_tuple(
                  NO_CALL,
                  HTTP2ResponseFinishedStatus::INTERNAL_ERROR,
                  MessageRequestObserverInterface::Status::INTERNAL_ERROR)},
              {std::make_tuple(
                  NO_CALL, HTTP2ResponseFinishedStatus::CANCELLED, MessageRequestObserverInterface::Status::CANCELED)},
              {std::make_tuple(
                  NO_CALL, HTTP2ResponseFinishedStatus::TIMEOUT, MessageRequestObserverInterface::Status::TIMEDOUT)},
              {std::make_tuple(
                  NO_CALL,
                  static_cast<HTTP2ResponseFinishedStatus>(-1),
                  MessageRequestObserverInterface::Status::INTERNAL_ERROR)},
              {std::make_tuple(
                  HTTPResponseCode::HTTP_RESPONSE_CODE_UNDEFINED,
                  HTTP2ResponseFinishedStatus::INTERNAL_ERROR,
                  MessageRequestObserverInterface::Status::INTERNAL_ERROR)},
              {std::make_tuple(
                  HTTPResponseCode::SUCCESS_OK,
                  HTTP2ResponseFinishedStatus::CANCELLED,
                  MessageRequestObserverInterface::Status::CANCELED)},
              {std::make_tuple(
                  HTTPResponseCode::REDIRECTION_START_CODE,
                  HTTP2ResponseFinishedStatus::TIMEOUT,
                  MessageRequestObserverInterface::Status::TIMEDOUT)},
              {std::make_tuple(
                  HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST,
                  static_cast<HTTP2ResponseFinishedStatus>(-1),
                  MessageRequestObserverInterface::Status::INTERNAL_ERROR)},
              {std::make_tuple(
                  HTTPResponseCode::HTTP_RESPONSE_CODE_UNDEFINED,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::INTERNAL_ERROR)},
              {std::make_tuple(
                  HTTPResponseCode::SUCCESS_OK,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::SUCCESS)},
              {std::make_tuple(
                  HTTPResponseCode::SUCCESS_NO_CONTENT,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::SUCCESS_NO_CONTENT)},
              {std::make_tuple(
                  HTTPResponseCode::REDIRECTION_START_CODE,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::SERVER_OTHER_ERROR)},
              {std::make_tuple(
                  HTTPResponseCode::REDIRECTION_END_CODE,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::SERVER_OTHER_ERROR)},
              {std::make_tuple(
                  HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::BAD_REQUEST)},
              {std::make_tuple(
                  HTTPResponseCode::CLIENT_ERROR_FORBIDDEN,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::INVALID_AUTH)},
              {std::make_tuple(
                  HTTPResponseCode::SERVER_ERROR_INTERNAL,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2)},
              {std::make_tuple(
                  -1,
                  HTTP2ResponseFinishedStatus::COMPLETE,
                  MessageRequestObserverInterface::Status::SERVER_OTHER_ERROR)},
          };
  
      authorizeAndConnect();
  
      // Send a message for each test case defined in the messageResponseMap.
      std::vector<std::shared_ptr<TestMessageRequestObserver>> messageObservers;
      unsigned messagesCount = messageResponseMap.size();  // number of test messages to send
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
          auto messageObserver = std::make_shared<TestMessageRequestObserver>();
          messageObservers.push_back(messageObserver);
          messageReq->addObserver(messageObserver);
          m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
          m_http2Transport->onRequestEnqueued();
      }
  
      // Send the response code for each POST request.
      unsigned int postsRequestsCount = 0;
      for (unsigned int i = 0; i < messagesCount; i++) {
          auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
          if (request) {
              m_mockHttp2Connection->dequePostRequest();
              postsRequestsCount++;
              auto responseCode = std::get<0>(messageResponseMap[i]);
              if (responseCode != NO_CALL) {
                  request->getSink()->onReceiveResponseCode(responseCode);
              }
              auto responseFinished = std::get<1>(messageResponseMap[i]);
              request->getSink()->onResponseFinished(static_cast<HTTP2ResponseFinishedStatus>(responseFinished));
  
          } else {
              break;
          }
      }
  
      // Check if we got all the POST requests.
      ASSERT_EQ(postsRequestsCount, messagesCount);
  
      // Check that we got the right onSendCompleted notifications.
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          if (messageObservers[messageNum]->m_status.waitFor(RESPONSE_TIMEOUT)) {
              auto& item = messageResponseMap[messageNum];
              auto responseCode = std::get<0>(item);
              auto responseFinished = std::get<1>(item);
              auto expectedMessageObserverStatus = std::get<2>(item);
              ASSERT_EQ(messageObservers[messageNum]->m_status.getValue(), expectedMessageObserverStatus)
                  << "messageNum=" << messageNum << " responseCode=" << responseCode
                  << " responseFinished=" << responseFinished
                  << " expectedMessageObserverStatus=" << expectedMessageObserverStatus;
          }
      }
  }
  
  /**
   * Test onExceptionReceived() notification for non 200 content.
   */
  TEST_F(HTTP2TransportTest, test_onExceptionReceivedNon200Content) {
      authorizeAndConnect();
  
      // Send a message.
      std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
      auto messageObserver = std::make_shared<TestMessageRequestObserver>();
      messageReq->addObserver(messageObserver);
      m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
      m_http2Transport->onRequestEnqueued();
  
      auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
      ASSERT_NE(request, nullptr);
      request->getSink()->onReceiveResponseCode(HTTPResponseCode::SERVER_ERROR_INTERNAL);
      request->getSink()->onReceiveData(NON_MIME_PAYLOAD.c_str(), NON_MIME_PAYLOAD.size());
      request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
  
      ASSERT_TRUE(messageObserver->m_exception.waitFor(RESPONSE_TIMEOUT));
      ASSERT_EQ(messageObserver->m_exception.getValue(), NON_MIME_PAYLOAD);
      ASSERT_TRUE(messageObserver->m_status.waitFor(RESPONSE_TIMEOUT));
      ASSERT_EQ(messageObserver->m_status.getValue(), MessageRequestObserverInterface::Status::SERVER_INTERNAL_ERROR_V2);
  }
  
  /**
   * Test MessageConsumerInterface receipt of directives on Downchannel and Event streams.
   */
  TEST_F(HTTP2TransportTest, test_messageConsumerReceiveDirective) {
      PromiseFuturePair<void> messagesAreConsumed;
      unsigned int consumedMessageCount = 0;
      std::vector<std::string> messages;
  
      authorizeAndConnect();
  
      EXPECT_CALL(*m_mockMessageConsumer, consumeMessage(_, _))
          .WillRepeatedly(Invoke([&messagesAreConsumed, &consumedMessageCount, &messages](
                                     const std::string& contextId, const std::string& message) {
              consumedMessageCount++;
              messages.push_back(message);
              if (consumedMessageCount == 2u) {
                  messagesAreConsumed.setValue();
              }
          })
  
          );
  
      // Send a message.
      std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
      auto messageObserver = std::make_shared<TestMessageRequestObserver>();
      messageReq->addObserver(messageObserver);
      m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
      m_http2Transport->onRequestEnqueued();
  
      auto eventStream = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
      ASSERT_NE(eventStream, nullptr);
      eventStream->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK);
      eventStream->getSink()->onReceiveHeaderLine(HTTP_BOUNDARY_HEADER);
      eventStream->getSink()->onReceiveData(MIME_BODY_DIRECTIVE1.c_str(), MIME_BODY_DIRECTIVE1.size());
      eventStream->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
  
      auto downchannelStream = m_mockHttp2Connection->getDownchannelRequest();
      ASSERT_NE(downchannelStream, nullptr);
      downchannelStream->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK);
      downchannelStream->getSink()->onReceiveHeaderLine(HTTP_BOUNDARY_HEADER);
      downchannelStream->getSink()->onReceiveData(MIME_BODY_DIRECTIVE2.c_str(), MIME_BODY_DIRECTIVE2.size());
  
      ASSERT_TRUE(messagesAreConsumed.waitFor(RESPONSE_TIMEOUT));
      ASSERT_EQ(messages[0], DIRECTIVE1);
      ASSERT_EQ(messages[1], DIRECTIVE2);
  }
  
  /**
   * Test broadcast onServerSideDisconnect() upon closure of successfully opened downchannel.
   */
  TEST_F(HTTP2TransportTest, test_onServerSideDisconnectOnDownchannelClosure) {
      authorizeAndConnect();
  
      // Send a message.
      std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
      m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
      m_http2Transport->onRequestEnqueued();
  
      PromiseFuturePair<void> gotOnServerSideDisconnect;
      auto setGotOnServerSideDisconnect = [&gotOnServerSideDisconnect] { gotOnServerSideDisconnect.setValue(); };
  
      PromiseFuturePair<void> gotOnDisconnected;
      auto setGotOnDisconnected = [&gotOnDisconnected] { gotOnDisconnected.setValue(); };
  
      // Expect disconnect events later when downchannel receives a COMPLETE finished response.
      {
          InSequence dummy;
          EXPECT_CALL(*m_mockTransportObserver, onServerSideDisconnect(_))
              .Times(1)
              .WillOnce(InvokeWithoutArgs(setGotOnServerSideDisconnect));
          EXPECT_CALL(*m_mockTransportObserver, onDisconnected(_, _))
              .Times(1)
              .WillOnce(InvokeWithoutArgs(setGotOnDisconnected));
      }
  
      // Upon receiving the message, HTTP2Connection/request will reply to the down-channel request with
      // onResponseFinished(COMPLETE).
      auto eventStream = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
      ASSERT_NE(eventStream, nullptr);
      auto downchannelStream = m_mockHttp2Connection->getDownchannelRequest();
      downchannelStream->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
  
      // Wait for onResponseFinished() to be handled.
      ASSERT_TRUE(gotOnServerSideDisconnect.waitFor(RESPONSE_TIMEOUT));
      ASSERT_TRUE(gotOnDisconnected.waitFor(RESPONSE_TIMEOUT));
  }
  
  /**
   * Test detection of MessageRequest timeout and trigger of ping request.
   */
  TEST_F(HTTP2TransportTest, test_messageRequestTimeoutPingRequest) {
      authorizeAndConnect();
  
      // Send a message.
      std::shared_ptr<MessageRequest> messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE, "");
      m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
      m_http2Transport->onRequestEnqueued();
  
      // Upon receiving the message, the mock HTTP2Connection/request will reply to the request with
      // onResponseFinished(TIMEOUT).
      auto eventStream = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
      ASSERT_NE(eventStream, nullptr);
      eventStream->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::TIMEOUT);
  
      // Wait for a long time (up to 5 seconds) for the mock HTTP2Connection to receive a ping request.
      ASSERT_NE(m_mockHttp2Connection->waitForPingRequest(RESPONSE_TIMEOUT), nullptr);
  }
  
  /**
   * Test detection of network inactivity and trigger of ping request and continued ping for continued inactivity.
   */
  TEST_F(HTTP2TransportTest, testTimer_networkInactivityPingRequest) {
      // Short time to wait for inactivity before sending a ping.
      static const std::chrono::seconds testInactivityTimeout = SHORT_DELAY;
      // This test just checks that a second and third ping will be sen
      static const unsigned expectedInactivityPingCount{3u};
      // How long until pings should be sent plus some extra time to allow notifications to be processed.
      static const std::chrono::seconds testInactivityTime = {testInactivityTimeout * expectedInactivityPingCount +
                                                              SHORT_DELAY};
  
      // Setup HTTP2Transport with shorter ping inactivity timeout.
      HTTP2Transport::Configuration cfg;
      cfg.inactivityTimeout = testInactivityTimeout;
      m_http2Transport = HTTP2Transport::create(
          m_mockAuthDelegate,
          TEST_AVS_GATEWAY_STRING,
          m_mockHttp2Connection,
          m_mockMessageConsumer,
          m_attachmentManager,
          m_mockTransportObserver,
          m_mockPostConnectFactory,
          m_synchronizedMessageRequestQueue,
          cfg,
          m_mockMetricRecorder,
          m_mockEventTracer);
  
      authorizeAndConnect();
  
      PromiseFuturePair<void> gotPings;
  
      // Respond 204 to ping requests.
      std::thread pingResponseThread([this, &gotPings]() {
          unsigned pingCount{0};
          while (pingCount < expectedInactivityPingCount) {
              auto pingRequest = m_mockHttp2Connection->waitForPingRequest(RESPONSE_TIMEOUT);
              if (!pingRequest) {
                  continue;
              }
              m_mockHttp2Connection->dequePingRequest();
              pingRequest->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_NO_CONTENT);
              pingRequest->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
              pingCount++;
          }
          gotPings.setValue();
      });
  
      ASSERT_TRUE(gotPings.waitFor(testInactivityTime));
  
      pingResponseThread.join();
  }
  
  /**
   * Test connection tear down for ping timeout.
   */
  TEST_F(HTTP2TransportTest, testSlow_tearDownPingTimeout) {
      // Short time to wait for inactivity before sending a ping.
      const std::chrono::seconds testInactivityTimeout = SHORT_DELAY;
  
      // Setup HTTP2Transport with shorter ping inactivity timeout.
      HTTP2Transport::Configuration cfg;
      cfg.inactivityTimeout = testInactivityTimeout;
      m_http2Transport = HTTP2Transport::create(
          m_mockAuthDelegate,
          TEST_AVS_GATEWAY_STRING,
          m_mockHttp2Connection,
          m_mockMessageConsumer,
          m_attachmentManager,
          m_mockTransportObserver,
          m_mockPostConnectFactory,
          m_synchronizedMessageRequestQueue,
          cfg,
          m_mockMetricRecorder,
          m_mockEventTracer);
  
      authorizeAndConnect();
  
      PromiseFuturePair<void> gotOnDisconnected;
      auto setGotOnDisconnected = [&gotOnDisconnected] { gotOnDisconnected.setValue(); };
  
      EXPECT_CALL(*m_mockTransportObserver, onDisconnected(_, _))
          .Times(1)
          .WillOnce(InvokeWithoutArgs(setGotOnDisconnected));
  
      // Reply to a ping request.
      std::thread pingThread([this]() {
          auto pingRequest = m_mockHttp2Connection->waitForPingRequest(RESPONSE_TIMEOUT);
          ASSERT_NE(pingRequest, nullptr);
          m_mockHttp2Connection->dequePingRequest();
          pingRequest->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::TIMEOUT);
      });
  
      ASSERT_TRUE(gotOnDisconnected.waitFor(RESPONSE_TIMEOUT));
  
      pingThread.join();
  }
  
  /**
   * Test connection tear down for ping failure.
   */
  TEST_F(HTTP2TransportTest, testSlow_tearDownPingFailure) {
      // Short time to wait for inactivity before sending a ping.
      const std::chrono::seconds testInactivityTimeout = SHORT_DELAY;
  
      // Setup HTTP2Transport with shorter ping inactivity timeout.
      HTTP2Transport::Configuration cfg;
      cfg.inactivityTimeout = testInactivityTimeout;
      m_http2Transport = HTTP2Transport::create(
          m_mockAuthDelegate,
          TEST_AVS_GATEWAY_STRING,
          m_mockHttp2Connection,
          m_mockMessageConsumer,
          m_attachmentManager,
          m_mockTransportObserver,
          m_mockPostConnectFactory,
          m_synchronizedMessageRequestQueue,
          cfg,
          m_mockMetricRecorder,
          m_mockEventTracer);
  
      authorizeAndConnect();
  
      PromiseFuturePair<void> gotOnDisconnected;
      auto setGotOnDisconnected = [&gotOnDisconnected] { gotOnDisconnected.setValue(); };
  
      EXPECT_CALL(*m_mockTransportObserver, onDisconnected(_, _))
          .Times(1)
          .WillOnce(InvokeWithoutArgs(setGotOnDisconnected));
  
      // Reply to a ping request.
      std::thread pingThread([this]() {
          auto pingRequest = m_mockHttp2Connection->waitForPingRequest(RESPONSE_TIMEOUT);
          ASSERT_NE(pingRequest, nullptr);
          m_mockHttp2Connection->dequePingRequest();
          pingRequest->getSink()->onReceiveResponseCode(HTTPResponseCode::CLIENT_ERROR_BAD_REQUEST);
          pingRequest->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
      });
  
      ASSERT_TRUE(gotOnDisconnected.waitFor(RESPONSE_TIMEOUT));
  
      pingThread.join();
  }
  
  /**
   * Test limiting use of AVS streams to 10.
   */
  TEST_F(HTTP2TransportTest, testSlow_avsStreamsLimit) {
      // Number of test messages to send for this test.  This number is much larger than MAX_POST_STREAMS
      // to assure that this test exercises the case where more requests are outstanding than are allowed
      // to be sent at one time, forcing HTTP2Transport to queue the requests until some requests complete.
      const unsigned messagesCount = MAX_POST_STREAMS * 2;
  
      authorizeAndConnect();
  
      m_mockHttp2Connection->setResponseToPOSTRequests(HTTPResponseCode::SUCCESS_OK);
  
      // Send the 20 messages.
      std::vector<std::shared_ptr<TestMessageRequestObserver>> messageObservers;
  
      for (unsigned messageNum = 0; messageNum < messagesCount; messageNum++) {
          std::shared_ptr<MessageRequest> messageReq =
              std::make_shared<MessageRequest>(TEST_MESSAGE + std::to_string(messageNum), "");
          auto messageObserver = std::make_shared<TestMessageRequestObserver>();
          messageObservers.push_back(messageObserver);
          messageReq->addObserver(messageObserver);
          m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
          m_http2Transport->onRequestEnqueued();
      }
  
      // Check that there was a downchannel request sent out.
      ASSERT_NE(m_mockHttp2Connection->getDownchannelRequest(RESPONSE_TIMEOUT), nullptr);
  
      // Check the messages we sent were limited.
      ASSERT_EQ(m_mockHttp2Connection->getPostRequestsNum(), MAX_POST_STREAMS);
  
      unsigned int completed = 0;
      std::shared_ptr<MockHTTP2Request> request;
      while ((request = m_mockHttp2Connection->dequePostRequest(RESPONSE_TIMEOUT)) != nullptr &&
             completed < messagesCount) {
          request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
          completed++;
          // Give m_http2Transport a little time to misbehave.
          std::this_thread::sleep_for(TEN_MILLISECOND_DELAY);
      }
  
      // Check all the POST requests have been enqueued
      ASSERT_EQ(completed, messagesCount);
  
      // Check that the maximum number of enqueued messages at any time has been limited.
      ASSERT_EQ(m_mockHttp2Connection->getMaxPostRequestsEnqueud(), MAX_POST_STREAMS);
  }
  
  /**
   * Test if the HTTP2Transport receives the onPostConnectFailure() event, it notifies observers with onDisconnected() and
   * ChangeReason as UNRECOVERABLE_ERROR
   */
  TEST_F(HTTP2TransportTest, test_onPostConnectFailureInitiatesShutdownAndNotifiesObservers) {
      InSequence dummy;
  
      // Handle PostConnectFactoryInterface::createPostConnect() when called.
      EXPECT_CALL(*m_mockPostConnectFactory, createPostConnect()).WillOnce(InvokeWithoutArgs([this] {
          m_createPostConnectCalled.setValue();
          return m_mockPostConnect;
      }));
  
      // Handle PostConnectInterface::doPostConnect() when called.
      EXPECT_CALL(*m_mockPostConnect, doPostConnect(_, _))
          .WillOnce(Invoke([this](
                               std::shared_ptr<MessageSenderInterface> postConnectSender,
                               std::shared_ptr<PostConnectObserverInterface> postConnectObserver) {
              m_doPostConnected.setValue(std::make_pair(postConnectSender, postConnectObserver));
              postConnectObserver->onUnRecoverablePostConnectFailure();
              return true;
          }));
  
      PromiseFuturePair<void> gotOnDisconnected;
      // Handle TransportObserverInterface::onDisconnected() when called.
      EXPECT_CALL(*m_mockTransportObserver, onDisconnected(_, _))
          .WillOnce(Invoke([this, &gotOnDisconnected](
                               std::shared_ptr<TransportInterface> transport,
                               ConnectionStatusObserverInterface::ChangedReason reason) {
              gotOnDisconnected.setValue();
              ASSERT_EQ(transport, m_http2Transport);
              ASSERT_EQ(reason, ConnectionStatusObserverInterface::ChangedReason::UNRECOVERABLE_ERROR);
          }));
  
      m_http2Transport->connect();
  
      ASSERT_TRUE(m_doPostConnected.waitFor(RESPONSE_TIMEOUT));
      ASSERT_TRUE(gotOnDisconnected.waitFor(RESPONSE_TIMEOUT));
  }
  
  /*
   * Test if the event tracer gets notified when messages are sent.
   */
  TEST_F(HTTP2TransportTest, test_eventTracerIsNotifiedForMessagesSent) {
      EXPECT_CALL(*m_mockEventTracer, traceEvent(_)).WillOnce(Invoke([](const std::string& content) {
          ASSERT_EQ(content, TEST_MESSAGE);
      }));
  
      authorizeAndConnect();
      m_mockHttp2Connection->setResponseToPOSTRequests(HTTPResponseCode::SUCCESS_OK);
  
      auto messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE);
      m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
      m_http2Transport->onRequestEnqueued();
  
      auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
      if (request) {
          m_mockHttp2Connection->dequePostRequest();
          request->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK);
          request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
      }
  }
  
  /**
   * Test the event tracer does not get notified if message fails to send.
   */
  TEST_F(HTTP2TransportTest, test_eventTracerIsNotNotifiedForFailedMessages) {
      EXPECT_CALL(*m_mockEventTracer, traceEvent(_)).Times(0);
      // Fail messages prefixed with AVSEvent-; which are ones created by calls to HTTP2Transport::send
      ON_CALL(*m_mockHttp2Connection, createAndSendRequest(Property(&HTTP2RequestConfig::getId, StartsWith("AVSEvent-"))))
          .WillByDefault(InvokeWithoutArgs(([]() { return nullptr; })));
  
      authorizeAndConnect();
  
      auto messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE);
      std::shared_ptr<TestMessageRequestObserver> observer = std::make_shared<TestMessageRequestObserver>();
      messageReq->addObserver(observer);
      m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
      m_http2Transport->onRequestEnqueued();
  
      observer->m_status.waitFor(RESPONSE_TIMEOUT);
  }
  
  /**
   * Test that the event tracer does get notified when the contents of the sent message is empty.
   */
  TEST_F(HTTP2TransportTest, test_eventTracerNotifiedForEmptyMessageContent) {
      EXPECT_CALL(*m_mockEventTracer, traceEvent(_)).WillOnce(Invoke([](const std::string& content) {
          ASSERT_EQ(content, "");
      }));
  
      authorizeAndConnect();
      m_mockHttp2Connection->setResponseToPOSTRequests(HTTPResponseCode::SUCCESS_OK);
  
      auto messageReq = std::make_shared<MessageRequest>("");
      m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
      m_http2Transport->onRequestEnqueued();
  
      auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
      if (request) {
          m_mockHttp2Connection->dequePostRequest();
          request->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK);
          request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
      }
  }
  
  /**
   * Test if the event tracer gets notified for multiple message requests.
   */
  TEST_F(HTTP2TransportTest, test_eventTracerIsNotifiedForMultipleMessages) {
      EXPECT_CALL(*m_mockEventTracer, traceEvent(_)).WillRepeatedly(Invoke([](const std::string& content) {
          static int i = 0;
          ASSERT_EQ(content, TEST_MESSAGE + std::to_string(i++));
      }));
  
      authorizeAndConnect();
      m_mockHttp2Connection->setResponseToPOSTRequests(HTTPResponseCode::SUCCESS_OK);
  
      int messagesCount = 10;
      for (int messageNum = 0; messageNum < messagesCount; messageNum++) {
          auto messageReq = std::make_shared<MessageRequest>(TEST_MESSAGE + std::to_string(messageNum));
          m_synchronizedMessageRequestQueue->enqueueRequest(messageReq);
          m_http2Transport->onRequestEnqueued();
      }
  
      // Send the response code for each POST request.
      int postsRequestsCount = 0;
      for (int i = 0; i < messagesCount; i++) {
          auto request = m_mockHttp2Connection->waitForPostRequest(RESPONSE_TIMEOUT);
          if (request) {
              m_mockHttp2Connection->dequePostRequest();
              postsRequestsCount++;
              request->getSink()->onReceiveResponseCode(HTTPResponseCode::SUCCESS_OK);
              request->getSink()->onResponseFinished(HTTP2ResponseFinishedStatus::COMPLETE);
          } else {
              break;
          }
      }
  
      // Check if we got all the POST requests.
      ASSERT_EQ(postsRequestsCount, messagesCount);
  }
  
  }  // namespace test
  }  // namespace transport
  }  // namespace acl
  }  // namespace alexaClientSDK
  