refactor: Subscription Manager uses async framework (#1605)

Fix #1209
This commit is contained in:
cyan317
2024-08-16 13:46:14 +01:00
committed by GitHub
parent 5332d3e9f0
commit 4cbd3f5e18
33 changed files with 396 additions and 488 deletions

View File

@@ -52,9 +52,6 @@
struct AccountTransactionsData;
struct NFTTransactionsData;
struct NFTsData;
namespace feed {
class SubscriptionManager;
} // namespace feed
/**
* @brief This namespace contains everything to do with the ETL and ETL sources.

View File

@@ -28,6 +28,8 @@
#include "feed/impl/LedgerFeed.hpp"
#include "feed/impl/ProposedTransactionFeed.hpp"
#include "feed/impl/TransactionFeed.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include "util/async/context/BasicExecutionContext.hpp"
#include "util/log/Logger.hpp"
#include <boost/asio/executor_work_guard.hpp>
@@ -40,10 +42,8 @@
#include <xrpl/protocol/LedgerHeader.h>
#include <cstdint>
#include <functional>
#include <memory>
#include <string>
#include <thread>
#include <vector>
/**
@@ -57,9 +57,8 @@ namespace feed {
* @brief A subscription manager is responsible for managing the subscriptions and publishing the feeds
*/
class SubscriptionManager : public SubscriptionManagerInterface {
std::reference_wrapper<boost::asio::io_context> ioContext_;
std::shared_ptr<data::BackendInterface const> backend_;
util::async::AnyExecutionContext ctx_;
impl::ForwardFeed manifestFeed_;
impl::ForwardFeed validationsFeed_;
impl::LedgerFeed ledgerFeed_;
@@ -71,24 +70,31 @@ public:
/**
* @brief Construct a new Subscription Manager object
*
* @param ioContext The io context to use
* @param executor The executor to use to publish the feeds
* @param backend The backend to use
*/
SubscriptionManager(
boost::asio::io_context& ioContext,
std::shared_ptr<data::BackendInterface const> const& backend
)
: ioContext_(ioContext)
, backend_(backend)
, manifestFeed_(ioContext, "manifest")
, validationsFeed_(ioContext, "validations")
, ledgerFeed_(ioContext)
, bookChangesFeed_(ioContext)
, transactionFeed_(ioContext)
, proposedTransactionFeed_(ioContext)
template <class ExecutorCtx>
SubscriptionManager(ExecutorCtx& executor, std::shared_ptr<data::BackendInterface const> const& backend)
: backend_(backend)
, ctx_(executor)
, manifestFeed_(ctx_, "manifest")
, validationsFeed_(ctx_, "validations")
, ledgerFeed_(ctx_)
, bookChangesFeed_(ctx_)
, transactionFeed_(ctx_)
, proposedTransactionFeed_(ctx_)
{
}
/**
* @brief Destructor of the SubscriptionManager object. It will block until all running jobs finished.
*/
~SubscriptionManager() override
{
ctx_.stop();
ctx_.join();
}
/**
* @brief Subscribe to the book changes feed.
* @param subscriber
@@ -286,16 +292,15 @@ public:
};
/**
* @brief The help class to run the subscription manager. The container of io_context which is used to publish the
* feeds.
* @brief The help class to run the subscription manager. The container of PoolExecutionContext which is used to publish
* the feeds.
*/
class SubscriptionManagerRunner {
boost::asio::io_context ioContext_;
std::uint64_t workersNum_;
using ActualExecutionCtx = util::async::PoolExecutionContext;
ActualExecutionCtx ctx_;
std::shared_ptr<SubscriptionManager> subscriptionManager_;
util::Logger logger_{"Subscriptions"};
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work_ =
boost::asio::make_work_guard(ioContext_);
std::vector<std::thread> workers_;
public:
/**
@@ -305,13 +310,11 @@ public:
* @param backend The backend to use
*/
SubscriptionManagerRunner(util::Config const& config, std::shared_ptr<data::BackendInterface> const& backend)
: subscriptionManager_(std::make_shared<SubscriptionManager>(ioContext_, backend))
: workersNum_(config.valueOr<std::uint64_t>("subscription_workers", 1))
, ctx_(workersNum_)
, subscriptionManager_(std::make_shared<SubscriptionManager>(ctx_, backend))
{
auto numThreads = config.valueOr<uint64_t>("subscription_workers", 1);
LOG(logger_.info()) << "Starting subscription manager with " << numThreads << " workers";
workers_.reserve(numThreads);
for (auto i = numThreads; i > 0; --i)
workers_.emplace_back([&] { ioContext_.run(); });
LOG(logger_.info()) << "Starting subscription manager with " << workersNum_ << " workers";
}
/**
@@ -324,12 +327,5 @@ public:
{
return subscriptionManager_;
}
~SubscriptionManagerRunner()
{
work_.reset();
for (auto& worker : workers_)
worker.join();
}
};
} // namespace feed

View File

@@ -22,6 +22,7 @@
#include "data/Types.hpp"
#include "feed/impl/SingleFeedBase.hpp"
#include "rpc/BookChangesHelper.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/json/serialize.hpp>
@@ -37,7 +38,7 @@ namespace feed::impl {
* '0A5010342D8AAFABDCA58A68F6F588E1C6E58C21B63ED6CA8DB2478F58F3ECD5', 'ledger_time': 756395682, 'changes': []}
*/
struct BookChangesFeed : public SingleFeedBase {
BookChangesFeed(boost::asio::io_context& ioContext) : SingleFeedBase(ioContext, "book_changes")
BookChangesFeed(util::async::AnyExecutionContext& executionCtx) : SingleFeedBase(executionCtx, "book_changes")
{
}

View File

@@ -22,6 +22,7 @@
#include "data/BackendInterface.hpp"
#include "feed/Types.hpp"
#include "feed/impl/SingleFeedBase.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
@@ -46,9 +47,9 @@ class LedgerFeed : public SingleFeedBase {
public:
/**
* @brief Construct a new Ledger Feed object
* @param ioContext The actual publish will be called in the strand of this.
* @param executionCtx The actual publish will be called in the strand of this.
*/
LedgerFeed(boost::asio::io_context& ioContext) : SingleFeedBase(ioContext, "ledger")
LedgerFeed(util::async::AnyExecutionContext& executionCtx) : SingleFeedBase(executionCtx, "ledger")
{
}

View File

@@ -101,17 +101,18 @@ ProposedTransactionFeed::pub(boost::json::object const& receivedTxJson)
auto const accounts = rpc::getAccountsFromTransaction(transaction);
auto affectedAccounts = std::unordered_set<ripple::AccountID>(accounts.cbegin(), accounts.cend());
boost::asio::post(strand_, [this, pubMsg = std::move(pubMsg), affectedAccounts = std::move(affectedAccounts)]() {
notified_.clear();
signal_.emit(pubMsg);
// Prevent the same connection from receiving the same message twice if it is subscribed to multiple accounts
// However, if the same connection subscribe both stream and account, it will still receive the message twice.
// notified_ can be cleared before signal_ emit to improve this, but let's keep it as is for now, since rippled
// acts like this.
notified_.clear();
for (auto const& account : affectedAccounts)
accountSignal_.emit(account, pubMsg);
});
[[maybe_unused]] auto task =
strand_.execute([this, pubMsg = std::move(pubMsg), affectedAccounts = std::move(affectedAccounts)]() {
notified_.clear();
signal_.emit(pubMsg);
// Prevent the same connection from receiving the same message twice if it is subscribed to multiple
// accounts However, if the same connection subscribe both stream and account, it will still receive the
// message twice. notified_ can be cleared before signal_ emit to improve this, but let's keep it as is for
// now, since rippled acts like this.
notified_.clear();
for (auto const& account : affectedAccounts)
accountSignal_.emit(account, pubMsg);
});
}
std::uint64_t

View File

@@ -23,6 +23,8 @@
#include "feed/impl/TrackableSignal.hpp"
#include "feed/impl/TrackableSignalMap.hpp"
#include "feed/impl/Util.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include "util/async/AnyStrand.hpp"
#include "util/log/Logger.hpp"
#include "util/prometheus/Gauge.hpp"
@@ -51,7 +53,7 @@ class ProposedTransactionFeed {
std::unordered_set<SubscriberPtr>
notified_; // Used by slots to prevent double notifications if tx contains multiple subscribed accounts
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
util::async::AnyStrand strand_;
std::reference_wrapper<util::prometheus::GaugeInt> subAllCount_;
std::reference_wrapper<util::prometheus::GaugeInt> subAccountCount_;
@@ -61,10 +63,10 @@ class ProposedTransactionFeed {
public:
/**
* @brief Construct a Proposed Transaction Feed object.
* @param ioContext The actual publish will be called in the strand of this.
* @param executionCtx The actual publish will be called in the strand of this.
*/
ProposedTransactionFeed(boost::asio::io_context& ioContext)
: strand_(boost::asio::make_strand(ioContext))
ProposedTransactionFeed(util::async::AnyExecutionContext& executionCtx)
: strand_(executionCtx.makeStrand())
, subAllCount_(getSubscriptionsGaugeInt("tx_proposed"))
, subAccountCount_(getSubscriptionsGaugeInt("account_proposed"))

View File

@@ -22,6 +22,7 @@
#include "feed/Types.hpp"
#include "feed/impl/TrackableSignal.hpp"
#include "feed/impl/Util.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include "util/log/Logger.hpp"
#include <boost/asio/io_context.hpp>
@@ -35,8 +36,8 @@
namespace feed::impl {
SingleFeedBase::SingleFeedBase(boost::asio::io_context& ioContext, std::string const& name)
: strand_(boost::asio::make_strand(ioContext)), subCount_(getSubscriptionsGaugeInt(name)), name_(name)
SingleFeedBase::SingleFeedBase(util::async::AnyExecutionContext& executionCtx, std::string const& name)
: strand_(executionCtx.makeStrand()), subCount_(getSubscriptionsGaugeInt(name)), name_(name)
{
}
@@ -67,8 +68,8 @@ SingleFeedBase::unsub(SubscriberSharedPtr const& subscriber)
void
SingleFeedBase::pub(std::string msg) const
{
boost::asio::post(strand_, [this, msg = std::move(msg)]() mutable {
auto const msgPtr = std::make_shared<std::string>(std::move(msg));
[[maybe_unused]] auto task = strand_.execute([this, msg = std::move(msg)]() {
auto const msgPtr = std::make_shared<std::string>(msg);
signal_.emit(msgPtr);
});
}

View File

@@ -21,6 +21,8 @@
#include "feed/Types.hpp"
#include "feed/impl/TrackableSignal.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include "util/async/AnyStrand.hpp"
#include "util/log/Logger.hpp"
#include "util/prometheus/Gauge.hpp"
@@ -38,7 +40,7 @@ namespace feed::impl {
* @brief Base class for single feed.
*/
class SingleFeedBase {
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
util::async::AnyStrand strand_;
std::reference_wrapper<util::prometheus::GaugeInt> subCount_;
TrackableSignal<Subscriber, std::shared_ptr<std::string> const&> signal_;
util::Logger logger_{"Subscriptions"};
@@ -47,10 +49,10 @@ class SingleFeedBase {
public:
/**
* @brief Construct a new Single Feed Base object
* @param ioContext The actual publish will be called in the strand of this.
* @param executionCtx The actual publish will be called in the strand of this.
* @param name The promethues counter name of the feed.
*/
SingleFeedBase(boost::asio::io_context& ioContext, std::string const& name);
SingleFeedBase(util::async::AnyExecutionContext& executionCtx, std::string const& name);
/**
* @brief Subscribe the feed.

View File

@@ -276,33 +276,30 @@ TransactionFeed::pub(
}
}
boost::asio::post(
strand_,
[this,
allVersionsMsgs = std::move(allVersionsMsgs),
affectedAccounts = std::move(affectedAccounts),
affectedBooks = std::move(affectedBooks)]() {
notified_.clear();
signal_.emit(allVersionsMsgs);
// clear the notified set. If the same connection subscribes both transactions + proposed_transactions,
// rippled SENDS the same message twice
notified_.clear();
txProposedsignal_.emit(allVersionsMsgs);
notified_.clear();
// check duplicate for account and proposed_account, this prevents sending the same message multiple times
// if it affects multiple accounts watched by the same connection
for (auto const& account : affectedAccounts) {
accountSignal_.emit(account, allVersionsMsgs);
accountProposedSignal_.emit(account, allVersionsMsgs);
}
notified_.clear();
// check duplicate for books, this prevents sending the same message multiple times if it affects multiple
// books watched by the same connection
for (auto const& book : affectedBooks) {
bookSignal_.emit(book, allVersionsMsgs);
}
[[maybe_unused]] auto task = strand_.execute([this,
allVersionsMsgs = std::move(allVersionsMsgs),
affectedAccounts = std::move(affectedAccounts),
affectedBooks = std::move(affectedBooks)]() {
notified_.clear();
signal_.emit(allVersionsMsgs);
// clear the notified set. If the same connection subscribes both transactions + proposed_transactions,
// rippled SENDS the same message twice
notified_.clear();
txProposedsignal_.emit(allVersionsMsgs);
notified_.clear();
// check duplicate for account and proposed_account, this prevents sending the same message multiple times
// if it affects multiple accounts watched by the same connection
for (auto const& account : affectedAccounts) {
accountSignal_.emit(account, allVersionsMsgs);
accountProposedSignal_.emit(account, allVersionsMsgs);
}
);
notified_.clear();
// check duplicate for books, this prevents sending the same message multiple times if it affects multiple
// books watched by the same connection
for (auto const& book : affectedBooks) {
bookSignal_.emit(book, allVersionsMsgs);
}
});
}
void

View File

@@ -25,6 +25,8 @@
#include "feed/impl/TrackableSignal.hpp"
#include "feed/impl/TrackableSignalMap.hpp"
#include "feed/impl/Util.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include "util/async/AnyStrand.hpp"
#include "util/log/Logger.hpp"
#include "util/prometheus/Gauge.hpp"
@@ -63,7 +65,7 @@ class TransactionFeed {
util::Logger logger_{"Subscriptions"};
boost::asio::strand<boost::asio::io_context::executor_type> strand_;
util::async::AnyStrand strand_;
std::reference_wrapper<util::prometheus::GaugeInt> subAllCount_;
std::reference_wrapper<util::prometheus::GaugeInt> subAccountCount_;
std::reference_wrapper<util::prometheus::GaugeInt> subBookCount_;
@@ -82,10 +84,10 @@ class TransactionFeed {
public:
/**
* @brief Construct a new Transaction Feed object.
* @param ioContext The actual publish will be called in the strand of this.
* @param executionCtx The actual publish will be called in the strand of this.
*/
TransactionFeed(boost::asio::io_context& ioContext)
: strand_(boost::asio::make_strand(ioContext))
TransactionFeed(util::async::AnyExecutionContext& executionCtx)
: strand_(executionCtx.makeStrand())
, subAllCount_(getSubscriptionsGaugeInt("tx"))
, subAccountCount_(getSubscriptionsGaugeInt("account"))
, subBookCount_(getSubscriptionsGaugeInt("book"))

View File

@@ -44,9 +44,6 @@
#include <utility>
// forward declarations
namespace feed {
class SubscriptionManager;
} // namespace feed
namespace etl {
class LoadBalancer;
class ETLService;

View File

@@ -43,9 +43,6 @@ class LoadBalancer;
namespace web {
struct ConnectionBase;
} // namespace web
namespace feed {
class SubscriptionManager;
} // namespace feed
namespace rpc {

View File

@@ -22,7 +22,7 @@
#include "data/AmendmentCenterInterface.hpp"
#include "data/BackendInterface.hpp"
#include "etl/ETLService.hpp"
#include "feed/SubscriptionManager.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
#include "rpc/Counters.hpp"
#include "rpc/common/AnyHandler.hpp"
#include "rpc/handlers/AMMInfo.hpp"
@@ -70,7 +70,7 @@ namespace rpc::impl {
ProductionHandlerProvider::ProductionHandlerProvider(
util::Config const& config,
std::shared_ptr<BackendInterface> const& backend,
std::shared_ptr<feed::SubscriptionManager> const& subscriptionManager,
std::shared_ptr<feed::SubscriptionManagerInterface> const& subscriptionManager,
std::shared_ptr<etl::LoadBalancer> const& balancer,
std::shared_ptr<etl::ETLService const> const& etl,
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter,

View File

@@ -21,7 +21,7 @@
#include "data/AmendmentCenterInterface.hpp"
#include "data/BackendInterface.hpp"
#include "feed/SubscriptionManager.hpp"
#include "feed/SubscriptionManagerInterface.hpp"
#include "rpc/common/AnyHandler.hpp"
#include "rpc/common/HandlerProvider.hpp"
#include "rpc/common/Types.hpp"
@@ -39,9 +39,6 @@ class LoadBalancer;
namespace rpc {
class Counters;
} // namespace rpc
namespace feed {
class SubscriptionManager;
} // namespace feed
namespace rpc::impl {
@@ -57,7 +54,7 @@ public:
ProductionHandlerProvider(
util::Config const& config,
std::shared_ptr<BackendInterface> const& backend,
std::shared_ptr<feed::SubscriptionManager> const& subscriptionManager,
std::shared_ptr<feed::SubscriptionManagerInterface> const& subscriptionManager,
std::shared_ptr<etl::LoadBalancer> const& balancer,
std::shared_ptr<etl::ETLService const> const& etl,
std::shared_ptr<data::AmendmentCenterInterface const> const& amendmentCenter,

View File

@@ -37,10 +37,6 @@
#include <string>
#include <vector>
namespace feed {
class SubscriptionManager;
} // namespace feed
namespace rpc {
/**

View File

@@ -202,6 +202,24 @@ public:
return pimpl_->makeStrand();
}
/**
* @brief Stop the execution context
*/
void
stop()
{
pimpl_->stop();
}
/**
* @brief Join the execution context
*/
void
join()
{
pimpl_->join();
}
private:
struct Concept {
virtual ~Concept() = default;
@@ -218,6 +236,10 @@ private:
scheduleAfter(std::chrono::milliseconds, std::function<std::any(AnyStopToken, bool)>) = 0;
virtual AnyStrand
makeStrand() = 0;
virtual void
stop() = 0;
virtual void
join() = 0;
};
template <typename CtxType>
@@ -257,6 +279,18 @@ private:
{
return ctx.get().makeStrand();
}
void
stop() override
{
ctx.get().stop();
}
void
join() override
{
ctx.get().join();
}
};
private:

View File

@@ -20,7 +20,6 @@
#pragma once
#include "util/async/AnyStopToken.hpp"
#include "util/async/Concepts.hpp"
#include "util/async/impl/ErasedOperation.hpp"
#include <any>
@@ -60,7 +59,7 @@ public:
* @return The type-erased operation
*/
[[nodiscard]] auto
execute(SomeHandlerWithoutStopToken auto&& fn)
execute(SomeHandlerWithoutStopToken auto&& fn) const
{
using RetType = std::decay_t<decltype(fn())>;
static_assert(not std::is_same_v<RetType, std::any>);
@@ -84,7 +83,7 @@ public:
* @return The type-erased operation
*/
[[nodiscard]] auto
execute(SomeHandlerWith<AnyStopToken> auto&& fn)
execute(SomeHandlerWith<AnyStopToken> auto&& fn) const
{
using RetType = std::decay_t<decltype(fn(std::declval<AnyStopToken>()))>;
static_assert(not std::is_same_v<RetType, std::any>);
@@ -109,7 +108,7 @@ public:
* @return The type-erased operation
*/
[[nodiscard]] auto
execute(SomeHandlerWith<AnyStopToken> auto&& fn, SomeStdDuration auto timeout)
execute(SomeHandlerWith<AnyStopToken> auto&& fn, SomeStdDuration auto timeout) const
{
using RetType = std::decay_t<decltype(fn(std::declval<AnyStopToken>()))>;
static_assert(not std::is_same_v<RetType, std::any>);
@@ -134,11 +133,9 @@ private:
virtual ~Concept() = default;
[[nodiscard]] virtual impl::ErasedOperation
execute(
std::function<std::any(AnyStopToken)>,
std::optional<std::chrono::milliseconds> timeout = std::nullopt
) = 0;
[[nodiscard]] virtual impl::ErasedOperation execute(std::function<std::any()>) = 0;
execute(std::function<std::any(AnyStopToken)>, std::optional<std::chrono::milliseconds> timeout = std::nullopt)
const = 0;
[[nodiscard]] virtual impl::ErasedOperation execute(std::function<std::any()>) const = 0;
};
template <typename StrandType>
@@ -152,13 +149,14 @@ private:
}
[[nodiscard]] impl::ErasedOperation
execute(std::function<std::any(AnyStopToken)> fn, std::optional<std::chrono::milliseconds> timeout) override
execute(std::function<std::any(AnyStopToken)> fn, std::optional<std::chrono::milliseconds> timeout)
const override
{
return strand.execute(std::move(fn), timeout);
}
[[nodiscard]] impl::ErasedOperation
execute(std::function<std::any()> fn) override
execute(std::function<std::any()> fn) const override
{
return strand.execute(std::move(fn));
}

View File

@@ -327,6 +327,15 @@ public:
{
context_.executor.stop();
}
/**
* @brief Block until all operations are completed
*/
void
join() noexcept
{
context_.executor.join();
}
};
/**

View File

@@ -40,6 +40,11 @@ struct SameThreadContext {
stop() noexcept
{
}
void
join() noexcept
{
}
};
// Note: these types are not actually used but needed for compilation

View File

@@ -64,10 +64,8 @@ public:
BasicStrand(BasicStrand const&) = delete;
[[nodiscard]] auto
execute(
SomeHandlerWith<StopToken> auto&& fn,
std::optional<std::chrono::milliseconds> timeout = std::nullopt
) noexcept(isNoexcept)
execute(SomeHandlerWith<StopToken> auto&& fn, std::optional<std::chrono::milliseconds> timeout = std::nullopt) const
noexcept(isNoexcept)
{
return DispatcherType::dispatch(
context_,
@@ -91,7 +89,7 @@ public:
}
[[nodiscard]] auto
execute(SomeHandlerWith<StopToken> auto&& fn, SomeStdDuration auto timeout) noexcept(isNoexcept)
execute(SomeHandlerWith<StopToken> auto&& fn, SomeStdDuration auto timeout) const noexcept(isNoexcept)
{
return execute(
std::forward<decltype(fn)>(fn),
@@ -100,7 +98,7 @@ public:
}
[[nodiscard]] auto
execute(SomeHandlerWithoutStopToken auto&& fn) noexcept(isNoexcept)
execute(SomeHandlerWithoutStopToken auto&& fn) const noexcept(isNoexcept)
{
return DispatcherType::dispatch(
context_,

View File

@@ -19,10 +19,10 @@
#pragma once
#include "util/AsioContextTestFixture.hpp"
#include "util/MockBackendTestFixture.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockWsBase.hpp"
#include "util/SyncExecutionCtxFixture.hpp"
#include "web/interface/ConnectionBase.hpp"
#include <boost/json/parse.hpp>
@@ -35,7 +35,7 @@
// Base class for feed tests, providing easy way to access the received feed
template <typename TestedFeed>
struct FeedBaseTest : util::prometheus::WithPrometheus, SyncAsioContextTest, MockBackendTest {
struct FeedBaseTest : util::prometheus::WithPrometheus, MockBackendTest, SyncExecutionCtxFixture {
protected:
std::shared_ptr<web::ConnectionBase> sessionPtr;
std::shared_ptr<TestedFeed> testFeedPtr;
@@ -44,7 +44,6 @@ protected:
void
SetUp() override
{
SyncAsioContextTest::SetUp();
testFeedPtr = std::make_shared<TestedFeed>(ctx);
sessionPtr = std::make_shared<MockSession>();
sessionPtr->apiSubVersion = 1;
@@ -56,7 +55,6 @@ protected:
{
sessionPtr.reset();
testFeedPtr.reset();
SyncAsioContextTest::TearDown();
}
};

View File

@@ -77,4 +77,5 @@ struct MockExecutionContext {
);
MOCK_METHOD(MockStrand const&, makeStrand, (), (const));
MOCK_METHOD(void, stop, (), (const));
MOCK_METHOD(void, join, (), (const));
};

View File

@@ -0,0 +1,37 @@
//------------------------------------------------------------------------------
/*
This file is part of clio: https://github.com/XRPLF/clio
Copyright (c) 2024, the clio developers.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above
copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*/
//==============================================================================
#pragma once
#include "util/LoggerFixtures.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include "util/async/context/SyncExecutionContext.hpp"
#include <gmock/gmock.h>
/**
* @brief Fixture with an embedded AnyExecutionContext wrapping a SyncExecutionContext
*
*/
struct SyncExecutionCtxFixture : virtual public NoLoggerFixture {
protected:
util::async::SyncExecutionContext syncCtx;
util::async::AnyExecutionContext ctx{syncCtx};
};

View File

@@ -23,7 +23,6 @@
#include "feed/impl/ForwardFeed.hpp"
#include "util/TestObject.hpp"
#include <boost/asio/io_context.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <xrpl/protocol/STObject.h>
@@ -55,7 +54,6 @@ TEST_F(FeedBookChangeTest, Pub)
trans1.metadata = metaObj.getSerializer().peekData();
transactions.push_back(trans1);
testFeedPtr->pub(ledgerHeader, transactions);
constexpr static auto bookChangePublish =
R"({
"type":"bookChanges",
@@ -78,11 +76,9 @@ TEST_F(FeedBookChangeTest, Pub)
})";
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(bookChangePublish))).Times(1);
ctx.run();
testFeedPtr->pub(ledgerHeader, transactions);
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->count(), 0);
testFeedPtr->pub(ledgerHeader, transactions);
ctx.restart();
ctx.run();
}

View File

@@ -19,6 +19,7 @@
#include "feed/FeedTestUtil.hpp"
#include "feed/impl/ForwardFeed.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/json/parse.hpp>
@@ -35,7 +36,7 @@ constexpr static auto FEED = R"({"test":"test"})";
class NamedForwardFeedTest : public ForwardFeed {
public:
NamedForwardFeedTest(boost::asio::io_context& ioContext) : ForwardFeed(ioContext, "test")
NamedForwardFeedTest(util::async::AnyExecutionContext& executionCtx) : ForwardFeed(executionCtx, "test")
{
}
};
@@ -47,15 +48,11 @@ TEST_F(FeedForwardTest, Pub)
testFeedPtr->sub(sessionPtr);
EXPECT_EQ(testFeedPtr->count(), 1);
auto const json = json::parse(FEED).as_object();
testFeedPtr->pub(json);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(FEED))).Times(1);
ctx.run();
testFeedPtr->pub(json);
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->count(), 0);
testFeedPtr->pub(json);
ctx.restart();
ctx.run();
}
TEST_F(FeedForwardTest, AutoDisconnect)
@@ -63,9 +60,8 @@ TEST_F(FeedForwardTest, AutoDisconnect)
testFeedPtr->sub(sessionPtr);
EXPECT_EQ(testFeedPtr->count(), 1);
auto const json = json::parse(FEED).as_object();
testFeedPtr->pub(json);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(FEED))).Times(1);
ctx.run();
testFeedPtr->pub(json);
sessionPtr.reset();
EXPECT_EQ(testFeedPtr->count(), 0);
testFeedPtr->pub(json);

View File

@@ -21,7 +21,6 @@
#include "feed/impl/LedgerFeed.hpp"
#include "util/TestObject.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/json/parse.hpp>
#include <gmock/gmock.h>
@@ -63,7 +62,6 @@ TEST_F(FeedLedgerTest, SubPub)
// check the response
EXPECT_EQ(res, json::parse(LedgerResponse));
});
ctx.run();
EXPECT_EQ(testFeedPtr->count(), 1);
constexpr static auto ledgerPub =
@@ -85,16 +83,12 @@ TEST_F(FeedLedgerTest, SubPub)
auto fee2 = ripple::Fees();
fee2.reserve = 10;
testFeedPtr->pub(ledgerHeader2, fee2, "10-31", 8);
ctx.restart();
ctx.run();
// test unsub, after unsub the send should not be called
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->count(), 0);
EXPECT_CALL(*mockSessionPtr, send(_)).Times(0);
testFeedPtr->pub(ledgerHeader2, fee2, "10-31", 8);
ctx.restart();
ctx.run();
}
TEST_F(FeedLedgerTest, AutoDisconnect)
@@ -120,7 +114,6 @@ TEST_F(FeedLedgerTest, AutoDisconnect)
// check the response
EXPECT_EQ(res, json::parse(LedgerResponse));
});
ctx.run();
EXPECT_EQ(testFeedPtr->count(), 1);
EXPECT_CALL(*mockSessionPtr, send(_)).Times(0);
@@ -132,6 +125,4 @@ TEST_F(FeedLedgerTest, AutoDisconnect)
fee2.reserve = 10;
// no error
testFeedPtr->pub(ledgerHeader2, fee2, "10-31", 8);
ctx.restart();
ctx.run();
}

