From d195bdb66db83a9e25ff20daf1c9de82f34ca350 Mon Sep 17 00:00:00 2001 From: cyan317 <120398799+cindyyan317@users.noreply.github.com> Date: Fri, 14 Jul 2023 13:08:08 +0100 Subject: [PATCH] Change limit tests (#766) Fixes #771 --- src/rpc/handlers/AccountChannels.h | 8 +- src/rpc/handlers/AccountCurrencies.h | 1 - src/rpc/handlers/AccountInfo.h | 1 - src/rpc/handlers/AccountLines.h | 8 +- src/rpc/handlers/AccountNFTs.h | 8 +- src/rpc/handlers/AccountObjects.h | 8 +- src/rpc/handlers/AccountOffers.h | 9 +- src/rpc/handlers/AccountTx.cpp | 3 +- src/rpc/handlers/AccountTx.h | 6 +- src/rpc/handlers/BookOffers.h | 8 +- src/rpc/handlers/GatewayBalances.h | 1 - src/rpc/handlers/LedgerData.h | 8 +- src/rpc/handlers/NFTHistory.cpp | 4 +- src/rpc/handlers/NFTHistory.h | 6 +- src/rpc/handlers/NFTOffersCommon.h | 8 +- src/rpc/handlers/NoRippleCheck.h | 8 +- src/rpc/handlers/Tx.cpp | 2 +- .../rpc/handlers/AccountChannelsTest.cpp | 94 ++++++++ .../rpc/handlers/AccountCurrenciesTest.cpp | 86 ------- unittests/rpc/handlers/AccountInfoTest.cpp | 66 ------ unittests/rpc/handlers/AccountLinesTest.cpp | 166 ++++++++++++++ unittests/rpc/handlers/AccountNFTsTest.cpp | 126 +++++++++++ unittests/rpc/handlers/AccountObjectsTest.cpp | 162 ++++++++++++++ unittests/rpc/handlers/AccountOffersTest.cpp | 210 +++++++++--------- unittests/rpc/handlers/AccountTxTest.cpp | 40 ++++ unittests/rpc/handlers/BookOffersTest.cpp | 144 +++++++++++- .../rpc/handlers/GatewayBalancesTest.cpp | 76 ------- unittests/rpc/handlers/LedgerDataTest.cpp | 89 ++++++++ unittests/rpc/handlers/NFTBuyOffersTest.cpp | 86 +++++++ unittests/rpc/handlers/NFTHistoryTest.cpp | 86 +++++++ unittests/rpc/handlers/NFTSellOffersTest.cpp | 86 +++++++ unittests/rpc/handlers/NoRippleCheckTest.cpp | 104 +++++++++ 32 files changed, 1355 insertions(+), 363 deletions(-) diff --git a/src/rpc/handlers/AccountChannels.h b/src/rpc/handlers/AccountChannels.h index 6f24b3f3..00e4a905 100644 --- a/src/rpc/handlers/AccountChannels.h +++ b/src/rpc/handlers/AccountChannels.h @@ -42,6 +42,10 @@ class AccountChannelsHandler std::shared_ptr const sharedPtrBackend_; public: + static constexpr auto LIMIT_MIN = 10; + static constexpr auto LIMIT_MAX = 400; + static constexpr auto LIMIT_DEFAULT = 200; + // type align with SField.h struct ChannelResponse { @@ -77,7 +81,7 @@ public: std::optional destinationAccount; std::optional ledgerHash; std::optional ledgerIndex; - uint32_t limit = 200; + uint32_t limit = LIMIT_DEFAULT; std::optional marker; }; @@ -95,7 +99,7 @@ public: {JS(account), validation::Required{}, validation::AccountValidator}, {JS(destination_account), validation::Type{}, validation::AccountValidator}, {JS(ledger_hash), validation::Uint256HexStringValidator}, - {JS(limit), validation::Type{}, modifiers::Clamp{10, 400}}, + {JS(limit), validation::Type{}, modifiers::Clamp{LIMIT_MIN, LIMIT_MAX}}, {JS(ledger_index), validation::LedgerIndexValidator}, {JS(marker), validation::AccountMarkerValidator}, }; diff --git a/src/rpc/handlers/AccountCurrencies.h b/src/rpc/handlers/AccountCurrencies.h index a5fd8c78..599387c1 100644 --- a/src/rpc/handlers/AccountCurrencies.h +++ b/src/rpc/handlers/AccountCurrencies.h @@ -51,7 +51,6 @@ public: bool validated = true; }; - // Note: clio only supports XRP Ledger addresses (i.e. `strict` is unsupported for `false`) struct Input { std::string account; diff --git a/src/rpc/handlers/AccountInfo.h b/src/rpc/handlers/AccountInfo.h index e7110c30..6749c466 100644 --- a/src/rpc/handlers/AccountInfo.h +++ b/src/rpc/handlers/AccountInfo.h @@ -66,7 +66,6 @@ public: // "queue" is not available in Reporting mode // "ident" is deprecated, keep it for now, in line with rippled - // Note: clio only supports XRP Ledger addresses (i.e. `strict` is unsupported for `false`) struct Input { std::optional account; diff --git a/src/rpc/handlers/AccountLines.h b/src/rpc/handlers/AccountLines.h index a26dc2e8..ba7c9a65 100644 --- a/src/rpc/handlers/AccountLines.h +++ b/src/rpc/handlers/AccountLines.h @@ -42,6 +42,10 @@ class AccountLinesHandler std::shared_ptr const sharedPtrBackend_; public: + static auto constexpr LIMIT_MIN = 10; + static auto constexpr LIMIT_MAX = 400; + static auto constexpr LIMIT_DEFAULT = 200; + struct LineResponse { std::string account; @@ -78,7 +82,7 @@ public: std::optional peer; bool ignoreDefault = false; // TODO: document // https://github.com/XRPLF/xrpl-dev-portal/issues/1839 - uint32_t limit = 200; + uint32_t limit = LIMIT_DEFAULT; std::optional marker; }; @@ -98,7 +102,7 @@ public: {JS(peer), meta::WithCustomError{validation::AccountValidator, Status(RippledError::rpcACT_MALFORMED)}}, {JS(ignore_default), validation::Type{}}, {JS(ledger_hash), validation::Uint256HexStringValidator}, - {JS(limit), validation::Type{}, modifiers::Clamp{10, 400}}, + {JS(limit), validation::Type{}, modifiers::Clamp{LIMIT_MIN, LIMIT_MAX}}, {JS(ledger_index), validation::LedgerIndexValidator}, {JS(marker), validation::AccountMarkerValidator}, }; diff --git a/src/rpc/handlers/AccountNFTs.h b/src/rpc/handlers/AccountNFTs.h index d89dfde0..baea9b9a 100644 --- a/src/rpc/handlers/AccountNFTs.h +++ b/src/rpc/handlers/AccountNFTs.h @@ -37,6 +37,10 @@ class AccountNFTsHandler std::shared_ptr sharedPtrBackend_; public: + static auto constexpr LIMIT_MIN = 20; + static auto constexpr LIMIT_MAX = 400; + static auto constexpr LIMIT_DEFAULT = 100; + struct Output { std::string account; @@ -54,7 +58,7 @@ public: std::string account; std::optional ledgerHash; std::optional ledgerIndex; - uint32_t limit = 100; // Limit the number of token pages to retrieve. [20,400] + uint32_t limit = LIMIT_DEFAULT; // Limit the number of token pages to retrieve. [20,400] std::optional marker; }; @@ -72,7 +76,7 @@ public: {JS(ledger_hash), validation::Uint256HexStringValidator}, {JS(ledger_index), validation::LedgerIndexValidator}, {JS(marker), validation::Uint256HexStringValidator}, - {JS(limit), validation::Type{}, modifiers::Clamp{20, 400}}, + {JS(limit), validation::Type{}, modifiers::Clamp{LIMIT_MIN, LIMIT_MAX}}, }; return rpcSpec; diff --git a/src/rpc/handlers/AccountObjects.h b/src/rpc/handlers/AccountObjects.h index 4e11f6d2..e3b1baed 100644 --- a/src/rpc/handlers/AccountObjects.h +++ b/src/rpc/handlers/AccountObjects.h @@ -46,6 +46,10 @@ class AccountObjectsHandler static std::unordered_map const TYPESMAP; public: + static auto constexpr LIMIT_MIN = 10; + static auto constexpr LIMIT_MAX = 400; + static auto constexpr LIMIT_DEFAULT = 200; + struct Output { std::string account; @@ -62,7 +66,7 @@ public: std::string account; std::optional ledgerHash; std::optional ledgerIndex; - uint32_t limit = 200; // [10,400] + uint32_t limit = LIMIT_DEFAULT; // [10,400] std::optional marker; std::optional type; bool deletionBlockersOnly = false; @@ -82,7 +86,7 @@ public: {JS(account), validation::Required{}, validation::AccountValidator}, {JS(ledger_hash), validation::Uint256HexStringValidator}, {JS(ledger_index), validation::LedgerIndexValidator}, - {JS(limit), validation::Type{}, modifiers::Clamp(10, 400)}, + {JS(limit), validation::Type{}, modifiers::Clamp(LIMIT_MIN, LIMIT_MAX)}, {JS(type), validation::Type{}, validation::OneOf{ diff --git a/src/rpc/handlers/AccountOffers.h b/src/rpc/handlers/AccountOffers.h index b85f66a5..48b5a86d 100644 --- a/src/rpc/handlers/AccountOffers.h +++ b/src/rpc/handlers/AccountOffers.h @@ -38,6 +38,10 @@ class AccountOffersHandler std::shared_ptr sharedPtrBackend_; public: + static auto constexpr LIMIT_MIN = 10; + static auto constexpr LIMIT_MAX = 400; + static auto constexpr LIMIT_DEFAULT = 200; + struct Offer { uint32_t flags; @@ -59,13 +63,12 @@ public: bool validated = true; }; - // Note: clio only supports XRP Ledger addresses (i.e. `strict` is unsupported for `false`) struct Input { std::string account; std::optional ledgerHash; std::optional ledgerIndex; - uint32_t limit = 200; + uint32_t limit = LIMIT_DEFAULT; std::optional marker; }; @@ -84,7 +87,7 @@ public: {JS(ledger_hash), validation::Uint256HexStringValidator}, {JS(ledger_index), validation::LedgerIndexValidator}, {JS(marker), validation::AccountMarkerValidator}, - {JS(limit), validation::Type{}, modifiers::Clamp{10, 400}}}; + {JS(limit), validation::Type{}, modifiers::Clamp{LIMIT_MIN, LIMIT_MAX}}}; return rpcSpec; } diff --git a/src/rpc/handlers/AccountTx.cpp b/src/rpc/handlers/AccountTx.cpp index e42e5dcc..be518b2c 100644 --- a/src/rpc/handlers/AccountTx.cpp +++ b/src/rpc/handlers/AccountTx.cpp @@ -81,8 +81,7 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con cursor = {maxIndex, std::numeric_limits::max()}; } - static auto constexpr limitDefault = 200; - auto const limit = input.limit.value_or(limitDefault); + auto const limit = input.limit.value_or(LIMIT_DEFAULT); auto const accountID = accountFromStringStrict(input.account); auto const [txnsAndCursor, timeDiff] = util::timed([&]() { return sharedPtrBackend_->fetchAccountTransactions(*accountID, limit, input.forward, cursor, ctx.yield); diff --git a/src/rpc/handlers/AccountTx.h b/src/rpc/handlers/AccountTx.h index 999f4d01..25c2fb1e 100644 --- a/src/rpc/handlers/AccountTx.h +++ b/src/rpc/handlers/AccountTx.h @@ -40,6 +40,10 @@ class AccountTxHandler std::shared_ptr sharedPtrBackend_; public: + // no max limit + static auto constexpr LIMIT_MIN = 1; + static auto constexpr LIMIT_DEFAULT = 200; + struct Marker { uint32_t ledger; @@ -94,7 +98,7 @@ public: {JS(forward), validation::Type{}}, {JS(limit), validation::Type{}, - modifiers::Clamp{1, std::numeric_limits::max()}}, + modifiers::Clamp{LIMIT_MIN, std::numeric_limits::max()}}, {JS(marker), meta::WithCustomError{ validation::Type{}, diff --git a/src/rpc/handlers/BookOffers.h b/src/rpc/handlers/BookOffers.h index 713e1e71..83566e97 100644 --- a/src/rpc/handlers/BookOffers.h +++ b/src/rpc/handlers/BookOffers.h @@ -37,6 +37,10 @@ class BookOffersHandler std::shared_ptr sharedPtrBackend_; public: + static auto constexpr LIMIT_MIN = 1; + static auto constexpr LIMIT_MAX = 100; + static auto constexpr LIMIT_DEFAULT = 60; + struct Output { std::string ledgerHash; @@ -51,7 +55,7 @@ public: { std::optional ledgerHash; std::optional ledgerIndex; - uint32_t limit = 60; + uint32_t limit = LIMIT_DEFAULT; std::optional taker; ripple::Currency paysCurrency; ripple::Currency getsCurrency; @@ -92,7 +96,7 @@ public: {JS(taker), meta::WithCustomError{ validation::AccountValidator, Status(RippledError::rpcINVALID_PARAMS, "Invalid field 'taker'")}}, - {JS(limit), validation::Type{}, modifiers::Clamp{1, 100}}, + {JS(limit), validation::Type{}, modifiers::Clamp{LIMIT_MIN, LIMIT_MAX}}, {JS(ledger_hash), validation::Uint256HexStringValidator}, {JS(ledger_index), validation::LedgerIndexValidator}, }; diff --git a/src/rpc/handlers/GatewayBalances.h b/src/rpc/handlers/GatewayBalances.h index 874cb90e..44d8091c 100644 --- a/src/rpc/handlers/GatewayBalances.h +++ b/src/rpc/handlers/GatewayBalances.h @@ -52,7 +52,6 @@ public: bool validated = true; }; - // Note: clio only supports XRP Ledger addresses (i.e. `strict` is unsupported for `false`) struct Input { std::string account; diff --git a/src/rpc/handlers/LedgerData.h b/src/rpc/handlers/LedgerData.h index 390d52b9..54485d55 100644 --- a/src/rpc/handlers/LedgerData.h +++ b/src/rpc/handlers/LedgerData.h @@ -42,15 +42,15 @@ class LedgerDataHandler std::shared_ptr sharedPtrBackend_; clio::Logger log_{"RPC"}; - // constants - static uint32_t constexpr LIMITBINARY = 2048; - static uint32_t constexpr LIMITJSON = 256; - static const std::unordered_map TYPES_MAP; static const std::unordered_set TYPES_KEYS; public: + // constants + static uint32_t constexpr LIMITBINARY = 2048; + static uint32_t constexpr LIMITJSON = 256; + struct Output { uint32_t ledgerIndex; diff --git a/src/rpc/handlers/NFTHistory.cpp b/src/rpc/handlers/NFTHistory.cpp index 5a831646..75076584 100644 --- a/src/rpc/handlers/NFTHistory.cpp +++ b/src/rpc/handlers/NFTHistory.cpp @@ -81,9 +81,7 @@ NFTHistoryHandler::process(NFTHistoryHandler::Input input, Context const& ctx) c cursor = {maxIndex, std::numeric_limits::max()}; } - static auto constexpr limitDefault = 50; - - auto const limit = input.limit.value_or(limitDefault); + auto const limit = input.limit.value_or(LIMIT_DEFAULT); auto const tokenID = ripple::uint256{input.nftID.c_str()}; auto const [txnsAndCursor, timeDiff] = util::timed( diff --git a/src/rpc/handlers/NFTHistory.h b/src/rpc/handlers/NFTHistory.h index 6ad21568..8693c770 100644 --- a/src/rpc/handlers/NFTHistory.h +++ b/src/rpc/handlers/NFTHistory.h @@ -40,6 +40,10 @@ class NFTHistoryHandler std::shared_ptr sharedPtrBackend_; public: + static auto constexpr LIMIT_MIN = 1; + static auto constexpr LIMIT_MAX = 100; + static auto constexpr LIMIT_DEFAULT = 50; + // TODO: this marker is same as account_tx, reuse in future struct Marker { @@ -92,7 +96,7 @@ public: {JS(ledger_index_max), validation::Type{}}, {JS(binary), validation::Type{}}, {JS(forward), validation::Type{}}, - {JS(limit), validation::Type{}, modifiers::Clamp{1, 100}}, + {JS(limit), validation::Type{}, modifiers::Clamp{LIMIT_MIN, LIMIT_MAX}}, {JS(marker), meta::WithCustomError{ validation::Type{}, Status{RippledError::rpcINVALID_PARAMS, "invalidMarker"}}, diff --git a/src/rpc/handlers/NFTOffersCommon.h b/src/rpc/handlers/NFTOffersCommon.h index f3474cd4..b610462f 100644 --- a/src/rpc/handlers/NFTOffersCommon.h +++ b/src/rpc/handlers/NFTOffersCommon.h @@ -32,6 +32,10 @@ class NFTOffersHandlerBase std::shared_ptr sharedPtrBackend_; public: + static auto constexpr LIMIT_MIN = 50; + static auto constexpr LIMIT_MAX = 500; + static auto constexpr LIMIT_DEFAULT = 250; + struct Output { std::string nftID; @@ -48,7 +52,7 @@ public: std::string nftID; std::optional ledgerHash; std::optional ledgerIndex; - uint32_t limit = 250; + uint32_t limit = LIMIT_DEFAULT; std::optional marker; }; @@ -66,7 +70,7 @@ public: {JS(nft_id), validation::Required{}, validation::Uint256HexStringValidator}, {JS(ledger_hash), validation::Uint256HexStringValidator}, {JS(ledger_index), validation::LedgerIndexValidator}, - {JS(limit), validation::Type{}, modifiers::Clamp{50, 500}}, + {JS(limit), validation::Type{}, modifiers::Clamp{LIMIT_MIN, LIMIT_MAX}}, {JS(marker), validation::Uint256HexStringValidator}, }; diff --git a/src/rpc/handlers/NoRippleCheck.h b/src/rpc/handlers/NoRippleCheck.h index 29647f1d..90b7bbb2 100644 --- a/src/rpc/handlers/NoRippleCheck.h +++ b/src/rpc/handlers/NoRippleCheck.h @@ -41,6 +41,10 @@ class NoRippleCheckHandler std::shared_ptr sharedPtrBackend_; public: + static auto constexpr LIMIT_MIN = 1; + static auto constexpr LIMIT_MAX = 500; + static auto constexpr LIMIT_DEFAULT = 300; + struct Output { std::string ledgerHash; @@ -57,7 +61,7 @@ public: bool roleGateway = false; std::optional ledgerHash; std::optional ledgerIndex; - uint32_t limit = 300; + uint32_t limit = LIMIT_DEFAULT; bool transactions = false; }; @@ -80,7 +84,7 @@ public: Status{RippledError::rpcINVALID_PARAMS, "role field is invalid"}}}, {JS(ledger_hash), validation::Uint256HexStringValidator}, {JS(ledger_index), validation::LedgerIndexValidator}, - {JS(limit), validation::Type(), modifiers::Clamp{1, 500}}, + {JS(limit), validation::Type(), modifiers::Clamp{LIMIT_MIN, LIMIT_MAX}}, {JS(transactions), validation::Type()}, }; diff --git a/src/rpc/handlers/Tx.cpp b/src/rpc/handlers/Tx.cpp index 4d660936..685d5d49 100644 --- a/src/rpc/handlers/Tx.cpp +++ b/src/rpc/handlers/Tx.cpp @@ -24,7 +24,7 @@ namespace RPC { TxHandler::Result TxHandler::process(Input input, Context const& ctx) const { - constexpr static auto maxLedgerRange = 1000u; + static auto constexpr maxLedgerRange = 1000u; auto const rangeSupplied = input.minLedger && input.maxLedger; if (rangeSupplied) diff --git a/unittests/rpc/handlers/AccountChannelsTest.cpp b/unittests/rpc/handlers/AccountChannelsTest.cpp index d385a9d2..3c33796f 100644 --- a/unittests/rpc/handlers/AccountChannelsTest.cpp +++ b/unittests/rpc/handlers/AccountChannelsTest.cpp @@ -810,3 +810,97 @@ TEST_F(RPCAccountChannelsHandlerTest, MarkerInput) EXPECT_EQ((*output).as_object().at("channels").as_array().size(), limit - 1); }); } + +TEST_F(RPCAccountChannelsHandlerTest, LimitLessThanMin) +{ + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + // fetch account object return something + auto account = GetAccountIDWithString(ACCOUNT); + auto accountKk = ripple::keylet::account(account).key; + auto owneDirKk = ripple::keylet::ownerDir(account).key; + auto fake = Blob{'f', 'a', 'k', 'e'}; + // return a non empty account + ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, testing::_, testing::_)).WillByDefault(Return(fake)); + + // return owner index containing 2 indexes + ripple::STObject ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}, ripple::uint256{INDEX2}}, INDEX1); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(owneDirKk, testing::_, testing::_)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + // return two payment channel objects + std::vector bbs; + ripple::STObject channel1 = CreatePaymentChannelLedgerObject(ACCOUNT, ACCOUNT2, 100, 10, 32, TXNID, 28); + bbs.push_back(channel1.getSerializer().peekData()); + bbs.push_back(channel1.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const input = json::parse(fmt::format( + R"({{ + "account": "{}", + "limit": {} + }})", + ACCOUNT, + AccountChannelsHandler::LIMIT_MIN - 1)); + runSpawn([&, this](auto& yield) { + auto handler = AnyHandler{AccountChannelsHandler{this->mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ((*output).as_object().at("channels").as_array().size(), 2); + EXPECT_EQ((*output).as_object().at("limit").as_uint64(), AccountChannelsHandler::LIMIT_MIN); + }); +} + +TEST_F(RPCAccountChannelsHandlerTest, LimitMoreThanMax) +{ + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + // fetch account object return something + auto account = GetAccountIDWithString(ACCOUNT); + auto accountKk = ripple::keylet::account(account).key; + auto owneDirKk = ripple::keylet::ownerDir(account).key; + auto fake = Blob{'f', 'a', 'k', 'e'}; + // return a non empty account + ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, testing::_, testing::_)).WillByDefault(Return(fake)); + + // return owner index containing 2 indexes + ripple::STObject ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}, ripple::uint256{INDEX2}}, INDEX1); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(owneDirKk, testing::_, testing::_)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + // return two payment channel objects + std::vector bbs; + ripple::STObject channel1 = CreatePaymentChannelLedgerObject(ACCOUNT, ACCOUNT2, 100, 10, 32, TXNID, 28); + bbs.push_back(channel1.getSerializer().peekData()); + bbs.push_back(channel1.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const input = json::parse(fmt::format( + R"({{ + "account": "{}", + "limit": {} + }})", + ACCOUNT, + AccountChannelsHandler::LIMIT_MAX + 1)); + runSpawn([&, this](auto& yield) { + auto handler = AnyHandler{AccountChannelsHandler{this->mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ((*output).as_object().at("channels").as_array().size(), 2); + EXPECT_EQ((*output).as_object().at("limit").as_uint64(), AccountChannelsHandler::LIMIT_MAX); + }); +} diff --git a/unittests/rpc/handlers/AccountCurrenciesTest.cpp b/unittests/rpc/handlers/AccountCurrenciesTest.cpp index a5626b16..eeaeafa1 100644 --- a/unittests/rpc/handlers/AccountCurrenciesTest.cpp +++ b/unittests/rpc/handlers/AccountCurrenciesTest.cpp @@ -290,89 +290,3 @@ TEST_F(RPCAccountCurrenciesHandlerTest, RequestViaLegderSeq) EXPECT_EQ((*output).as_object().at("ledger_index").as_uint64(), ledgerSeq); }); } - -TEST_F(RPCAccountCurrenciesHandlerTest, RequestViaLegderSeqWithStrictTrue) -{ - auto const rawBackendPtr = static_cast(mockBackendPtr.get()); - mockBackendPtr->updateRange(10); // min - mockBackendPtr->updateRange(30); // max - auto const ledgerSeq = 29; - // return valid ledgerinfo - auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, ledgerSeq); - EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); - ON_CALL(*rawBackendPtr, fetchLedgerBySequence(ledgerSeq, _)).WillByDefault(Return(ledgerinfo)); - // return valid account - auto const accountKk = ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key; - ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, ledgerSeq, _)) - .WillByDefault(Return(Blob{'f', 'a', 'k', 'e'})); - - auto const ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}}, INDEX1); - auto const ownerDirKk = ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key; - ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, ledgerSeq, _)) - .WillByDefault(Return(ownerDir.getSerializer().peekData())); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); - std::vector bbs; - auto const line1 = - CreateRippleStateLedgerObject(ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123, 0); - bbs.push_back(line1.getSerializer().peekData()); - - ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); - auto const static input = boost::json::parse(fmt::format( - R"({{ - "account": "{}", - "ledger_index": {}, - "strict": true - }})", - ACCOUNT, - ledgerSeq)); - auto const handler = AnyHandler{AccountCurrenciesHandler{mockBackendPtr}}; - runSpawn([&](auto& yield) { - auto const output = handler.process(input, Context{std::ref(yield)}); - ASSERT_TRUE(output); - EXPECT_EQ((*output).as_object().at("ledger_index").as_uint64(), ledgerSeq); - }); -} - -TEST_F(RPCAccountCurrenciesHandlerTest, RequestViaLegderSeqWithStrictOfInvalidType) -{ - auto const rawBackendPtr = static_cast(mockBackendPtr.get()); - mockBackendPtr->updateRange(10); // min - mockBackendPtr->updateRange(30); // max - auto const ledgerSeq = 29; - // return valid ledgerinfo - auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, ledgerSeq); - EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); - ON_CALL(*rawBackendPtr, fetchLedgerBySequence(ledgerSeq, _)).WillByDefault(Return(ledgerinfo)); - // return valid account - auto const accountKk = ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key; - ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, ledgerSeq, _)) - .WillByDefault(Return(Blob{'f', 'a', 'k', 'e'})); - - auto const ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}}, INDEX1); - auto const ownerDirKk = ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key; - ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, ledgerSeq, _)) - .WillByDefault(Return(ownerDir.getSerializer().peekData())); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); - std::vector bbs; - auto const line1 = - CreateRippleStateLedgerObject(ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123, 0); - bbs.push_back(line1.getSerializer().peekData()); - - ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); - auto const static input = boost::json::parse(fmt::format( - R"({{ - "account": "{}", - "ledger_index": {}, - "strict": "test" - }})", - ACCOUNT, - ledgerSeq)); - auto const handler = AnyHandler{AccountCurrenciesHandler{mockBackendPtr}}; - runSpawn([&](auto& yield) { - auto const output = handler.process(input, Context{std::ref(yield)}); - ASSERT_TRUE(output); - EXPECT_EQ((*output).as_object().at("ledger_index").as_uint64(), ledgerSeq); - }); -} diff --git a/unittests/rpc/handlers/AccountInfoTest.cpp b/unittests/rpc/handlers/AccountInfoTest.cpp index f348a44c..bf2f0f85 100644 --- a/unittests/rpc/handlers/AccountInfoTest.cpp +++ b/unittests/rpc/handlers/AccountInfoTest.cpp @@ -449,72 +449,6 @@ TEST_F(RPCAccountInfoHandlerTest, Flags) }); } -TEST_F(RPCAccountInfoHandlerTest, StrictTrue) -{ - auto const rawBackendPtr = static_cast(mockBackendPtr.get()); - - mockBackendPtr->updateRange(10); // min - mockBackendPtr->updateRange(30); // max - - auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30); - EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); - ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); - - auto const account = GetAccountIDWithString(ACCOUNT); - auto const accountKk = ripple::keylet::account(account).key; - auto const accountRoot = CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2); - ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, 30, _)) - .WillByDefault(Return(accountRoot.getSerializer().peekData())); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1); - - auto const static input = boost::json::parse(fmt::format( - R"({{ - "account": "{}", - "strict": true - }})", - ACCOUNT)); - - auto const handler = AnyHandler{AccountInfoHandler{mockBackendPtr}}; - - runSpawn([&](auto& yield) { - auto const output = handler.process(input, Context{std::ref(yield)}); - ASSERT_TRUE(output); - }); -} - -TEST_F(RPCAccountInfoHandlerTest, StrictInvalidTypeHasNoEffect) -{ - auto const rawBackendPtr = static_cast(mockBackendPtr.get()); - - mockBackendPtr->updateRange(10); // min - mockBackendPtr->updateRange(30); // max - - auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30); - EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); - ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); - - auto const account = GetAccountIDWithString(ACCOUNT); - auto const accountKk = ripple::keylet::account(account).key; - auto const accountRoot = CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2); - ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, 30, _)) - .WillByDefault(Return(accountRoot.getSerializer().peekData())); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1); - - auto const static input = boost::json::parse(fmt::format( - R"({{ - "account": "{}", - "strict": "test" - }})", - ACCOUNT)); - - auto const handler = AnyHandler{AccountInfoHandler{mockBackendPtr}}; - - runSpawn([&](auto& yield) { - auto const output = handler.process(input, Context{std::ref(yield)}); - ASSERT_TRUE(output); - }); -} - TEST_F(RPCAccountInfoHandlerTest, IdentAndSignerListsFalse) { auto const rawBackendPtr = static_cast(mockBackendPtr.get()); diff --git a/unittests/rpc/handlers/AccountLinesTest.cpp b/unittests/rpc/handlers/AccountLinesTest.cpp index 56ba05fe..157aea6d 100644 --- a/unittests/rpc/handlers/AccountLinesTest.cpp +++ b/unittests/rpc/handlers/AccountLinesTest.cpp @@ -867,3 +867,169 @@ TEST_F(RPCAccountLinesHandlerTest, MarkerInput) EXPECT_EQ((*output).as_object().at("lines").as_array().size(), limit - 1); }); } + +TEST_F(RPCAccountLinesHandlerTest, LimitLessThanMin) +{ + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + // fetch account object return something + auto account = GetAccountIDWithString(ACCOUNT); + auto accountKk = ripple::keylet::account(account).key; + auto owneDirKk = ripple::keylet::ownerDir(account).key; + auto fake = Blob{'f', 'a', 'k', 'e'}; + // return a non empty account + ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, testing::_, testing::_)).WillByDefault(Return(fake)); + + // return owner index containing 2 indexes + ripple::STObject ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}, ripple::uint256{INDEX2}}, INDEX1); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(owneDirKk, testing::_, testing::_)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + // return two trust lines + std::vector bbs; + auto const line1 = + CreateRippleStateLedgerObject(ACCOUNT, "USD", ACCOUNT2, 10, ACCOUNT, 100, ACCOUNT2, 200, TXNID, 123); + auto const line2 = + CreateRippleStateLedgerObject(ACCOUNT2, "USD", ACCOUNT, 10, ACCOUNT2, 100, ACCOUNT, 200, TXNID, 123); + bbs.push_back(line1.getSerializer().peekData()); + bbs.push_back(line2.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + runSpawn([this](auto& yield) { + auto const input = json::parse(fmt::format( + R"({{ + "account": "{}", + "limit": {} + }})", + ACCOUNT, + AccountLinesHandler::LIMIT_MIN - 1)); + auto const correctOutput = fmt::format( + R"({{ + "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", + "ledger_index": 30, + "validated": true, + "limit": {}, + "lines": [ + {{ + "account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "balance": "10", + "currency": "USD", + "limit": "100", + "limit_peer": "200", + "quality_in": 0, + "quality_out": 0, + "no_ripple": false, + "no_ripple_peer": false + }}, + {{ + "account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "balance": "-10", + "currency": "USD", + "limit": "200", + "limit_peer": "100", + "quality_in": 0, + "quality_out": 0, + "no_ripple": false, + "no_ripple_peer": false + }} + ] + }})", + AccountLinesHandler::LIMIT_MIN); + + auto handler = AnyHandler{AccountLinesHandler{this->mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(json::parse(correctOutput), *output); + }); +} + +TEST_F(RPCAccountLinesHandlerTest, LimitMoreThanMax) +{ + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + // fetch account object return something + auto account = GetAccountIDWithString(ACCOUNT); + auto accountKk = ripple::keylet::account(account).key; + auto owneDirKk = ripple::keylet::ownerDir(account).key; + auto fake = Blob{'f', 'a', 'k', 'e'}; + // return a non empty account + ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, testing::_, testing::_)).WillByDefault(Return(fake)); + + // return owner index containing 2 indexes + ripple::STObject ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}, ripple::uint256{INDEX2}}, INDEX1); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(owneDirKk, testing::_, testing::_)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + // return two trust lines + std::vector bbs; + auto const line1 = + CreateRippleStateLedgerObject(ACCOUNT, "USD", ACCOUNT2, 10, ACCOUNT, 100, ACCOUNT2, 200, TXNID, 123); + auto const line2 = + CreateRippleStateLedgerObject(ACCOUNT2, "USD", ACCOUNT, 10, ACCOUNT2, 100, ACCOUNT, 200, TXNID, 123); + bbs.push_back(line1.getSerializer().peekData()); + bbs.push_back(line2.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + runSpawn([this](auto& yield) { + auto const input = json::parse(fmt::format( + R"({{ + "account": "{}", + "limit": {} + }})", + ACCOUNT, + AccountLinesHandler::LIMIT_MAX + 1)); + auto const correctOutput = fmt::format( + R"({{ + "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", + "ledger_index": 30, + "validated": true, + "limit": {}, + "lines": [ + {{ + "account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "balance": "10", + "currency": "USD", + "limit": "100", + "limit_peer": "200", + "quality_in": 0, + "quality_out": 0, + "no_ripple": false, + "no_ripple_peer": false + }}, + {{ + "account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "balance": "-10", + "currency": "USD", + "limit": "200", + "limit_peer": "100", + "quality_in": 0, + "quality_out": 0, + "no_ripple": false, + "no_ripple_peer": false + }} + ] + }})", + AccountLinesHandler::LIMIT_MAX); + + auto handler = AnyHandler{AccountLinesHandler{this->mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(json::parse(correctOutput), *output); + }); +} diff --git a/unittests/rpc/handlers/AccountNFTsTest.cpp b/unittests/rpc/handlers/AccountNFTsTest.cpp index 8c52a2d8..3ce11795 100644 --- a/unittests/rpc/handlers/AccountNFTsTest.cpp +++ b/unittests/rpc/handlers/AccountNFTsTest.cpp @@ -389,3 +389,129 @@ TEST_F(RPCAccountNFTsHandlerTest, Marker) EXPECT_EQ(output->as_object().at("account_nfts").as_array().size(), 1); }); } + +TEST_F(RPCAccountNFTsHandlerTest, LimitLessThanMin) +{ + static auto const expectedOutput = fmt::format( + R"({{ + "ledger_hash":"{}", + "ledger_index":30, + "validated":true, + "account":"{}", + "account_nfts":[ + {{ + "NFTokenID":"{}", + "URI":"7777772E6F6B2E636F6D", + "Flags":{}, + "Issuer":"{}", + "NFTokenTaxon":{}, + "nft_serial":{}, + "TransferFee":10000 + }} + ], + "limit":{} + }})", + LEDGERHASH, + ACCOUNT, + TOKENID, + FLAG, + ISSUER, + TAXON, + SERIAL, + AccountNFTsHandler::LIMIT_MIN); + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(MINSEQ); + mockBackendPtr->updateRange(MAXSEQ); + auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, MAXSEQ); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + + auto const accountObject = CreateAccountRootObject(ACCOUNT, 0, 1, 10, 2, TXNID, 3); + auto const accountID = GetAccountIDWithString(ACCOUNT); + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::account(accountID).key, 30, _)) + .WillByDefault(Return(accountObject.getSerializer().peekData())); + + auto const firstPage = ripple::keylet::nftpage_max(accountID).key; + auto const pageObject = + CreateNFTTokenPage(std::vector{std::make_pair(TOKENID, "www.ok.com")}, std::nullopt); + ON_CALL(*rawBackendPtr, doFetchLedgerObject(firstPage, 30, _)) + .WillByDefault(Return(pageObject.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + auto const static input = boost::json::parse(fmt::format( + R"({{ + "account":"{}", + "limit":{} + }})", + ACCOUNT, + AccountNFTsHandler::LIMIT_MIN - 1)); + auto const handler = AnyHandler{AccountNFTsHandler{mockBackendPtr}}; + runSpawn([&](auto& yield) { + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(*output, json::parse(expectedOutput)); + }); +} + +TEST_F(RPCAccountNFTsHandlerTest, LimitMoreThanMax) +{ + static auto const expectedOutput = fmt::format( + R"({{ + "ledger_hash":"{}", + "ledger_index":30, + "validated":true, + "account":"{}", + "account_nfts":[ + {{ + "NFTokenID":"{}", + "URI":"7777772E6F6B2E636F6D", + "Flags":{}, + "Issuer":"{}", + "NFTokenTaxon":{}, + "nft_serial":{}, + "TransferFee":10000 + }} + ], + "limit":{} + }})", + LEDGERHASH, + ACCOUNT, + TOKENID, + FLAG, + ISSUER, + TAXON, + SERIAL, + AccountNFTsHandler::LIMIT_MAX); + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(MINSEQ); + mockBackendPtr->updateRange(MAXSEQ); + auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, MAXSEQ); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + + auto const accountObject = CreateAccountRootObject(ACCOUNT, 0, 1, 10, 2, TXNID, 3); + auto const accountID = GetAccountIDWithString(ACCOUNT); + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::account(accountID).key, 30, _)) + .WillByDefault(Return(accountObject.getSerializer().peekData())); + + auto const firstPage = ripple::keylet::nftpage_max(accountID).key; + auto const pageObject = + CreateNFTTokenPage(std::vector{std::make_pair(TOKENID, "www.ok.com")}, std::nullopt); + ON_CALL(*rawBackendPtr, doFetchLedgerObject(firstPage, 30, _)) + .WillByDefault(Return(pageObject.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + auto const static input = boost::json::parse(fmt::format( + R"({{ + "account":"{}", + "limit":{} + }})", + ACCOUNT, + AccountNFTsHandler::LIMIT_MAX + 1)); + auto const handler = AnyHandler{AccountNFTsHandler{mockBackendPtr}}; + runSpawn([&](auto& yield) { + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(*output, json::parse(expectedOutput)); + }); +} diff --git a/unittests/rpc/handlers/AccountObjectsTest.cpp b/unittests/rpc/handlers/AccountObjectsTest.cpp index d78a5982..58593d6c 100644 --- a/unittests/rpc/handlers/AccountObjectsTest.cpp +++ b/unittests/rpc/handlers/AccountObjectsTest.cpp @@ -1526,3 +1526,165 @@ TEST_F(RPCAccountObjectsHandlerTest, NFTZeroMarkerNotAffectOtherMarker) EXPECT_EQ(output->as_object().at("marker").as_string(), fmt::format("{},{}", INDEX1, 0)); }); } + +TEST_F(RPCAccountObjectsHandlerTest, LimitLessThanMin) +{ + static auto const expectedOut = fmt::format( + R"({{ + "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", + "ledger_index":30, + "validated":true, + "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "limit": {}, + "account_objects":[ + {{ + "Balance":{{ + "currency":"USD", + "issuer":"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW", + "value":"100" + }}, + "Flags":0, + "HighLimit":{{ + "currency":"USD", + "issuer":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "value":"20" + }}, + "LedgerEntryType":"RippleState", + "LowLimit":{{ + "currency":"USD", + "issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "value":"10" + }}, + "PreviousTxnID":"E3FE6EA3D48F0C2B639448020EA4F03D4F4F8FFDB243A852A0F59177921B4879", + "PreviousTxnLgrSeq":123, + "index":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" + }} + ] + }})", + AccountObjectsHandler::LIMIT_MIN); + + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(MINSEQ); // min + mockBackendPtr->updateRange(MAXSEQ); // max + auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, MAXSEQ); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + + auto const account = GetAccountIDWithString(ACCOUNT); + auto const accountKk = ripple::keylet::account(account).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, MAXSEQ, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'})); + + auto const ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}}, INDEX1); + auto const ownerDirKk = ripple::keylet::ownerDir(account).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, 30, _)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + + // nft null + auto const nftMaxKK = ripple::keylet::nftpage_max(account).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(nftMaxKK, 30, _)).WillByDefault(Return(std::nullopt)); + + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(3); + std::vector bbs; + auto const line1 = + CreateRippleStateLedgerObject(ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123, 0); + bbs.push_back(line1.getSerializer().peekData()); + + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const static input = boost::json::parse(fmt::format( + R"({{ + "account":"{}", + "limit": {} + }})", + ACCOUNT, + AccountObjectsHandler::LIMIT_MIN - 1)); + + auto const handler = AnyHandler{AccountObjectsHandler{mockBackendPtr}}; + runSpawn([&](auto& yield) { + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(*output, json::parse(expectedOut)); + }); +} + +TEST_F(RPCAccountObjectsHandlerTest, LimitMoreThanMax) +{ + static auto const expectedOut = fmt::format( + R"({{ + "ledger_hash":"4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", + "ledger_index":30, + "validated":true, + "account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "limit": {}, + "account_objects":[ + {{ + "Balance":{{ + "currency":"USD", + "issuer":"rsA2LpzuawewSBQXkiju3YQTMzW13pAAdW", + "value":"100" + }}, + "Flags":0, + "HighLimit":{{ + "currency":"USD", + "issuer":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", + "value":"20" + }}, + "LedgerEntryType":"RippleState", + "LowLimit":{{ + "currency":"USD", + "issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn", + "value":"10" + }}, + "PreviousTxnID":"E3FE6EA3D48F0C2B639448020EA4F03D4F4F8FFDB243A852A0F59177921B4879", + "PreviousTxnLgrSeq":123, + "index":"1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC" + }} + ] + }})", + AccountObjectsHandler::LIMIT_MAX); + + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(MINSEQ); // min + mockBackendPtr->updateRange(MAXSEQ); // max + auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, MAXSEQ); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + + auto const account = GetAccountIDWithString(ACCOUNT); + auto const accountKk = ripple::keylet::account(account).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, MAXSEQ, _)).WillByDefault(Return(Blob{'f', 'a', 'k', 'e'})); + + auto const ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}}, INDEX1); + auto const ownerDirKk = ripple::keylet::ownerDir(account).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, 30, _)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + + // nft null + auto const nftMaxKK = ripple::keylet::nftpage_max(account).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(nftMaxKK, 30, _)).WillByDefault(Return(std::nullopt)); + + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(3); + std::vector bbs; + auto const line1 = + CreateRippleStateLedgerObject(ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123, 0); + bbs.push_back(line1.getSerializer().peekData()); + + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const static input = boost::json::parse(fmt::format( + R"({{ + "account":"{}", + "limit": {} + }})", + ACCOUNT, + AccountObjectsHandler::LIMIT_MAX + 1)); + + auto const handler = AnyHandler{AccountObjectsHandler{mockBackendPtr}}; + runSpawn([&](auto& yield) { + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(*output, json::parse(expectedOut)); + }); +} diff --git a/unittests/rpc/handlers/AccountOffersTest.cpp b/unittests/rpc/handlers/AccountOffersTest.cpp index d0049fb9..3565b030 100644 --- a/unittests/rpc/handlers/AccountOffersTest.cpp +++ b/unittests/rpc/handlers/AccountOffersTest.cpp @@ -325,108 +325,6 @@ TEST_F(RPCAccountOffersHandlerTest, DefaultParams) }); } -TEST_F(RPCAccountOffersHandlerTest, DefaultParamsWithStrictTrue) -{ - auto constexpr ledgerSeq = 30; - auto const rawBackendPtr = static_cast(mockBackendPtr.get()); - - mockBackendPtr->updateRange(10); // min - mockBackendPtr->updateRange(ledgerSeq); // max - - auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, ledgerSeq); - EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); - - ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); - auto const accountKk = ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key; - ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, ledgerSeq, _)) - .WillByDefault(Return(Blob{'f', 'a', 'k', 'e'})); - - auto const ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}}, INDEX1); - auto const ownerDirKk = ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key; - ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, ledgerSeq, _)) - .WillByDefault(Return(ownerDir.getSerializer().peekData())); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); - - std::vector bbs; - auto offer = CreateOfferLedgerObject( - ACCOUNT, - 10, - 20, - ripple::to_string(ripple::to_currency("USD")), - ripple::to_string(ripple::xrpCurrency()), - ACCOUNT2, - toBase58(ripple::xrpAccount()), - INDEX1); - offer.setFieldU32(ripple::sfExpiration, 123); - bbs.push_back(offer.getSerializer().peekData()); - - ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); - - auto const static input = boost::json::parse(fmt::format( - R"({{ - "account": "{}", - "strict": true - }})", - ACCOUNT)); - auto const handler = AnyHandler{AccountOffersHandler{mockBackendPtr}}; - runSpawn([&](auto& yield) { - auto const output = handler.process(input, Context{std::ref(yield)}); - ASSERT_TRUE(output); - }); -} - -TEST_F(RPCAccountOffersHandlerTest, DefaultParamsWithInvalidTypeStrict) -{ - auto constexpr ledgerSeq = 30; - auto const rawBackendPtr = static_cast(mockBackendPtr.get()); - - mockBackendPtr->updateRange(10); // min - mockBackendPtr->updateRange(ledgerSeq); // max - - auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, ledgerSeq); - EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); - - ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); - auto const accountKk = ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key; - ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, ledgerSeq, _)) - .WillByDefault(Return(Blob{'f', 'a', 'k', 'e'})); - - auto const ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}}, INDEX1); - auto const ownerDirKk = ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key; - ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, ledgerSeq, _)) - .WillByDefault(Return(ownerDir.getSerializer().peekData())); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); - - std::vector bbs; - auto offer = CreateOfferLedgerObject( - ACCOUNT, - 10, - 20, - ripple::to_string(ripple::to_currency("USD")), - ripple::to_string(ripple::xrpCurrency()), - ACCOUNT2, - toBase58(ripple::xrpAccount()), - INDEX1); - offer.setFieldU32(ripple::sfExpiration, 123); - bbs.push_back(offer.getSerializer().peekData()); - - ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); - EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); - - auto const static input = boost::json::parse(fmt::format( - R"({{ - "account": "{}", - "strict": "test" - }})", - ACCOUNT)); - auto const handler = AnyHandler{AccountOffersHandler{mockBackendPtr}}; - runSpawn([&](auto& yield) { - auto const output = handler.process(input, Context{std::ref(yield)}); - ASSERT_TRUE(output); - }); -} - TEST_F(RPCAccountOffersHandlerTest, Limit) { auto constexpr ledgerSeq = 30; @@ -574,3 +472,111 @@ TEST_F(RPCAccountOffersHandlerTest, MarkerNotExists) EXPECT_EQ(err.at("error_message").as_string(), "Invalid marker."); }); } + +TEST_F(RPCAccountOffersHandlerTest, LimitLessThanMin) +{ + auto constexpr ledgerSeq = 30; + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(ledgerSeq); // max + auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, ledgerSeq); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + auto const accountKk = ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, ledgerSeq, _)) + .WillByDefault(Return(Blob{'f', 'a', 'k', 'e'})); + + auto const ownerDir = + CreateOwnerDirLedgerObject(std::vector{AccountOffersHandler::LIMIT_MIN + 1, ripple::uint256{INDEX1}}, INDEX1); + auto const ownerDirKk = ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, ledgerSeq, _)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + std::vector bbs; + auto offer = CreateOfferLedgerObject( + ACCOUNT, + 10, + 20, + ripple::to_string(ripple::to_currency("USD")), + ripple::to_string(ripple::xrpCurrency()), + ACCOUNT2, + toBase58(ripple::xrpAccount()), + INDEX1); + offer.setFieldU32(ripple::sfExpiration, 123); + + for (auto i = 0; i < AccountOffersHandler::LIMIT_MIN + 1; i++) + bbs.push_back(offer.getSerializer().peekData()); + + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const static input = boost::json::parse(fmt::format( + R"({{ + "account":"{}", + "limit":{} + }})", + ACCOUNT, + AccountOffersHandler::LIMIT_MIN - 1)); + auto const handler = AnyHandler{AccountOffersHandler{mockBackendPtr}}; + runSpawn([&](auto& yield) { + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output->at("offers").as_array().size(), AccountOffersHandler::LIMIT_MIN); + }); +} + +TEST_F(RPCAccountOffersHandlerTest, LimitMoreThanMax) +{ + auto constexpr ledgerSeq = 30; + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(ledgerSeq); // max + auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, ledgerSeq); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo)); + auto const accountKk = ripple::keylet::account(GetAccountIDWithString(ACCOUNT)).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(accountKk, ledgerSeq, _)) + .WillByDefault(Return(Blob{'f', 'a', 'k', 'e'})); + + auto const ownerDir = + CreateOwnerDirLedgerObject(std::vector{AccountOffersHandler::LIMIT_MAX + 1, ripple::uint256{INDEX1}}, INDEX1); + + auto const ownerDirKk = ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, ledgerSeq, _)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + std::vector bbs; + auto offer = CreateOfferLedgerObject( + ACCOUNT, + 10, + 20, + ripple::to_string(ripple::to_currency("USD")), + ripple::to_string(ripple::xrpCurrency()), + ACCOUNT2, + toBase58(ripple::xrpAccount()), + INDEX1); + offer.setFieldU32(ripple::sfExpiration, 123); + for (auto i = 0; i < AccountOffersHandler::LIMIT_MAX + 1; i++) + bbs.push_back(offer.getSerializer().peekData()); + + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const static input = boost::json::parse(fmt::format( + R"({{ + "account":"{}", + "limit":{} + }})", + ACCOUNT, + AccountOffersHandler::LIMIT_MAX + 1)); + auto const handler = AnyHandler{AccountOffersHandler{mockBackendPtr}}; + runSpawn([&](auto& yield) { + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output->at("offers").as_array().size(), AccountOffersHandler::LIMIT_MAX); + }); +} diff --git a/unittests/rpc/handlers/AccountTxTest.cpp b/unittests/rpc/handlers/AccountTxTest.cpp index ca0d6854..5bcae854 100644 --- a/unittests/rpc/handlers/AccountTxTest.cpp +++ b/unittests/rpc/handlers/AccountTxTest.cpp @@ -763,3 +763,43 @@ TEST_F(RPCAccountTxHandlerTest, TxLargerThanMaxSeq) EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); }); } + +TEST_F(RPCAccountTxHandlerTest, LimitLessThanMin) +{ + mockBackendPtr->updateRange(MINSEQ); // min + mockBackendPtr->updateRange(MAXSEQ); // max + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1); + auto const transCursor = TransactionsAndCursor{transactions, TransactionsCursor{12, 34}}; + ON_CALL(*rawBackendPtr, fetchAccountTransactions).WillByDefault(Return(transCursor)); + EXPECT_CALL( + *rawBackendPtr, + fetchAccountTransactions( + testing::_, testing::_, false, testing::Optional(testing::Eq(TransactionsCursor{10, 11})), testing::_)) + .Times(1); + + runSpawn([&, this](auto& yield) { + auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}}; + auto const static input = boost::json::parse(fmt::format( + R"({{ + "account":"{}", + "ledger_index_min": {}, + "ledger_index_max": {}, + "limit": {}, + "forward": false, + "marker": {{"ledger":10,"seq":11}} + }})", + ACCOUNT, + -1, + -1, + AccountTxHandler::LIMIT_MIN - 1)); + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output->at("account").as_string(), ACCOUNT); + EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ); + EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ); + EXPECT_EQ(output->at("limit").as_uint64(), AccountTxHandler::LIMIT_MIN); + EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output->at("transactions").as_array().size(), 2); + }); +} diff --git a/unittests/rpc/handlers/BookOffersTest.cpp b/unittests/rpc/handlers/BookOffersTest.cpp index a2b5bd83..3e1adce7 100644 --- a/unittests/rpc/handlers/BookOffersTest.cpp +++ b/unittests/rpc/handlers/BookOffersTest.cpp @@ -1238,4 +1238,146 @@ TEST_F(RPCBookOffersHandlerTest, Limit) }); } -// taker +TEST_F(RPCBookOffersHandlerTest, LimitLessThanMin) +{ + auto const seq = 300; + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(seq); // max + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + // return valid ledgerinfo + auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, seq); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence(seq, _)).WillByDefault(Return(ledgerinfo)); + + auto const issuer = GetAccountIDWithString(ACCOUNT); + // return valid book dir + EXPECT_CALL(*rawBackendPtr, doFetchSuccessorKey).Times(1); + + auto const getsXRPPaysUSDBook = getBookBase(std::get( + RPC::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()))); + ON_CALL(*rawBackendPtr, doFetchSuccessorKey(getsXRPPaysUSDBook, seq, _)) + .WillByDefault(Return(ripple::uint256{PAYS20USDGETS10XRPBOOKDIR})); + + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(5); + auto const indexes = std::vector(10, ripple::uint256{INDEX2}); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::uint256{PAYS20USDGETS10XRPBOOKDIR}, seq, _)) + .WillByDefault(Return(CreateOwnerDirLedgerObject(indexes, INDEX1).getSerializer().peekData())); + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT2)).key, seq, _)) + .WillByDefault(Return(CreateAccountRootObject(ACCOUNT2, 0, 2, 200, 2, INDEX1, 2).getSerializer().peekData())); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::fees().key, seq, _)) + .WillByDefault(Return(CreateFeeSettingBlob(1, 2, 3, 4, 0))); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::account(issuer).key, seq, _)) + .WillByDefault(Return( + CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2, TRANSFERRATEX2).getSerializer().peekData())); + + auto const gets10XRPPays20USDOffer = CreateOfferLedgerObject( + ACCOUNT2, + 10, + 20, + ripple::to_string(ripple::xrpCurrency()), + ripple::to_string(ripple::to_currency("USD")), + toBase58(ripple::xrpAccount()), + ACCOUNT, + PAYS20USDGETS10XRPBOOKDIR); + + std::vector bbs(10, gets10XRPPays20USDOffer.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const static input = boost::json::parse(fmt::format( + R"({{ + "taker_gets": + {{ + "currency": "XRP" + }}, + "taker_pays": + {{ + "currency": "USD", + "issuer": "{}" + }}, + "limit": {} + }})", + ACCOUNT, + BookOffersHandler::LIMIT_MIN - 1)); + auto const handler = AnyHandler{BookOffersHandler{mockBackendPtr}}; + runSpawn([&](boost::asio::yield_context yield) { + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output.value().as_object().at("offers").as_array().size(), BookOffersHandler::LIMIT_MIN); + }); +} + +TEST_F(RPCBookOffersHandlerTest, LimitMoreThanMax) +{ + auto const seq = 300; + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(seq); // max + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + // return valid ledgerinfo + auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, seq); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence(seq, _)).WillByDefault(Return(ledgerinfo)); + + auto const issuer = GetAccountIDWithString(ACCOUNT); + // return valid book dir + EXPECT_CALL(*rawBackendPtr, doFetchSuccessorKey).Times(1); + + auto const getsXRPPaysUSDBook = getBookBase(std::get( + RPC::parseBook(ripple::to_currency("USD"), issuer, ripple::xrpCurrency(), ripple::xrpAccount()))); + ON_CALL(*rawBackendPtr, doFetchSuccessorKey(getsXRPPaysUSDBook, seq, _)) + .WillByDefault(Return(ripple::uint256{PAYS20USDGETS10XRPBOOKDIR})); + + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(5); + auto const indexes = std::vector(BookOffersHandler::LIMIT_MAX + 1, ripple::uint256{INDEX2}); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::uint256{PAYS20USDGETS10XRPBOOKDIR}, seq, _)) + .WillByDefault(Return(CreateOwnerDirLedgerObject(indexes, INDEX1).getSerializer().peekData())); + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::account(GetAccountIDWithString(ACCOUNT2)).key, seq, _)) + .WillByDefault(Return(CreateAccountRootObject(ACCOUNT2, 0, 2, 200, 2, INDEX1, 2).getSerializer().peekData())); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::fees().key, seq, _)) + .WillByDefault(Return(CreateFeeSettingBlob(1, 2, 3, 4, 0))); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::account(issuer).key, seq, _)) + .WillByDefault(Return( + CreateAccountRootObject(ACCOUNT, 0, 2, 200, 2, INDEX1, 2, TRANSFERRATEX2).getSerializer().peekData())); + + auto const gets10XRPPays20USDOffer = CreateOfferLedgerObject( + ACCOUNT2, + 10, + 20, + ripple::to_string(ripple::xrpCurrency()), + ripple::to_string(ripple::to_currency("USD")), + toBase58(ripple::xrpAccount()), + ACCOUNT, + PAYS20USDGETS10XRPBOOKDIR); + + std::vector bbs(BookOffersHandler::LIMIT_MAX + 1, gets10XRPPays20USDOffer.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const static input = boost::json::parse(fmt::format( + R"({{ + "taker_gets": + {{ + "currency": "XRP" + }}, + "taker_pays": + {{ + "currency": "USD", + "issuer": "{}" + }}, + "limit": {} + }})", + ACCOUNT, + BookOffersHandler::LIMIT_MAX + 1)); + auto const handler = AnyHandler{BookOffersHandler{mockBackendPtr}}; + runSpawn([&](boost::asio::yield_context yield) { + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output.value().as_object().at("offers").as_array().size(), BookOffersHandler::LIMIT_MAX); + }); +} diff --git a/unittests/rpc/handlers/GatewayBalancesTest.cpp b/unittests/rpc/handlers/GatewayBalancesTest.cpp index 076d71fb..ac20f120 100644 --- a/unittests/rpc/handlers/GatewayBalancesTest.cpp +++ b/unittests/rpc/handlers/GatewayBalancesTest.cpp @@ -608,82 +608,6 @@ generateNormalPathTestBundles() ACCOUNT2, ACCOUNT), fmt::format(R"("hotwallet": ["{}", "{}"])", ACCOUNT2, ACCOUNT3)}, - NormalTestBundle{ - "StrictTrue", - CreateOwnerDirLedgerObject( - {ripple::uint256{INDEX2}, ripple::uint256{INDEX2}, ripple::uint256{INDEX2}}, INDEX1), - std::vector{ - CreateRippleStateLedgerObject(ACCOUNT, "USD", ISSUER, -10, ACCOUNT, 100, ACCOUNT2, 200, TXNID, 123), - CreateRippleStateLedgerObject(ACCOUNT, "CNY", ISSUER, -20, ACCOUNT, 100, ACCOUNT2, 200, TXNID, 123), - CreateRippleStateLedgerObject(ACCOUNT, "EUR", ISSUER, -30, ACCOUNT, 100, ACCOUNT3, 200, TXNID, 123) - - }, - fmt::format( - R"({{ - "balances": {{ - "{}": [ - {{ - "currency": "EUR", - "value": "30" - }} - ], - "{}": [ - {{ - "currency": "USD", - "value": "10" - }}, - {{ - "currency": "CNY", - "value": "20" - }} - ] - }}, - "account": "{}", - "ledger_index": 300, - "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - }})", - ACCOUNT3, - ACCOUNT2, - ACCOUNT), - fmt::format(R"("hotwallet": ["{}", "{}"], "strict": true)", ACCOUNT2, ACCOUNT3)}, - NormalTestBundle{ - "StrictInvalidTypeHasNoEffect", - CreateOwnerDirLedgerObject( - {ripple::uint256{INDEX2}, ripple::uint256{INDEX2}, ripple::uint256{INDEX2}}, INDEX1), - std::vector{ - CreateRippleStateLedgerObject(ACCOUNT, "USD", ISSUER, -10, ACCOUNT, 100, ACCOUNT2, 200, TXNID, 123), - CreateRippleStateLedgerObject(ACCOUNT, "CNY", ISSUER, -20, ACCOUNT, 100, ACCOUNT2, 200, TXNID, 123), - CreateRippleStateLedgerObject(ACCOUNT, "EUR", ISSUER, -30, ACCOUNT, 100, ACCOUNT3, 200, TXNID, 123) - - }, - fmt::format( - R"({{ - "balances": {{ - "{}": [ - {{ - "currency": "EUR", - "value": "30" - }} - ], - "{}": [ - {{ - "currency": "USD", - "value": "10" - }}, - {{ - "currency": "CNY", - "value": "20" - }} - ] - }}, - "account": "{}", - "ledger_index": 300, - "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652" - }})", - ACCOUNT3, - ACCOUNT2, - ACCOUNT), - fmt::format(R"("hotwallet": ["{}", "{}"], "strict": "test")", ACCOUNT2, ACCOUNT3)}, }; } diff --git a/unittests/rpc/handlers/LedgerDataTest.cpp b/unittests/rpc/handlers/LedgerDataTest.cpp index 778372fb..27042bc9 100644 --- a/unittests/rpc/handlers/LedgerDataTest.cpp +++ b/unittests/rpc/handlers/LedgerDataTest.cpp @@ -556,3 +556,92 @@ TEST_F(RPCLedgerDataHandlerTest, Binary) EXPECT_EQ(output->as_object().at("ledger_index").as_uint64(), RANGEMAX); }); } + +TEST_F(RPCLedgerDataHandlerTest, BinaryLimitMoreThanMax) +{ + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(RANGEMIN); // min + mockBackendPtr->updateRange(RANGEMAX); // max + + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence(RANGEMAX, _)) + .WillByDefault(Return(CreateLedgerInfo(LEDGERHASH, RANGEMAX))); + + auto limit = LedgerDataHandler::LIMITBINARY + 1; + std::vector bbs; + + EXPECT_CALL(*rawBackendPtr, doFetchSuccessorKey).Times(LedgerDataHandler::LIMITBINARY); + ON_CALL(*rawBackendPtr, doFetchSuccessorKey(_, RANGEMAX, _)).WillByDefault(Return(ripple::uint256{INDEX2})); + + while (limit--) + { + auto const line = + CreateRippleStateLedgerObject(ACCOUNT, "USD", ACCOUNT2, 10, ACCOUNT, 100, ACCOUNT2, 200, TXNID, 123); + bbs.push_back(line.getSerializer().peekData()); + } + + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + runSpawn([&, this](auto& yield) { + auto const handler = AnyHandler{LedgerDataHandler{mockBackendPtr}}; + auto const req = json::parse(fmt::format( + R"({{ + "limit":{}, + "binary": true + }})", + LedgerDataHandler::LIMITBINARY + 1)); + auto const output = handler.process(req, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_TRUE(output->as_object().contains("ledger")); + EXPECT_TRUE(output->as_object().at("ledger").as_object().contains("ledger_data")); + EXPECT_TRUE(output->as_object().at("ledger").as_object().at("closed").as_bool()); + EXPECT_EQ(output->as_object().at("state").as_array().size(), LedgerDataHandler::LIMITBINARY); + EXPECT_EQ(output->as_object().at("ledger_hash").as_string(), LEDGERHASH); + EXPECT_EQ(output->as_object().at("ledger_index").as_uint64(), RANGEMAX); + }); +} + +TEST_F(RPCLedgerDataHandlerTest, JsonLimitMoreThanMax) +{ + auto const rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(RANGEMIN); // min + mockBackendPtr->updateRange(RANGEMAX); // max + + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence(RANGEMAX, _)) + .WillByDefault(Return(CreateLedgerInfo(LEDGERHASH, RANGEMAX))); + + auto limit = LedgerDataHandler::LIMITJSON + 1; + std::vector bbs; + + EXPECT_CALL(*rawBackendPtr, doFetchSuccessorKey).Times(LedgerDataHandler::LIMITJSON); + ON_CALL(*rawBackendPtr, doFetchSuccessorKey(_, RANGEMAX, _)).WillByDefault(Return(ripple::uint256{INDEX2})); + + while (limit--) + { + auto const line = + CreateRippleStateLedgerObject(ACCOUNT, "USD", ACCOUNT2, 10, ACCOUNT, 100, ACCOUNT2, 200, TXNID, 123); + bbs.push_back(line.getSerializer().peekData()); + } + + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + runSpawn([&, this](auto& yield) { + auto const handler = AnyHandler{LedgerDataHandler{mockBackendPtr}}; + auto const req = json::parse(fmt::format( + R"({{ + "limit":{}, + "binary": false + }})", + LedgerDataHandler::LIMITJSON + 1)); + auto const output = handler.process(req, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_TRUE(output->as_object().contains("ledger")); + EXPECT_TRUE(output->as_object().at("ledger").as_object().at("closed").as_bool()); + EXPECT_EQ(output->as_object().at("state").as_array().size(), LedgerDataHandler::LIMITJSON); + EXPECT_EQ(output->as_object().at("ledger_hash").as_string(), LEDGERHASH); + EXPECT_EQ(output->as_object().at("ledger_index").as_uint64(), RANGEMAX); + }); +} diff --git a/unittests/rpc/handlers/NFTBuyOffersTest.cpp b/unittests/rpc/handlers/NFTBuyOffersTest.cpp index 61266e7b..625f3d8e 100644 --- a/unittests/rpc/handlers/NFTBuyOffersTest.cpp +++ b/unittests/rpc/handlers/NFTBuyOffersTest.cpp @@ -567,3 +567,89 @@ TEST_F(RPCNFTBuyOffersHandlerTest, ResultsWithoutMarkerForInputWithMarkerAndLimi ASSERT_TRUE(output); // todo: check limit somehow? }); } + +TEST_F(RPCNFTBuyOffersHandlerTest, LimitLessThanMin) +{ + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerInfo = CreateLedgerInfo(LEDGERHASH, 30); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerInfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + + // return owner index containing 2 indexes + auto const directory = ripple::keylet::nft_buys(ripple::uint256{NFTID}); + auto const ownerDir = + CreateOwnerDirLedgerObject(std::vector{NFTBuyOffersHandler::LIMIT_MIN + 1, ripple::uint256{INDEX1}}, INDEX1); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(directory.key, testing::_, testing::_)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject(directory.key, testing::_, testing::_)).Times(2); + + // return two nft buy offers + std::vector bbs; + auto const offer = CreateNFTBuyOffer(NFTID, ACCOUNT); + for (auto i = 0; i < NFTBuyOffersHandler::LIMIT_MIN + 1; i++) + bbs.push_back(offer.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const input = json::parse(fmt::format( + R"({{ + "nft_id": "{}", + "limit": {} + }})", + NFTID, + NFTBuyOffersHandler::LIMIT_MIN - 1)); + runSpawn([&, this](auto& yield) { + auto handler = AnyHandler{NFTBuyOffersHandler{this->mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + + ASSERT_TRUE(output); + EXPECT_EQ(output->at("offers").as_array().size(), NFTBuyOffersHandler::LIMIT_MIN); + EXPECT_EQ(output->at("limit").as_uint64(), NFTBuyOffersHandler::LIMIT_MIN); + }); +} + +TEST_F(RPCNFTBuyOffersHandlerTest, LimitMoreThanMax) +{ + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerInfo = CreateLedgerInfo(LEDGERHASH, 30); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerInfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + + // return owner index containing 2 indexes + auto const directory = ripple::keylet::nft_buys(ripple::uint256{NFTID}); + auto const ownerDir = + CreateOwnerDirLedgerObject(std::vector{NFTBuyOffersHandler::LIMIT_MAX + 1, ripple::uint256{INDEX1}}, INDEX1); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(directory.key, testing::_, testing::_)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject(directory.key, testing::_, testing::_)).Times(2); + + // return two nft buy offers + std::vector bbs; + auto const offer = CreateNFTBuyOffer(NFTID, ACCOUNT); + for (auto i = 0; i < NFTBuyOffersHandler::LIMIT_MAX + 1; i++) + bbs.push_back(offer.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const input = json::parse(fmt::format( + R"({{ + "nft_id": "{}", + "limit": {} + }})", + NFTID, + NFTBuyOffersHandler::LIMIT_MAX + 1)); + runSpawn([&, this](auto& yield) { + auto handler = AnyHandler{NFTBuyOffersHandler{this->mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + + ASSERT_TRUE(output); + EXPECT_EQ(output->at("offers").as_array().size(), NFTBuyOffersHandler::LIMIT_MAX); + EXPECT_EQ(output->at("limit").as_uint64(), NFTBuyOffersHandler::LIMIT_MAX); + }); +} diff --git a/unittests/rpc/handlers/NFTHistoryTest.cpp b/unittests/rpc/handlers/NFTHistoryTest.cpp index 14fd88f2..aa5467b2 100644 --- a/unittests/rpc/handlers/NFTHistoryTest.cpp +++ b/unittests/rpc/handlers/NFTHistoryTest.cpp @@ -709,3 +709,89 @@ TEST_F(RPCNFTHistoryHandlerTest, TxLargerThanMaxSeq) EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); }); } + +TEST_F(RPCNFTHistoryHandlerTest, LimitLessThanMin) +{ + mockBackendPtr->updateRange(MINSEQ); // min + mockBackendPtr->updateRange(MAXSEQ); // max + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1); + auto const transCursor = TransactionsAndCursor{transactions, TransactionsCursor{12, 34}}; + ON_CALL(*rawBackendPtr, fetchNFTTransactions).WillByDefault(Return(transCursor)); + EXPECT_CALL( + *rawBackendPtr, + fetchNFTTransactions( + testing::_, + testing::_, + false, + testing::Optional(testing::Eq(TransactionsCursor{MAXSEQ - 1, INT32_MAX})), + testing::_)) + .Times(1); + + runSpawn([&, this](auto& yield) { + auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}}; + auto const static input = boost::json::parse(fmt::format( + R"({{ + "nft_id":"{}", + "ledger_index_min": {}, + "ledger_index_max": {}, + "forward": false, + "limit": {} + }})", + NFTID, + MINSEQ + 1, + MAXSEQ - 1, + NFTHistoryHandler::LIMIT_MIN - 1)); + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output->at("nft_id").as_string(), NFTID); + EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ + 1); + EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1); + EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output->at("transactions").as_array().size(), 2); + EXPECT_EQ(output->as_object().at("limit").as_uint64(), NFTHistoryHandler::LIMIT_MIN); + }); +} + +TEST_F(RPCNFTHistoryHandlerTest, LimitMoreThanMax) +{ + mockBackendPtr->updateRange(MINSEQ); // min + mockBackendPtr->updateRange(MAXSEQ); // max + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + auto const transactions = genTransactions(MINSEQ + 1, MAXSEQ - 1); + auto const transCursor = TransactionsAndCursor{transactions, TransactionsCursor{12, 34}}; + ON_CALL(*rawBackendPtr, fetchNFTTransactions).WillByDefault(Return(transCursor)); + EXPECT_CALL( + *rawBackendPtr, + fetchNFTTransactions( + testing::_, + testing::_, + false, + testing::Optional(testing::Eq(TransactionsCursor{MAXSEQ - 1, INT32_MAX})), + testing::_)) + .Times(1); + + runSpawn([&, this](auto& yield) { + auto const handler = AnyHandler{NFTHistoryHandler{mockBackendPtr}}; + auto const static input = boost::json::parse(fmt::format( + R"({{ + "nft_id":"{}", + "ledger_index_min": {}, + "ledger_index_max": {}, + "forward": false, + "limit": {} + }})", + NFTID, + MINSEQ + 1, + MAXSEQ - 1, + NFTHistoryHandler::LIMIT_MAX + 1)); + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output->at("nft_id").as_string(), NFTID); + EXPECT_EQ(output->at("ledger_index_min").as_uint64(), MINSEQ + 1); + EXPECT_EQ(output->at("ledger_index_max").as_uint64(), MAXSEQ - 1); + EXPECT_EQ(output->at("marker").as_object(), json::parse(R"({"ledger":12,"seq":34})")); + EXPECT_EQ(output->at("transactions").as_array().size(), 2); + EXPECT_EQ(output->as_object().at("limit").as_uint64(), NFTHistoryHandler::LIMIT_MAX); + }); +} diff --git a/unittests/rpc/handlers/NFTSellOffersTest.cpp b/unittests/rpc/handlers/NFTSellOffersTest.cpp index 167f34a7..59e0af88 100644 --- a/unittests/rpc/handlers/NFTSellOffersTest.cpp +++ b/unittests/rpc/handlers/NFTSellOffersTest.cpp @@ -567,3 +567,89 @@ TEST_F(RPCNFTSellOffersHandlerTest, ResultsWithoutMarkerForInputWithMarkerAndLim ASSERT_TRUE(output); // todo: check limit? }); } + +TEST_F(RPCNFTSellOffersHandlerTest, LimitLessThanMin) +{ + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerInfo = CreateLedgerInfo(LEDGERHASH, 30); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerInfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + + // return owner index containing 2 indexes + auto const directory = ripple::keylet::nft_sells(ripple::uint256{NFTID}); + auto const ownerDir = + CreateOwnerDirLedgerObject(std::vector{NFTSellOffersHandler::LIMIT_MIN + 1, ripple::uint256{INDEX1}}, INDEX1); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(directory.key, testing::_, testing::_)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject(directory.key, testing::_, testing::_)).Times(2); + + // return two nft buy offers + std::vector bbs; + auto const offer = CreateNFTSellOffer(NFTID, ACCOUNT); + for (auto i = 0; i < NFTSellOffersHandler::LIMIT_MIN + 1; i++) + bbs.push_back(offer.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const input = json::parse(fmt::format( + R"({{ + "nft_id": "{}", + "limit": {} + }})", + NFTID, + NFTSellOffersHandler::LIMIT_MIN - 1)); + runSpawn([&, this](auto& yield) { + auto handler = AnyHandler{NFTSellOffersHandler{this->mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + + ASSERT_TRUE(output); + EXPECT_EQ(output->at("offers").as_array().size(), NFTSellOffersHandler::LIMIT_MIN); + EXPECT_EQ(output->at("limit").as_uint64(), NFTSellOffersHandler::LIMIT_MIN); + }); +} + +TEST_F(RPCNFTSellOffersHandlerTest, LimitMoreThanMax) +{ + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerInfo = CreateLedgerInfo(LEDGERHASH, 30); + ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerInfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1); + + // return owner index containing 2 indexes + auto const directory = ripple::keylet::nft_sells(ripple::uint256{NFTID}); + auto const ownerDir = + CreateOwnerDirLedgerObject(std::vector{NFTSellOffersHandler::LIMIT_MAX + 1, ripple::uint256{INDEX1}}, INDEX1); + + ON_CALL(*rawBackendPtr, doFetchLedgerObject(directory.key, testing::_, testing::_)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject(directory.key, testing::_, testing::_)).Times(2); + + // return two nft buy offers + std::vector bbs; + auto const offer = CreateNFTSellOffer(NFTID, ACCOUNT); + for (auto i = 0; i < NFTSellOffersHandler::LIMIT_MAX + 1; i++) + bbs.push_back(offer.getSerializer().peekData()); + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const input = json::parse(fmt::format( + R"({{ + "nft_id": "{}", + "limit": {} + }})", + NFTID, + NFTSellOffersHandler::LIMIT_MAX + 1)); + runSpawn([&, this](auto& yield) { + auto handler = AnyHandler{NFTSellOffersHandler{this->mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + + ASSERT_TRUE(output); + EXPECT_EQ(output->at("offers").as_array().size(), NFTSellOffersHandler::LIMIT_MAX); + EXPECT_EQ(output->at("limit").as_uint64(), NFTSellOffersHandler::LIMIT_MAX); + }); +} diff --git a/unittests/rpc/handlers/NoRippleCheckTest.cpp b/unittests/rpc/handlers/NoRippleCheckTest.cpp index 841a75b4..cd023776 100644 --- a/unittests/rpc/handlers/NoRippleCheckTest.cpp +++ b/unittests/rpc/handlers/NoRippleCheckTest.cpp @@ -695,3 +695,107 @@ TEST_F(RPCNoRippleCheckTest, NormalPathTransactions) EXPECT_EQ(*output, json::parse(expectedOutput)); }); } + +TEST_F(RPCNoRippleCheckTest, LimitLessThanMin) +{ + constexpr auto seq = 30; + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, seq); + ON_CALL(*rawBackendPtr, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(ledgerinfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1); + // fetch account object return valid account with DefaultRippleSet flag + + ON_CALL(*rawBackendPtr, doFetchLedgerObject) + .WillByDefault(Return(CreateAccountRootObject(ACCOUNT, ripple::lsfDefaultRipple, 2, 200, 2, INDEX1, 2) + .getSerializer() + .peekData())); + auto const ownerDir = CreateOwnerDirLedgerObject({ripple::uint256{INDEX1}, ripple::uint256{INDEX2}}, INDEX1); + auto const ownerDirKk = ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, seq, _)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + auto const line1 = CreateRippleStateLedgerObject( + ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123, ripple::lsfLowNoRipple); + + auto const line2 = CreateRippleStateLedgerObject( + ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123, ripple::lsfLowNoRipple); + + std::vector bbs; + bbs.push_back(line1.getSerializer().peekData()); + bbs.push_back(line2.getSerializer().peekData()); + + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const input = json::parse(fmt::format( + R"({{ + "account": "{}", + "ledger_hash": "{}", + "role": "gateway", + "limit": {} + }})", + ACCOUNT, + LEDGERHASH, + NoRippleCheckHandler::LIMIT_MIN - 1)); + runSpawn([&, this](auto& yield) { + auto const handler = AnyHandler{NoRippleCheckHandler{mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output->as_object().at("problems").as_array().size(), NoRippleCheckHandler::LIMIT_MIN); + }); +} + +TEST_F(RPCNoRippleCheckTest, LimitMoreThanMax) +{ + constexpr auto seq = 30; + MockBackend* rawBackendPtr = static_cast(mockBackendPtr.get()); + mockBackendPtr->updateRange(10); // min + mockBackendPtr->updateRange(30); // max + auto ledgerinfo = CreateLedgerInfo(LEDGERHASH, seq); + ON_CALL(*rawBackendPtr, fetchLedgerByHash(ripple::uint256{LEDGERHASH}, _)).WillByDefault(Return(ledgerinfo)); + EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).Times(1); + // fetch account object return valid account with DefaultRippleSet flag + + ON_CALL(*rawBackendPtr, doFetchLedgerObject) + .WillByDefault(Return(CreateAccountRootObject(ACCOUNT, ripple::lsfDefaultRipple, 2, 200, 2, INDEX1, 2) + .getSerializer() + .peekData())); + auto const ownerDir = + CreateOwnerDirLedgerObject(std::vector{NoRippleCheckHandler::LIMIT_MAX + 1, ripple::uint256{INDEX1}}, INDEX1); + auto const ownerDirKk = ripple::keylet::ownerDir(GetAccountIDWithString(ACCOUNT)).key; + ON_CALL(*rawBackendPtr, doFetchLedgerObject(ownerDirKk, seq, _)) + .WillByDefault(Return(ownerDir.getSerializer().peekData())); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(2); + + auto const line1 = CreateRippleStateLedgerObject( + ACCOUNT, "USD", ISSUER, 100, ACCOUNT, 10, ACCOUNT2, 20, TXNID, 123, ripple::lsfLowNoRipple); + + std::vector bbs; + for (auto i = 0; i < NoRippleCheckHandler::LIMIT_MAX + 1; i++) + { + bbs.push_back(line1.getSerializer().peekData()); + } + + ON_CALL(*rawBackendPtr, doFetchLedgerObjects).WillByDefault(Return(bbs)); + EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(1); + + auto const input = json::parse(fmt::format( + R"({{ + "account": "{}", + "ledger_hash": "{}", + "role": "gateway", + "limit": {} + }})", + ACCOUNT, + LEDGERHASH, + NoRippleCheckHandler::LIMIT_MAX + 1)); + runSpawn([&, this](auto& yield) { + auto const handler = AnyHandler{NoRippleCheckHandler{mockBackendPtr}}; + auto const output = handler.process(input, Context{std::ref(yield)}); + ASSERT_TRUE(output); + EXPECT_EQ(output->as_object().at("problems").as_array().size(), NoRippleCheckHandler::LIMIT_MAX); + }); +}