feat: More verbose forwarding errors (#1523)

Fixes #1516.
This commit is contained in:
Sergey Kuznetsov
2024-07-09 15:22:01 +01:00
committed by GitHub
parent 094ed8f299
commit 46bd67a9ec
19 changed files with 176 additions and 53 deletions

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include "etl/ETLState.hpp"
#include "rpc/Errors.hpp"
#include "util/LoggerFixtures.hpp"
#include "util/MockSource.hpp"
@@ -37,7 +38,7 @@ struct ETLStateTest : public NoLoggerFixture {
TEST_F(ETLStateTest, Error)
{
EXPECT_CALL(source, forwardToRippled).WillOnce(Return(std::nullopt));
EXPECT_CALL(source, forwardToRippled).WillOnce(Return(std::unexpected{rpc::ClioError::etlINVALID_RESPONSE}));
auto const state = etl::ETLState::fetchETLStateFromSource(source);
EXPECT_FALSE(state);
}

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include "etl/impl/ForwardingSource.hpp"
#include "rpc/Errors.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/TestWsServer.hpp"
@@ -50,7 +51,8 @@ TEST_F(ForwardingSourceTests, ConnectionFailed)
{
runSpawn([&](boost::asio::yield_context yield) {
auto result = forwardingSource.forwardToRippled({}, {}, {}, yield);
EXPECT_FALSE(result);
ASSERT_FALSE(result);
EXPECT_EQ(result.error(), rpc::ClioError::etlCONNECTION_ERROR);
});
}
@@ -90,7 +92,8 @@ TEST_F(ForwardingSourceOperationsTests, XUserHeader)
runSpawn([&](boost::asio::yield_context yield) {
auto result =
forwardingSource.forwardToRippled(boost::json::parse(message_).as_object(), {}, xUserValue, yield);
EXPECT_FALSE(result);
ASSERT_FALSE(result);
EXPECT_EQ(result.error(), rpc::ClioError::etlREQUEST_ERROR);
});
}
@@ -103,7 +106,8 @@ TEST_F(ForwardingSourceOperationsTests, ReadFailed)
runSpawn([&](boost::asio::yield_context yield) {
auto result = forwardingSource.forwardToRippled(boost::json::parse(message_).as_object(), {}, {}, yield);
EXPECT_FALSE(result);
ASSERT_FALSE(result);
EXPECT_EQ(result.error(), rpc::ClioError::etlREQUEST_ERROR);
});
}
@@ -116,7 +120,8 @@ TEST_F(ForwardingSourceOperationsTests, ReadTimeout)
runSpawn([&](boost::asio::yield_context yield) {
auto result = forwardingSource.forwardToRippled(boost::json::parse(message_).as_object(), {}, {}, yield);
EXPECT_FALSE(result);
ASSERT_FALSE(result);
EXPECT_EQ(result.error(), rpc::ClioError::etlREQUEST_TIMEOUT);
});
}
@@ -137,7 +142,8 @@ TEST_F(ForwardingSourceOperationsTests, ParseFailed)
runSpawn([&](boost::asio::yield_context yield) {
auto result = forwardingSource.forwardToRippled(boost::json::parse(message_).as_object(), {}, {}, yield);
EXPECT_FALSE(result);
ASSERT_FALSE(result);
EXPECT_EQ(result.error(), rpc::ClioError::etlINVALID_RESPONSE);
});
}
@@ -159,7 +165,8 @@ TEST_F(ForwardingSourceOperationsTests, GotNotAnObject)
runSpawn([&](boost::asio::yield_context yield) {
auto result = forwardingSource.forwardToRippled(boost::json::parse(message_).as_object(), {}, {}, yield);
EXPECT_FALSE(result);
ASSERT_FALSE(result);
EXPECT_EQ(result.error(), rpc::ClioError::etlINVALID_RESPONSE);
});
}

View File

