mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-04 11:55:51 +00:00
Compare commits
8 Commits
clang_tidy
...
release/2.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7a1f902f42 | ||
|
|
7e621b2518 | ||
|
|
e32e2ebee4 | ||
|
|
7742c4a5e3 | ||
|
|
c24c3b536f | ||
|
|
4d42f7c4e4 | ||
|
|
c634f0f0ba | ||
|
|
2ef766a740 |
@@ -19,13 +19,7 @@ set(COMPILER_FLAGS
|
|||||||
-Wunused
|
-Wunused
|
||||||
)
|
)
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
|
if (is_gcc AND NOT lint)
|
||||||
list(APPEND COMPILER_FLAGS
|
|
||||||
-Wshadow # gcc is to aggressive with shadowing https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78147
|
|
||||||
)
|
|
||||||
endif ()
|
|
||||||
|
|
||||||
if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|
||||||
list(APPEND COMPILER_FLAGS
|
list(APPEND COMPILER_FLAGS
|
||||||
-Wduplicated-branches
|
-Wduplicated-branches
|
||||||
-Wduplicated-cond
|
-Wduplicated-cond
|
||||||
@@ -34,6 +28,18 @@ if (CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
|
|||||||
)
|
)
|
||||||
endif ()
|
endif ()
|
||||||
|
|
||||||
|
if (is_clang)
|
||||||
|
list(APPEND COMPILER_FLAGS
|
||||||
|
-Wshadow # gcc is to aggressive with shadowing https://gcc.gnu.org/bugzilla/show_bug.cgi?id=78147
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
|
||||||
|
if (is_appleclang)
|
||||||
|
list(APPEND COMPILER_FLAGS
|
||||||
|
-Wreorder-init-list
|
||||||
|
)
|
||||||
|
endif ()
|
||||||
|
|
||||||
# See https://github.com/cpp-best-practices/cppbestpractices/blob/master/02-Use_the_Tools_Available.md#gcc--clang for the flags description
|
# See https://github.com/cpp-best-practices/cppbestpractices/blob/master/02-Use_the_Tools_Available.md#gcc--clang for the flags description
|
||||||
|
|
||||||
target_compile_options (clio PUBLIC ${COMPILER_FLAGS})
|
target_compile_options (clio PUBLIC ${COMPILER_FLAGS})
|
||||||
|
|||||||
@@ -171,6 +171,16 @@ public:
|
|||||||
|
|
||||||
subscriptions_->pubLedger(lgrInfo, *fees, range, transactions.size());
|
subscriptions_->pubLedger(lgrInfo, *fees, range, transactions.size());
|
||||||
|
|
||||||
|
// order with transaction index
|
||||||
|
std::sort(transactions.begin(), transactions.end(), [](auto const& t1, auto const& t2) {
|
||||||
|
ripple::SerialIter iter1{t1.metadata.data(), t1.metadata.size()};
|
||||||
|
ripple::STObject const object1(iter1, ripple::sfMetadata);
|
||||||
|
ripple::SerialIter iter2{t2.metadata.data(), t2.metadata.size()};
|
||||||
|
ripple::STObject const object2(iter2, ripple::sfMetadata);
|
||||||
|
return object1.getFieldU32(ripple::sfTransactionIndex) <
|
||||||
|
object2.getFieldU32(ripple::sfTransactionIndex);
|
||||||
|
});
|
||||||
|
|
||||||
for (auto& txAndMeta : transactions)
|
for (auto& txAndMeta : transactions)
|
||||||
subscriptions_->pubTransaction(txAndMeta, lgrInfo);
|
subscriptions_->pubTransaction(txAndMeta, lgrInfo);
|
||||||
|
|
||||||
|
|||||||
@@ -150,11 +150,9 @@ public:
|
|||||||
|
|
||||||
if (v)
|
if (v)
|
||||||
return v->as_object();
|
return v->as_object();
|
||||||
else
|
|
||||||
{
|
notifyErrored(ctx.method);
|
||||||
notifyErrored(ctx.method);
|
return Status{v.error()};
|
||||||
return Status{v.error()};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
catch (data::DatabaseTimeout const& t)
|
catch (data::DatabaseTimeout const& t)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1149,21 +1149,15 @@ parseBook(ripple::Currency pays, ripple::AccountID payIssuer, ripple::Currency g
|
|||||||
{
|
{
|
||||||
if (isXRP(pays) && !isXRP(payIssuer))
|
if (isXRP(pays) && !isXRP(payIssuer))
|
||||||
return Status{
|
return Status{
|
||||||
RippledError::rpcSRC_ISR_MALFORMED,
|
RippledError::rpcSRC_ISR_MALFORMED, "Unneeded field 'taker_pays.issuer' for XRP currency specification."};
|
||||||
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
|
||||||
"specification."};
|
|
||||||
|
|
||||||
if (!isXRP(pays) && isXRP(payIssuer))
|
if (!isXRP(pays) && isXRP(payIssuer))
|
||||||
return Status{
|
return Status{
|
||||||
RippledError::rpcSRC_ISR_MALFORMED,
|
RippledError::rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer."};
|
||||||
"Invalid field 'taker_pays.issuer', expected non-XRP "
|
|
||||||
"issuer."};
|
|
||||||
|
|
||||||
if (ripple::isXRP(gets) && !ripple::isXRP(getIssuer))
|
if (ripple::isXRP(gets) && !ripple::isXRP(getIssuer))
|
||||||
return Status{
|
return Status{
|
||||||
RippledError::rpcDST_ISR_MALFORMED,
|
RippledError::rpcDST_ISR_MALFORMED, "Unneeded field 'taker_gets.issuer' for XRP currency specification."};
|
||||||
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
|
||||||
"specification."};
|
|
||||||
|
|
||||||
if (!ripple::isXRP(gets) && ripple::isXRP(getIssuer))
|
if (!ripple::isXRP(gets) && ripple::isXRP(getIssuer))
|
||||||
return Status{
|
return Status{
|
||||||
@@ -1233,15 +1227,11 @@ parseBook(boost::json::object const& request)
|
|||||||
|
|
||||||
if (isXRP(pay_currency) && !isXRP(pay_issuer))
|
if (isXRP(pay_currency) && !isXRP(pay_issuer))
|
||||||
return Status{
|
return Status{
|
||||||
RippledError::rpcSRC_ISR_MALFORMED,
|
RippledError::rpcSRC_ISR_MALFORMED, "Unneeded field 'taker_pays.issuer' for XRP currency specification."};
|
||||||
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
|
||||||
"specification."};
|
|
||||||
|
|
||||||
if (!isXRP(pay_currency) && isXRP(pay_issuer))
|
if (!isXRP(pay_currency) && isXRP(pay_issuer))
|
||||||
return Status{
|
return Status{
|
||||||
RippledError::rpcSRC_ISR_MALFORMED,
|
RippledError::rpcSRC_ISR_MALFORMED, "Invalid field 'taker_pays.issuer', expected non-XRP issuer."};
|
||||||
"Invalid field 'taker_pays.issuer', expected non-XRP "
|
|
||||||
"issuer."};
|
|
||||||
|
|
||||||
if ((!isXRP(pay_currency)) && (!taker_pays.contains("issuer")))
|
if ((!isXRP(pay_currency)) && (!taker_pays.contains("issuer")))
|
||||||
return Status{RippledError::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."};
|
return Status{RippledError::rpcSRC_ISR_MALFORMED, "Missing non-XRP issuer."};
|
||||||
@@ -1258,9 +1248,7 @@ parseBook(boost::json::object const& request)
|
|||||||
|
|
||||||
if (get_issuer == ripple::noAccount())
|
if (get_issuer == ripple::noAccount())
|
||||||
return Status{
|
return Status{
|
||||||
RippledError::rpcDST_ISR_MALFORMED,
|
RippledError::rpcDST_ISR_MALFORMED, "Invalid field 'taker_gets.issuer', bad issuer account one."};
|
||||||
"Invalid field 'taker_gets.issuer', bad issuer account "
|
|
||||||
"one."};
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -1269,9 +1257,7 @@ parseBook(boost::json::object const& request)
|
|||||||
|
|
||||||
if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer))
|
if (ripple::isXRP(get_currency) && !ripple::isXRP(get_issuer))
|
||||||
return Status{
|
return Status{
|
||||||
RippledError::rpcDST_ISR_MALFORMED,
|
RippledError::rpcDST_ISR_MALFORMED, "Unneeded field 'taker_gets.issuer' for XRP currency specification."};
|
||||||
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
|
||||||
"specification."};
|
|
||||||
|
|
||||||
if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer))
|
if (!ripple::isXRP(get_currency) && ripple::isXRP(get_issuer))
|
||||||
return Status{
|
return Status{
|
||||||
|
|||||||
@@ -146,11 +146,7 @@ CustomValidator IssuerValidator =
|
|||||||
|
|
||||||
if (issuer == ripple::noAccount())
|
if (issuer == ripple::noAccount())
|
||||||
return Error{Status{
|
return Error{Status{
|
||||||
RippledError::rpcINVALID_PARAMS,
|
RippledError::rpcINVALID_PARAMS, fmt::format("Invalid field '{}', bad issuer account one.", key)}};
|
||||||
fmt::format(
|
|
||||||
"Invalid field '{}', bad issuer account "
|
|
||||||
"one.",
|
|
||||||
key)}};
|
|
||||||
|
|
||||||
return MaybeError{};
|
return MaybeError{};
|
||||||
}};
|
}};
|
||||||
|
|||||||
@@ -417,7 +417,7 @@ public:
|
|||||||
|
|
||||||
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
auto const res = value_to<Type>(value.as_object().at(key.data()));
|
||||||
if (std::find(std::begin(options_), std::end(options_), res) == std::end(options_))
|
if (std::find(std::begin(options_), std::end(options_), res) == std::end(options_))
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
return Error{Status{RippledError::rpcINVALID_PARAMS, fmt::format("Invalid field '{}'.", key)}};
|
||||||
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -107,13 +107,18 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con
|
|||||||
if (ctx.apiVersion > 1u && (input.ledgerIndexMax || input.ledgerIndexMin))
|
if (ctx.apiVersion > 1u && (input.ledgerIndexMax || input.ledgerIndexMin))
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS, "containsLedgerSpecifierAndRange"}};
|
return Error{Status{RippledError::rpcINVALID_PARAMS, "containsLedgerSpecifierAndRange"}};
|
||||||
|
|
||||||
auto const lgrInfoOrStatus = getLedgerInfoFromHashOrSeq(
|
if (!input.ledgerIndexMax && !input.ledgerIndexMin)
|
||||||
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence);
|
{
|
||||||
|
// mimic rippled, when both range and index specified, respect the range.
|
||||||
|
// take ledger from ledgerHash or ledgerIndex only when range is not specified
|
||||||
|
auto const lgrInfoOrStatus = getLedgerInfoFromHashOrSeq(
|
||||||
|
*sharedPtrBackend_, ctx.yield, input.ledgerHash, input.ledgerIndex, range->maxSequence);
|
||||||
|
|
||||||
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
|
if (auto status = std::get_if<Status>(&lgrInfoOrStatus))
|
||||||
return Error{*status};
|
return Error{*status};
|
||||||
|
|
||||||
maxIndex = minIndex = std::get<ripple::LedgerHeader>(lgrInfoOrStatus).seq;
|
maxIndex = minIndex = std::get<ripple::LedgerHeader>(lgrInfoOrStatus).seq;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<data::TransactionsCursor> cursor;
|
std::optional<data::TransactionsCursor> cursor;
|
||||||
@@ -180,8 +185,11 @@ AccountTxHandler::process(AccountTxHandler::Input input, Context const& ctx) con
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
obj[JS(tx)].as_object()[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
|
|
||||||
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
|
obj[JS(tx)].as_object()[JS(date)] = txnPlusMeta.date;
|
||||||
|
obj[JS(tx)].as_object()[JS(ledger_index)] = txnPlusMeta.ledgerSequence;
|
||||||
|
|
||||||
|
if (ctx.apiVersion < 2u)
|
||||||
|
obj[JS(tx)].as_object()[JS(inLedger)] = txnPlusMeta.ledgerSequence;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ public:
|
|||||||
// return INVALID_PARAMS if account format is wrong for "taker"
|
// return INVALID_PARAMS if account format is wrong for "taker"
|
||||||
{JS(taker),
|
{JS(taker),
|
||||||
meta::WithCustomError{
|
meta::WithCustomError{
|
||||||
validation::AccountValidator, Status(RippledError::rpcINVALID_PARAMS, "Invalid field 'taker'")}},
|
validation::AccountValidator, Status(RippledError::rpcINVALID_PARAMS, "Invalid field 'taker'.")}},
|
||||||
{JS(limit),
|
{JS(limit),
|
||||||
validation::Type<uint32_t>{},
|
validation::Type<uint32_t>{},
|
||||||
validation::Min(1u),
|
validation::Min(1u),
|
||||||
|
|||||||
@@ -100,9 +100,7 @@ public:
|
|||||||
meta::WithCustomError{
|
meta::WithCustomError{
|
||||||
validation::Type<std::string>{},
|
validation::Type<std::string>{},
|
||||||
Status{ripple::rpcINVALID_PARAMS, "Invalid field 'type', not string."}},
|
Status{ripple::rpcINVALID_PARAMS, "Invalid field 'type', not string."}},
|
||||||
meta::WithCustomError{
|
validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend())},
|
||||||
validation::OneOf<std::string>(TYPES_KEYS.cbegin(), TYPES_KEYS.cend()),
|
|
||||||
Status{ripple::rpcINVALID_PARAMS, "Invalid field 'type'."}}},
|
|
||||||
|
|
||||||
};
|
};
|
||||||
return rpcSpec;
|
return rpcSpec;
|
||||||
|
|||||||
@@ -83,8 +83,8 @@ LedgerEntryHandler::process(LedgerEntryHandler::Input input, Context const& ctx)
|
|||||||
{
|
{
|
||||||
// Must specify 1 of the following fields to indicate what type
|
// Must specify 1 of the following fields to indicate what type
|
||||||
if (ctx.apiVersion == 1)
|
if (ctx.apiVersion == 1)
|
||||||
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
return Error{Status{ClioError::rpcUNKNOWN_OPTION}};
|
||||||
return Error{Status{ClioError::rpcUNKNOWN_OPTION}};
|
return Error{Status{RippledError::rpcINVALID_PARAMS}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// check ledger exists
|
// check ledger exists
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ TxHandler::process(Input input, Context const& ctx) const
|
|||||||
return Error{Status{RippledError::rpcEXCESSIVE_LGR_RANGE}};
|
return Error{Status{RippledError::rpcEXCESSIVE_LGR_RANGE}};
|
||||||
}
|
}
|
||||||
|
|
||||||
auto output = TxHandler::Output{};
|
auto output = TxHandler::Output{.apiVersion = ctx.apiVersion};
|
||||||
auto const dbResponse =
|
auto const dbResponse =
|
||||||
sharedPtrBackend_->fetchTransaction(ripple::uint256{std::string_view(input.transaction)}, ctx.yield);
|
sharedPtrBackend_->fetchTransaction(ripple::uint256{std::string_view(input.transaction)}, ctx.yield);
|
||||||
|
|
||||||
@@ -55,7 +55,6 @@ TxHandler::process(Input input, Context const& ctx) const
|
|||||||
return Error{Status{RippledError::rpcTXN_NOT_FOUND}};
|
return Error{Status{RippledError::rpcTXN_NOT_FOUND}};
|
||||||
}
|
}
|
||||||
|
|
||||||
// clio does not implement 'inLedger' which is a deprecated field
|
|
||||||
if (!input.binary)
|
if (!input.binary)
|
||||||
{
|
{
|
||||||
auto const [txn, meta] = toExpandedJson(*dbResponse, NFTokenjson::ENABLE);
|
auto const [txn, meta] = toExpandedJson(*dbResponse, NFTokenjson::ENABLE);
|
||||||
@@ -95,6 +94,9 @@ tag_invoke(boost::json::value_from_tag, boost::json::value& jv, TxHandler::Outpu
|
|||||||
obj[JS(date)] = output.date;
|
obj[JS(date)] = output.date;
|
||||||
obj[JS(ledger_index)] = output.ledgerIndex;
|
obj[JS(ledger_index)] = output.ledgerIndex;
|
||||||
|
|
||||||
|
if (output.apiVersion < 2u)
|
||||||
|
obj[JS(inLedger)] = output.ledgerIndex;
|
||||||
|
|
||||||
jv = std::move(obj);
|
jv = std::move(obj);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,13 +38,14 @@ class TxHandler
|
|||||||
public:
|
public:
|
||||||
struct Output
|
struct Output
|
||||||
{
|
{
|
||||||
uint32_t date;
|
uint32_t date = 0u;
|
||||||
std::string hash;
|
std::string hash{};
|
||||||
uint32_t ledgerIndex;
|
uint32_t ledgerIndex = 0u;
|
||||||
std::optional<boost::json::object> meta;
|
std::optional<boost::json::object> meta{};
|
||||||
std::optional<boost::json::object> tx;
|
std::optional<boost::json::object> tx{};
|
||||||
std::optional<std::string> metaStr;
|
std::optional<std::string> metaStr{};
|
||||||
std::optional<std::string> txStr;
|
std::optional<std::string> txStr{};
|
||||||
|
uint32_t apiVersion = 0u;
|
||||||
bool validated = true;
|
bool validated = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -89,8 +89,8 @@ public:
|
|||||||
auto req = boost::json::parse(request).as_object();
|
auto req = boost::json::parse(request).as_object();
|
||||||
LOG(perfLog_.debug()) << connection->tag() << "Adding to work queue";
|
LOG(perfLog_.debug()) << connection->tag() << "Adding to work queue";
|
||||||
|
|
||||||
if (not connection->upgraded and not req.contains("params"))
|
if (not connection->upgraded and shouldReplaceParams(req))
|
||||||
req["params"] = boost::json::array({boost::json::object{}});
|
req[JS(params)] = boost::json::array({boost::json::object{}});
|
||||||
|
|
||||||
if (!rpcEngine_->post(
|
if (!rpcEngine_->post(
|
||||||
[this, request = std::move(req), connection](boost::asio::yield_context yield) mutable {
|
[this, request = std::move(req), connection](boost::asio::yield_context yield) mutable {
|
||||||
@@ -267,6 +267,27 @@ private:
|
|||||||
return web::detail::ErrorHelper(connection, request).sendInternalError();
|
return web::detail::ErrorHelper(connection, request).sendInternalError();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
shouldReplaceParams(boost::json::object const& req) const
|
||||||
|
{
|
||||||
|
auto const hasParams = req.contains(JS(params));
|
||||||
|
auto const paramsIsArray = hasParams and req.at(JS(params)).is_array();
|
||||||
|
auto const paramsIsEmptyString =
|
||||||
|
hasParams and req.at(JS(params)).is_string() and req.at(JS(params)).as_string().empty();
|
||||||
|
auto const paramsIsEmptyObject =
|
||||||
|
hasParams and req.at(JS(params)).is_object() and req.at(JS(params)).as_object().empty();
|
||||||
|
auto const paramsIsNull = hasParams and req.at(JS(params)).is_null();
|
||||||
|
auto const arrayIsEmpty = paramsIsArray and req.at(JS(params)).as_array().empty();
|
||||||
|
auto const arrayIsNotEmpty = paramsIsArray and not req.at(JS(params)).as_array().empty();
|
||||||
|
auto const firstArgIsNull = arrayIsNotEmpty and req.at(JS(params)).as_array().at(0).is_null();
|
||||||
|
auto const firstArgIsEmptyString = arrayIsNotEmpty and req.at(JS(params)).as_array().at(0).is_string() and
|
||||||
|
req.at(JS(params)).as_array().at(0).as_string().empty();
|
||||||
|
|
||||||
|
// Note: all this compatibility dance is to match `rippled` as close as possible
|
||||||
|
return not hasParams or paramsIsEmptyString or paramsIsNull or paramsIsEmptyObject or arrayIsEmpty or
|
||||||
|
firstArgIsEmptyString or firstArgIsNull;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace web
|
} // namespace web
|
||||||
|
|||||||
@@ -180,10 +180,10 @@ public:
|
|||||||
|
|
||||||
if (boost::beast::websocket::is_upgrade(req_))
|
if (boost::beast::websocket::is_upgrade(req_))
|
||||||
{
|
{
|
||||||
upgraded = true;
|
// Disable the timeout. The websocket::stream uses its own timeout settings.
|
||||||
// Disable the timeout.
|
|
||||||
// The websocket::stream uses its own timeout settings.
|
|
||||||
boost::beast::get_lowest_layer(derived().stream()).expires_never();
|
boost::beast::get_lowest_layer(derived().stream()).expires_never();
|
||||||
|
|
||||||
|
upgraded = true;
|
||||||
return derived().upgrade();
|
return derived().upgrade();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -121,6 +121,10 @@ public:
|
|||||||
void
|
void
|
||||||
maybeSendNext()
|
maybeSendNext()
|
||||||
{
|
{
|
||||||
|
// cleanup if needed. can't do this in destructor so it's here
|
||||||
|
if (dead())
|
||||||
|
(*handler_)(ec_, derived().shared_from_this());
|
||||||
|
|
||||||
if (ec_ || sending_ || messages_.empty())
|
if (ec_ || sending_ || messages_.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -204,8 +208,8 @@ public:
|
|||||||
if (dead())
|
if (dead())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Clear the buffer
|
// Note: use entirely new buffer so previously used, potentially large, capacity is deallocated
|
||||||
buffer_.consume(buffer_.size());
|
buffer_ = boost::beast::flat_buffer{};
|
||||||
|
|
||||||
derived().ws().async_read(buffer_, boost::beast::bind_front_handler(&WsBase::onRead, this->shared_from_this()));
|
derived().ws().async_read(buffer_, boost::beast::bind_front_handler(&WsBase::onRead, this->shared_from_this()));
|
||||||
}
|
}
|
||||||
|
|||||||
304
unittests/etl/LedgerPublisherTests.cpp
Normal file
304
unittests/etl/LedgerPublisherTests.cpp
Normal file
@@ -0,0 +1,304 @@
|
|||||||
|
//------------------------------------------------------------------------------
|
||||||
|
/*
|
||||||
|
This file is part of clio: https://github.com/XRPLF/clio
|
||||||
|
Copyright (c) 2023, 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 <etl/impl/LedgerPublisher.h>
|
||||||
|
#include <util/Fixtures.h>
|
||||||
|
#include <util/MockCache.h>
|
||||||
|
#include <util/TestObject.h>
|
||||||
|
|
||||||
|
#include <fmt/core.h>
|
||||||
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
|
|
||||||
|
using namespace testing;
|
||||||
|
using namespace etl;
|
||||||
|
namespace json = boost::json;
|
||||||
|
using namespace std::chrono;
|
||||||
|
|
||||||
|
static auto constexpr ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
||||||
|
static auto constexpr ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
||||||
|
static auto constexpr LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
|
||||||
|
static auto constexpr SEQ = 30;
|
||||||
|
static auto constexpr AGE = 800;
|
||||||
|
|
||||||
|
class ETLLedgerPublisherTest : public MockBackendTest, public SyncAsioContextTest, public MockSubscriptionManagerTest
|
||||||
|
{
|
||||||
|
void
|
||||||
|
SetUp() override
|
||||||
|
{
|
||||||
|
MockBackendTest::SetUp();
|
||||||
|
SyncAsioContextTest::SetUp();
|
||||||
|
MockSubscriptionManagerTest::SetUp();
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
TearDown() override
|
||||||
|
{
|
||||||
|
MockSubscriptionManagerTest::TearDown();
|
||||||
|
SyncAsioContextTest::TearDown();
|
||||||
|
MockBackendTest::TearDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
util::Config cfg{json::parse("{}")};
|
||||||
|
MockCache mockCache;
|
||||||
|
};
|
||||||
|
|
||||||
|
TEST_F(ETLLedgerPublisherTest, PublishLedgerInfoIsWritingFalse)
|
||||||
|
{
|
||||||
|
SystemState dummyState;
|
||||||
|
dummyState.isWriting = false;
|
||||||
|
auto const dummyLedgerInfo = CreateLedgerInfo(LEDGERHASH, SEQ, AGE);
|
||||||
|
detail::LedgerPublisher publisher(ctx, mockBackendPtr, mockCache, mockSubscriptionManagerPtr, dummyState);
|
||||||
|
publisher.publish(dummyLedgerInfo);
|
||||||
|
|
||||||
|
MockBackend* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
ASSERT_NE(rawBackendPtr, nullptr);
|
||||||
|
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerDiff(SEQ, _)).WillByDefault(Return(std::vector<LedgerObject>{}));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerDiff(SEQ, _)).Times(1);
|
||||||
|
|
||||||
|
// setLastPublishedSequence not in strand, should verify before run
|
||||||
|
EXPECT_TRUE(publisher.getLastPublishedSequence());
|
||||||
|
EXPECT_EQ(publisher.getLastPublishedSequence().value(), SEQ);
|
||||||
|
|
||||||
|
EXPECT_CALL(mockCache, updateImp).Times(1);
|
||||||
|
|
||||||
|
ctx.run();
|
||||||
|
EXPECT_TRUE(rawBackendPtr->fetchLedgerRange());
|
||||||
|
EXPECT_EQ(rawBackendPtr->fetchLedgerRange().value().minSequence, SEQ);
|
||||||
|
EXPECT_EQ(rawBackendPtr->fetchLedgerRange().value().maxSequence, SEQ);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ETLLedgerPublisherTest, PublishLedgerInfoIsWritingTrue)
|
||||||
|
{
|
||||||
|
SystemState dummyState;
|
||||||
|
dummyState.isWriting = true;
|
||||||
|
auto const dummyLedgerInfo = CreateLedgerInfo(LEDGERHASH, SEQ, AGE);
|
||||||
|
detail::LedgerPublisher publisher(ctx, mockBackendPtr, mockCache, mockSubscriptionManagerPtr, dummyState);
|
||||||
|
publisher.publish(dummyLedgerInfo);
|
||||||
|
|
||||||
|
MockBackend* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerDiff(_, _)).Times(0);
|
||||||
|
|
||||||
|
// setLastPublishedSequence not in strand, should verify before run
|
||||||
|
EXPECT_TRUE(publisher.getLastPublishedSequence());
|
||||||
|
EXPECT_EQ(publisher.getLastPublishedSequence().value(), SEQ);
|
||||||
|
|
||||||
|
ctx.run();
|
||||||
|
EXPECT_FALSE(rawBackendPtr->fetchLedgerRange());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ETLLedgerPublisherTest, PublishLedgerInfoInRange)
|
||||||
|
{
|
||||||
|
SystemState dummyState;
|
||||||
|
dummyState.isWriting = true;
|
||||||
|
|
||||||
|
auto const dummyLedgerInfo = CreateLedgerInfo(LEDGERHASH, SEQ, 0); // age is 0
|
||||||
|
detail::LedgerPublisher publisher(ctx, mockBackendPtr, mockCache, mockSubscriptionManagerPtr, dummyState);
|
||||||
|
mockBackendPtr->updateRange(SEQ - 1);
|
||||||
|
mockBackendPtr->updateRange(SEQ);
|
||||||
|
|
||||||
|
publisher.publish(dummyLedgerInfo);
|
||||||
|
|
||||||
|
MockBackend* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerDiff(_, _)).Times(0);
|
||||||
|
|
||||||
|
// mock fetch fee
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::fees().key, SEQ, _))
|
||||||
|
.WillByDefault(Return(CreateFeeSettingBlob(1, 2, 3, 4, 0)));
|
||||||
|
|
||||||
|
// mock fetch transactions
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchAllTransactionsInLedger).Times(1);
|
||||||
|
TransactionAndMetadata t1;
|
||||||
|
t1.transaction = CreatePaymentTransactionObject(ACCOUNT, ACCOUNT2, 100, 3, SEQ).getSerializer().peekData();
|
||||||
|
t1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT, ACCOUNT2, 110, 30).getSerializer().peekData();
|
||||||
|
t1.ledgerSequence = SEQ;
|
||||||
|
ON_CALL(*rawBackendPtr, fetchAllTransactionsInLedger(SEQ, _))
|
||||||
|
.WillByDefault(Return(std::vector<TransactionAndMetadata>{t1}));
|
||||||
|
|
||||||
|
// setLastPublishedSequence not in strand, should verify before run
|
||||||
|
EXPECT_TRUE(publisher.getLastPublishedSequence());
|
||||||
|
EXPECT_EQ(publisher.getLastPublishedSequence().value(), SEQ);
|
||||||
|
|
||||||
|
MockSubscriptionManager* rawSubscriptionManagerPtr =
|
||||||
|
dynamic_cast<MockSubscriptionManager*>(mockSubscriptionManagerPtr.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubLedger(_, _, fmt::format("{}-{}", SEQ - 1, SEQ), 1)).Times(1);
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubBookChanges).Times(1);
|
||||||
|
// mock 1 transaction
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubTransaction).Times(1);
|
||||||
|
|
||||||
|
ctx.run();
|
||||||
|
// last publish time should be set
|
||||||
|
EXPECT_TRUE(publisher.lastPublishAgeSeconds() <= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ETLLedgerPublisherTest, PublishLedgerInfoCloseTimeGreaterThanNow)
|
||||||
|
{
|
||||||
|
SystemState dummyState;
|
||||||
|
dummyState.isWriting = true;
|
||||||
|
|
||||||
|
ripple::LedgerInfo dummyLedgerInfo = CreateLedgerInfo(LEDGERHASH, SEQ, 0);
|
||||||
|
auto const nowPlus10 = system_clock::now() + seconds(10);
|
||||||
|
auto const closeTime = duration_cast<seconds>(nowPlus10.time_since_epoch()).count() - rippleEpochStart;
|
||||||
|
dummyLedgerInfo.closeTime = ripple::NetClock::time_point{seconds{closeTime}};
|
||||||
|
|
||||||
|
mockBackendPtr->updateRange(SEQ - 1);
|
||||||
|
mockBackendPtr->updateRange(SEQ);
|
||||||
|
|
||||||
|
detail::LedgerPublisher publisher(ctx, mockBackendPtr, mockCache, mockSubscriptionManagerPtr, dummyState);
|
||||||
|
publisher.publish(dummyLedgerInfo);
|
||||||
|
|
||||||
|
MockBackend* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerDiff(_, _)).Times(0);
|
||||||
|
|
||||||
|
// mock fetch fee
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::fees().key, SEQ, _))
|
||||||
|
.WillByDefault(Return(CreateFeeSettingBlob(1, 2, 3, 4, 0)));
|
||||||
|
|
||||||
|
// mock fetch transactions
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchAllTransactionsInLedger).Times(1);
|
||||||
|
TransactionAndMetadata t1;
|
||||||
|
t1.transaction = CreatePaymentTransactionObject(ACCOUNT, ACCOUNT2, 100, 3, SEQ).getSerializer().peekData();
|
||||||
|
t1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT, ACCOUNT2, 110, 30).getSerializer().peekData();
|
||||||
|
t1.ledgerSequence = SEQ;
|
||||||
|
ON_CALL(*rawBackendPtr, fetchAllTransactionsInLedger(SEQ, _))
|
||||||
|
.WillByDefault(Return(std::vector<TransactionAndMetadata>{t1}));
|
||||||
|
|
||||||
|
// setLastPublishedSequence not in strand, should verify before run
|
||||||
|
EXPECT_TRUE(publisher.getLastPublishedSequence());
|
||||||
|
EXPECT_EQ(publisher.getLastPublishedSequence().value(), SEQ);
|
||||||
|
|
||||||
|
MockSubscriptionManager* rawSubscriptionManagerPtr =
|
||||||
|
dynamic_cast<MockSubscriptionManager*>(mockSubscriptionManagerPtr.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubLedger(_, _, fmt::format("{}-{}", SEQ - 1, SEQ), 1)).Times(1);
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubBookChanges).Times(1);
|
||||||
|
// mock 1 transaction
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubTransaction).Times(1);
|
||||||
|
|
||||||
|
ctx.run();
|
||||||
|
// last publish time should be set
|
||||||
|
EXPECT_TRUE(publisher.lastPublishAgeSeconds() <= 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ETLLedgerPublisherTest, PublishLedgerSeqStopIsTrue)
|
||||||
|
{
|
||||||
|
SystemState dummyState;
|
||||||
|
dummyState.isStopping = true;
|
||||||
|
detail::LedgerPublisher publisher(ctx, mockBackendPtr, mockCache, mockSubscriptionManagerPtr, dummyState);
|
||||||
|
EXPECT_FALSE(publisher.publish(SEQ, {}));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ETLLedgerPublisherTest, PublishLedgerSeqMaxAttampt)
|
||||||
|
{
|
||||||
|
SystemState dummyState;
|
||||||
|
dummyState.isStopping = false;
|
||||||
|
detail::LedgerPublisher publisher(ctx, mockBackendPtr, mockCache, mockSubscriptionManagerPtr, dummyState);
|
||||||
|
|
||||||
|
static auto constexpr MAX_ATTEMPT = 2;
|
||||||
|
MockBackend* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
EXPECT_CALL(*rawBackendPtr, hardFetchLedgerRange).Times(MAX_ATTEMPT);
|
||||||
|
|
||||||
|
LedgerRange const range{.minSequence = SEQ - 1, .maxSequence = SEQ - 1};
|
||||||
|
ON_CALL(*rawBackendPtr, hardFetchLedgerRange(_)).WillByDefault(Return(range));
|
||||||
|
EXPECT_FALSE(publisher.publish(SEQ, MAX_ATTEMPT));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ETLLedgerPublisherTest, PublishLedgerSeqStopIsFalse)
|
||||||
|
{
|
||||||
|
SystemState dummyState;
|
||||||
|
dummyState.isStopping = false;
|
||||||
|
detail::LedgerPublisher publisher(ctx, mockBackendPtr, mockCache, mockSubscriptionManagerPtr, dummyState);
|
||||||
|
|
||||||
|
MockBackend* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
LedgerRange const range{.minSequence = SEQ, .maxSequence = SEQ};
|
||||||
|
ON_CALL(*rawBackendPtr, hardFetchLedgerRange(_)).WillByDefault(Return(range));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, hardFetchLedgerRange).Times(1);
|
||||||
|
|
||||||
|
auto const dummyLedgerInfo = CreateLedgerInfo(LEDGERHASH, SEQ, AGE);
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerBySequence(SEQ, _)).WillByDefault(Return(dummyLedgerInfo));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerDiff(SEQ, _)).WillByDefault(Return(std::vector<LedgerObject>{}));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerDiff(SEQ, _)).Times(1);
|
||||||
|
EXPECT_CALL(mockCache, updateImp).Times(1);
|
||||||
|
|
||||||
|
EXPECT_TRUE(publisher.publish(SEQ, {}));
|
||||||
|
ctx.run();
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(ETLLedgerPublisherTest, PublishMultipleTxInOrder)
|
||||||
|
{
|
||||||
|
SystemState dummyState;
|
||||||
|
dummyState.isWriting = true;
|
||||||
|
|
||||||
|
auto const dummyLedgerInfo = CreateLedgerInfo(LEDGERHASH, SEQ, 0); // age is 0
|
||||||
|
detail::LedgerPublisher publisher(ctx, mockBackendPtr, mockCache, mockSubscriptionManagerPtr, dummyState);
|
||||||
|
mockBackendPtr->updateRange(SEQ - 1);
|
||||||
|
mockBackendPtr->updateRange(SEQ);
|
||||||
|
|
||||||
|
publisher.publish(dummyLedgerInfo);
|
||||||
|
|
||||||
|
MockBackend* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerDiff(_, _)).Times(0);
|
||||||
|
|
||||||
|
// mock fetch fee
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject(ripple::keylet::fees().key, SEQ, _))
|
||||||
|
.WillByDefault(Return(CreateFeeSettingBlob(1, 2, 3, 4, 0)));
|
||||||
|
|
||||||
|
// mock fetch transactions
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchAllTransactionsInLedger).Times(1);
|
||||||
|
// t1 index > t2 index
|
||||||
|
TransactionAndMetadata t1;
|
||||||
|
t1.transaction = CreatePaymentTransactionObject(ACCOUNT, ACCOUNT2, 100, 3, SEQ).getSerializer().peekData();
|
||||||
|
t1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT, ACCOUNT2, 110, 30, 2).getSerializer().peekData();
|
||||||
|
t1.ledgerSequence = SEQ;
|
||||||
|
t1.date = 1;
|
||||||
|
TransactionAndMetadata t2;
|
||||||
|
t2.transaction = CreatePaymentTransactionObject(ACCOUNT, ACCOUNT2, 100, 3, SEQ).getSerializer().peekData();
|
||||||
|
t2.metadata = CreatePaymentTransactionMetaObject(ACCOUNT, ACCOUNT2, 110, 30, 1).getSerializer().peekData();
|
||||||
|
t2.ledgerSequence = SEQ;
|
||||||
|
t2.date = 2;
|
||||||
|
ON_CALL(*rawBackendPtr, fetchAllTransactionsInLedger(SEQ, _))
|
||||||
|
.WillByDefault(Return(std::vector<TransactionAndMetadata>{t1, t2}));
|
||||||
|
|
||||||
|
// setLastPublishedSequence not in strand, should verify before run
|
||||||
|
EXPECT_TRUE(publisher.getLastPublishedSequence());
|
||||||
|
EXPECT_EQ(publisher.getLastPublishedSequence().value(), SEQ);
|
||||||
|
|
||||||
|
MockSubscriptionManager* rawSubscriptionManagerPtr =
|
||||||
|
dynamic_cast<MockSubscriptionManager*>(mockSubscriptionManagerPtr.get());
|
||||||
|
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubLedger(_, _, fmt::format("{}-{}", SEQ - 1, SEQ), 2)).Times(1);
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubBookChanges).Times(1);
|
||||||
|
// should call pubTransaction t2 first (greater tx index)
|
||||||
|
Sequence const s;
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubTransaction(t2, _)).InSequence(s);
|
||||||
|
EXPECT_CALL(*rawSubscriptionManagerPtr, pubTransaction(t1, _)).InSequence(s);
|
||||||
|
|
||||||
|
ctx.run();
|
||||||
|
// last publish time should be set
|
||||||
|
EXPECT_TRUE(publisher.lastPublishAgeSeconds() <= 1);
|
||||||
|
}
|
||||||
@@ -85,7 +85,7 @@ generateTestValuesForParametersTest()
|
|||||||
"TypeInvalid",
|
"TypeInvalid",
|
||||||
R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "type":"wrong"})",
|
R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "type":"wrong"})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Invalid parameters."},
|
"Invalid field 'type'."},
|
||||||
AccountObjectsParamTestCaseBundle{
|
AccountObjectsParamTestCaseBundle{
|
||||||
"LedgerHashInvalid",
|
"LedgerHashInvalid",
|
||||||
R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":"1"})",
|
R"({"account":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun", "ledger_hash":"1"})",
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ struct AccountTxParamTestCaseBundle
|
|||||||
std::string testJson;
|
std::string testJson;
|
||||||
std::optional<std::string> expectedError;
|
std::optional<std::string> expectedError;
|
||||||
std::optional<std::string> expectedErrorMessage;
|
std::optional<std::string> expectedErrorMessage;
|
||||||
std::uint32_t apiVersion = 2;
|
std::uint32_t apiVersion = 2u;
|
||||||
};
|
};
|
||||||
|
|
||||||
// parameterized test cases for parameters check
|
// parameterized test cases for parameters check
|
||||||
@@ -353,10 +353,9 @@ TEST_P(AccountTxParameterTest, CheckParams)
|
|||||||
mockBackendPtr->updateRange(MINSEQ); // min
|
mockBackendPtr->updateRange(MINSEQ); // min
|
||||||
mockBackendPtr->updateRange(MAXSEQ); // max
|
mockBackendPtr->updateRange(MAXSEQ); // max
|
||||||
auto const testBundle = GetParam();
|
auto const testBundle = GetParam();
|
||||||
auto* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
auto* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
std::cout << "Before parse" << std::endl;
|
ASSERT_NE(rawBackendPtr, nullptr);
|
||||||
auto const req = json::parse(testBundle.testJson);
|
auto const req = json::parse(testBundle.testJson);
|
||||||
std::cout << "After parse" << std::endl;
|
|
||||||
if (testBundle.expectedError.has_value())
|
if (testBundle.expectedError.has_value())
|
||||||
{
|
{
|
||||||
ASSERT_TRUE(testBundle.expectedErrorMessage.has_value());
|
ASSERT_TRUE(testBundle.expectedErrorMessage.has_value());
|
||||||
@@ -372,14 +371,6 @@ TEST_P(AccountTxParameterTest, CheckParams)
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (req.as_object().contains("ledger_hash"))
|
|
||||||
{
|
|
||||||
EXPECT_CALL(*rawBackendPtr, fetchLedgerByHash).WillOnce(testing::Return(ripple::LedgerHeader{}));
|
|
||||||
}
|
|
||||||
else if (req.as_object().contains("ledger_index"))
|
|
||||||
{
|
|
||||||
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).WillOnce(testing::Return(ripple::LedgerHeader{}));
|
|
||||||
}
|
|
||||||
EXPECT_CALL(*rawBackendPtr, fetchAccountTransactions);
|
EXPECT_CALL(*rawBackendPtr, fetchAccountTransactions);
|
||||||
|
|
||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
@@ -656,7 +647,7 @@ TEST_F(RPCAccountTxHandlerTest, BinaryTrue)
|
|||||||
"144B4E9C06F24296074F7BC48F92A97916C6DC5EA98314D31252CF902EF8DD8451"
|
"144B4E9C06F24296074F7BC48F92A97916C6DC5EA98314D31252CF902EF8DD8451"
|
||||||
"243869B38667CBD89DF3");
|
"243869B38667CBD89DF3");
|
||||||
EXPECT_FALSE(output->at("transactions").as_array()[0].as_object().contains("date"));
|
EXPECT_FALSE(output->at("transactions").as_array()[0].as_object().contains("date"));
|
||||||
|
EXPECT_FALSE(output->at("transactions").as_array()[0].as_object().contains("inLedger"));
|
||||||
EXPECT_FALSE(output->as_object().contains("limit"));
|
EXPECT_FALSE(output->as_object().contains("limit"));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -962,200 +953,204 @@ TEST_F(RPCAccountTxHandlerTest, TxLargerThanMaxSeq)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCAccountTxHandlerTest, NFTTxs)
|
TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v1)
|
||||||
{
|
{
|
||||||
auto const OUT = R"({
|
auto const OUT = R"({
|
||||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"ledger_index_min": 10,
|
"ledger_index_min": 10,
|
||||||
"ledger_index_max": 30,
|
"ledger_index_max": 30,
|
||||||
"transactions": [
|
"transactions": [
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"AffectedNodes":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ModifiedNode":
|
||||||
{
|
{
|
||||||
"meta": {
|
"FinalFields":
|
||||||
"AffectedNodes":
|
{
|
||||||
|
"NFTokens":
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"ModifiedNode":
|
"NFToken":
|
||||||
{
|
{
|
||||||
"FinalFields":
|
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF",
|
||||||
{
|
"URI": "7465737475726C"
|
||||||
"NFTokens":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"NFToken":
|
|
||||||
{
|
|
||||||
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF",
|
|
||||||
"URI": "7465737475726C"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"NFToken":
|
|
||||||
{
|
|
||||||
"NFTokenID": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
|
||||||
"URI": "7465737475726C"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "NFTokenPage",
|
|
||||||
"PreviousFields":
|
|
||||||
{
|
|
||||||
"NFTokens":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"NFToken":
|
|
||||||
{
|
|
||||||
"NFTokenID": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
|
||||||
"URI": "7465737475726C"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"TransactionIndex": 0,
|
|
||||||
"TransactionResult": "tesSUCCESS",
|
|
||||||
"nftoken_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
|
||||||
},
|
|
||||||
"tx":
|
|
||||||
{
|
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
|
||||||
"Fee": "50",
|
|
||||||
"NFTokenTaxon": 123,
|
|
||||||
"Sequence": 1,
|
|
||||||
"SigningPubKey": "74657374",
|
|
||||||
"TransactionType": "NFTokenMint",
|
|
||||||
"hash": "C74463F49CFDCBEF3E9902672719918CDE5042DC7E7660BEBD1D1105C4B6DFF4",
|
|
||||||
"ledger_index": 11,
|
|
||||||
"date": 1
|
|
||||||
},
|
|
||||||
"validated": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"meta":
|
|
||||||
{
|
|
||||||
"AffectedNodes":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"DeletedNode":
|
|
||||||
{
|
|
||||||
"FinalFields":
|
|
||||||
{
|
|
||||||
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "NFTokenOffer"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"TransactionIndex": 0,
|
|
||||||
"TransactionResult": "tesSUCCESS",
|
|
||||||
"nftoken_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
|
||||||
},
|
|
||||||
"tx":
|
|
||||||
{
|
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
|
||||||
"Fee": "50",
|
|
||||||
"NFTokenBuyOffer": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
|
||||||
"Sequence": 1,
|
|
||||||
"SigningPubKey": "74657374",
|
|
||||||
"TransactionType": "NFTokenAcceptOffer",
|
|
||||||
"hash": "7682BE6BCDE62F8142915DD852936623B68FC3839A8A424A6064B898702B0CDF",
|
|
||||||
"ledger_index": 11,
|
|
||||||
"date": 2
|
|
||||||
},
|
|
||||||
"validated": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"meta":
|
|
||||||
{
|
|
||||||
"AffectedNodes":
|
|
||||||
[
|
|
||||||
{
|
|
||||||
"DeletedNode": {
|
|
||||||
"FinalFields":
|
|
||||||
{
|
|
||||||
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "NFTokenOffer"
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"DeletedNode":
|
"NFToken":
|
||||||
{
|
{
|
||||||
"FinalFields":
|
"NFTokenID": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
||||||
{
|
"URI": "7465737475726C"
|
||||||
"NFTokenID": "15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
|
||||||
},
|
|
||||||
"LedgerEntryType": "NFTokenOffer"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"TransactionIndex": 0,
|
|
||||||
"TransactionResult": "tesSUCCESS",
|
|
||||||
"nftoken_ids":
|
|
||||||
[
|
|
||||||
"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA",
|
|
||||||
"15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"tx":
|
"LedgerEntryType": "NFTokenPage",
|
||||||
|
"PreviousFields":
|
||||||
{
|
{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"NFTokens":
|
||||||
"Fee": "50",
|
|
||||||
"NFTokenOffers":
|
|
||||||
[
|
|
||||||
"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA",
|
|
||||||
"15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
|
||||||
],
|
|
||||||
"Sequence": 1,
|
|
||||||
"SigningPubKey": "74657374",
|
|
||||||
"TransactionType": "NFTokenCancelOffer",
|
|
||||||
"hash": "9F82743EEB30065FB9CB92C61F0F064B5859C5A590FA811FAAAD9C988E5B47DB",
|
|
||||||
"ledger_index": 11,
|
|
||||||
"date": 3
|
|
||||||
},
|
|
||||||
"validated": true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"meta":
|
|
||||||
{
|
|
||||||
"AffectedNodes":
|
|
||||||
[
|
[
|
||||||
{
|
{
|
||||||
"CreatedNode":
|
"NFToken":
|
||||||
{
|
{
|
||||||
"LedgerEntryType": "NFTokenOffer",
|
"NFTokenID": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
||||||
"LedgerIndex": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
"URI": "7465737475726C"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
],
|
]
|
||||||
"TransactionIndex": 0,
|
}
|
||||||
"TransactionResult": "tesSUCCESS",
|
|
||||||
"offer_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
|
||||||
},
|
|
||||||
"tx":
|
|
||||||
{
|
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
|
||||||
"Amount": "123",
|
|
||||||
"Fee": "50",
|
|
||||||
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF",
|
|
||||||
"Sequence": 1,
|
|
||||||
"SigningPubKey": "74657374",
|
|
||||||
"TransactionType": "NFTokenCreateOffer",
|
|
||||||
"hash": "ECB1837EB7C7C0AC22ECDCCE59FDD4795C70E0B9D8F4E1C9A9408BB7EC75DA5C",
|
|
||||||
"ledger_index": 11,
|
|
||||||
"date": 4
|
|
||||||
},
|
|
||||||
"validated": true
|
|
||||||
}
|
}
|
||||||
],
|
|
||||||
"validated": true,
|
|
||||||
"marker":
|
|
||||||
{
|
|
||||||
"ledger": 12,
|
|
||||||
"seq": 34
|
|
||||||
}
|
}
|
||||||
})";
|
],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"nftoken_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
||||||
|
},
|
||||||
|
"tx":
|
||||||
|
{
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Fee": "50",
|
||||||
|
"NFTokenTaxon": 123,
|
||||||
|
"Sequence": 1,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "NFTokenMint",
|
||||||
|
"hash": "C74463F49CFDCBEF3E9902672719918CDE5042DC7E7660BEBD1D1105C4B6DFF4",
|
||||||
|
"ledger_index": 11,
|
||||||
|
"inLedger": 11,
|
||||||
|
"date": 1
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"meta":
|
||||||
|
{
|
||||||
|
"AffectedNodes":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"DeletedNode":
|
||||||
|
{
|
||||||
|
"FinalFields":
|
||||||
|
{
|
||||||
|
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "NFTokenOffer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"nftoken_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
},
|
||||||
|
"tx":
|
||||||
|
{
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Fee": "50",
|
||||||
|
"NFTokenBuyOffer": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
||||||
|
"Sequence": 1,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "NFTokenAcceptOffer",
|
||||||
|
"hash": "7682BE6BCDE62F8142915DD852936623B68FC3839A8A424A6064B898702B0CDF",
|
||||||
|
"ledger_index": 11,
|
||||||
|
"inLedger": 11,
|
||||||
|
"date": 2
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"meta":
|
||||||
|
{
|
||||||
|
"AffectedNodes":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"DeletedNode": {
|
||||||
|
"FinalFields":
|
||||||
|
{
|
||||||
|
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "NFTokenOffer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DeletedNode":
|
||||||
|
{
|
||||||
|
"FinalFields":
|
||||||
|
{
|
||||||
|
"NFTokenID": "15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "NFTokenOffer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"nftoken_ids":
|
||||||
|
[
|
||||||
|
"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA",
|
||||||
|
"15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tx":
|
||||||
|
{
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Fee": "50",
|
||||||
|
"NFTokenOffers":
|
||||||
|
[
|
||||||
|
"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA",
|
||||||
|
"15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
||||||
|
],
|
||||||
|
"Sequence": 1,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "NFTokenCancelOffer",
|
||||||
|
"hash": "9F82743EEB30065FB9CB92C61F0F064B5859C5A590FA811FAAAD9C988E5B47DB",
|
||||||
|
"ledger_index": 11,
|
||||||
|
"inLedger": 11,
|
||||||
|
"date": 3
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"meta":
|
||||||
|
{
|
||||||
|
"AffectedNodes":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"CreatedNode":
|
||||||
|
{
|
||||||
|
"LedgerEntryType": "NFTokenOffer",
|
||||||
|
"LedgerIndex": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"offer_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
},
|
||||||
|
"tx":
|
||||||
|
{
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Amount": "123",
|
||||||
|
"Fee": "50",
|
||||||
|
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF",
|
||||||
|
"Sequence": 1,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "NFTokenCreateOffer",
|
||||||
|
"hash": "ECB1837EB7C7C0AC22ECDCCE59FDD4795C70E0B9D8F4E1C9A9408BB7EC75DA5C",
|
||||||
|
"ledger_index": 11,
|
||||||
|
"inLedger": 11,
|
||||||
|
"date": 4
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validated": true,
|
||||||
|
"marker":
|
||||||
|
{
|
||||||
|
"ledger": 12,
|
||||||
|
"seq": 34
|
||||||
|
}
|
||||||
|
})";
|
||||||
mockBackendPtr->updateRange(MINSEQ); // min
|
mockBackendPtr->updateRange(MINSEQ); // min
|
||||||
mockBackendPtr->updateRange(MAXSEQ); // max
|
mockBackendPtr->updateRange(MAXSEQ); // max
|
||||||
MockBackend* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
MockBackend* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
@@ -1181,7 +1176,233 @@ TEST_F(RPCAccountTxHandlerTest, NFTTxs)
|
|||||||
ACCOUNT,
|
ACCOUNT,
|
||||||
-1,
|
-1,
|
||||||
-1));
|
-1));
|
||||||
auto const output = handler.process(input, Context{yield});
|
auto const output = handler.process(input, Context{.yield = yield, .apiVersion = 1u});
|
||||||
|
ASSERT_TRUE(output);
|
||||||
|
EXPECT_EQ(*output, json::parse(OUT));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCAccountTxHandlerTest, NFTTxs_API_v2)
|
||||||
|
{
|
||||||
|
auto const OUT = R"({
|
||||||
|
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index_min": 10,
|
||||||
|
"ledger_index_max": 30,
|
||||||
|
"transactions": [
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"AffectedNodes":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"ModifiedNode":
|
||||||
|
{
|
||||||
|
"FinalFields":
|
||||||
|
{
|
||||||
|
"NFTokens":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"NFToken":
|
||||||
|
{
|
||||||
|
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF",
|
||||||
|
"URI": "7465737475726C"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"NFToken":
|
||||||
|
{
|
||||||
|
"NFTokenID": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
||||||
|
"URI": "7465737475726C"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "NFTokenPage",
|
||||||
|
"PreviousFields":
|
||||||
|
{
|
||||||
|
"NFTokens":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"NFToken":
|
||||||
|
{
|
||||||
|
"NFTokenID": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
||||||
|
"URI": "7465737475726C"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"nftoken_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
||||||
|
},
|
||||||
|
"tx":
|
||||||
|
{
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Fee": "50",
|
||||||
|
"NFTokenTaxon": 123,
|
||||||
|
"Sequence": 1,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "NFTokenMint",
|
||||||
|
"hash": "C74463F49CFDCBEF3E9902672719918CDE5042DC7E7660BEBD1D1105C4B6DFF4",
|
||||||
|
"ledger_index": 11,
|
||||||
|
"date": 1
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"meta":
|
||||||
|
{
|
||||||
|
"AffectedNodes":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"DeletedNode":
|
||||||
|
{
|
||||||
|
"FinalFields":
|
||||||
|
{
|
||||||
|
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "NFTokenOffer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"nftoken_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
},
|
||||||
|
"tx":
|
||||||
|
{
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Fee": "50",
|
||||||
|
"NFTokenBuyOffer": "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC",
|
||||||
|
"Sequence": 1,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "NFTokenAcceptOffer",
|
||||||
|
"hash": "7682BE6BCDE62F8142915DD852936623B68FC3839A8A424A6064B898702B0CDF",
|
||||||
|
"ledger_index": 11,
|
||||||
|
"date": 2
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"meta":
|
||||||
|
{
|
||||||
|
"AffectedNodes":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"DeletedNode": {
|
||||||
|
"FinalFields":
|
||||||
|
{
|
||||||
|
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "NFTokenOffer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"DeletedNode":
|
||||||
|
{
|
||||||
|
"FinalFields":
|
||||||
|
{
|
||||||
|
"NFTokenID": "15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "NFTokenOffer"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"nftoken_ids":
|
||||||
|
[
|
||||||
|
"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA",
|
||||||
|
"15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"tx":
|
||||||
|
{
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Fee": "50",
|
||||||
|
"NFTokenOffers":
|
||||||
|
[
|
||||||
|
"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA",
|
||||||
|
"15FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF"
|
||||||
|
],
|
||||||
|
"Sequence": 1,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "NFTokenCancelOffer",
|
||||||
|
"hash": "9F82743EEB30065FB9CB92C61F0F064B5859C5A590FA811FAAAD9C988E5B47DB",
|
||||||
|
"ledger_index": 11,
|
||||||
|
"date": 3
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"meta":
|
||||||
|
{
|
||||||
|
"AffectedNodes":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"CreatedNode":
|
||||||
|
{
|
||||||
|
"LedgerEntryType": "NFTokenOffer",
|
||||||
|
"LedgerIndex": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"offer_id": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA"
|
||||||
|
},
|
||||||
|
"tx":
|
||||||
|
{
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Amount": "123",
|
||||||
|
"Fee": "50",
|
||||||
|
"NFTokenID": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF",
|
||||||
|
"Sequence": 1,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "NFTokenCreateOffer",
|
||||||
|
"hash": "ECB1837EB7C7C0AC22ECDCCE59FDD4795C70E0B9D8F4E1C9A9408BB7EC75DA5C",
|
||||||
|
"ledger_index": 11,
|
||||||
|
"date": 4
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"validated": true,
|
||||||
|
"marker":
|
||||||
|
{
|
||||||
|
"ledger": 12,
|
||||||
|
"seq": 34
|
||||||
|
}
|
||||||
|
})";
|
||||||
|
mockBackendPtr->updateRange(MINSEQ); // min
|
||||||
|
mockBackendPtr->updateRange(MAXSEQ); // max
|
||||||
|
MockBackend* rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
ASSERT_NE(rawBackendPtr, nullptr);
|
||||||
|
auto const transactions = genNFTTransactions(MINSEQ + 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 = json::parse(fmt::format(
|
||||||
|
R"({{
|
||||||
|
"account": "{}",
|
||||||
|
"ledger_index_min": {},
|
||||||
|
"ledger_index_max": {},
|
||||||
|
"forward": false,
|
||||||
|
"marker": {{"ledger": 10, "seq": 11}}
|
||||||
|
}})",
|
||||||
|
ACCOUNT,
|
||||||
|
-1,
|
||||||
|
-1));
|
||||||
|
auto const output = handler.process(input, Context{.yield = yield, .apiVersion = 2u});
|
||||||
ASSERT_TRUE(output);
|
ASSERT_TRUE(output);
|
||||||
EXPECT_EQ(*output, json::parse(OUT));
|
EXPECT_EQ(*output, json::parse(OUT));
|
||||||
});
|
});
|
||||||
@@ -1192,6 +1413,7 @@ struct AccountTxTransactionBundle
|
|||||||
std::string testName;
|
std::string testName;
|
||||||
std::string testJson;
|
std::string testJson;
|
||||||
std::string result;
|
std::string result;
|
||||||
|
std::uint32_t apiVersion = 2u;
|
||||||
};
|
};
|
||||||
|
|
||||||
// parameterized test cases for parameters check
|
// parameterized test cases for parameters check
|
||||||
@@ -1351,7 +1573,57 @@ generateTransactionTypeTestValues()
|
|||||||
})",
|
})",
|
||||||
"[]"},
|
"[]"},
|
||||||
AccountTxTransactionBundle{
|
AccountTxTransactionBundle{
|
||||||
"Payment",
|
"Payment_API_v1",
|
||||||
|
R"({
|
||||||
|
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"ledger_index": "validated",
|
||||||
|
"tx_type": "Payment"
|
||||||
|
})",
|
||||||
|
R"([
|
||||||
|
{
|
||||||
|
"meta": {
|
||||||
|
"AffectedNodes": [
|
||||||
|
{
|
||||||
|
"ModifiedNode": {
|
||||||
|
"FinalFields": {
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Balance": "22"
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "AccountRoot"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"ModifiedNode": {
|
||||||
|
"FinalFields": {
|
||||||
|
"Account": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
|
||||||
|
"Balance": "23"
|
||||||
|
},
|
||||||
|
"LedgerEntryType": "AccountRoot"
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
"TransactionIndex": 0,
|
||||||
|
"TransactionResult": "tesSUCCESS",
|
||||||
|
"delivered_amount": "unavailable"
|
||||||
|
},
|
||||||
|
"tx": {
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Amount": "1",
|
||||||
|
"Destination": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
|
||||||
|
"Fee": "1",
|
||||||
|
"Sequence": 32,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TransactionType": "Payment",
|
||||||
|
"hash": "51D2AAA6B8E4E16EF22F6424854283D8391B56875858A711B8CE4D5B9A422CC2",
|
||||||
|
"ledger_index": 30,
|
||||||
|
"inLedger": 30,
|
||||||
|
"date": 1
|
||||||
|
},
|
||||||
|
"validated": true
|
||||||
|
}
|
||||||
|
])",
|
||||||
|
1u},
|
||||||
|
AccountTxTransactionBundle{
|
||||||
|
"Payment_API_v2",
|
||||||
R"({
|
R"({
|
||||||
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
"ledger_index": "validated",
|
"ledger_index": "validated",
|
||||||
@@ -1397,7 +1669,8 @@ generateTransactionTypeTestValues()
|
|||||||
},
|
},
|
||||||
"validated": true
|
"validated": true
|
||||||
}
|
}
|
||||||
])"},
|
])",
|
||||||
|
2u},
|
||||||
AccountTxTransactionBundle{
|
AccountTxTransactionBundle{
|
||||||
"PaymentChannelClaim",
|
"PaymentChannelClaim",
|
||||||
R"({
|
R"({
|
||||||
@@ -1484,7 +1757,7 @@ TEST_P(AccountTxTransactionTypeTest, SpecificTransactionType)
|
|||||||
runSpawn([&, this](auto yield) {
|
runSpawn([&, this](auto yield) {
|
||||||
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
auto const handler = AnyHandler{AccountTxHandler{mockBackendPtr}};
|
||||||
auto const req = json::parse(testBundle.testJson);
|
auto const req = json::parse(testBundle.testJson);
|
||||||
auto const output = handler.process(req, Context{yield});
|
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = testBundle.apiVersion});
|
||||||
EXPECT_TRUE(output);
|
EXPECT_TRUE(output);
|
||||||
|
|
||||||
auto const transactions = output->at("transactions").as_array();
|
auto const transactions = output->at("transactions").as_array();
|
||||||
|
|||||||
@@ -256,7 +256,7 @@ generateParameterBookOffersTestBundles()
|
|||||||
"taker": "123"
|
"taker": "123"
|
||||||
})",
|
})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Invalid field 'taker'"},
|
"Invalid field 'taker'."},
|
||||||
ParameterTestBundle{
|
ParameterTestBundle{
|
||||||
"TakerNotString",
|
"TakerNotString",
|
||||||
R"({
|
R"({
|
||||||
@@ -272,7 +272,7 @@ generateParameterBookOffersTestBundles()
|
|||||||
"taker": 123
|
"taker": 123
|
||||||
})",
|
})",
|
||||||
"invalidParams",
|
"invalidParams",
|
||||||
"Invalid field 'taker'"},
|
"Invalid field 'taker'."},
|
||||||
ParameterTestBundle{
|
ParameterTestBundle{
|
||||||
"LimitNotInt",
|
"LimitNotInt",
|
||||||
R"({
|
R"({
|
||||||
@@ -384,8 +384,7 @@ generateParameterBookOffersTestBundles()
|
|||||||
}
|
}
|
||||||
})",
|
})",
|
||||||
"srcIsrMalformed",
|
"srcIsrMalformed",
|
||||||
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
"Unneeded field 'taker_pays.issuer' for XRP currency specification."},
|
||||||
"specification."},
|
|
||||||
ParameterTestBundle{
|
ParameterTestBundle{
|
||||||
"PaysCurrencyWithXRPIssuer",
|
"PaysCurrencyWithXRPIssuer",
|
||||||
R"({
|
R"({
|
||||||
@@ -430,8 +429,7 @@ generateParameterBookOffersTestBundles()
|
|||||||
}
|
}
|
||||||
})",
|
})",
|
||||||
"dstIsrMalformed",
|
"dstIsrMalformed",
|
||||||
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
"Unneeded field 'taker_gets.issuer' for XRP currency specification."},
|
||||||
"specification."},
|
|
||||||
ParameterTestBundle{
|
ParameterTestBundle{
|
||||||
"BadMarket",
|
"BadMarket",
|
||||||
R"({
|
R"({
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ generateTestValuesForParametersTest()
|
|||||||
"invalidParams",
|
"invalidParams",
|
||||||
"ledgerIndexMalformed"},
|
"ledgerIndexMalformed"},
|
||||||
|
|
||||||
ParamTestCaseBundle{"UnknownOption", R"({})", "unknownOption", "Unknown option."},
|
ParamTestCaseBundle{"UnknownOption", R"({})", "invalidParams", "Invalid parameters."},
|
||||||
|
|
||||||
ParamTestCaseBundle{
|
ParamTestCaseBundle{
|
||||||
"InvalidDepositPreauthType",
|
"InvalidDepositPreauthType",
|
||||||
@@ -1094,8 +1094,8 @@ TEST_F(RPCLedgerEntryTest, InvalidEntryTypeVersion2)
|
|||||||
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2});
|
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2});
|
||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
auto const err = rpc::makeError(output.error());
|
auto const err = rpc::makeError(output.error());
|
||||||
EXPECT_EQ(err.at("error").as_string(), "unknownOption");
|
EXPECT_EQ(err.at("error").as_string(), "invalidParams");
|
||||||
EXPECT_EQ(err.at("error_message").as_string(), "Unknown option.");
|
EXPECT_EQ(err.at("error_message").as_string(), "Invalid parameters.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1107,7 +1107,7 @@ TEST_F(RPCLedgerEntryTest, InvalidEntryTypeVersion1)
|
|||||||
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 1});
|
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 1});
|
||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
auto const err = rpc::makeError(output.error());
|
auto const err = rpc::makeError(output.error());
|
||||||
EXPECT_EQ(err.at("error").as_string(), "invalidParams");
|
EXPECT_EQ(err.at("error").as_string(), "unknownOption");
|
||||||
EXPECT_EQ(err.at("error_message").as_string(), "Invalid parameters.");
|
EXPECT_EQ(err.at("error_message").as_string(), "Unknown option.");
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -410,8 +410,7 @@ generateTestValuesForParametersTest()
|
|||||||
]
|
]
|
||||||
})",
|
})",
|
||||||
"dstIsrMalformed",
|
"dstIsrMalformed",
|
||||||
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
"Unneeded field 'taker_gets.issuer' for XRP currency specification."},
|
||||||
"specification."},
|
|
||||||
SubscribeParamTestCaseBundle{
|
SubscribeParamTestCaseBundle{
|
||||||
"BooksItemTakerPaysXRPHasIssuer",
|
"BooksItemTakerPaysXRPHasIssuer",
|
||||||
R"({
|
R"({
|
||||||
@@ -430,8 +429,7 @@ generateTestValuesForParametersTest()
|
|||||||
]
|
]
|
||||||
})",
|
})",
|
||||||
"srcIsrMalformed",
|
"srcIsrMalformed",
|
||||||
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
"Unneeded field 'taker_pays.issuer' for XRP currency specification."},
|
||||||
"specification."},
|
|
||||||
SubscribeParamTestCaseBundle{
|
SubscribeParamTestCaseBundle{
|
||||||
"BooksItemBadMartket",
|
"BooksItemBadMartket",
|
||||||
R"({
|
R"({
|
||||||
|
|||||||
@@ -28,12 +28,49 @@ using namespace rpc;
|
|||||||
namespace json = boost::json;
|
namespace json = boost::json;
|
||||||
using namespace testing;
|
using namespace testing;
|
||||||
|
|
||||||
constexpr static auto TXNID = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD";
|
auto constexpr static TXNID = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD";
|
||||||
constexpr static auto NFTID = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF";
|
auto constexpr static NFTID = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DF";
|
||||||
constexpr static auto NFTID2 = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA";
|
auto constexpr static NFTID2 = "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DA";
|
||||||
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
auto constexpr static ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
|
||||||
constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
auto constexpr static ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
|
||||||
constexpr static auto CURRENCY = "0158415500000000C1F76FF6ECB0BAC600000000";
|
auto constexpr static CURRENCY = "0158415500000000C1F76FF6ECB0BAC600000000";
|
||||||
|
|
||||||
|
auto constexpr static DEFAULT_OUT = R"({
|
||||||
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"Fee": "2",
|
||||||
|
"Sequence": 100,
|
||||||
|
"SigningPubKey": "74657374",
|
||||||
|
"TakerGets": {
|
||||||
|
"currency": "0158415500000000C1F76FF6ECB0BAC600000000",
|
||||||
|
"issuer": "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
|
||||||
|
"value": "200"
|
||||||
|
},
|
||||||
|
"TakerPays": "300",
|
||||||
|
"TransactionType": "OfferCreate",
|
||||||
|
"hash": "2E2FBAAFF767227FE4381C4BE9855986A6B9F96C62F6E443731AB36F7BBB8A08",
|
||||||
|
"meta": {
|
||||||
|
"AffectedNodes": [
|
||||||
|
{
|
||||||
|
"CreatedNode": {
|
||||||
|
"LedgerEntryType": "Offer",
|
||||||
|
"NewFields": {
|
||||||
|
"TakerGets": "200",
|
||||||
|
"TakerPays": {
|
||||||
|
"currency": "0158415500000000C1F76FF6ECB0BAC600000000",
|
||||||
|
"issuer": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
|
"value": "300"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"TransactionIndex": 100,
|
||||||
|
"TransactionResult": "tesSUCCESS"
|
||||||
|
},
|
||||||
|
"date": 123456,
|
||||||
|
"ledger_index": 100,
|
||||||
|
"validated": true
|
||||||
|
})";
|
||||||
|
|
||||||
class RPCTxTest : public HandlerBaseTest
|
class RPCTxTest : public HandlerBaseTest
|
||||||
{
|
{
|
||||||
@@ -48,8 +85,8 @@ TEST_F(RPCTxTest, ExcessiveLgrRange)
|
|||||||
"command": "tx",
|
"command": "tx",
|
||||||
"transaction": "{}",
|
"transaction": "{}",
|
||||||
"min_ledger": 1,
|
"min_ledger": 1,
|
||||||
"max_ledger":1002
|
"max_ledger": 1002
|
||||||
}})",
|
}})",
|
||||||
TXNID));
|
TXNID));
|
||||||
auto const output = handler.process(req, Context{yield});
|
auto const output = handler.process(req, Context{yield});
|
||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
@@ -70,7 +107,7 @@ TEST_F(RPCTxTest, InvalidLgrRange)
|
|||||||
"transaction": "{}",
|
"transaction": "{}",
|
||||||
"max_ledger": 1,
|
"max_ledger": 1,
|
||||||
"min_ledger": 10
|
"min_ledger": 10
|
||||||
}})",
|
}})",
|
||||||
TXNID));
|
TXNID));
|
||||||
auto const output = handler.process(req, Context{yield});
|
auto const output = handler.process(req, Context{yield});
|
||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
@@ -93,7 +130,7 @@ TEST_F(RPCTxTest, TxnNotFound)
|
|||||||
R"({{
|
R"({{
|
||||||
"command": "tx",
|
"command": "tx",
|
||||||
"transaction": "{}"
|
"transaction": "{}"
|
||||||
}})",
|
}})",
|
||||||
TXNID));
|
TXNID));
|
||||||
auto const output = handler.process(req, Context{yield});
|
auto const output = handler.process(req, Context{yield});
|
||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
@@ -119,8 +156,8 @@ TEST_F(RPCTxTest, TxnNotFoundInGivenRangeSearchAllFalse)
|
|||||||
"command": "tx",
|
"command": "tx",
|
||||||
"transaction": "{}",
|
"transaction": "{}",
|
||||||
"min_ledger": 1,
|
"min_ledger": 1,
|
||||||
"max_ledger":1000
|
"max_ledger": 1000
|
||||||
}})",
|
}})",
|
||||||
TXNID));
|
TXNID));
|
||||||
auto const output = handler.process(req, Context{yield});
|
auto const output = handler.process(req, Context{yield});
|
||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
@@ -147,8 +184,8 @@ TEST_F(RPCTxTest, TxnNotFoundInGivenRangeSearchAllTrue)
|
|||||||
"command": "tx",
|
"command": "tx",
|
||||||
"transaction": "{}",
|
"transaction": "{}",
|
||||||
"min_ledger": 1,
|
"min_ledger": 1,
|
||||||
"max_ledger":1000
|
"max_ledger": 1000
|
||||||
}})",
|
}})",
|
||||||
TXNID));
|
TXNID));
|
||||||
auto const output = handler.process(req, Context{yield});
|
auto const output = handler.process(req, Context{yield});
|
||||||
ASSERT_FALSE(output);
|
ASSERT_FALSE(output);
|
||||||
@@ -160,75 +197,77 @@ TEST_F(RPCTxTest, TxnNotFoundInGivenRangeSearchAllTrue)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCTxTest, DefaultParameter)
|
TEST_F(RPCTxTest, DefaultParameter_API_v1)
|
||||||
{
|
{
|
||||||
auto constexpr static OUT = R"({
|
auto const rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
"Account":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
ASSERT_NE(rawBackendPtr, nullptr);
|
||||||
"Fee":"2",
|
|
||||||
"Sequence":100,
|
|
||||||
"SigningPubKey":"74657374",
|
|
||||||
"TakerGets":{
|
|
||||||
"currency":"0158415500000000C1F76FF6ECB0BAC600000000",
|
|
||||||
"issuer":"rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun",
|
|
||||||
"value":"200"
|
|
||||||
},
|
|
||||||
"TakerPays":"300",
|
|
||||||
"TransactionType":"OfferCreate",
|
|
||||||
"hash":"2E2FBAAFF767227FE4381C4BE9855986A6B9F96C62F6E443731AB36F7BBB8A08",
|
|
||||||
"meta":{
|
|
||||||
"AffectedNodes":[
|
|
||||||
{
|
|
||||||
"CreatedNode":{
|
|
||||||
"LedgerEntryType":"Offer",
|
|
||||||
"NewFields":{
|
|
||||||
"TakerGets":"200",
|
|
||||||
"TakerPays":{
|
|
||||||
"currency":"0158415500000000C1F76FF6ECB0BAC600000000",
|
|
||||||
"issuer":"rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
|
||||||
"value":"300"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"TransactionIndex":100,
|
|
||||||
"TransactionResult":"tesSUCCESS"
|
|
||||||
},
|
|
||||||
"date":123456,
|
|
||||||
"ledger_index":100,
|
|
||||||
"validated": true
|
|
||||||
})";
|
|
||||||
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
|
||||||
TransactionAndMetadata tx;
|
TransactionAndMetadata tx;
|
||||||
tx.metadata = CreateMetaDataForCreateOffer(CURRENCY, ACCOUNT, 100, 200, 300).getSerializer().peekData();
|
tx.metadata = CreateMetaDataForCreateOffer(CURRENCY, ACCOUNT, 100, 200, 300).getSerializer().peekData();
|
||||||
tx.transaction =
|
tx.transaction =
|
||||||
CreateCreateOfferTransactionObject(ACCOUNT, 2, 100, CURRENCY, ACCOUNT2, 200, 300).getSerializer().peekData();
|
CreateCreateOfferTransactionObject(ACCOUNT, 2, 100, CURRENCY, ACCOUNT2, 200, 300).getSerializer().peekData();
|
||||||
tx.date = 123456;
|
tx.date = 123456;
|
||||||
tx.ledgerSequence = 100;
|
tx.ledgerSequence = 100;
|
||||||
|
|
||||||
ON_CALL(*rawBackendPtr, fetchTransaction(ripple::uint256{TXNID}, _)).WillByDefault(Return(tx));
|
ON_CALL(*rawBackendPtr, fetchTransaction(ripple::uint256{TXNID}, _)).WillByDefault(Return(tx));
|
||||||
EXPECT_CALL(*rawBackendPtr, fetchTransaction).Times(1);
|
EXPECT_CALL(*rawBackendPtr, fetchTransaction).Times(1);
|
||||||
|
|
||||||
runSpawn([this](auto yield) {
|
runSpawn([this](auto yield) {
|
||||||
auto const handler = AnyHandler{TxHandler{mockBackendPtr}};
|
auto const handler = AnyHandler{TxHandler{mockBackendPtr}};
|
||||||
auto const req = json::parse(fmt::format(
|
auto const req = json::parse(fmt::format(
|
||||||
R"({{
|
R"({{
|
||||||
"command": "tx",
|
"command": "tx",
|
||||||
"transaction": "{}"
|
"transaction": "{}"
|
||||||
}})",
|
}})",
|
||||||
TXNID));
|
TXNID));
|
||||||
auto const output = handler.process(req, Context{yield});
|
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 1u});
|
||||||
ASSERT_TRUE(output);
|
ASSERT_TRUE(output);
|
||||||
EXPECT_EQ(*output, json::parse(OUT));
|
|
||||||
|
auto v1Output = json::parse(DEFAULT_OUT);
|
||||||
|
v1Output.as_object()[JS(inLedger)] = v1Output.as_object()[JS(ledger_index)];
|
||||||
|
EXPECT_EQ(*output, v1Output);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCTxTest, DefaultParameter_API_v2)
|
||||||
|
{
|
||||||
|
auto const rawBackendPtr = dynamic_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
ASSERT_NE(rawBackendPtr, nullptr);
|
||||||
|
|
||||||
|
TransactionAndMetadata tx;
|
||||||
|
tx.metadata = CreateMetaDataForCreateOffer(CURRENCY, ACCOUNT, 100, 200, 300).getSerializer().peekData();
|
||||||
|
tx.transaction =
|
||||||
|
CreateCreateOfferTransactionObject(ACCOUNT, 2, 100, CURRENCY, ACCOUNT2, 200, 300).getSerializer().peekData();
|
||||||
|
tx.date = 123456;
|
||||||
|
tx.ledgerSequence = 100;
|
||||||
|
|
||||||
|
ON_CALL(*rawBackendPtr, fetchTransaction(ripple::uint256{TXNID}, _)).WillByDefault(Return(tx));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchTransaction).Times(1);
|
||||||
|
|
||||||
|
runSpawn([this](auto yield) {
|
||||||
|
auto const handler = AnyHandler{TxHandler{mockBackendPtr}};
|
||||||
|
auto const req = json::parse(fmt::format(
|
||||||
|
R"({{
|
||||||
|
"command": "tx",
|
||||||
|
"transaction": "{}"
|
||||||
|
}})",
|
||||||
|
TXNID));
|
||||||
|
auto const output = handler.process(req, Context{.yield = yield, .apiVersion = 2u});
|
||||||
|
ASSERT_TRUE(output);
|
||||||
|
EXPECT_EQ(*output, json::parse(DEFAULT_OUT));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RPCTxTest, ReturnBinary)
|
TEST_F(RPCTxTest, ReturnBinary)
|
||||||
{
|
{
|
||||||
|
// Note: `inLedger` is API v1 only. See DefaultOutput_*
|
||||||
auto constexpr static OUT = R"({
|
auto constexpr static OUT = R"({
|
||||||
"meta":"201C00000064F8E311006FE864D50AA87BEE5380000158415500000000C1F76FF6ECB0BAC6000000004B4E9C06F24296074F7BC48F92A97916C6DC5EA96540000000000000C8E1E1F1031000",
|
"meta": "201C00000064F8E311006FE864D50AA87BEE5380000158415500000000C1F76FF6ECB0BAC6000000004B4E9C06F24296074F7BC48F92A97916C6DC5EA96540000000000000C8E1E1F1031000",
|
||||||
"tx":"120007240000006464400000000000012C65D5071AFD498D00000158415500000000C1F76FF6ECB0BAC600000000D31252CF902EF8DD8451243869B38667CBD89DF368400000000000000273047465737481144B4E9C06F24296074F7BC48F92A97916C6DC5EA9",
|
"tx": "120007240000006464400000000000012C65D5071AFD498D00000158415500000000C1F76FF6ECB0BAC600000000D31252CF902EF8DD8451243869B38667CBD89DF368400000000000000273047465737481144B4E9C06F24296074F7BC48F92A97916C6DC5EA9",
|
||||||
"hash":"05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD",
|
"hash": "05FB0EB4B899F056FA095537C5817163801F544BAFCEA39C995D76DB4D16F9DD",
|
||||||
"date":123456,
|
"date": 123456,
|
||||||
"ledger_index":100,
|
"ledger_index": 100,
|
||||||
|
"inLedger": 100,
|
||||||
"validated": true
|
"validated": true
|
||||||
})";
|
})";
|
||||||
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
auto const rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
@@ -247,7 +286,7 @@ TEST_F(RPCTxTest, ReturnBinary)
|
|||||||
"command": "tx",
|
"command": "tx",
|
||||||
"transaction": "{}",
|
"transaction": "{}",
|
||||||
"binary": true
|
"binary": true
|
||||||
}})",
|
}})",
|
||||||
TXNID));
|
TXNID));
|
||||||
auto const output = handler.process(req, Context{yield});
|
auto const output = handler.process(req, Context{yield});
|
||||||
ASSERT_TRUE(output);
|
ASSERT_TRUE(output);
|
||||||
@@ -257,6 +296,7 @@ TEST_F(RPCTxTest, ReturnBinary)
|
|||||||
|
|
||||||
TEST_F(RPCTxTest, MintNFT)
|
TEST_F(RPCTxTest, MintNFT)
|
||||||
{
|
{
|
||||||
|
// Note: `inLedger` is API v1 only. See DefaultOutput_*
|
||||||
auto const static OUT = fmt::format(
|
auto const static OUT = fmt::format(
|
||||||
R"({{
|
R"({{
|
||||||
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
"Account": "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn",
|
||||||
@@ -307,9 +347,10 @@ TEST_F(RPCTxTest, MintNFT)
|
|||||||
"TransactionResult": "tesSUCCESS",
|
"TransactionResult": "tesSUCCESS",
|
||||||
"nftoken_id": "{}"
|
"nftoken_id": "{}"
|
||||||
}},
|
}},
|
||||||
"validated": true,
|
|
||||||
"date": 123456,
|
"date": 123456,
|
||||||
"ledger_index": 100
|
"ledger_index": 100,
|
||||||
|
"inLedger": 100,
|
||||||
|
"validated": true
|
||||||
}})",
|
}})",
|
||||||
NFTID,
|
NFTID,
|
||||||
NFTID);
|
NFTID);
|
||||||
|
|||||||
@@ -405,8 +405,7 @@ generateTestValuesForParametersTest()
|
|||||||
]
|
]
|
||||||
})",
|
})",
|
||||||
"dstIsrMalformed",
|
"dstIsrMalformed",
|
||||||
"Unneeded field 'taker_gets.issuer' for XRP currency "
|
"Unneeded field 'taker_gets.issuer' for XRP currency specification."},
|
||||||
"specification."},
|
|
||||||
UnsubscribeParamTestCaseBundle{
|
UnsubscribeParamTestCaseBundle{
|
||||||
"BooksItemTakerPaysXRPHasIssuer",
|
"BooksItemTakerPaysXRPHasIssuer",
|
||||||
R"({
|
R"({
|
||||||
@@ -426,8 +425,7 @@ generateTestValuesForParametersTest()
|
|||||||
]
|
]
|
||||||
})",
|
})",
|
||||||
"srcIsrMalformed",
|
"srcIsrMalformed",
|
||||||
"Unneeded field 'taker_pays.issuer' for XRP currency "
|
"Unneeded field 'taker_pays.issuer' for XRP currency specification."},
|
||||||
"specification."},
|
|
||||||
UnsubscribeParamTestCaseBundle{
|
UnsubscribeParamTestCaseBundle{
|
||||||
"BooksItemBadMartket",
|
"BooksItemBadMartket",
|
||||||
R"({
|
R"({
|
||||||
|
|||||||
@@ -106,7 +106,8 @@ CreatePaymentTransactionMetaObject(
|
|||||||
std::string_view accountId1,
|
std::string_view accountId1,
|
||||||
std::string_view accountId2,
|
std::string_view accountId2,
|
||||||
int finalBalance1,
|
int finalBalance1,
|
||||||
int finalBalance2)
|
int finalBalance2,
|
||||||
|
uint32_t transactionIndex)
|
||||||
{
|
{
|
||||||
ripple::STObject finalFields(ripple::sfFinalFields);
|
ripple::STObject finalFields(ripple::sfFinalFields);
|
||||||
finalFields.setAccountID(ripple::sfAccount, GetAccountIDWithString(accountId1));
|
finalFields.setAccountID(ripple::sfAccount, GetAccountIDWithString(accountId1));
|
||||||
@@ -128,7 +129,7 @@ CreatePaymentTransactionMetaObject(
|
|||||||
metaArray.push_back(node2);
|
metaArray.push_back(node2);
|
||||||
metaObj.setFieldArray(ripple::sfAffectedNodes, metaArray);
|
metaObj.setFieldArray(ripple::sfAffectedNodes, metaArray);
|
||||||
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
|
metaObj.setFieldU8(ripple::sfTransactionResult, ripple::tesSUCCESS);
|
||||||
metaObj.setFieldU32(ripple::sfTransactionIndex, 0);
|
metaObj.setFieldU32(ripple::sfTransactionIndex, transactionIndex);
|
||||||
return metaObj;
|
return metaObj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,7 +75,8 @@ CreatePaymentTransactionMetaObject(
|
|||||||
std::string_view accountId1,
|
std::string_view accountId1,
|
||||||
std::string_view accountId2,
|
std::string_view accountId2,
|
||||||
int finalBalance1,
|
int finalBalance1,
|
||||||
int finalBalance2);
|
int finalBalance2,
|
||||||
|
uint32_t transactionIndex = 0);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create an account root ledger object
|
* Create an account root ledger object
|
||||||
|
|||||||
@@ -549,7 +549,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparseableNotArray)
|
|||||||
EXPECT_EQ(session->lastStatus, boost::beast::http::status::bad_request);
|
EXPECT_EQ(session->lastStatus, boost::beast::http::status::bad_request);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparseableEmptyArray)
|
TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparseableArrayWithDigit)
|
||||||
{
|
{
|
||||||
static auto constexpr response = "params unparseable";
|
static auto constexpr response = "params unparseable";
|
||||||
|
|
||||||
@@ -558,7 +558,7 @@ TEST_F(WebRPCServerHandlerTest, HTTPParamsUnparseableEmptyArray)
|
|||||||
|
|
||||||
static auto constexpr requestJSON = R"({
|
static auto constexpr requestJSON = R"({
|
||||||
"method": "ledger",
|
"method": "ledger",
|
||||||
"params": []
|
"params": [1]
|
||||||
})";
|
})";
|
||||||
|
|
||||||
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
EXPECT_CALL(*rpcEngine, notifyBadSyntax).Times(1);
|
||||||
|
|||||||
Reference in New Issue
Block a user