Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions src/rpc/handlers/ServerInfo.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ namespace rpc {
* @brief Contains common functionality for handling the `server_info` command
*
* @tparam CountersType The type of the counters
* @tparam ClockType Clock used for output time and ledger age; `now()` returns
* `std::chrono::system_clock::time_point`
*/
template <typename CountersType>
template <typename CountersType, typename ClockType = std::chrono::system_clock>
class BaseServerInfoHandler {
static constexpr auto kBackendCountersKey = "backend_counters";

Expand Down Expand Up @@ -100,7 +102,7 @@ class BaseServerInfoHandler {
std::optional<AdminSection> adminSection = std::nullopt;
std::string completeLedgers;
uint32_t loadFactor = 1u;
std::chrono::time_point<std::chrono::system_clock> time = std::chrono::system_clock::now();
std::chrono::time_point<std::chrono::system_clock> time = ClockType::now();
std::chrono::seconds uptime = {};
std::string clioVersion = util::build::getClioVersionString();
std::string xrplVersion = xrpl::BuildInfo::getVersionString();
Expand Down Expand Up @@ -189,8 +191,7 @@ class BaseServerInfoHandler {
return Error{Status{RippledError::RpcInternal}};

auto output = Output{};
auto const sinceEpoch =
duration_cast<seconds>(system_clock::now().time_since_epoch()).count();
auto const sinceEpoch = duration_cast<seconds>(output.info.time.time_since_epoch()).count();
auto const age = static_cast<int32_t>(sinceEpoch) -
static_cast<int32_t>(lgrInfo->closeTime.time_since_epoch().count()) -
static_cast<int32_t>(kRippleEpochStart);
Expand Down
125 changes: 85 additions & 40 deletions tests/unit/rpc/handlers/ServerInfoTests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,74 @@
#include <boost/json/value_to.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <xrpl/basics/chrono.h>
#include <xrpl/protocol/LedgerHeader.h>

#include <chrono>
#include <cstddef>
#include <optional>
#include <string>

using namespace rpc;
using namespace data;
using namespace testing;

using TestServerInfoHandler = BaseServerInfoHandler<MockCounters>;

namespace {

constexpr auto kLedgerHash = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
constexpr auto kClientIp = "1.1.1.1";
constexpr auto kNowUnix = 1'700'000'000u;
constexpr auto kNow = std::chrono::system_clock::time_point{std::chrono::seconds{kNowUnix}};

struct TestClock {
static inline std::size_t callCount = 0;

static std::chrono::system_clock::time_point
now()
{
++callCount;
return kNow;
}
};

using TestServerInfoHandler = BaseServerInfoHandler<MockCounters, TestClock>;

} // namespace

struct RPCServerInfoHandlerTest : HandlerBaseTest, MockLoadBalancerTest, MockCountersTest {
RPCServerInfoHandlerTest()
{
TestClock::callCount = 0;
backend_->setRange(10, 30);
}

template <typename Callback>
void
runNormalRequest(xrpl::LedgerHeader const& ledgerHeader, Callback callback)
{
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));
EXPECT_CALL(*backend_, doFetchLedgerObject)
.WillOnce(Return(createLegacyFeeSettingBlob(1, 2, 3, 4, 0)));
EXPECT_CALL(*mockLoadBalancerPtr_, forwardToRippled(_, Eq(kClientIp), false, _))
.WillOnce(Return(std::unexpected{rpc::ClioError::EtlInvalidResponse}));
EXPECT_CALL(*mockCountersPtr_, uptime).WillOnce(Return(std::chrono::seconds{1234}));
EXPECT_CALL(*mockETLServicePtr_, isAmendmentBlocked).WillOnce(Return(false));

auto const handler = AnyHandler{TestServerInfoHandler{
backend_,
mockSubscriptionManagerPtr_,
mockLoadBalancerPtr_,
mockETLServicePtr_,
*mockCountersPtr_
}};

runSpawn([&](auto yield) {
callback(
handler.process(boost::json::parse("{}"), Context{yield, {}, false, kClientIp})
);
});
}

static void
validateNormalOutput(rpc::ReturnType const& output)
{
Expand All @@ -56,6 +100,10 @@ struct RPCServerInfoHandlerTest : HandlerBaseTest, MockLoadBalancerTest, MockCou
EXPECT_TRUE(info.contains("libxrpl_version"));
EXPECT_TRUE(info.contains("validated_ledger"));
EXPECT_TRUE(info.contains("time"));
EXPECT_EQ(
boost::json::value_to<std::string>(info.at("time")),
xrpl::to_string(std::chrono::time_point_cast<std::chrono::microseconds>(kNow))
);
EXPECT_TRUE(info.contains("uptime"));

auto const& validated = info.at("validated_ledger").as_object();
Expand Down Expand Up @@ -134,6 +182,7 @@ TEST_F(RPCServerInfoHandlerTest, NoLedgerHeaderErrorsOutWithInternal)
auto const err = rpc::makeError(output.result.error());
EXPECT_EQ(err.at("error").as_string(), "internal");
EXPECT_EQ(err.at("error_message").as_string(), "Internal error.");
EXPECT_EQ(TestClock::callCount, 0u);
});
}

Expand All @@ -159,42 +208,14 @@ TEST_F(RPCServerInfoHandlerTest, NoFeesErrorsOutWithInternal)
auto const err = rpc::makeError(output.result.error());
EXPECT_EQ(err.at("error").as_string(), "internal");
EXPECT_EQ(err.at("error_message").as_string(), "Internal error.");
EXPECT_EQ(TestClock::callCount, 0u);
});
}