@@ -19,12 +19,14 @@
#include "etl/LoadBalancer.hpp"
#include "etl/Source.hpp"
#include "rpc/Errors.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/MockBackendTestFixture.hpp"
#include "util/MockNetworkValidatedLedgers.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockSource.hpp"
#include "util/MockSubscriptionManager.hpp"
#include "util/NameGenerator.hpp"
#include "util/Random.hpp"
#include "util/config/Config.hpp"
@@ -41,10 +43,12 @@
#include <chrono>
#include <cstdint>
#include <expected>
#include <memory>
#include <optional>
#include <stdexcept>
#include <string>
#include <string_view>
#include <utility>
#include <vector>
@@ -111,8 +115,10 @@ TEST_F(LoadBalancerConstructorTests, forwardingTimeoutPassedToSourceFactory)
TEST_F(LoadBalancerConstructorTests, fetchETLState_AllSourcesFail)
{
EXPECT_CALL(sourceFactory_, makeSource).Times(2);
EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(std::nullopt));
EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled).WillOnce(Return(std::nullopt));
EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled)
.WillOnce(Return(std::unexpected{rpc::ClioError::etlCONNECTION_ERROR}));
EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled)
.WillOnce(Return(std::unexpected{rpc::ClioError::etlCONNECTION_ERROR}));
EXPECT_THROW({ makeLoadBalancer(); }, std::logic_error);
}
@@ -130,7 +136,8 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_Source1Fails0OK)
{
EXPECT_CALL(sourceFactory_, makeSource).Times(2);
EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(boost::json::object{}));
EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled).WillOnce(Return(std::nullopt));
EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled)
.WillOnce(Return(std::unexpected{rpc::ClioError::etlCONNECTION_ERROR}));
EXPECT_CALL(sourceFactory_.sourceAt(0), run);
EXPECT_CALL(sourceFactory_.sourceAt(1), run);
makeLoadBalancer();
@@ -139,7 +146,8 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_Source1Fails0OK)
TEST_F(LoadBalancerConstructorTests, fetchETLState_Source0Fails1OK)
{
EXPECT_CALL(sourceFactory_, makeSource).Times(2);
EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(std::nullopt));
EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled)
.WillOnce(Return(std::unexpected{rpc::ClioError::etlCONNECTION_ERROR}));
EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled).WillOnce(Return(boost::json::object{}));
EXPECT_CALL(sourceFactory_.sourceAt(0), run);
EXPECT_CALL(sourceFactory_.sourceAt(1), run);
@@ -162,7 +170,8 @@ TEST_F(LoadBalancerConstructorTests, fetchETLState_AllSourcesFailButAllowNoEtlIs
EXPECT_CALL(sourceFactory_, makeSource).Times(2);
EXPECT_CALL(sourceFactory_.sourceAt(0), forwardToRippled).WillOnce(Return(boost::json::object{}));
EXPECT_CALL(sourceFactory_.sourceAt(0), run);
EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled).WillOnce(Return(std::nullopt));
EXPECT_CALL(sourceFactory_.sourceAt(1), forwardToRippled)
.WillOnce(Return(std::unexpected{rpc::ClioError::etlCONNECTION_ERROR}));
EXPECT_CALL(sourceFactory_.sourceAt(1), run);
configJson_.as_object()["allow_no_etl"] = true;
@@ -566,7 +575,7 @@ TEST_F(LoadBalancerForwardToRippledTests, source0Fails)
sourceFactory_.sourceAt(0),
forwardToRippled(request_, clientIP_, LoadBalancer::USER_FORWARDING_X_USER_VALUE, testing::_)
)
.WillOnce(Return(std::nullopt));
.WillOnce(Return(std::unexpected{rpc::ClioError::etlCONNECTION_ERROR}));
EXPECT_CALL(
sourceFactory_.sourceAt(1),
forwardToRippled(request_, clientIP_, LoadBalancer::USER_FORWARDING_X_USER_VALUE, testing::_)
@@ -578,7 +587,56 @@ TEST_F(LoadBalancerForwardToRippledTests, source0Fails)
});
}
TEST_F(LoadBalancerForwardToRippledTests, bothSourcesFail)
struct LoadBalancerForwardToRippledErrorTestBundle {
std::string testName;
rpc::ClioError firstSourceError;
rpc::ClioError secondSourceError;
rpc::ClioError responseExpectedError;
};
struct LoadBalancerForwardToRippledErrorTests
: LoadBalancerForwardToRippledTests,
testing::WithParamInterface<LoadBalancerForwardToRippledErrorTestBundle> {};
INSTANTIATE_TEST_SUITE_P(
LoadBalancerForwardToRippledErrorTests,
LoadBalancerForwardToRippledErrorTests,
testing::Values(
LoadBalancerForwardToRippledErrorTestBundle{
"ConnectionError_RequestError",
rpc::ClioError::etlCONNECTION_ERROR,
rpc::ClioError::etlREQUEST_ERROR,
rpc::ClioError::etlREQUEST_ERROR
},
LoadBalancerForwardToRippledErrorTestBundle{
"RequestError_RequestTimeout",
rpc::ClioError::etlREQUEST_ERROR,
rpc::ClioError::etlREQUEST_TIMEOUT,
rpc::ClioError::etlREQUEST_TIMEOUT
},
LoadBalancerForwardToRippledErrorTestBundle{
"RequestTimeout_InvalidResponse",
rpc::ClioError::etlREQUEST_TIMEOUT,
rpc::ClioError::etlINVALID_RESPONSE,
rpc::ClioError::etlINVALID_RESPONSE
},
LoadBalancerForwardToRippledErrorTestBundle{
"BothRequestTimeout",
rpc::ClioError::etlREQUEST_TIMEOUT,
rpc::ClioError::etlREQUEST_TIMEOUT,
rpc::ClioError::etlREQUEST_TIMEOUT
},
LoadBalancerForwardToRippledErrorTestBundle{
"InvalidResponse_RequestError",
rpc::ClioError::etlINVALID_RESPONSE,
rpc::ClioError::etlREQUEST_ERROR,
rpc::ClioError::etlINVALID_RESPONSE
}
),
tests::util::NameGenerator
);
TEST_P(LoadBalancerForwardToRippledErrorTests, bothSourcesFail)
{
EXPECT_CALL(sourceFactory_, makeSource).Times(2);
auto loadBalancer = makeLoadBalancer();
@@ -586,15 +644,17 @@ TEST_F(LoadBalancerForwardToRippledTests, bothSourcesFail)
sourceFactory_.sourceAt(0),
forwardToRippled(request_, clientIP_, LoadBalancer::USER_FORWARDING_X_USER_VALUE, testing::_)
)
.WillOnce(Return(std::nullopt));
.WillOnce(Return(std::unexpected{GetParam().firstSourceError}));
EXPECT_CALL(
sourceFactory_.sourceAt(1),
forwardToRippled(request_, clientIP_, LoadBalancer::USER_FORWARDING_X_USER_VALUE, testing::_)
)
.WillOnce(Return(std::nullopt));
.WillOnce(Return(std::unexpected{GetParam().secondSourceError}));
runSpawn([&](boost::asio::yield_context yield) {
EXPECT_EQ(loadBalancer->forwardToRippled(request_, clientIP_, false, yield), std::nullopt);
auto const response = loadBalancer->forwardToRippled(request_, clientIP_, false, yield);
ASSERT_FALSE(response);
EXPECT_EQ(response.error(), GetParam().responseExpectedError);
});
}

View File

@@ -18,6 +18,7 @@
//==============================================================================
#include "etl/impl/SourceImpl.hpp"
#include "rpc/Errors.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
@@ -63,7 +64,7 @@ struct SubscriptionSourceMock {
struct ForwardingSourceMock {
MOCK_METHOD(void, constructor, (std::string const&, std::string const&, std::chrono::steady_clock::duration));
using ForwardToRippledReturnType = std::optional<boost::json::object>;
using ForwardToRippledReturnType = std::expected<boost::json::object, rpc::ClioError>;
using ClientIpOpt = std::optional<std::string>;
MOCK_METHOD(
ForwardToRippledReturnType,