1 /**
2  * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3  * SPDX-License-Identifier: Apache-2.0.
4  */
5 
6 #include <aws/external/gtest.h>
7 #include <aws/testing/mocks/aws/client/MockAWSClient.h>
8 #include <aws/core/monitoring/CoreMetrics.h>
9 #include <aws/core/monitoring/MonitoringInterface.h>
10 #include <aws/core/monitoring/MonitoringFactory.h>
11 #include <aws/core/monitoring/MonitoringManager.h>
12 #include <aws/core/monitoring/DefaultMonitoring.h>
13 
14 using namespace Aws::Client;
15 using namespace Aws::Http;
16 using namespace Aws::Http::Standard;
17 using namespace Aws::Monitoring;
18 
19 static const char ALLOCATION_TAG[] = "MonitoringTest";
20 static const char URI_STRING[] = "http://domain.com/something";
21 
22 static int MonitorOneAPICalledFlag = 0x0;
23 static int MonitorTwoAPICalledFlag = 0x0;
24 
25 static const int MonitorAPIsNum = 5;
26 static std::vector<int> MonitorOneAPICalledCounter(MonitorAPIsNum);
27 static std::vector<int> MonitorTwoAPICalledCounter(MonitorAPIsNum);
28 
29 class MockMonitoringOne : public MonitoringInterface
30 {
31 public:
OnRequestStarted(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request) const32     void* OnRequestStarted(const Aws::String& serviceName, const Aws::String& requestName, const std::shared_ptr<const Aws::Http::HttpRequest>& request) const override
33     {
34         EXPECT_STREQ(URI_STRING, request->GetURIString().c_str());
35         EXPECT_STREQ("MockAWSClient", serviceName.c_str());
36         EXPECT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
37         MonitorOneAPICalledFlag |= 0x1;
38         MonitorOneAPICalledCounter[0] ++;
39         return reinterpret_cast<void*>(1);
40     }
41 
42 
OnRequestSucceeded(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request,const Aws::Client::HttpResponseOutcome & outcome,const CoreMetricsCollection & metricsFromCore,void * context) const43     void OnRequestSucceeded(const Aws::String& serviceName, const Aws::String& requestName, const std::shared_ptr<const Aws::Http::HttpRequest>& request,
44         const Aws::Client::HttpResponseOutcome& outcome, const CoreMetricsCollection& metricsFromCore, void* context) const override
45     {
46         ASSERT_TRUE(outcome.IsSuccess());
47         ASSERT_STREQ(URI_STRING, request->GetURIString().c_str());
48         ASSERT_STREQ(URI_STRING, outcome.GetResult()->GetOriginatingRequest().GetURIString().c_str());
49         ASSERT_TRUE(metricsFromCore.httpClientMetrics.size() == 0);
50         ASSERT_STREQ("MockAWSClient", serviceName.c_str());
51         ASSERT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
52         ASSERT_EQ(1u, reinterpret_cast<size_t>(context));
53         MonitorOneAPICalledFlag |= 0x2;
54         MonitorOneAPICalledCounter[1] ++;
55     }
56 
OnRequestFailed(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request,const Aws::Client::HttpResponseOutcome & outcome,const CoreMetricsCollection & metricsFromCore,void * context) const57     void OnRequestFailed(const Aws::String& serviceName, const Aws::String& requestName, const std::shared_ptr<const Aws::Http::HttpRequest>& request,
58         const Aws::Client::HttpResponseOutcome& outcome, const CoreMetricsCollection& metricsFromCore, void* context) const override
59     {
60         ASSERT_FALSE(outcome.IsSuccess());
61         ASSERT_STREQ(URI_STRING, request->GetURIString().c_str());
62         ASSERT_TRUE(metricsFromCore.httpClientMetrics.size() == 0);
63         ASSERT_STREQ("MockAWSClient", serviceName.c_str());
64         ASSERT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
65         ASSERT_EQ(1u, reinterpret_cast<size_t>(context));
66         MonitorOneAPICalledFlag |= 0x4;
67         MonitorOneAPICalledCounter[2] ++;
68     }
69 
OnRequestRetry(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request,void * context) const70     void OnRequestRetry(const Aws::String& serviceName, const Aws::String& requestName,
71         const std::shared_ptr<const Aws::Http::HttpRequest>& request, void* context) const override
72     {
73         ASSERT_STREQ("MockAWSClient", serviceName.c_str());
74         ASSERT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
75         ASSERT_STREQ(URI_STRING, request->GetURIString().c_str());
76         ASSERT_EQ(1u, reinterpret_cast<size_t>(context));
77         MonitorOneAPICalledFlag |= 0x8;
78         MonitorOneAPICalledCounter[3] ++;
79     }
80 
OnFinish(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request,void * context) const81     void OnFinish(const Aws::String& serviceName, const Aws::String& requestName,
82         const std::shared_ptr<const Aws::Http::HttpRequest>& request, void* context) const override
83     {
84         ASSERT_STREQ("MockAWSClient", serviceName.c_str());
85         ASSERT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
86         ASSERT_STREQ(URI_STRING, request->GetURIString().c_str());
87         ASSERT_EQ(1u, reinterpret_cast<size_t>(context));
88         MonitorOneAPICalledFlag |= 0x10;
89         MonitorOneAPICalledCounter[4] ++;
90     }
91 };
92 
93 class MockMonitoringFactoryOne : public MonitoringFactory
94 {
95 public:
CreateMonitoringInstance() const96     Aws::UniquePtr<MonitoringInterface> CreateMonitoringInstance() const override
97     {
98         return Aws::MakeUnique<MockMonitoringOne>(ALLOCATION_TAG);
99     }
100 };
101 
CreateMonitoringFactoryOne()102 Aws::UniquePtr<MonitoringFactory> CreateMonitoringFactoryOne()
103 {
104     return Aws::MakeUnique<MockMonitoringFactoryOne>(ALLOCATION_TAG);
105 }
106 
107 class MockMonitoringTwo : public MonitoringInterface
108 {
109 public:
OnRequestStarted(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request) const110     void* OnRequestStarted(const Aws::String& serviceName, const Aws::String& requestName, const std::shared_ptr<const Aws::Http::HttpRequest>& request) const override
111     {
112         EXPECT_STREQ("MockAWSClient", serviceName.c_str());
113         EXPECT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
114         EXPECT_STREQ(URI_STRING, request->GetURIString().c_str());
115         MonitorTwoAPICalledFlag |= 0x1;
116         MonitorTwoAPICalledCounter[0] ++;
117         return reinterpret_cast<void*>(2);
118     }
119 
120 
OnRequestSucceeded(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request,const Aws::Client::HttpResponseOutcome & outcome,const CoreMetricsCollection & metricsFromCore,void * context) const121     void OnRequestSucceeded(const Aws::String& serviceName, const Aws::String& requestName, const std::shared_ptr<const Aws::Http::HttpRequest>& request,
122         const Aws::Client::HttpResponseOutcome& outcome, const CoreMetricsCollection& metricsFromCore, void* context) const override
123     {
124         ASSERT_TRUE(outcome.IsSuccess());
125         ASSERT_STREQ(URI_STRING, request->GetURIString().c_str());
126         ASSERT_STREQ(URI_STRING, outcome.GetResult()->GetOriginatingRequest().GetURIString().c_str());
127         ASSERT_TRUE(metricsFromCore.httpClientMetrics.size() == 0);
128         ASSERT_STREQ("MockAWSClient", serviceName.c_str());
129         ASSERT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
130         ASSERT_EQ(2u, reinterpret_cast<size_t>(context));
131         MonitorTwoAPICalledCounter[1] ++;
132         MonitorTwoAPICalledFlag |= 0x2;
133     }
134 
OnRequestFailed(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request,const Aws::Client::HttpResponseOutcome & outcome,const CoreMetricsCollection & metricsFromCore,void * context) const135     void OnRequestFailed(const Aws::String& serviceName, const Aws::String& requestName, const std::shared_ptr<const Aws::Http::HttpRequest>& request,
136         const Aws::Client::HttpResponseOutcome& outcome, const CoreMetricsCollection& metricsFromCore, void* context) const override
137     {
138         ASSERT_FALSE(outcome.IsSuccess());
139         ASSERT_TRUE(metricsFromCore.httpClientMetrics.size() == 0);
140         ASSERT_STREQ("MockAWSClient", serviceName.c_str());
141         ASSERT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
142         ASSERT_STREQ(URI_STRING, request->GetURIString().c_str());
143         ASSERT_EQ(2u, reinterpret_cast<size_t>(context));
144         MonitorTwoAPICalledFlag |= 0x4;
145         MonitorTwoAPICalledCounter[2] ++;
146     }
147 
OnRequestRetry(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request,void * context) const148     void OnRequestRetry(const Aws::String& serviceName, const Aws::String& requestName,
149         const std::shared_ptr<const Aws::Http::HttpRequest>& request, void* context) const override
150     {
151         ASSERT_STREQ("MockAWSClient", serviceName.c_str());
152         ASSERT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
153         ASSERT_STREQ(URI_STRING, request->GetURIString().c_str());
154         ASSERT_EQ(2u, reinterpret_cast<size_t>(context));
155         MonitorTwoAPICalledFlag |= 0x8;
156         MonitorTwoAPICalledCounter[3] ++;
157     }
158 
OnFinish(const Aws::String & serviceName,const Aws::String & requestName,const std::shared_ptr<const Aws::Http::HttpRequest> & request,void * context) const159     void OnFinish(const Aws::String& serviceName, const Aws::String& requestName,
160         const std::shared_ptr<const Aws::Http::HttpRequest>& request, void* context) const override
161     {
162         ASSERT_STREQ("MockAWSClient", serviceName.c_str());
163         ASSERT_STREQ("AmazonWebServiceRequestMock", requestName.c_str());
164         ASSERT_STREQ(URI_STRING, request->GetURIString().c_str());
165         ASSERT_EQ(2u, reinterpret_cast<size_t>(context));
166         MonitorTwoAPICalledFlag |= 0x10;
167         MonitorTwoAPICalledCounter[4] ++;
168     }
169 };
170 
171 class MockMonitoringFactoryTwo : public MonitoringFactory
172 {
173 public:
CreateMonitoringInstance() const174     Aws::UniquePtr<MonitoringInterface> CreateMonitoringInstance() const override
175     {
176         return Aws::MakeUnique<MockMonitoringTwo>(ALLOCATION_TAG);
177     }
178 };
179 
CreateMonitoringFactoryTwo()180 Aws::UniquePtr<MonitoringFactory> CreateMonitoringFactoryTwo()
181 {
182     return Aws::MakeUnique<MockMonitoringFactoryTwo>(ALLOCATION_TAG);
183 }
184 
185 class MonitoringTestSuite : public ::testing::Test
186 {
187 protected:
188     std::shared_ptr<MockHttpClient> mockHttpClient;
189     std::shared_ptr<MockHttpClientFactory> mockHttpClientFactory;
190     Aws::UniquePtr<MockAWSClient> client;
191 
SetUp()192     void SetUp()
193     {
194         ClientConfiguration config;
195         config.scheme = Scheme::HTTP;
196         config.connectTimeoutMs = 30000;
197         config.requestTimeoutMs = 30000;
198         auto countedRetryStrategy = Aws::MakeShared<CountedRetryStrategy>(ALLOCATION_TAG);
199         config.retryStrategy = std::static_pointer_cast<DefaultRetryStrategy>(countedRetryStrategy);
200 
201         mockHttpClient = Aws::MakeShared<MockHttpClient>(ALLOCATION_TAG);
202         mockHttpClientFactory = Aws::MakeShared<MockHttpClientFactory>(ALLOCATION_TAG);
203         mockHttpClientFactory->SetClient(mockHttpClient);
204         SetHttpClientFactory(mockHttpClientFactory);
205         client = Aws::MakeUnique<MockAWSClient>(ALLOCATION_TAG, config);
206 
207         Aws::Monitoring::CleanupMonitoring();
208         std::vector<MonitoringFactoryCreateFunction> factoryFunctions;
209         factoryFunctions.emplace_back(CreateMonitoringFactoryOne);
210         factoryFunctions.emplace_back(CreateMonitoringFactoryTwo);
211         Aws::Monitoring::InitMonitoring(factoryFunctions);
212 
213         MonitorOneAPICalledFlag = 0;
214         MonitorTwoAPICalledFlag = 0;
215         std::fill(MonitorOneAPICalledCounter.begin(), MonitorOneAPICalledCounter.end(), 0);
216         std::fill(MonitorTwoAPICalledCounter.begin(), MonitorTwoAPICalledCounter.end(), 0);
217     }
218 
TearDown()219     void TearDown()
220     {
221         client = nullptr;
222         mockHttpClient = nullptr;
223         mockHttpClientFactory = nullptr;
224 
225         CleanupMonitoring();
226         CleanupHttp();
227         InitHttp();
228         InitMonitoring(std::vector<Aws::Monitoring::MonitoringFactoryCreateFunction>());
229     }
230 
QueueMockResponse(HttpResponseCode code,const HeaderValueCollection & headers)231     void QueueMockResponse(HttpResponseCode code, const HeaderValueCollection& headers)
232     {
233         auto httpRequest = CreateHttpRequest(URI(URI_STRING),
234                 HttpMethod::HTTP_GET, Aws::Utils::Stream::DefaultResponseStreamFactoryMethod);
235         auto httpResponse = Aws::MakeShared<StandardHttpResponse>(ALLOCATION_TAG, httpRequest);
236         httpResponse->SetResponseCode(code);
237         httpResponse->GetResponseBody() << "";
238         for(auto&& header : headers)
239         {
240             httpResponse->AddHeader(header.first, header.second);
241         }
242         mockHttpClient->AddResponseToReturn(httpResponse);
243     }
244 };
245 
TEST_F(MonitoringTestSuite,TestMonitoringListenersAreCalledCorrectlyWithRetryAndSucceededRequest)246 TEST_F(MonitoringTestSuite, TestMonitoringListenersAreCalledCorrectlyWithRetryAndSucceededRequest)
247 {
248     HeaderValueCollection responseHeaders, requestHeaders;
249     responseHeaders.emplace("Date", (Aws::Utils::DateTime::Now() + std::chrono::hours(1)).ToGmtString(Aws::Utils::DateFormat::RFC822)); // server is ahead of us by 1 hour
250     AmazonWebServiceRequestMock request;
251     requestHeaders.emplace("X-Amz-Date", Aws::Utils::DateTime::Now().ToGmtString(Aws::Utils::DateFormat::ISO_8601));
252     request.SetHeaders(requestHeaders);
253     // BAD_REQUEST is not retryable, but since this is triggered by clock skew, it's set to mandatory retryable.
254     QueueMockResponse(HttpResponseCode::BAD_REQUEST, responseHeaders);
255     QueueMockResponse(HttpResponseCode::OK, responseHeaders);
256     auto outcome = client->MakeRequest(request);
257     ASSERT_TRUE(outcome.IsSuccess());
258     ASSERT_EQ(1, client->GetRequestAttemptedRetries());
259     ASSERT_EQ(0x1F, MonitorOneAPICalledFlag);
260     ASSERT_EQ(0x1F, MonitorTwoAPICalledFlag);
261     ASSERT_EQ(1, MonitorOneAPICalledCounter[0]); // started 1 time
262     ASSERT_EQ(1, MonitorOneAPICalledCounter[1]); // succeeded 1 time
263     ASSERT_EQ(1, MonitorOneAPICalledCounter[2]); // failed 1 times
264     ASSERT_EQ(1, MonitorOneAPICalledCounter[3]); // retried 1 times
265     ASSERT_EQ(1, MonitorOneAPICalledCounter[4]); // finished 1 time
266 }
267 
TEST_F(MonitoringTestSuite,TestMonitoringListenersAreCalledCorrectlyWithRetryAndFailedRequest)268 TEST_F(MonitoringTestSuite, TestMonitoringListenersAreCalledCorrectlyWithRetryAndFailedRequest)
269 {
270     HeaderValueCollection responseHeaders, requestHeaders;
271     responseHeaders.emplace("Date", (Aws::Utils::DateTime::Now() + std::chrono::hours(1)).ToGmtString(Aws::Utils::DateFormat::RFC822)); // server is ahead of us by 1 hour
272     AmazonWebServiceRequestMock request;
273     requestHeaders.emplace("X-Amz-Date", Aws::Utils::DateTime::Now().ToGmtString(Aws::Utils::DateFormat::ISO_8601));
274     request.SetHeaders(requestHeaders);
275     QueueMockResponse(HttpResponseCode::BAD_REQUEST, responseHeaders);
276     QueueMockResponse(HttpResponseCode::BAD_REQUEST, responseHeaders);
277     auto outcome = client->MakeRequest(request);
278     ASSERT_FALSE(outcome.IsSuccess());
279     ASSERT_EQ(1, client->GetRequestAttemptedRetries());
280     ASSERT_EQ(0x1D, MonitorOneAPICalledFlag);
281     ASSERT_EQ(0x1D, MonitorTwoAPICalledFlag);
282     ASSERT_EQ(1, MonitorOneAPICalledCounter[0]); // started 1 time
283     ASSERT_EQ(0, MonitorOneAPICalledCounter[1]); // succeeded 0 time
284     ASSERT_EQ(2, MonitorOneAPICalledCounter[2]); // failed 2 times
285     ASSERT_EQ(1, MonitorOneAPICalledCounter[3]); // retried 1 time
286     ASSERT_EQ(1, MonitorOneAPICalledCounter[4]); // finished 1 time
287 }
288 
TEST_F(MonitoringTestSuite,TestMonitoringListenersAreCalledCorrectlyWithoutRetryAndSucceededRequest)289 TEST_F(MonitoringTestSuite, TestMonitoringListenersAreCalledCorrectlyWithoutRetryAndSucceededRequest)
290 {
291     HeaderValueCollection responseHeaders, requestHeaders;
292     responseHeaders.emplace("Date", (Aws::Utils::DateTime::Now() + std::chrono::hours(1)).ToGmtString(Aws::Utils::DateFormat::RFC822)); // server is ahead of us by 1 hour
293     AmazonWebServiceRequestMock request;
294     requestHeaders.emplace("X-Amz-Date", Aws::Utils::DateTime::Now().ToGmtString(Aws::Utils::DateFormat::ISO_8601));
295     request.SetHeaders(requestHeaders);
296     QueueMockResponse(HttpResponseCode::OK, responseHeaders);
297     auto outcome = client->MakeRequest(request);
298     ASSERT_TRUE(outcome.IsSuccess());
299     ASSERT_EQ(0, client->GetRequestAttemptedRetries());
300     ASSERT_EQ(0x13, MonitorOneAPICalledFlag);
301     ASSERT_EQ(0x13, MonitorTwoAPICalledFlag);
302     ASSERT_EQ(1, MonitorOneAPICalledCounter[0]); // started 1 time
303     ASSERT_EQ(1, MonitorOneAPICalledCounter[1]); // succeeded 1 time
304     ASSERT_EQ(0, MonitorOneAPICalledCounter[2]); // failed 0 time
305     ASSERT_EQ(0, MonitorOneAPICalledCounter[3]); // retried 0 time
306     ASSERT_EQ(1, MonitorOneAPICalledCounter[4]); // finished 1 time
307 }
308 
TEST_F(MonitoringTestSuite,TestHttpClientMetrics)309 TEST_F(MonitoringTestSuite, TestHttpClientMetrics)
310 {
311     ASSERT_EQ(HttpClientMetricsType::DestinationIp, GetHttpClientMetricTypeByName("DestinationIp"));
312     ASSERT_EQ(HttpClientMetricsType::AcquireConnectionLatency, GetHttpClientMetricTypeByName("AcquireConnectionLatency"));
313     ASSERT_EQ(HttpClientMetricsType::ConnectionReused, GetHttpClientMetricTypeByName("ConnectionReused"));
314     ASSERT_EQ(HttpClientMetricsType::ConnectLatency, GetHttpClientMetricTypeByName("ConnectLatency"));
315     ASSERT_EQ(HttpClientMetricsType::RequestLatency, GetHttpClientMetricTypeByName("RequestLatency"));
316     ASSERT_EQ(HttpClientMetricsType::DnsLatency, GetHttpClientMetricTypeByName("DnsLatency"));
317     ASSERT_EQ(HttpClientMetricsType::TcpLatency, GetHttpClientMetricTypeByName("TcpLatency"));
318     ASSERT_EQ(HttpClientMetricsType::SslLatency, GetHttpClientMetricTypeByName("SslLatency"));
319     ASSERT_EQ(HttpClientMetricsType::Unknown, GetHttpClientMetricTypeByName("Unknown"));
320     ASSERT_EQ(HttpClientMetricsType::Unknown, GetHttpClientMetricTypeByName("RandomMetricsUnknown"));
321 
322     ASSERT_STREQ("DestinationIp", GetHttpClientMetricNameByType(HttpClientMetricsType::DestinationIp).c_str());
323     ASSERT_STREQ("AcquireConnectionLatency", GetHttpClientMetricNameByType(HttpClientMetricsType::AcquireConnectionLatency).c_str());
324     ASSERT_STREQ("ConnectionReused", GetHttpClientMetricNameByType(HttpClientMetricsType::ConnectionReused).c_str());
325     ASSERT_STREQ("ConnectLatency", GetHttpClientMetricNameByType(HttpClientMetricsType::ConnectLatency).c_str());
326     ASSERT_STREQ("RequestLatency", GetHttpClientMetricNameByType(HttpClientMetricsType::RequestLatency).c_str());
327     ASSERT_STREQ("DnsLatency", GetHttpClientMetricNameByType(HttpClientMetricsType::DnsLatency).c_str());
328     ASSERT_STREQ("TcpLatency", GetHttpClientMetricNameByType(HttpClientMetricsType::TcpLatency).c_str());
329     ASSERT_STREQ("SslLatency", GetHttpClientMetricNameByType(HttpClientMetricsType::SslLatency).c_str());
330     ASSERT_STREQ("Unknown", GetHttpClientMetricNameByType(HttpClientMetricsType::Unknown).c_str());
331 }
332