View File

@@ -19,14 +19,13 @@
#include "feed/FeedTestUtil.hpp"
#include "feed/impl/ProposedTransactionFeed.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockWsBase.hpp"
#include "util/SyncExecutionCtxFixture.hpp"
#include "util/TestObject.hpp"
#include "util/prometheus/Gauge.hpp"
#include "web/interface/ConnectionBase.hpp"
#include <boost/asio/io_context.hpp>
#include <boost/json/parse.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -66,14 +65,11 @@ TEST_F(FeedProposedTransactionTest, ProposedTransaction)
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(DUMMY_TRANSACTION))).Times(1);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.run();
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->transactionSubcount(), 0);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.restart();
ctx.run();
}
TEST_F(FeedProposedTransactionTest, AccountProposedTransaction)
@@ -90,15 +86,12 @@ TEST_F(FeedProposedTransactionTest, AccountProposedTransaction)
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(DUMMY_TRANSACTION))).Times(1);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.run();
// unsub
testFeedPtr->unsub(account, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 1);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.restart();
ctx.run();
}
TEST_F(FeedProposedTransactionTest, SubStreamAndAccount)
@@ -111,7 +104,6 @@ TEST_F(FeedProposedTransactionTest, SubStreamAndAccount)
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(DUMMY_TRANSACTION))).Times(2);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.run();
// unsub
testFeedPtr->unsub(account, sessionPtr);
@@ -119,16 +111,12 @@ TEST_F(FeedProposedTransactionTest, SubStreamAndAccount)
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(DUMMY_TRANSACTION))).Times(1);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.restart();
ctx.run();
// unsub transaction
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->transactionSubcount(), 0);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.restart();
ctx.run();
}
TEST_F(FeedProposedTransactionTest, AccountProposedTransactionDuplicate)
@@ -142,23 +130,18 @@ TEST_F(FeedProposedTransactionTest, AccountProposedTransactionDuplicate)
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(DUMMY_TRANSACTION))).Times(1);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.run();
// unsub account1
testFeedPtr->unsub(account, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 1);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(DUMMY_TRANSACTION))).Times(1);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.restart();
ctx.run();
// unsub account2
testFeedPtr->unsub(account2, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 0);
testFeedPtr->pub(json::parse(DUMMY_TRANSACTION).get_object());
ctx.restart();
ctx.run();
}
TEST_F(FeedProposedTransactionTest, Count)
@@ -231,7 +214,7 @@ TEST_F(FeedProposedTransactionTest, AutoDisconnect)
EXPECT_EQ(testFeedPtr->transactionSubcount(), 0);
}
struct ProposedTransactionFeedMockPrometheusTest : WithMockPrometheus, SyncAsioContextTest {
struct ProposedTransactionFeedMockPrometheusTest : WithMockPrometheus, SyncExecutionCtxFixture {
protected:
std::shared_ptr<web::ConnectionBase> sessionPtr;
std::shared_ptr<ProposedTransactionFeed> testFeedPtr;
@@ -239,7 +222,6 @@ protected:
void
SetUp() override
{
SyncAsioContextTest::SetUp();
testFeedPtr = std::make_shared<ProposedTransactionFeed>(ctx);
sessionPtr = std::make_shared<MockSession>();
}
@@ -248,7 +230,6 @@ protected:
{
sessionPtr.reset();
testFeedPtr.reset();
SyncAsioContextTest::TearDown();
}
};

View File

@@ -19,13 +19,13 @@
#include "feed/FeedTestUtil.hpp"
#include "feed/impl/SingleFeedBase.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockWsBase.hpp"
#include "util/SyncExecutionCtxFixture.hpp"
#include "util/async/AnyExecutionContext.hpp"
#include "util/prometheus/Gauge.hpp"
#include "web/interface/ConnectionBase.hpp"
#include <boost/asio/io_context.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
@@ -36,7 +36,7 @@ constexpr static auto FEED = R"({"test":"test"})";
using namespace feed::impl;
using namespace util::prometheus;
struct FeedBaseMockPrometheusTest : WithMockPrometheus, SyncAsioContextTest {
struct FeedBaseMockPrometheusTest : WithMockPrometheus, SyncExecutionCtxFixture {
protected:
std::shared_ptr<web::ConnectionBase> sessionPtr;
std::shared_ptr<SingleFeedBase> testFeedPtr;
@@ -45,7 +45,6 @@ protected:
void
SetUp() override
{
SyncAsioContextTest::SetUp();
testFeedPtr = std::make_shared<SingleFeedBase>(ctx, "testFeed");
sessionPtr = std::make_shared<MockSession>();
mockSessionPtr = dynamic_cast<MockSession*>(sessionPtr.get());
@@ -55,7 +54,6 @@ protected:
{
sessionPtr.reset();
testFeedPtr.reset();
SyncAsioContextTest::TearDown();
}
};
@@ -81,7 +79,7 @@ TEST_F(FeedBaseMockPrometheusTest, AutoUnsub)
class NamedSingleFeedTest : public SingleFeedBase {
public:
NamedSingleFeedTest(boost::asio::io_context& ioContext) : SingleFeedBase(ioContext, "forTest")
NamedSingleFeedTest(util::async::AnyExecutionContext& executionCtx) : SingleFeedBase(executionCtx, "forTest")
{
}
};
@@ -94,13 +92,10 @@ TEST_F(SingleFeedBaseTest, Test)
testFeedPtr->sub(sessionPtr);
EXPECT_EQ(testFeedPtr->count(), 1);
testFeedPtr->pub(FEED);
ctx.run();
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->count(), 0);
testFeedPtr->pub(FEED);
ctx.restart();
ctx.run();
}
TEST_F(SingleFeedBaseTest, TestAutoDisconnect)
@@ -109,7 +104,6 @@ TEST_F(SingleFeedBaseTest, TestAutoDisconnect)
testFeedPtr->sub(sessionPtr);
EXPECT_EQ(testFeedPtr->count(), 1);
testFeedPtr->pub(FEED);
ctx.run();
sessionPtr.reset();
EXPECT_EQ(testFeedPtr->count(), 0);

View File

@@ -20,15 +20,15 @@
#include "data/Types.hpp"
#include "feed/FeedTestUtil.hpp"
#include "feed/SubscriptionManager.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/MockBackendTestFixture.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockWsBase.hpp"
#include "util/TestObject.hpp"
#include "util/async/context/BasicExecutionContext.hpp"
#include "util/async/context/SyncExecutionContext.hpp"
#include "web/interface/ConnectionBase.hpp"
#include <boost/asio/executor_work_guard.hpp>
#include <boost/asio/io_context.hpp>
#include <boost/asio/spawn.hpp>
#include <boost/json/object.hpp>
#include <boost/json/parse.hpp>
@@ -41,7 +41,6 @@
#include <memory>
#include <string>
#include <thread>
#include <vector>
constexpr static auto ACCOUNT1 = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
@@ -54,19 +53,18 @@ namespace json = boost::json;
using namespace feed;
using namespace feed::impl;
class SubscriptionManagerTest : public util::prometheus::WithPrometheus,
public MockBackendTest,
public SyncAsioContextTest {
template <class Execution>
class SubscriptionManagerBaseTest : public util::prometheus::WithPrometheus, public MockBackendTest {
protected:
std::shared_ptr<SubscriptionManager> SubscriptionManagerPtr;
std::shared_ptr<SubscriptionManager> subscriptionManagerPtr;
std::shared_ptr<web::ConnectionBase> session;
Execution ctx{2};
MockSession* sessionPtr = nullptr;
void
SetUp() override
{
SyncAsioContextTest::SetUp();
SubscriptionManagerPtr = std::make_shared<SubscriptionManager>(ctx, backend);
subscriptionManagerPtr = std::make_shared<SubscriptionManager>(ctx, backend);
session = std::make_shared<MockSession>();
session->apiSubVersion = 1;
sessionPtr = dynamic_cast<MockSession*>(session.get());
@@ -76,65 +74,38 @@ protected:
TearDown() override
{
session.reset();
SubscriptionManagerPtr.reset();
SyncAsioContextTest::TearDown();
subscriptionManagerPtr.reset();
}
};
// TODO enable when fixed :/
/*
TEST_F(SubscriptionManagerTest, MultipleThreadCtx)
{
std::vector<std::thread> workers;
workers.reserve(2);
using SubscriptionManagerTest = SubscriptionManagerBaseTest<util::async::SyncExecutionContext>;
SubscriptionManagerPtr->subManifest(session);
SubscriptionManagerPtr->subValidation(session);
using SubscriptionManagerAsyncTest = SubscriptionManagerBaseTest<util::async::PoolExecutionContext>;
TEST_F(SubscriptionManagerAsyncTest, MultipleThreadCtx)
{
subscriptionManagerPtr->subManifest(session);
subscriptionManagerPtr->subValidation(session);
constexpr static auto jsonManifest = R"({"manifest":"test"})";
constexpr static auto jsonValidation = R"({"validation":"test"})";
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(jsonManifest))).Times(1);
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(jsonValidation))).Times(1);
EXPECT_CALL(*sessionPtr, send(testing::_)).Times(testing::AtMost(2));
SubscriptionManagerPtr->forwardManifest(json::parse(jsonManifest).get_object());
SubscriptionManagerPtr->forwardValidation(json::parse(jsonValidation).get_object());
for (int i = 0; i < 2; ++i)
workers.emplace_back([this]() { ctx.run(); });
// wait for all jobs in ctx to finish
for (auto& worker : workers)
worker.join();
session.reset();
SubscriptionManagerPtr.reset();
subscriptionManagerPtr->forwardManifest(json::parse(jsonManifest).get_object());
subscriptionManagerPtr->forwardValidation(json::parse(jsonValidation).get_object());
}
*/
TEST_F(SubscriptionManagerTest, MultipleThreadCtxSessionDieEarly)
TEST_F(SubscriptionManagerAsyncTest, MultipleThreadCtxSessionDieEarly)
{
boost::asio::executor_work_guard<boost::asio::io_context::executor_type> work_ = boost::asio::make_work_guard(ctx);
std::vector<std::thread> workers;
workers.reserve(2);
for (int i = 0; i < 2; ++i)
workers.emplace_back([this]() { ctx.run(); });
SubscriptionManagerPtr->subManifest(session);
SubscriptionManagerPtr->subValidation(session);
SubscriptionManagerPtr->forwardManifest(json::parse(R"({"manifest":"test"})").get_object());
SubscriptionManagerPtr->forwardValidation(json::parse(R"({"validation":"test"})").get_object());
subscriptionManagerPtr->subManifest(session);
subscriptionManagerPtr->subValidation(session);
EXPECT_CALL(*sessionPtr, send(testing::_)).Times(0);
session.reset();
work_.reset();
for (auto& worker : workers)
worker.join();
// SubscriptionManager's pub job is running in thread pool, so we let thread pool run out of work, otherwise
// SubscriptionManager will die before the job is called
SubscriptionManagerPtr.reset();
subscriptionManagerPtr->forwardManifest(json::parse(R"({"manifest":"test"})").get_object());
subscriptionManagerPtr->forwardValidation(json::parse(R"({"validation":"test"})").get_object());
}
TEST_F(SubscriptionManagerTest, ReportCurrentSubscriber)
@@ -153,42 +124,42 @@ TEST_F(SubscriptionManagerTest, ReportCurrentSubscriber)
})";
std::shared_ptr<web::ConnectionBase> const session1 = std::make_shared<MockSession>();
std::shared_ptr<web::ConnectionBase> session2 = std::make_shared<MockSession>();
SubscriptionManagerPtr->subBookChanges(session1);
SubscriptionManagerPtr->subBookChanges(session2);
SubscriptionManagerPtr->subManifest(session1);
SubscriptionManagerPtr->subManifest(session2);
SubscriptionManagerPtr->subProposedTransactions(session1);
SubscriptionManagerPtr->subProposedTransactions(session2);
SubscriptionManagerPtr->subTransactions(session1);
subscriptionManagerPtr->subBookChanges(session1);
subscriptionManagerPtr->subBookChanges(session2);
subscriptionManagerPtr->subManifest(session1);
subscriptionManagerPtr->subManifest(session2);
subscriptionManagerPtr->subProposedTransactions(session1);
subscriptionManagerPtr->subProposedTransactions(session2);
subscriptionManagerPtr->subTransactions(session1);
session2->apiSubVersion = 2;
SubscriptionManagerPtr->subTransactions(session2);
SubscriptionManagerPtr->subValidation(session1);
SubscriptionManagerPtr->subValidation(session2);
subscriptionManagerPtr->subTransactions(session2);
subscriptionManagerPtr->subValidation(session1);
subscriptionManagerPtr->subValidation(session2);
auto const account = GetAccountIDWithString(ACCOUNT1);
SubscriptionManagerPtr->subAccount(account, session1);
SubscriptionManagerPtr->subAccount(account, session2);
SubscriptionManagerPtr->subProposedAccount(account, session1);
SubscriptionManagerPtr->subProposedAccount(account, session2);
subscriptionManagerPtr->subAccount(account, session1);
subscriptionManagerPtr->subAccount(account, session2);
subscriptionManagerPtr->subProposedAccount(account, session1);
subscriptionManagerPtr->subProposedAccount(account, session2);
auto const issue1 = GetIssue(CURRENCY, ISSUER);
ripple::Book const book{ripple::xrpIssue(), issue1};
SubscriptionManagerPtr->subBook(book, session1);
SubscriptionManagerPtr->subBook(book, session2);
EXPECT_EQ(SubscriptionManagerPtr->report(), json::parse(ReportReturn));
subscriptionManagerPtr->subBook(book, session1);
subscriptionManagerPtr->subBook(book, session2);
EXPECT_EQ(subscriptionManagerPtr->report(), json::parse(ReportReturn));
// count down when unsub manually
SubscriptionManagerPtr->unsubBookChanges(session1);
SubscriptionManagerPtr->unsubManifest(session1);
SubscriptionManagerPtr->unsubProposedTransactions(session1);
SubscriptionManagerPtr->unsubTransactions(session1);
SubscriptionManagerPtr->unsubValidation(session1);
SubscriptionManagerPtr->unsubAccount(account, session1);
SubscriptionManagerPtr->unsubProposedAccount(account, session1);
SubscriptionManagerPtr->unsubBook(book, session1);
subscriptionManagerPtr->unsubBookChanges(session1);
subscriptionManagerPtr->unsubManifest(session1);
subscriptionManagerPtr->unsubProposedTransactions(session1);
subscriptionManagerPtr->unsubTransactions(session1);
subscriptionManagerPtr->unsubValidation(session1);
subscriptionManagerPtr->unsubAccount(account, session1);
subscriptionManagerPtr->unsubProposedAccount(account, session1);
subscriptionManagerPtr->unsubBook(book, session1);
// try to unsub an account which is not subscribed
auto const account2 = GetAccountIDWithString(ACCOUNT2);
SubscriptionManagerPtr->unsubAccount(account2, session1);
SubscriptionManagerPtr->unsubProposedAccount(account2, session1);
subscriptionManagerPtr->unsubAccount(account2, session1);
subscriptionManagerPtr->unsubProposedAccount(account2, session1);
auto checkResult = [](json::object reportReturn, int result) {
EXPECT_EQ(reportReturn["book_changes"], result);
EXPECT_EQ(reportReturn["validations"], result);
@@ -199,46 +170,41 @@ TEST_F(SubscriptionManagerTest, ReportCurrentSubscriber)
EXPECT_EQ(reportReturn["account"], result);
EXPECT_EQ(reportReturn["books"], result);
};
checkResult(SubscriptionManagerPtr->report(), 1);
checkResult(subscriptionManagerPtr->report(), 1);
// count down when session disconnect
session2.reset();
checkResult(SubscriptionManagerPtr->report(), 0);
checkResult(subscriptionManagerPtr->report(), 0);
}
TEST_F(SubscriptionManagerTest, ManifestTest)
{
constexpr static auto dummyManifest = R"({"manifest":"test"})";
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(dummyManifest))).Times(1);
SubscriptionManagerPtr->subManifest(session);
SubscriptionManagerPtr->forwardManifest(json::parse(dummyManifest).get_object());
ctx.run();
subscriptionManagerPtr->subManifest(session);
subscriptionManagerPtr->forwardManifest(json::parse(dummyManifest).get_object());
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(dummyManifest))).Times(0);
SubscriptionManagerPtr->unsubManifest(session);
SubscriptionManagerPtr->forwardManifest(json::parse(dummyManifest).get_object());
ctx.run();
subscriptionManagerPtr->unsubManifest(session);
subscriptionManagerPtr->forwardManifest(json::parse(dummyManifest).get_object());
}
TEST_F(SubscriptionManagerTest, ValidationTest)
{
constexpr static auto dummy = R"({"validation":"test"})";
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(dummy))).Times(1);
SubscriptionManagerPtr->subValidation(session);
SubscriptionManagerPtr->forwardValidation(json::parse(dummy).get_object());
ctx.run();
subscriptionManagerPtr->subValidation(session);
subscriptionManagerPtr->forwardValidation(json::parse(dummy).get_object());
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(dummy))).Times(0);
SubscriptionManagerPtr->unsubValidation(session);
SubscriptionManagerPtr->forwardValidation(json::parse(dummy).get_object());
ctx.restart();
ctx.run();
subscriptionManagerPtr->unsubValidation(session);
subscriptionManagerPtr->forwardValidation(json::parse(dummy).get_object());
}
TEST_F(SubscriptionManagerTest, BookChangesTest)
{
SubscriptionManagerPtr->subBookChanges(session);
EXPECT_EQ(SubscriptionManagerPtr->report()["book_changes"], 1);
subscriptionManagerPtr->subBookChanges(session);
EXPECT_EQ(subscriptionManagerPtr->report()["book_changes"], 1);
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, 32);
auto transactions = std::vector<TransactionAndMetadata>{};
@@ -249,8 +215,6 @@ TEST_F(SubscriptionManagerTest, BookChangesTest)
ripple::STObject const metaObj = CreateMetaDataForBookChange(CURRENCY, ISSUER, 22, 1, 3, 3, 1);
trans1.metadata = metaObj.getSerializer().peekData();
transactions.push_back(trans1);
SubscriptionManagerPtr->pubBookChanges(ledgerHeader, transactions);
constexpr static auto bookChangePublish =
R"({
"type":"bookChanges",
@@ -272,10 +236,11 @@ TEST_F(SubscriptionManagerTest, BookChangesTest)
]
})";
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(bookChangePublish))).Times(1);
ctx.run();
SubscriptionManagerPtr->unsubBookChanges(session);
EXPECT_EQ(SubscriptionManagerPtr->report()["book_changes"], 0);
subscriptionManagerPtr->pubBookChanges(ledgerHeader, transactions);
subscriptionManagerPtr->unsubBookChanges(session);
EXPECT_EQ(subscriptionManagerPtr->report()["book_changes"], 0);
}
TEST_F(SubscriptionManagerTest, LedgerTest)
@@ -301,18 +266,16 @@ TEST_F(SubscriptionManagerTest, LedgerTest)
"reserve_inc":2
})";
boost::asio::spawn(ctx, [this](boost::asio::yield_context yield) {
auto const res = SubscriptionManagerPtr->subLedger(yield, session);
auto const res = subscriptionManagerPtr->subLedger(yield, session);
// check the response
EXPECT_EQ(res, json::parse(LedgerResponse));
});
ctx.run();
EXPECT_EQ(SubscriptionManagerPtr->report()["ledger"], 1);
EXPECT_EQ(subscriptionManagerPtr->report()["ledger"], 1);
// test publish
auto const ledgerHeader2 = CreateLedgerHeader(LEDGERHASH, 31);
auto fee2 = ripple::Fees();
fee2.reserve = 10;
SubscriptionManagerPtr->pubLedger(ledgerHeader2, fee2, "10-31", 8);
constexpr static auto ledgerPub =
R"({
"type":"ledgerClosed",
@@ -326,12 +289,11 @@ TEST_F(SubscriptionManagerTest, LedgerTest)
"txn_count":8
})";
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(ledgerPub))).Times(1);
ctx.restart();
ctx.run();
subscriptionManagerPtr->pubLedger(ledgerHeader2, fee2, "10-31", 8);
// test unsub
SubscriptionManagerPtr->unsubLedger(session);
EXPECT_EQ(SubscriptionManagerPtr->report()["ledger"], 0);
subscriptionManagerPtr->unsubLedger(session);
EXPECT_EQ(subscriptionManagerPtr->report()["ledger"], 0);
}
TEST_F(SubscriptionManagerTest, TransactionTest)
@@ -339,12 +301,12 @@ TEST_F(SubscriptionManagerTest, TransactionTest)
auto const issue1 = GetIssue(CURRENCY, ISSUER);
auto const account = GetAccountIDWithString(ISSUER);
ripple::Book const book{ripple::xrpIssue(), issue1};
SubscriptionManagerPtr->subBook(book, session);
SubscriptionManagerPtr->subTransactions(session);
SubscriptionManagerPtr->subAccount(account, session);
EXPECT_EQ(SubscriptionManagerPtr->report()["account"], 1);
EXPECT_EQ(SubscriptionManagerPtr->report()["transactions"], 1);
EXPECT_EQ(SubscriptionManagerPtr->report()["books"], 1);
subscriptionManagerPtr->subBook(book, session);
subscriptionManagerPtr->subTransactions(session);
subscriptionManagerPtr->subAccount(account, session);
EXPECT_EQ(subscriptionManagerPtr->report()["account"], 1);
EXPECT_EQ(subscriptionManagerPtr->report()["transactions"], 1);
EXPECT_EQ(subscriptionManagerPtr->report()["books"], 1);
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, 33);
auto trans1 = TransactionAndMetadata();
@@ -354,8 +316,6 @@ TEST_F(SubscriptionManagerTest, TransactionTest)
auto const metaObj = CreateMetaDataForBookChange(CURRENCY, ISSUER, 22, 3, 1, 1, 3);
trans1.metadata = metaObj.getSerializer().peekData();
SubscriptionManagerPtr->pubTransaction(trans1, ledgerHeader);
constexpr static auto OrderbookPublish =
R"({
"transaction":
@@ -417,23 +377,23 @@ TEST_F(SubscriptionManagerTest, TransactionTest)
"engine_result_message":"The transaction was applied. Only final in a validated ledger."
})";
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(OrderbookPublish))).Times(3);
ctx.run();
subscriptionManagerPtr->pubTransaction(trans1, ledgerHeader);
SubscriptionManagerPtr->unsubBook(book, session);
SubscriptionManagerPtr->unsubTransactions(session);
SubscriptionManagerPtr->unsubAccount(account, session);
EXPECT_EQ(SubscriptionManagerPtr->report()["account"], 0);
EXPECT_EQ(SubscriptionManagerPtr->report()["transactions"], 0);
EXPECT_EQ(SubscriptionManagerPtr->report()["books"], 0);
subscriptionManagerPtr->unsubBook(book, session);
subscriptionManagerPtr->unsubTransactions(session);
subscriptionManagerPtr->unsubAccount(account, session);
EXPECT_EQ(subscriptionManagerPtr->report()["account"], 0);
EXPECT_EQ(subscriptionManagerPtr->report()["transactions"], 0);
EXPECT_EQ(subscriptionManagerPtr->report()["books"], 0);
}
TEST_F(SubscriptionManagerTest, ProposedTransactionTest)
{
auto const account = GetAccountIDWithString(ACCOUNT1);
SubscriptionManagerPtr->subProposedAccount(account, session);
SubscriptionManagerPtr->subProposedTransactions(session);
EXPECT_EQ(SubscriptionManagerPtr->report()["accounts_proposed"], 1);
EXPECT_EQ(SubscriptionManagerPtr->report()["transactions_proposed"], 1);
subscriptionManagerPtr->subProposedAccount(account, session);
subscriptionManagerPtr->subProposedTransactions(session);
EXPECT_EQ(subscriptionManagerPtr->report()["accounts_proposed"], 1);
EXPECT_EQ(subscriptionManagerPtr->report()["transactions_proposed"], 1);
constexpr static auto dummyTransaction =
R"({
@@ -505,7 +465,7 @@ TEST_F(SubscriptionManagerTest, ProposedTransactionTest)
})";
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(dummyTransaction))).Times(2);
EXPECT_CALL(*sessionPtr, send(SharedStringJsonEq(OrderbookPublish))).Times(2);
SubscriptionManagerPtr->forwardProposedTransaction(json::parse(dummyTransaction).get_object());
subscriptionManagerPtr->forwardProposedTransaction(json::parse(dummyTransaction).get_object());
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, 33);
auto trans1 = TransactionAndMetadata();
@@ -515,22 +475,21 @@ TEST_F(SubscriptionManagerTest, ProposedTransactionTest)
auto const metaObj = CreateMetaDataForBookChange(CURRENCY, ACCOUNT1, 22, 3, 1, 1, 3);
trans1.metadata = metaObj.getSerializer().peekData();
SubscriptionManagerPtr->pubTransaction(trans1, ledgerHeader);
ctx.run();
subscriptionManagerPtr->pubTransaction(trans1, ledgerHeader);
// unsub account1
SubscriptionManagerPtr->unsubProposedAccount(account, session);
EXPECT_EQ(SubscriptionManagerPtr->report()["accounts_proposed"], 0);
SubscriptionManagerPtr->unsubProposedTransactions(session);
EXPECT_EQ(SubscriptionManagerPtr->report()["transactions_proposed"], 0);
subscriptionManagerPtr->unsubProposedAccount(account, session);
EXPECT_EQ(subscriptionManagerPtr->report()["accounts_proposed"], 0);
subscriptionManagerPtr->unsubProposedTransactions(session);
EXPECT_EQ(subscriptionManagerPtr->report()["transactions_proposed"], 0);
}
TEST_F(SubscriptionManagerTest, DuplicateResponseSubTxAndProposedTx)
{
SubscriptionManagerPtr->subProposedTransactions(session);
SubscriptionManagerPtr->subTransactions(session);
EXPECT_EQ(SubscriptionManagerPtr->report()["transactions"], 1);
EXPECT_EQ(SubscriptionManagerPtr->report()["transactions_proposed"], 1);
subscriptionManagerPtr->subProposedTransactions(session);
subscriptionManagerPtr->subTransactions(session);
EXPECT_EQ(subscriptionManagerPtr->report()["transactions"], 1);
EXPECT_EQ(subscriptionManagerPtr->report()["transactions_proposed"], 1);
EXPECT_CALL(*sessionPtr, send(testing::_)).Times(2);
@@ -542,22 +501,21 @@ TEST_F(SubscriptionManagerTest, DuplicateResponseSubTxAndProposedTx)
auto const metaObj = CreateMetaDataForBookChange(CURRENCY, ACCOUNT1, 22, 3, 1, 1, 3);
trans1.metadata = metaObj.getSerializer().peekData();
SubscriptionManagerPtr->pubTransaction(trans1, ledgerHeader);
ctx.run();
subscriptionManagerPtr->pubTransaction(trans1, ledgerHeader);
SubscriptionManagerPtr->unsubTransactions(session);
EXPECT_EQ(SubscriptionManagerPtr->report()["transactions"], 0);
SubscriptionManagerPtr->unsubProposedTransactions(session);
EXPECT_EQ(SubscriptionManagerPtr->report()["transactions_proposed"], 0);
subscriptionManagerPtr->unsubTransactions(session);
EXPECT_EQ(subscriptionManagerPtr->report()["transactions"], 0);
subscriptionManagerPtr->unsubProposedTransactions(session);
EXPECT_EQ(subscriptionManagerPtr->report()["transactions_proposed"], 0);
}
TEST_F(SubscriptionManagerTest, NoDuplicateResponseSubAccountAndProposedAccount)
{
auto const account = GetAccountIDWithString(ACCOUNT1);
SubscriptionManagerPtr->subProposedAccount(account, session);
SubscriptionManagerPtr->subAccount(account, session);
EXPECT_EQ(SubscriptionManagerPtr->report()["accounts_proposed"], 1);
EXPECT_EQ(SubscriptionManagerPtr->report()["account"], 1);
subscriptionManagerPtr->subProposedAccount(account, session);
subscriptionManagerPtr->subAccount(account, session);
EXPECT_EQ(subscriptionManagerPtr->report()["accounts_proposed"], 1);
EXPECT_EQ(subscriptionManagerPtr->report()["account"], 1);
EXPECT_CALL(*sessionPtr, send(testing::_)).Times(1);
@@ -569,12 +527,11 @@ TEST_F(SubscriptionManagerTest, NoDuplicateResponseSubAccountAndProposedAccount)
auto const metaObj = CreateMetaDataForBookChange(CURRENCY, ACCOUNT1, 22, 3, 1, 1, 3);
trans1.metadata = metaObj.getSerializer().peekData();
SubscriptionManagerPtr->pubTransaction(trans1, ledgerHeader);
ctx.run();
subscriptionManagerPtr->pubTransaction(trans1, ledgerHeader);
// unsub account1
SubscriptionManagerPtr->unsubProposedAccount(account, session);
EXPECT_EQ(SubscriptionManagerPtr->report()["accounts_proposed"], 0);
SubscriptionManagerPtr->unsubAccount(account, session);
EXPECT_EQ(SubscriptionManagerPtr->report()["account"], 0);
subscriptionManagerPtr->unsubProposedAccount(account, session);
EXPECT_EQ(subscriptionManagerPtr->report()["accounts_proposed"], 0);
subscriptionManagerPtr->unsubAccount(account, session);
EXPECT_EQ(subscriptionManagerPtr->report()["account"], 0);
}

