mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-18 18:55:51 +00:00
@@ -31,6 +31,7 @@ target_sources(
|
||||
handlers/Ledger.cpp
|
||||
handlers/LedgerData.cpp
|
||||
handlers/LedgerEntry.cpp
|
||||
handlers/LedgerIndex.cpp
|
||||
handlers/LedgerRange.cpp
|
||||
handlers/NFTsByIssuer.cpp
|
||||
handlers/NFTBuyOffers.cpp
|
||||
|
||||
@@ -34,6 +34,9 @@
|
||||
|
||||
#include <charconv>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
@@ -51,6 +54,26 @@ Required::verify(boost::json::value const& value, std::string_view key)
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
TimeFormatValidator::verify(boost::json::value const& value, std::string_view key) const
|
||||
{
|
||||
using boost::json::value_to;
|
||||
|
||||
if (not value.is_object() or not value.as_object().contains(key))
|
||||
return {}; // ignore. field does not exist, let 'required' fail instead
|
||||
|
||||
if (not value.as_object().at(key).is_string())
|
||||
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
std::tm time = {};
|
||||
std::stringstream stream(value_to<std::string>(value.as_object().at(key)));
|
||||
stream >> std::get_time(&time, format_.c_str());
|
||||
if (stream.fail())
|
||||
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] MaybeError
|
||||
CustomValidator::verify(boost::json::value const& value, std::string_view key) const
|
||||
{
|
||||
|
||||
@@ -30,8 +30,11 @@
|
||||
#include <xrpl/protocol/ErrorCodes.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <functional>
|
||||
#include <initializer_list>
|
||||
#include <iomanip>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
@@ -288,6 +291,33 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Validate that value can be converted to time according to the given format.
|
||||
*/
|
||||
class TimeFormatValidator final {
|
||||
std::string format_;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Construct the validator storing format value.
|
||||
*
|
||||
* @param format The format to use for time conversion
|
||||
*/
|
||||
explicit TimeFormatValidator(std::string format) : format_{std::move(format)}
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Verify that the JSON value is valid formatted time.
|
||||
*
|
||||
* @param value The JSON value representing the outer object
|
||||
* @param key The key used to retrieve the tested value from the outer object
|
||||
* @return `RippledError::rpcINVALID_PARAMS` if validation failed; otherwise no error is returned
|
||||
*/
|
||||
[[nodiscard]] MaybeError
|
||||
verify(boost::json::value const& value, std::string_view key) const;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Validates that the value is equal to the one passed in.
|
||||
*/
|
||||
|
||||
@@ -43,6 +43,7 @@
|
||||
#include "rpc/handlers/Ledger.hpp"
|
||||
#include "rpc/handlers/LedgerData.hpp"
|
||||
#include "rpc/handlers/LedgerEntry.hpp"
|
||||
#include "rpc/handlers/LedgerIndex.hpp"
|
||||
#include "rpc/handlers/LedgerRange.hpp"
|
||||
#include "rpc/handlers/NFTBuyOffers.hpp"
|
||||
#include "rpc/handlers/NFTHistory.hpp"
|
||||
@@ -94,6 +95,7 @@ ProductionHandlerProvider::ProductionHandlerProvider(
|
||||
{"ledger", {LedgerHandler{backend}}},
|
||||
{"ledger_data", {LedgerDataHandler{backend}}},
|
||||
{"ledger_entry", {LedgerEntryHandler{backend}}},
|
||||
{"ledger_index", {LedgerIndexHandler{backend}, true}}, // clio only
|
||||
{"ledger_range", {LedgerRangeHandler{backend}}},
|
||||
{"nfts_by_issuer", {NFTsByIssuerHandler{backend}, true}}, // clio only
|
||||
{"nft_history", {NFTHistoryHandler{backend}, true}}, // clio only
|
||||
|
||||
119
src/rpc/handlers/LedgerIndex.cpp
Normal file
119
src/rpc/handlers/LedgerIndex.cpp
Normal file
@@ -0,0 +1,119 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "rpc/handlers/LedgerIndex.hpp"
|
||||
|
||||
#include "rpc/JS.hpp"
|
||||
#include "rpc/common/Types.hpp"
|
||||
#include "util/Assert.hpp"
|
||||
|
||||
#include <boost/json/conversion.hpp>
|
||||
#include <boost/json/object.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
#include <fmt/core.h>
|
||||
#include <xrpl/basics/chrono.h>
|
||||
#include <xrpl/basics/strHex.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <cstdint>
|
||||
#include <ctime>
|
||||
#include <numeric>
|
||||
#include <ranges>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
LedgerIndexHandler::Result
|
||||
LedgerIndexHandler::process(LedgerIndexHandler::Input input, Context const& ctx) const
|
||||
{
|
||||
auto const range = sharedPtrBackend_->fetchLedgerRange();
|
||||
auto const [minIndex, maxIndex] = *range;
|
||||
|
||||
auto const fillOutputByIndex = [&](std::uint32_t index) {
|
||||
auto const ledger = sharedPtrBackend_->fetchLedgerBySequence(index, ctx.yield);
|
||||
return Output{
|
||||
.ledgerIndex = index,
|
||||
.ledgerHash = ripple::strHex(ledger->hash),
|
||||
.closeTimeIso = ripple::to_string_iso(ledger->closeTime)
|
||||
};
|
||||
};
|
||||
|
||||
// if no date is provided, return the latest ledger
|
||||
if (!input.date)
|
||||
return fillOutputByIndex(maxIndex);
|
||||
|
||||
auto const convertISOTimeStrToTicks = [](std::string const& isoTimeStr) {
|
||||
std::tm time = {};
|
||||
std::stringstream ss(isoTimeStr);
|
||||
ss >> std::get_time(&time, DATE_FORMAT);
|
||||
return std::chrono::system_clock::from_time_t(std::mktime(&time)).time_since_epoch().count();
|
||||
};
|
||||
|
||||
auto const ticks = convertISOTimeStrToTicks(*input.date);
|
||||
|
||||
auto const earlierThan = [&](std::uint32_t ledgerIndex) {
|
||||
auto const header = sharedPtrBackend_->fetchLedgerBySequence(ledgerIndex, ctx.yield);
|
||||
auto const ledgerTime =
|
||||
std::chrono::system_clock::time_point{header->closeTime.time_since_epoch() + ripple::epoch_offset};
|
||||
return ticks < ledgerTime.time_since_epoch().count();
|
||||
};
|
||||
|
||||
// If the given date is earlier than the first valid ledger, return lgrNotFound
|
||||
if (earlierThan(minIndex))
|
||||
return Error{Status{RippledError::rpcLGR_NOT_FOUND, "ledgerNotInRange"}};
|
||||
|
||||
auto const view = std::ranges::iota_view{minIndex, maxIndex + 1};
|
||||
|
||||
auto const greaterEqLedgerIter = std::ranges::lower_bound(
|
||||
view, ticks, [&](std::uint32_t ledgerIndex, std::int64_t) { return not earlierThan(ledgerIndex); }
|
||||
);
|
||||
|
||||
if (greaterEqLedgerIter != view.end())
|
||||
return fillOutputByIndex(std::max(static_cast<std::uint32_t>(*greaterEqLedgerIter) - 1, minIndex));
|
||||
|
||||
return fillOutputByIndex(maxIndex);
|
||||
}
|
||||
|
||||
LedgerIndexHandler::Input
|
||||
tag_invoke(boost::json::value_to_tag<LedgerIndexHandler::Input>, boost::json::value const& jv)
|
||||
{
|
||||
auto input = LedgerIndexHandler::Input{};
|
||||
|
||||
if (jv.as_object().contains(JS(date)))
|
||||
input.date = jv.at(JS(date)).as_string();
|
||||
|
||||
return input;
|
||||
}
|
||||
|
||||
void
|
||||
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, LedgerIndexHandler::Output const& output)
|
||||
{
|
||||
jv = boost::json::object{
|
||||
{JS(ledger_index), output.ledgerIndex},
|
||||
{JS(ledger_hash), output.ledgerHash},
|
||||
{JS(close_time_iso), output.closeTimeIso},
|
||||
{JS(validated), true},
|
||||
};
|
||||
}
|
||||
|
||||
} // namespace rpc
|
||||
119
src/rpc/handlers/LedgerIndex.hpp
Normal file
119
src/rpc/handlers/LedgerIndex.hpp
Normal file
@@ -0,0 +1,119 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#pragma once
|
||||
#include "data/BackendInterface.hpp"
|
||||
#include "rpc/JS.hpp"
|
||||
#include "rpc/common/Specs.hpp"
|
||||
#include "rpc/common/Types.hpp"
|
||||
#include "rpc/common/Validators.hpp"
|
||||
|
||||
#include <boost/json/conversion.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
|
||||
namespace rpc {
|
||||
|
||||
/**
|
||||
* @brief The ledger_index method fetches the lastest closed ledger before the given date.
|
||||
*
|
||||
*/
|
||||
class LedgerIndexHandler {
|
||||
std::shared_ptr<BackendInterface> sharedPtrBackend_;
|
||||
static constexpr auto DATE_FORMAT = "%Y-%m-%dT%TZ";
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief A struct to hold the output data of the command
|
||||
*/
|
||||
struct Output {
|
||||
uint32_t ledgerIndex{};
|
||||
std::string ledgerHash;
|
||||
std::string closeTimeIso;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A struct to hold the input data for the command
|
||||
*/
|
||||
struct Input {
|
||||
std::optional<std::string> date;
|
||||
};
|
||||
|
||||
using Result = HandlerReturnType<Output>;
|
||||
|
||||
/**
|
||||
* @brief Construct a new LedgerIndexHandler object
|
||||
*
|
||||
* @param sharedPtrBackend The backend to use
|
||||
*/
|
||||
LedgerIndexHandler(std::shared_ptr<BackendInterface> const& sharedPtrBackend) : sharedPtrBackend_(sharedPtrBackend)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the API specification for the command
|
||||
*
|
||||
* @param apiVersion The api version to return the spec for
|
||||
* @return The spec for the given apiVersion
|
||||
*/
|
||||
static RpcSpecConstRef
|
||||
spec([[maybe_unused]] uint32_t apiVersion)
|
||||
{
|
||||
static auto const rpcSpec = RpcSpec{
|
||||
{JS(date), validation::Type<std::string>{}, validation::TimeFormatValidator{DATE_FORMAT}},
|
||||
};
|
||||
return rpcSpec;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Process the LedgerIndex command
|
||||
*
|
||||
* @param input The input data for the command
|
||||
* @param ctx The context of the request
|
||||
* @return The result of the operation
|
||||
*/
|
||||
Result
|
||||
process(Input input, Context const& ctx) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Convert the Output to a JSON object
|
||||
*
|
||||
* @param [out] jv The JSON object to convert to
|
||||
* @param output The output to convert
|
||||
*/
|
||||
friend void
|
||||
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, Output const& output);
|
||||
|
||||
/**
|
||||
* @brief Convert a JSON object to Input type
|
||||
*
|
||||
* @param jv The JSON object to convert
|
||||
* @return Input parsed from the JSON object
|
||||
*/
|
||||
friend Input
|
||||
tag_invoke(boost::json::value_to_tag<Input>, boost::json::value const& jv);
|
||||
};
|
||||
|
||||
} // namespace rpc
|
||||
@@ -93,6 +93,21 @@ CreateLedgerHeader(std::string_view ledgerHash, ripple::LedgerIndex seq, std::op
|
||||
return ledgerHeader;
|
||||
}
|
||||
|
||||
ripple::LedgerHeader
|
||||
CreateLedgerHeaderWithUnixTime(std::string_view ledgerHash, ripple::LedgerIndex seq, uint64_t closeTimeUnixStamp)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
auto ledgerHeader = ripple::LedgerHeader();
|
||||
ledgerHeader.hash = ripple::uint256{ledgerHash};
|
||||
ledgerHeader.seq = seq;
|
||||
|
||||
auto const closeTime = closeTimeUnixStamp - seconds{rippleEpochStart}.count();
|
||||
ledgerHeader.closeTime = ripple::NetClock::time_point{seconds{closeTime}};
|
||||
|
||||
return ledgerHeader;
|
||||
}
|
||||
|
||||
ripple::STObject
|
||||
CreateLegacyFeeSettingLedgerObject(
|
||||
uint64_t base,
|
||||
|
||||
@@ -65,6 +65,12 @@ GetAccountKey(ripple::AccountID const& acc);
|
||||
[[nodiscard]] ripple::LedgerHeader
|
||||
CreateLedgerHeader(std::string_view ledgerHash, ripple::LedgerIndex seq, std::optional<uint32_t> age = std::nullopt);
|
||||
|
||||
/*
|
||||
* Create a simple ledgerHeader object with hash, seq and unix timestamp
|
||||
*/
|
||||
[[nodiscard]] ripple::LedgerHeader
|
||||
CreateLedgerHeaderWithUnixTime(std::string_view ledgerHash, ripple::LedgerIndex seq, uint64_t closeTimeUnixStamp);
|
||||
|
||||
/*
|
||||
* Create a Legacy (pre XRPFees amendment) FeeSetting ledger object
|
||||
*/
|
||||
|
||||
@@ -72,6 +72,7 @@ target_sources(
|
||||
rpc/handlers/GetAggregatePriceTests.cpp
|
||||
rpc/handlers/LedgerDataTests.cpp
|
||||
rpc/handlers/LedgerEntryTests.cpp
|
||||
rpc/handlers/LedgerIndexTests.cpp
|
||||
rpc/handlers/LedgerRangeTests.cpp
|
||||
rpc/handlers/LedgerTests.cpp
|
||||
rpc/handlers/NFTBuyOffersTests.cpp
|
||||
|
||||
@@ -355,6 +355,42 @@ TEST_F(RPCBaseTest, WithCustomError)
|
||||
ASSERT_EQ(err.error(), ripple::rpcALREADY_MULTISIG);
|
||||
}
|
||||
|
||||
TEST_F(RPCBaseTest, TimeFormatValidator)
|
||||
{
|
||||
auto const spec = RpcSpec{
|
||||
{"date", TimeFormatValidator{"%Y-%m-%dT%H:%M:%SZ"}},
|
||||
};
|
||||
|
||||
auto passingInput = json::parse(R"({ "date": "2023-01-01T00:00:00Z" })");
|
||||
EXPECT_TRUE(spec.process(passingInput));
|
||||
|
||||
passingInput = json::parse("123");
|
||||
EXPECT_TRUE(spec.process(passingInput));
|
||||
|
||||
// key not exists
|
||||
passingInput = json::parse(R"({ "date1": "2023-01-01T00:00:00Z" })");
|
||||
EXPECT_TRUE(spec.process(passingInput));
|
||||
|
||||
auto failingInput = json::parse(R"({ "date": "2023-01-01-00:00:00" })");
|
||||
auto err = spec.process(failingInput);
|
||||
EXPECT_FALSE(err);
|
||||
EXPECT_EQ(err.error(), ripple::rpcINVALID_PARAMS);
|
||||
|
||||
failingInput = json::parse(R"({ "date": "01-01-2024T00:00:00" })");
|
||||
EXPECT_FALSE(spec.process(failingInput));
|
||||
|
||||
failingInput = json::parse(R"({ "date": "2024-01-01T29:00:00" })");
|
||||
EXPECT_FALSE(spec.process(failingInput));
|
||||
|
||||
failingInput = json::parse(R"({ "date": "" })");
|
||||
EXPECT_FALSE(spec.process(failingInput));
|
||||
|
||||
failingInput = json::parse(R"({ "date": 1 })");
|
||||
err = spec.process(failingInput);
|
||||
EXPECT_FALSE(err);
|
||||
EXPECT_EQ(err.error(), ripple::rpcINVALID_PARAMS);
|
||||
}
|
||||
|
||||
TEST_F(RPCBaseTest, CustomValidator)
|
||||
{
|
||||
auto customFormatCheck = CustomValidator{[](json::value const& value, std::string_view /* key */) -> MaybeError {
|
||||
|
||||
150
tests/unit/rpc/handlers/LedgerIndexTests.cpp
Normal file
150
tests/unit/rpc/handlers/LedgerIndexTests.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
This file is part of clio: https://github.com/XRPLF/clio
|
||||
Copyright (c) 2024, the clio developers.
|
||||
|
||||
Permission to use, copy, modify, and distribute this software for any
|
||||
purpose with or without fee is hereby granted, provided that the above
|
||||
copyright notice and this permission notice appear in all copies.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
*/
|
||||
//==============================================================================
|
||||
|
||||
#include "rpc/Errors.hpp"
|
||||
#include "rpc/common/AnyHandler.hpp"
|
||||
#include "rpc/common/Types.hpp"
|
||||
#include "rpc/handlers/LedgerIndex.hpp"
|
||||
#include "util/HandlerBaseTestFixture.hpp"
|
||||
#include "util/NameGenerator.hpp"
|
||||
#include "util/TestObject.hpp"
|
||||
|
||||
#include <boost/json/parse.hpp>
|
||||
#include <boost/json/value.hpp>
|
||||
#include <fmt/core.h>
|
||||
#include <gmock/gmock.h>
|
||||
#include <gtest/gtest.h>
|
||||
#include <xrpl/basics/base_uint.h>
|
||||
#include <xrpl/protocol/Indexes.h>
|
||||
#include <xrpl/protocol/LedgerFormats.h>
|
||||
#include <xrpl/protocol/UintTypes.h>
|
||||
#include <xrpl/protocol/jss.h>
|
||||
|
||||
constexpr static auto RANGEMIN = 10;
|
||||
constexpr static auto RANGEMAX = 30;
|
||||
constexpr static auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
||||
|
||||
using namespace rpc;
|
||||
namespace json = boost::json;
|
||||
using namespace testing;
|
||||
|
||||
class RPCLedgerIndexTest : public HandlerBaseTestStrict {};
|
||||
|
||||
TEST_F(RPCLedgerIndexTest, DateStrNotValid)
|
||||
{
|
||||
auto const handler = AnyHandler{LedgerIndexHandler{backend}};
|
||||
auto const req = json::parse(R"({"date": "not_a_number"})");
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(req, Context{yield});
|
||||
ASSERT_FALSE(output);
|
||||
auto const err = rpc::makeError(output.result.error());
|
||||
EXPECT_EQ(err.at("error").as_string(), "invalidParams");
|
||||
EXPECT_EQ(err.at("error_message").as_string(), "Invalid parameters.");
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(RPCLedgerIndexTest, NoDateGiven)
|
||||
{
|
||||
backend->setRange(RANGEMIN, RANGEMAX);
|
||||
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, RANGEMAX, 5);
|
||||
EXPECT_CALL(*backend, fetchLedgerBySequence(RANGEMAX, _)).WillOnce(Return(ledgerHeader));
|
||||
|
||||
auto const handler = AnyHandler{LedgerIndexHandler{backend}};
|
||||
auto const req = json::parse(R"({})");
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(req, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
EXPECT_EQ(output.result->at("ledger_index").as_uint64(), RANGEMAX);
|
||||
EXPECT_EQ(output.result->at("ledger_hash").as_string(), LEDGERHASH);
|
||||
EXPECT_TRUE(output.result->as_object().contains("close_time_iso"));
|
||||
});
|
||||
}
|
||||
|
||||
TEST_F(RPCLedgerIndexTest, EarlierThanMinLedger)
|
||||
{
|
||||
backend->setRange(RANGEMIN, RANGEMAX);
|
||||
auto const handler = AnyHandler{LedgerIndexHandler{backend}};
|
||||
auto const req = json::parse(R"({"date": "2024-06-25T12:23:05Z"})");
|
||||
auto const ledgerHeader =
|
||||
CreateLedgerHeaderWithUnixTime(LEDGERHASH, RANGEMIN, 1719318190); //"2024-06-25T12:23:10Z"
|
||||
EXPECT_CALL(*backend, fetchLedgerBySequence(RANGEMIN, _)).WillOnce(Return(ledgerHeader));
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(req, Context{yield});
|
||||
ASSERT_FALSE(output);
|
||||
auto const err = rpc::makeError(output.result.error());
|
||||
EXPECT_EQ(err.at("error").as_string(), "lgrNotFound");
|
||||
});
|
||||
}
|
||||
|
||||
struct LedgerIndexTestsCaseBundle {
|
||||
std::string testName;
|
||||
std::string json;
|
||||
std::uint32_t expectedLedgerIndex;
|
||||
std::string closeTimeIso;
|
||||
};
|
||||
|
||||
class LedgerIndexTests : public RPCLedgerIndexTest, public WithParamInterface<LedgerIndexTestsCaseBundle> {
|
||||
public:
|
||||
static auto
|
||||
generateTestValuesForParametersTest()
|
||||
{
|
||||
// start from 2024-06-25T12:23:10Z to 2024-06-25T12:23:50Z with step 2
|
||||
return std::vector<LedgerIndexTestsCaseBundle>{
|
||||
{"LaterThanMaxLedger", R"({"date": "2024-06-25T12:23:55Z"})", RANGEMAX, "2024-06-25T12:23:50Z"},
|
||||
{"GreaterThanMinLedger", R"({"date": "2024-06-25T12:23:11Z"})", RANGEMIN, "2024-06-25T12:23:10Z"},
|
||||
{"IsMinLedger", R"({"date": "2024-06-25T12:23:10Z"})", RANGEMIN, "2024-06-25T12:23:10Z"},
|
||||
{"IsMaxLedger", R"({"date": "2024-06-25T12:23:50Z"})", RANGEMAX, "2024-06-25T12:23:50Z"},
|
||||
{"IsMidLedger", R"({"date": "2024-06-25T12:23:30Z"})", 20, "2024-06-25T12:23:30Z"},
|
||||
{"BetweenLedgers", R"({"date": "2024-06-25T12:23:29Z"})", 19, "2024-06-25T12:23:28Z"}
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(
|
||||
RPCLedgerIndexTestsGroup,
|
||||
LedgerIndexTests,
|
||||
ValuesIn(LedgerIndexTests::generateTestValuesForParametersTest()),
|
||||
tests::util::NameGenerator
|
||||
);
|
||||
|
||||
TEST_P(LedgerIndexTests, SearchFromLedgerRange)
|
||||
{
|
||||
auto const testBundle = GetParam();
|
||||
auto const jv = json::parse(testBundle.json).as_object();
|
||||
backend->setRange(RANGEMIN, RANGEMAX);
|
||||
|
||||
// start from 1719318190 , which is the unix time for 2024-06-25T12:23:10Z to 2024-06-25T12:23:50Z with
|
||||
// step 2
|
||||
for (uint32_t i = RANGEMIN; i <= RANGEMAX; i++) {
|
||||
auto const ledgerHeader = CreateLedgerHeaderWithUnixTime(LEDGERHASH, i, 1719318190 + 2 * (i - RANGEMIN));
|
||||
ON_CALL(*backend, fetchLedgerBySequence(i, _)).WillByDefault(Return(ledgerHeader));
|
||||
EXPECT_CALL(*backend, fetchLedgerBySequence(i, _))
|
||||
.Times(i == testBundle.expectedLedgerIndex ? (i == RANGEMIN ? Exactly(3) : Exactly(2)) : AtMost(1));
|
||||
}
|
||||
|
||||
auto const handler = AnyHandler{LedgerIndexHandler{backend}};
|
||||
auto const req = json::parse(testBundle.json);
|
||||
runSpawn([&](auto yield) {
|
||||
auto const output = handler.process(req, Context{yield});
|
||||
ASSERT_TRUE(output);
|
||||
EXPECT_EQ(output.result->at("ledger_index").as_uint64(), testBundle.expectedLedgerIndex);
|
||||
EXPECT_EQ(output.result->at("ledger_hash").as_string(), LEDGERHASH);
|
||||
EXPECT_EQ(output.result->at("close_time_iso").as_string(), testBundle.closeTimeIso);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user