Skip to content

Commit 8198e66

Browse files
diachenko-mischamykhailo-kuchma
authored andcommitted
Add errors and timeouts to mock server
Add emulation of errors and timeouts for mock server. This behavior is controlled by 'debug' header in http requests. Network wrapper for performance test implements interface to set this header. Relates-To: OLPEDGE-1115 Signed-off-by: Diachenko Mykahilo <[email protected]>
1 parent bd0640e commit 8198e66

File tree

4 files changed

+182
-52
lines changed

4 files changed

+182
-52
lines changed

tests/performance/MemoryTest.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,10 @@ class MemoryTest : public ::testing::TestWithParam<TestConfiguration> {
9797
std::shared_ptr<olp::http::Network> MemoryTest::s_network;
9898

9999
void MemoryTest::SetUpTestSuite() {
100-
s_network = std::make_shared<olp::tests::http::Http2HttpNetworkWrapper>();
100+
auto network = std::make_shared<olp::tests::http::Http2HttpNetworkWrapper>();
101+
network->EnableErrors(true);
102+
network->EnableTimeouts(true);
103+
s_network = std::move(network);
101104
}
102105

103106
void MemoryTest::TearDownTestSuite() { s_network.reset(); }

tests/performance/NetworkWrapper.h

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,15 +33,14 @@ using namespace olp::http;
3333
*/
3434
class Http2HttpNetworkWrapper : public Network {
3535
public:
36+
Http2HttpNetworkWrapper()
37+
: network_{std::move(olp::http::CreateDefaultNetwork(32))} {}
38+
3639
SendOutcome Send(NetworkRequest request, Payload payload, Callback callback,
3740
HeaderCallback header_callback = nullptr,
3841
DataCallback data_callback = nullptr) override {
39-
auto url = request.GetUrl();
40-
auto pos = url.find("https");
41-
if (pos != std::string::npos) {
42-
url.replace(pos, 5, "http");
43-
request.WithUrl(url);
44-
}
42+
ReplaceHttps2Http(request);
43+
InsertDebugHeaders(request);
4544

4645
return network_->Send(std::move(request), std::move(payload),
4746
std::move(callback), std::move(header_callback),
@@ -50,10 +49,43 @@ class Http2HttpNetworkWrapper : public Network {
5049

5150
void Cancel(RequestId id) override { network_->Cancel(id); }
5251

53-
Http2HttpNetworkWrapper()
54-
: network_{std::move(olp::http::CreateDefaultNetwork(32))} {}
52+
/*
53+
* Adds special header, which signal mock server to generate timeouts and
54+
* stalls when serving requests.
55+
*/
56+
void EnableTimeouts(bool with_timeouts) { with_timeouts_ = with_timeouts; }
57+
58+
/*
59+
* Adds special header, which signal mock server to generate errors when
60+
* serving requests. Error rate is 10%.
61+
*/
62+
void EnableErrors(bool with_errors) { with_errors_ = with_errors; }
5563

5664
private:
65+
static void ReplaceHttps2Http(NetworkRequest &request) {
66+
auto url = request.GetUrl();
67+
auto pos = url.find("https");
68+
if (pos != std::string::npos) {
69+
url.replace(pos, 5, "http");
70+
request.WithUrl(url);
71+
}
72+
}
73+
74+
/*
75+
* Note: headers with empty values are optimized out.
76+
*/
77+
void InsertDebugHeaders(NetworkRequest &request) {
78+
if (with_errors_) {
79+
request.WithHeader("debug-with-errors", "Ok");
80+
}
81+
82+
if (with_timeouts_) {
83+
request.WithHeader("debug-with-timeouts", "Ok");
84+
}
85+
}
86+
87+
bool with_timeouts_ = false;
88+
bool with_errors_ = false;
5789
std::shared_ptr<Network> network_;
5890
};
5991
} // namespace http
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright (C) 2019 HERE Europe B.V.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* SPDX-License-Identifier: Apache-2.0
17+
* License-Filename: LICENSE
18+
*/
19+
20+
function generateErrorAtRandom() {
21+
const errorList = [
22+
{
23+
status: 403,
24+
text: JSON.stringify(
25+
'{"error":"Forbidden","error_description":"These credentials do not authorize access"}'),
26+
headers: { 'Content-Type': 'application/json' }
27+
},
28+
{
29+
status: 404,
30+
text: JSON.stringify(
31+
'{"error":"Resource not Found","error_description":"Requested resource not found"}'),
32+
headers: { 'Content-Type': 'application/json' }
33+
},
34+
{
35+
status: 401,
36+
text: JSON.stringify(
37+
'{"errorId":"ERROR-3aaaa33b-3fb6-41cc-a238-7ff97c17bca7","httpStatus":401,"errorCode":401202,"message":"Invalid Client Authorization header, expecting signed request format."}'),
38+
headers: { 'Content-Type': 'application/json' }
39+
},
40+
{
41+
status: 404,
42+
text: JSON.stringify(
43+
'{"title":"Not Found","detail":[{"name":"other","error":"API not found blob/v10"}],"status":404}'),
44+
headers: { 'Content-Type': 'application/json' }
45+
},
46+
{
47+
status: 500,
48+
text: JSON.stringify('{"title":"Internal Server Error","status":500}'),
49+
headers: { 'Content-Type': 'application/json' }
50+
}
51+
];
52+
53+
const index = Math.floor(Math.random() * errorList.length);
54+
return errorList[index];
55+
}
56+
57+
exports.handler = generateErrorAtRandom

tests/utils/olp_server/server.js

Lines changed: 81 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,42 @@ const config_service_handler = require('./config_service.js')
2626
const metadata_service_handler = require('./metadata_service.js')
2727
const query_service_handler = require('./query_service.js')
2828
const blob_service_handler = require('./blob_service.js')
29+
const errors_generator = require('./errors_generator.js')
2930

3031
const port = 3000
3132

32-
const sleep = (milliseconds) => {
33-
return new Promise(resolve => setTimeout(resolve, milliseconds))
33+
function sleep(milliseconds) {
34+
var waitTill = new Date(new Date().getTime() + milliseconds);
35+
while (waitTill > new Date()) {
36+
}
3437
}
3538

36-
const handlers = {}
39+
function handleRequest(response, pathname, request, handler) {
40+
const result = handler(pathname, request)
41+
response.writeHead(result.status, result.headers)
42+
response.end(result.text)
43+
}
44+
45+
function errorsDecorator(processor) {
46+
return function (response, pathname, request, handler) {
47+
if (Math.floor(Math.random() * 100) > 10) {
48+
processor(response, pathname, request, handler);
49+
} else {
50+
processor(response, pathname, request, errors_generator.handler);
51+
}
52+
}
53+
}
54+
55+
function timeoutDecorator(processor) {
56+
return function (response, pathname, request, handler) {
57+
milliseconds = Math.floor(Math.random() * 250);
58+
console.log('Waiting for ' + milliseconds + 'ms');
59+
sleep(milliseconds);
60+
processor(response, pathname, request, handler);
61+
}
62+
}
63+
64+
const handlers = {};
3765
handlers[services.lookup] = lookup_service_handler.handler
3866
handlers[services.config] = config_service_handler.handler
3967
handlers[services.metadata] = metadata_service_handler.handler
@@ -42,54 +70,64 @@ handlers[services.blob] = blob_service_handler.handler
4270

4371
const requestHandler = async (request, response) => {
4472

45-
request.on('error', (err) => {
46-
console.error(err);
47-
response.writeHead(400, {}).end();
48-
});
49-
response.on('error', (err) => {
50-
console.error(err);
51-
});
52-
53-
const { headers, method, url } = request;
54-
55-
// Currently we support only read operations
56-
if (method != 'GET') {
57-
response.writeHead(404, {})
58-
response.end('Not Found')
59-
return
60-
}
61-
62-
// For debug purpose
63-
console.log(url)
64-
65-
const { host, query, pathname } = URL.parse(url, true)
66-
67-
const handler = handlers[host]
68-
if (handler) {
69-
const result = handler(pathname, query)
70-
response.writeHead(result.status, result.headers)
71-
response.end(result.text)
72-
return
73-
}
74-
75-
response.writeHead(400, {})
76-
response.end('Not implemented')
73+
request.on('error', (err) => {
74+
console.error(err);
75+
response.writeHead(400, {}).end();
76+
});
77+
response.on('error', (err) => {
78+
console.error(err);
79+
});
80+
81+
const { headers, method, url } = request;
82+
83+
// Currently we support only read operations
84+
if (method != 'GET') {
85+
response.writeHead(404, {})
86+
response.end('Not Found')
87+
return
88+
}
89+
90+
// For debug purpose
91+
console.log(url)
92+
93+
processor = handleRequest
94+
95+
if (headers['debug-with-errors']) {
96+
console.log('This one will be decorated with errors')
97+
processor = errorsDecorator(processor)
98+
}
99+
100+
if (headers['debug-with-timeouts']) {
101+
console.log('This one will be decorated with timeouts')
102+
processor = timeoutDecorator(processor)
103+
}
104+
105+
const { host, query, pathname } = URL.parse(url, true)
106+
107+
const handler = handlers[host]
108+
if (handler) {
109+
processor(response, pathname, query, handler)
110+
return
111+
}
112+
113+
response.writeHead(400, {})
114+
response.end('Not implemented')
77115
}
78116

79117
const server = http.createServer(requestHandler)
80118

81119
server.listen(port, (err) => {
82-
if (err) {
83-
return console.log('something bad happened', err)
84-
}
120+
if (err) {
121+
return console.log('something bad happened', err)
122+
}
85123

86-
console.log(`server is listening on ${port}`)
124+
console.log(`server is listening on ${port}`)
87125
})
88126

89127
// Handle termination by Travis
90128
process.on('SIGTERM', function () {
91-
server.close(function () {
92-
console.log(`Graceful shutdown`)
93-
process.exit(0);
94-
});
129+
server.close(function () {
130+
console.log(`Graceful shutdown`)
131+
process.exit(0);
132+
});
95133
});

0 commit comments

Comments
 (0)