TEST_F(RPCServerInfoHandlerTest, DefaultOutputIsPresent)
{
MockLoadBalancer* rawBalancerPtr = mockLoadBalancerPtr_.get();
MockCounters const* rawCountersPtr = mockCountersPtr_.get();
MockETLService const* rawETLServicePtr = mockETLServicePtr_.get();

auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30, 3); // 3 seconds old
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));

auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0);
EXPECT_CALL(*backend_, doFetchLedgerObject).WillOnce(Return(feeBlob));

EXPECT_CALL(
*rawBalancerPtr, forwardToRippled(testing::_, testing::Eq(kClientIp), false, testing::_)
)
.WillOnce(Return(std::unexpected{rpc::ClioError::EtlInvalidResponse}));

EXPECT_CALL(*rawCountersPtr, uptime).WillOnce(Return(std::chrono::seconds{1234}));

EXPECT_CALL(*rawETLServicePtr, isAmendmentBlocked).WillOnce(Return(false));

auto const handler = AnyHandler{TestServerInfoHandler{
backend_,
mockSubscriptionManagerPtr_,
mockLoadBalancerPtr_,
mockETLServicePtr_,
*mockCountersPtr_
}};

runSpawn([&](auto yield) {
auto const req = boost::json::parse("{}");
auto const output = handler.process(req, Context{yield, {}, false, kClientIp});

auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
runNormalRequest(ledgerHeader, [&](auto const& output) {
validateNormalOutput(output);

// no admin section present by default
Expand All @@ -205,13 +226,37 @@ TEST_F(RPCServerInfoHandlerTest, DefaultOutputIsPresent)
});
}

TEST_F(RPCServerInfoHandlerTest, SamplesTheClockOnceForTimeAndAge)
{
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
runNormalRequest(ledgerHeader, [&](auto const& output) {
ASSERT_TRUE(output);
EXPECT_EQ(TestClock::callCount, 1u);
});
}

TEST_F(RPCServerInfoHandlerTest, FutureLedgerCloseTimeReportsZeroAge)
{
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix + 5);
runNormalRequest(ledgerHeader, [&](auto const& output) {
ASSERT_TRUE(output);
auto const& validated = output.result.value()
.as_object()
.at("info")
.as_object()
.at("validated_ledger")
.as_object();
EXPECT_EQ(validated.at("age").as_uint64(), 0u);
});
}

TEST_F(RPCServerInfoHandlerTest, AmendmentBlockedIsPresentIfSet)
{
MockLoadBalancer* rawBalancerPtr = mockLoadBalancerPtr_.get();
MockCounters const* rawCountersPtr = mockCountersPtr_.get();
MockETLService const* rawETLServicePtr = mockETLServicePtr_.get();

auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30, 3); // 3 seconds old
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));

auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0);
Expand Down Expand Up @@ -252,7 +297,7 @@ TEST_F(RPCServerInfoHandlerTest, CorruptionDetectedIsPresentIfSet)
MockCounters const* rawCountersPtr = mockCountersPtr_.get();
MockETLService const* rawETLServicePtr = mockETLServicePtr_.get();

auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30, 3); // 3 seconds old
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));

auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0);
Expand Down Expand Up @@ -292,7 +337,7 @@ TEST_F(RPCServerInfoHandlerTest, CacheReportsEnabledFlagCorrectly)
MockLoadBalancer* rawBalancerPtr = mockLoadBalancerPtr_.get();
MockCounters const* rawCountersPtr = mockCountersPtr_.get();

auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30, 3); // 3 seconds old
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
EXPECT_CALL(*backend_, fetchLedgerBySequence).Times(2).WillRepeatedly(Return(ledgerHeader));

auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0);
Expand Down Expand Up @@ -350,7 +395,7 @@ TEST_F(RPCServerInfoHandlerTest, AdminSectionPresentWhenAdminFlagIsSet)
MockETLService const* rawETLServicePtr = mockETLServicePtr_.get();

auto const empty = boost::json::object{};
auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30, 3); // 3 seconds old
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));

auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0);
Expand Down Expand Up @@ -393,7 +438,7 @@ TEST_F(RPCServerInfoHandlerTest, BackendCountersPresentWhenRequestWithParam)
MockETLService const* rawETLServicePtr = mockETLServicePtr_.get();

auto const empty = boost::json::object{};
auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30, 3); // 3 seconds old
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));

auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0);
Expand Down Expand Up @@ -443,7 +488,7 @@ TEST_F(RPCServerInfoHandlerTest, RippledForwardedValuesPresent)
MockETLService const* rawETLServicePtr = mockETLServicePtr_.get();

auto const empty = boost::json::object{};
auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30, 3); // 3 seconds old
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));

auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0);
Expand Down Expand Up @@ -497,7 +542,7 @@ TEST_F(RPCServerInfoHandlerTest, RippledForwardedValuesMissingNoExceptionThrown)
MockETLService const* rawETLServicePtr = mockETLServicePtr_.get();

auto const empty = boost::json::object{};
auto const ledgerHeader = createLedgerHeader(kLedgerHash, 30, 3); // 3 seconds old
auto const ledgerHeader = createLedgerHeaderWithUnixTime(kLedgerHash, 30, kNowUnix - 3);
EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(Return(ledgerHeader));

auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0);
Expand Down