Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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()
Comment thread
godexsoft marked this conversation as resolved.
Outdated
.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
Loading