View File

@@ -20,14 +20,13 @@
#include "data/Types.hpp"
#include "feed/FeedTestUtil.hpp"
#include "feed/impl/TransactionFeed.hpp"
#include "util/AsioContextTestFixture.hpp"
#include "util/MockPrometheus.hpp"
#include "util/MockWsBase.hpp"
#include "util/SyncExecutionCtxFixture.hpp"
#include "util/TestObject.hpp"
#include "util/prometheus/Gauge.hpp"
#include "web/interface/ConnectionBase.hpp"
#include <boost/asio/io_context.hpp>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <xrpl/basics/base_uint.h>
@@ -174,17 +173,12 @@ TEST_F(FeedTransactionTest, SubTransactionV1)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V1))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->transactionSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
EXPECT_EQ(testFeedPtr->transactionSubCount(), 0);
}
TEST_F(FeedTransactionTest, SubTransactionForProposedTx)
@@ -198,15 +192,12 @@ TEST_F(FeedTransactionTest, SubTransactionForProposedTx)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V1))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsubProposed(sessionPtr);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubTransactionV2)
@@ -221,17 +212,14 @@ TEST_F(FeedTransactionTest, SubTransactionV2)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(1);
ctx.run();
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(1);
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->transactionSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubAccountV1)
@@ -247,16 +235,14 @@ TEST_F(FeedTransactionTest, SubAccountV1)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V1))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(account, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubForProposedAccount)
@@ -272,14 +258,12 @@ TEST_F(FeedTransactionTest, SubForProposedAccount)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V1))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsubProposed(account, sessionPtr);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubAccountV2)
@@ -297,16 +281,13 @@ TEST_F(FeedTransactionTest, SubAccountV2)
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(account, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubBothTransactionAndAccount)
@@ -326,10 +307,8 @@ TEST_F(FeedTransactionTest, SubBothTransactionAndAccount)
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(2);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(account, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 0);
@@ -337,8 +316,6 @@ TEST_F(FeedTransactionTest, SubBothTransactionAndAccount)
EXPECT_EQ(testFeedPtr->transactionSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubBookV1)
@@ -356,7 +333,6 @@ TEST_F(FeedTransactionTest, SubBookV1)
auto metaObj = CreateMetaDataForBookChange(CURRENCY, ISSUER, 22, 3, 1, 1, 3);
trans1.metadata = metaObj.getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
constexpr static auto OrderbookPublish =
R"({
@@ -419,12 +395,11 @@ TEST_F(FeedTransactionTest, SubBookV1)
})";
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(OrderbookPublish))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
// trigger by offer cancel meta data
metaObj = CreateMetaDataForCancelOffer(CURRENCY, ISSUER, 22, 3, 1);
trans1.metadata = metaObj.getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
constexpr static auto OrderbookCancelPublish =
R"({
@@ -473,9 +448,8 @@ TEST_F(FeedTransactionTest, SubBookV1)
"close_time_iso": "2000-01-01T00:00:00Z",
"engine_result_message":"The transaction was applied. Only final in a validated ledger."
})";
ctx.restart();
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(OrderbookCancelPublish))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
// trigger by offer create meta data
constexpr static auto OrderbookCreatePublish =
@@ -529,17 +503,14 @@ TEST_F(FeedTransactionTest, SubBookV1)
})";
metaObj = CreateMetaDataForCreateOffer(CURRENCY, ISSUER, 22, 3, 1);
trans1.metadata = metaObj.getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(OrderbookCreatePublish))).Times(1);
ctx.restart();
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(book, sessionPtr);
EXPECT_EQ(testFeedPtr->bookSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubBookV2)
@@ -558,7 +529,6 @@ TEST_F(FeedTransactionTest, SubBookV2)
auto const metaObj = CreateMetaDataForBookChange(CURRENCY, ISSUER, 22, 3, 1, 1, 3);
trans1.metadata = metaObj.getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
constexpr static auto OrderbookPublish =
R"({
@@ -621,14 +591,12 @@ TEST_F(FeedTransactionTest, SubBookV2)
})";
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(OrderbookPublish))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(book, sessionPtr);
EXPECT_EQ(testFeedPtr->bookSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, TransactionContainsBothAccountsSubed)
@@ -648,23 +616,19 @@ TEST_F(FeedTransactionTest, TransactionContainsBothAccountsSubed)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(account, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 1);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(account2, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubAccountRepeatWithDifferentVersion)
@@ -684,24 +648,20 @@ TEST_F(FeedTransactionTest, SubAccountRepeatWithDifferentVersion)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(account, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 1);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(account2, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubTransactionRepeatWithDifferentVersion)
@@ -721,16 +681,13 @@ TEST_F(FeedTransactionTest, SubTransactionRepeatWithDifferentVersion)
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V2))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(sessionPtr);
EXPECT_EQ(testFeedPtr->transactionSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubRepeat)
@@ -820,7 +777,6 @@ TEST_F(FeedTransactionTest, PubTransactionWithOwnerFund)
ON_CALL(*backend, doFetchLedgerObject(kk, testing::_, testing::_))
.WillByDefault(testing::Return(accountRoot.getSerializer().peekData()));
testFeedPtr->pub(trans1, ledgerHeader, backend);
constexpr static auto TransactionForOwnerFund =
R"({
"transaction":
@@ -859,7 +815,7 @@ TEST_F(FeedTransactionTest, PubTransactionWithOwnerFund)
})";
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TransactionForOwnerFund))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
}
constexpr static auto TRAN_FROZEN =
@@ -931,9 +887,9 @@ TEST_F(FeedTransactionTest, PubTransactionOfferCreationFrozenLine)
ripple::STObject const accountRoot = CreateAccountRootObject(ISSUER, 0, 1, 10, 2, TXNID, 3);
ON_CALL(*backend, doFetchLedgerObject(kk, testing::_, testing::_))
.WillByDefault(testing::Return(accountRoot.getSerializer().peekData()));
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_FROZEN))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
}
TEST_F(FeedTransactionTest, SubTransactionOfferCreationGlobalFrozen)
@@ -969,10 +925,9 @@ TEST_F(FeedTransactionTest, SubTransactionOfferCreationGlobalFrozen)
ripple::STObject const accountRoot = CreateAccountRootObject(ISSUER, ripple::lsfGlobalFreeze, 1, 10, 2, TXNID, 3);
ON_CALL(*backend, doFetchLedgerObject(kk, testing::_, testing::_))
.WillByDefault(testing::Return(accountRoot.getSerializer().peekData()));
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_FROZEN))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
}
TEST_F(FeedTransactionTest, SubBothProposedAndValidatedAccount)
@@ -988,17 +943,14 @@ TEST_F(FeedTransactionTest, SubBothProposedAndValidatedAccount)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V1))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(account, sessionPtr);
testFeedPtr->unsubProposed(account, sessionPtr);
EXPECT_EQ(testFeedPtr->accountSubCount(), 0);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubBothProposedAndValidated)
@@ -1013,15 +965,13 @@ TEST_F(FeedTransactionTest, SubBothProposedAndValidated)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V1))).Times(2);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
testFeedPtr->unsub(sessionPtr);
testFeedPtr->unsubProposed(sessionPtr);
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubProposedDisconnect)
@@ -1035,14 +985,12 @@ TEST_F(FeedTransactionTest, SubProposedDisconnect)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V1))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
sessionPtr.reset();
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
TEST_F(FeedTransactionTest, SubProposedAccountDisconnect)
@@ -1057,17 +1005,15 @@ TEST_F(FeedTransactionTest, SubProposedAccountDisconnect)
trans1.transaction = obj.getSerializer().peekData();
trans1.ledgerSequence = 32;
trans1.metadata = CreatePaymentTransactionMetaObject(ACCOUNT1, ACCOUNT2, 110, 30, 22).getSerializer().peekData();
testFeedPtr->pub(trans1, ledgerHeader, backend);
EXPECT_CALL(*mockSessionPtr, send(SharedStringJsonEq(TRAN_V1))).Times(1);
ctx.run();
testFeedPtr->pub(trans1, ledgerHeader, backend);
sessionPtr.reset();
testFeedPtr->pub(trans1, ledgerHeader, backend);
ctx.restart();
ctx.run();
}
struct TransactionFeedMockPrometheusTest : WithMockPrometheus, SyncAsioContextTest {
struct TransactionFeedMockPrometheusTest : WithMockPrometheus, SyncExecutionCtxFixture {
protected:
std::shared_ptr<web::ConnectionBase> sessionPtr;
std::shared_ptr<TransactionFeed> testFeedPtr;
@@ -1075,7 +1021,6 @@ protected:
void
SetUp() override
{
SyncAsioContextTest::SetUp();
testFeedPtr = std::make_shared<TransactionFeed>(ctx);
sessionPtr = std::make_shared<MockSession>();
}
@@ -1084,7 +1029,6 @@ protected:
{
sessionPtr.reset();
testFeedPtr.reset();
SyncAsioContextTest::TearDown();
}
};

View File

@@ -18,18 +18,19 @@
//==============================================================================
#include "data/Types.hpp"
#include "feed/SubscriptionManager.hpp"
#include "rpc/Errors.hpp"
#include "rpc/RPCHelpers.hpp"
#include "rpc/common/AnyHandler.hpp"
#include "rpc/common/Types.hpp"
#include "rpc/handlers/Subscribe.hpp"
#include "util/HandlerBaseTestFixture.hpp"
#include "util/MockSubscriptionManager.hpp"
#include "util/MockWsBase.hpp"
#include "util/NameGenerator.hpp"
#include "util/TestObject.hpp"
#include "web/interface/ConnectionBase.hpp"
#include <boost/json/object.hpp>
#include <boost/json/parse.hpp>
#include <boost/json/value.hpp>
#include <fmt/core.h>
@@ -57,7 +58,6 @@ constexpr static auto MINSEQ = 10;
constexpr static auto MAXSEQ = 30;
constexpr static auto ACCOUNT = "rf1BiGeXwwQoi8Z2ueFYTEXSwuJYfV2Jpn";
constexpr static auto ACCOUNT2 = "rLEsXccBGNR3UPuPu2hUXPjziKC3qKSBun";
constexpr static auto LEDGERHASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652";
constexpr static auto PAYS20USDGETS10XRPBOOKDIR = "43B83ADC452B85FCBADA6CAEAC5181C255A213630D58FFD455071AFD498D0000";
constexpr static auto PAYS20XRPGETS10USDBOOKDIR = "7B1767D41DBCE79D9585CF9D0262A5FEC45E5206FF524F8B55071AFD498D0000";
constexpr static auto INDEX1 = "1B8590C01B0006EDFA9ED60296DD052DC5E90F99659B25014D08E1BC983515BC";
@@ -69,8 +69,6 @@ protected:
SetUp() override
{
HandlerBaseTest::SetUp();
subManager_ = std::make_shared<feed::SubscriptionManager>(ctx, backend);
session_ = std::make_shared<MockSession>();
}
void
@@ -79,8 +77,8 @@ protected:
HandlerBaseTest::TearDown();
}
std::shared_ptr<feed::SubscriptionManager> subManager_;
std::shared_ptr<web::ConnectionBase> session_;
StrictMockSubscriptionManagerSharedPtr mockSubscriptionManagerPtr;
};
struct SubscribeParamTestCaseBundle {
@@ -575,7 +573,7 @@ TEST_P(SubscribeParameterTest, InvalidParams)
{
auto const testBundle = GetParam();
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const req = json::parse(testBundle.testJson);
auto const output = handler.process(req, Context{yield});
ASSERT_FALSE(output);
@@ -588,7 +586,7 @@ TEST_P(SubscribeParameterTest, InvalidParams)
TEST_F(RPCSubscribeHandlerTest, EmptyResponse)
{
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
auto const output = handler.process(json::parse(R"({})"), Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
@@ -604,16 +602,16 @@ TEST_F(RPCSubscribeHandlerTest, StreamsWithoutLedger)
})"
);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subTransactions).Times(1);
EXPECT_CALL(*mockSubscriptionManagerPtr, subValidation).Times(1);
EXPECT_CALL(*mockSubscriptionManagerPtr, subManifest).Times(1);
EXPECT_CALL(*mockSubscriptionManagerPtr, subBookChanges).Times(1);
EXPECT_CALL(*mockSubscriptionManagerPtr, subProposedTransactions).Times(1);
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
auto const report = subManager_->report();
EXPECT_EQ(report.at("transactions_proposed").as_uint64(), 1);
EXPECT_EQ(report.at("transactions").as_uint64(), 1);
EXPECT_EQ(report.at("validations").as_uint64(), 1);
EXPECT_EQ(report.at("manifests").as_uint64(), 1);
EXPECT_EQ(report.at("book_changes").as_uint64(), 1);
});
}
@@ -629,31 +627,21 @@ TEST_F(RPCSubscribeHandlerTest, StreamsLedger)
"reserve_base":3,
"reserve_inc":2
})";
backend->setRange(MINSEQ, MAXSEQ);
EXPECT_CALL(*backend, fetchLedgerBySequence).Times(1);
// return valid ledgerHeader
auto const ledgerHeader = CreateLedgerHeader(LEDGERHASH, MAXSEQ);
ON_CALL(*backend, fetchLedgerBySequence(MAXSEQ, _)).WillByDefault(Return(ledgerHeader));
// fee
auto feeBlob = CreateLegacyFeeSettingBlob(1, 2, 3, 4, 0);
ON_CALL(*backend, doFetchLedgerObject).WillByDefault(Return(feeBlob));
EXPECT_CALL(*backend, doFetchLedgerObject).Times(1);
// ledger stream returns information about the ledgers on hand and current
// fee schedule.
auto const input = json::parse(
R"({
"streams": ["ledger"]
})"
);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subLedger)
.WillOnce(testing::Return(boost::json::parse(expectedOutput).as_object()));
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_EQ(output.result->as_object(), json::parse(expectedOutput));
auto const report = subManager_->report();
EXPECT_EQ(report.at("ledger").as_uint64(), 1);
});
}
@@ -668,13 +656,13 @@ TEST_F(RPCSubscribeHandlerTest, Accounts)
ACCOUNT2
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subAccount(GetAccountIDWithString(ACCOUNT), session_)).Times(1);
EXPECT_CALL(*mockSubscriptionManagerPtr, subAccount(GetAccountIDWithString(ACCOUNT2), session_)).Times(2);
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
auto const report = subManager_->report();
// filter the duplicates
EXPECT_EQ(report.at("account").as_uint64(), 2);
});
}
@@ -689,13 +677,15 @@ TEST_F(RPCSubscribeHandlerTest, AccountsProposed)
ACCOUNT2
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subProposedAccount(GetAccountIDWithString(ACCOUNT), session_))
.Times(1);
EXPECT_CALL(*mockSubscriptionManagerPtr, subProposedAccount(GetAccountIDWithString(ACCOUNT2), session_))
.Times(2);
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
auto const report = subManager_->report();
// filter the duplicates
EXPECT_EQ(report.at("accounts_proposed").as_uint64(), 2);
});
}
@@ -721,12 +711,11 @@ TEST_F(RPCSubscribeHandlerTest, JustBooks)
ACCOUNT
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subBook).Times(1);
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
auto const report = subManager_->report();
EXPECT_EQ(report.at("books").as_uint64(), 1);
});
}
@@ -753,13 +742,11 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSet)
ACCOUNT
));
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subBook).Times(2);
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_TRUE(output.result->as_object().empty());
auto const report = subManager_->report();
// original book + reverse book
EXPECT_EQ(report.at("books").as_uint64(), 2);
});
}
@@ -919,16 +906,14 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothSnapshotSet)
ACCOUNT
);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subBook).Times(2);
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_EQ(output.result->as_object().at("bids").as_array().size(), 10);
EXPECT_EQ(output.result->as_object().at("asks").as_array().size(), 10);
EXPECT_EQ(output.result->as_object().at("bids").as_array()[0].as_object(), json::parse(expectedOffer));
EXPECT_EQ(output.result->as_object().at("asks").as_array()[0].as_object(), json::parse(expectedReversedOffer));
auto const report = subManager_->report();
// original book + reverse book
EXPECT_EQ(report.at("books").as_uint64(), 2);
});
}
@@ -1061,14 +1046,12 @@ TEST_F(RPCSubscribeHandlerTest, BooksBothUnsetSnapshotSet)
);
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subBook).Times(1);
auto const output = handler.process(input, Context{yield, session_});
ASSERT_TRUE(output);
EXPECT_EQ(output.result->as_object().at("offers").as_array().size(), 10);
EXPECT_EQ(output.result->as_object().at("offers").as_array()[0].as_object(), json::parse(expectedOffer));
auto const report = subManager_->report();
// original book + reverse book
EXPECT_EQ(report.at("books").as_uint64(), 1);
});
}
@@ -1081,7 +1064,8 @@ TEST_F(RPCSubscribeHandlerTest, APIVersion)
);
auto const apiVersion = 2;
runSpawn([&, this](auto yield) {
auto const handler = AnyHandler{SubscribeHandler{backend, subManager_}};
auto const handler = AnyHandler{SubscribeHandler{backend, mockSubscriptionManagerPtr}};
EXPECT_CALL(*mockSubscriptionManagerPtr, subProposedTransactions).Times(1);
auto const output =
handler.process(input, Context{.yield = yield, .session = session_, .apiVersion = apiVersion});
ASSERT_TRUE(output);

View File

@@ -61,7 +61,6 @@ struct RPCUnsubscribeTest : HandlerBaseTest {
HandlerBaseTest::TearDown();
}
std::shared_ptr<feed::SubscriptionManager> subManager_;
std::shared_ptr<web::ConnectionBase> session_;
StrictMockSubscriptionManagerSharedPtr mockSubscriptionManagerPtr;
};

View File

@@ -37,7 +37,6 @@
#include <stdexcept>
#include <string>
using namespace feed;
using namespace web;
constexpr static auto MINSEQ = 10;