1
0
Fork 0
arangodb/tests/Network/MethodsTest.cpp

235 lines
8.1 KiB
C++

////////////////////////////////////////////////////////////////////////////////
/// @brief test suite for Network/Methods.cpp
///
/// @file
///
/// DISCLAIMER
///
/// Copyright 2019 ArangoDB GmbH, Cologne, Germany
///
/// Licensed under the Apache License, Version 2.0 (the "License");
/// you may not use this file except in compliance with the License.
/// You may obtain a copy of the License at
///
/// http://www.apache.org/licenses/LICENSE-2.0
///
/// Unless required by applicable law or agreed to in writing, software
/// distributed under the License 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.
///
/// Copyright holder is ArangoDB GmbH, Cologne, Germany
///
/// @author Simon Grätzer
////////////////////////////////////////////////////////////////////////////////
#include "gtest/gtest.h"
#include <fuerte/connection.h>
#include <fuerte/requests.h>
#include <velocypack/Parser.h>
#include <velocypack/velocypack-aliases.h>
#include "Mocks/LogLevels.h"
#include "Mocks/Servers.h"
#include "ApplicationFeatures/GreetingsFeaturePhase.h"
#include "Cluster/ClusterFeature.h"
#include "Network/ConnectionPool.h"
#include "Network/Methods.h"
#include "Network/NetworkFeature.h"
#include "RestServer/FileDescriptorsFeature.h"
#include "Scheduler/Scheduler.h"
#include "Scheduler/SchedulerFeature.h"
using namespace arangodb;
struct DummyConnection final : public fuerte::Connection {
DummyConnection(fuerte::detail::ConnectionConfiguration const& conf) : fuerte::Connection(conf) {}
fuerte::MessageID sendRequest(std::unique_ptr<fuerte::Request> r,
fuerte::RequestCallback cb) override {
_sendRequestNum++;
cb(_err, std::move(r), std::move(_response));
return 0;
}
std::size_t requestsLeft() const override {
return 1;
}
State state() const override {
return _state;
}
void cancel() override {}
void startConnection() override {}
fuerte::Connection::State _state = fuerte::Connection::State::Connected;
fuerte::Error _err = fuerte::Error::NoError;
std::unique_ptr<fuerte::Response> _response;
int _sendRequestNum = 0;
};
struct DummyPool : public network::ConnectionPool {
DummyPool(network::ConnectionPool::Config const& c)
: network::ConnectionPool(c),
_conn(std::make_shared<DummyConnection>(fuerte::detail::ConnectionConfiguration())) {
}
std::shared_ptr<fuerte::Connection> createConnection(fuerte::ConnectionBuilder&) override {
return _conn;
}
std::shared_ptr<DummyConnection> _conn;
};
struct NetworkMethodsTest
: public ::testing::Test,
public arangodb::tests::LogSuppressor<arangodb::Logger::THREADS, arangodb::LogLevel::FATAL> {
NetworkMethodsTest() : server(false) {
server.addFeature<SchedulerFeature>(true);
server.startFeatures();
pool = std::make_unique<DummyPool>(config());
}
private:
network::ConnectionPool::Config config() {
network::ConnectionPool::Config config;
config.clusterInfo = &server.getFeature<ClusterFeature>().clusterInfo();
config.numIOThreads = 1;
config.minOpenConnections = 1;
config.maxOpenConnections = 3;
config.verifyHosts = false;
return config;
}
protected:
tests::mocks::MockCoordinator server;
std::unique_ptr<DummyPool> pool;
};
TEST_F(NetworkMethodsTest, simple_request) {
pool->_conn->_err = fuerte::Error::NoError;
network::RequestOptions reqOpts;
reqOpts.timeout = network::Timeout(60.0);
fuerte::ResponseHeader header;
header.responseCode = fuerte::StatusAccepted;
header.contentType(fuerte::ContentType::VPack);
pool->_conn->_response = std::make_unique<fuerte::Response>(std::move(header));
std::shared_ptr<VPackBuilder> b = VPackParser::fromJson("{\"error\":false}");
auto resBuffer = b->steal();
pool->_conn->_response->setPayload(std::move(*resBuffer), 0);
VPackBuffer<uint8_t> buffer;
auto f = network::sendRequest(pool.get(), "tcp://example.org:80", fuerte::RestVerb::Get,
"/", buffer, reqOpts);
network::Response res = std::move(f).get();
ASSERT_EQ(res.destination, "tcp://example.org:80");
ASSERT_EQ(res.error, fuerte::Error::NoError);
ASSERT_NE(res.response, nullptr);
ASSERT_EQ(res.response->statusCode(), fuerte::StatusAccepted);
}
TEST_F(NetworkMethodsTest, request_failure) {
pool->_conn->_err = fuerte::Error::ConnectionClosed;
network::RequestOptions reqOpts;
reqOpts.timeout = network::Timeout(60.0);
VPackBuffer<uint8_t> buffer;
auto f = network::sendRequest(pool.get(), "tcp://example.org:80", fuerte::RestVerb::Get,
"/", buffer, reqOpts);
network::Response res = std::move(f).get();
ASSERT_EQ(res.destination, "tcp://example.org:80");
ASSERT_EQ(res.error, fuerte::Error::ConnectionClosed);
ASSERT_EQ(res.response, nullptr);
}
TEST_F(NetworkMethodsTest, request_with_retry_after_error) {
// Step 1: Provoke a connection error
pool->_conn->_err = fuerte::Error::CouldNotConnect;
network::RequestOptions reqOpts;
reqOpts.timeout = network::Timeout(5.0);
VPackBuffer<uint8_t> buffer;
auto f = network::sendRequestRetry(pool.get(), "tcp://example.org:80",
fuerte::RestVerb::Get, "/", buffer,
reqOpts);
// the default behaviour should be to retry after 200 ms
std::this_thread::sleep_for(std::chrono::milliseconds(5));
ASSERT_FALSE(f.isReady());
ASSERT_EQ(pool->_conn->_sendRequestNum, 1);
// Step 2: Now respond with no error
pool->_conn->_err = fuerte::Error::NoError;
fuerte::ResponseHeader header;
header.contentType(fuerte::ContentType::VPack);
header.responseCode = fuerte::StatusAccepted;
pool->_conn->_response = std::make_unique<fuerte::Response>(std::move(header));
std::shared_ptr<VPackBuilder> b = VPackParser::fromJson("{\"error\":false}");
auto resBuffer = b->steal();
pool->_conn->_response->setPayload(std::move(*resBuffer), 0);
auto status = f.wait_for(std::chrono::milliseconds(350));
ASSERT_EQ(futures::FutureStatus::Ready, status);
network::Response res = std::move(f).get();
ASSERT_EQ(res.destination, "tcp://example.org:80");
ASSERT_EQ(res.error, fuerte::Error::NoError);
ASSERT_NE(res.response, nullptr);
ASSERT_EQ(res.response->statusCode(), fuerte::StatusAccepted);
}
TEST_F(NetworkMethodsTest, request_with_retry_after_not_found_error) {
// Step 1: Provoke a data source not found error
pool->_conn->_err = fuerte::Error::NoError;
fuerte::ResponseHeader header;
header.contentType(fuerte::ContentType::VPack);
header.responseCode = fuerte::StatusNotFound;
pool->_conn->_response = std::make_unique<fuerte::Response>(std::move(header));
std::shared_ptr<VPackBuilder> b = VPackParser::fromJson("{\"errorNum\":1203}");
auto resBuffer = b->steal();
pool->_conn->_response->setPayload(std::move(*resBuffer), 0);
network::RequestOptions reqOpts;
reqOpts.timeout = network::Timeout(60.0);
reqOpts.retryNotFound = true;
VPackBuffer<uint8_t> buffer;
auto f = network::sendRequestRetry(pool.get(), "tcp://example.org:80",
fuerte::RestVerb::Get, "/", buffer,
reqOpts);
// the default behaviour should be to retry after 200 ms
std::this_thread::sleep_for(std::chrono::milliseconds(5));
ASSERT_FALSE(f.isReady());
// Step 2: Now respond with no error
pool->_conn->_err = fuerte::Error::NoError;
header.responseCode = fuerte::StatusAccepted;
header.contentType(fuerte::ContentType::VPack);
pool->_conn->_response = std::make_unique<fuerte::Response>(std::move(header));
b = VPackParser::fromJson("{\"error\":false}");
resBuffer = b->steal();
pool->_conn->_response->setPayload(std::move(*resBuffer), 0);
auto status = f.wait_for(std::chrono::milliseconds(350));
ASSERT_EQ(futures::FutureStatus::Ready, status);
network::Response res = std::move(f).get();
ASSERT_EQ(res.destination, "tcp://example.org:80");
ASSERT_EQ(res.error, fuerte::Error::NoError);
ASSERT_NE(res.response, nullptr);
ASSERT_EQ(res.response->statusCode(), fuerte::StatusAccepted);
}