mirror of
				https://github.com/XRPLF/clio.git
				synced 2025-11-04 11:55:51 +00:00 
			
		
		
		
	@@ -167,6 +167,7 @@ if (tests)
 | 
			
		||||
    unittests/etl/TransformerTests.cpp
 | 
			
		||||
    unittests/etl/CacheLoaderTests.cpp
 | 
			
		||||
    unittests/etl/AmendmentBlockHandlerTests.cpp
 | 
			
		||||
    unittests/etl/LedgerPublisherTests.cpp
 | 
			
		||||
    # RPC
 | 
			
		||||
    unittests/rpc/ErrorTests.cpp
 | 
			
		||||
    unittests/rpc/BaseTests.cpp
 | 
			
		||||
 
 | 
			
		||||
@@ -279,7 +279,7 @@ ETLService::ETLService(
 | 
			
		||||
    , cacheLoader_(config, ioc, backend, backend->cache())
 | 
			
		||||
    , ledgerFetcher_(backend, balancer)
 | 
			
		||||
    , ledgerLoader_(backend, balancer, ledgerFetcher_, state_)
 | 
			
		||||
    , ledgerPublisher_(ioc, backend, subscriptions, state_)
 | 
			
		||||
    , ledgerPublisher_(ioc, backend, backend->cache(), subscriptions, state_)
 | 
			
		||||
    , amendmentBlockHandler_(ioc, state_)
 | 
			
		||||
{
 | 
			
		||||
    startSequence_ = config.maybeValue<uint32_t>("start_sequence");
 | 
			
		||||
 
 | 
			
		||||
@@ -73,11 +73,12 @@ class ETLService
 | 
			
		||||
    using LoadBalancerType = LoadBalancer;
 | 
			
		||||
    using NetworkValidatedLedgersType = NetworkValidatedLedgers;
 | 
			
		||||
    using DataPipeType = etl::detail::ExtractionDataPipe<org::xrpl::rpc::v1::GetLedgerResponse>;
 | 
			
		||||
    using CacheLoaderType = etl::detail::CacheLoader<data::LedgerCache>;
 | 
			
		||||
    using CacheType = data::LedgerCache;
 | 
			
		||||
    using CacheLoaderType = etl::detail::CacheLoader<CacheType>;
 | 
			
		||||
    using LedgerFetcherType = etl::detail::LedgerFetcher<LoadBalancerType>;
 | 
			
		||||
    using ExtractorType = etl::detail::Extractor<DataPipeType, NetworkValidatedLedgersType, LedgerFetcherType>;
 | 
			
		||||
    using LedgerLoaderType = etl::detail::LedgerLoader<LoadBalancerType, LedgerFetcherType>;
 | 
			
		||||
    using LedgerPublisherType = etl::detail::LedgerPublisher<SubscriptionManagerType>;
 | 
			
		||||
    using LedgerPublisherType = etl::detail::LedgerPublisher<SubscriptionManagerType, CacheType>;
 | 
			
		||||
    using AmendmentBlockHandlerType = etl::detail::AmendmentBlockHandler<>;
 | 
			
		||||
    using TransformerType =
 | 
			
		||||
        etl::detail::Transformer<DataPipeType, LedgerLoaderType, LedgerPublisherType, AmendmentBlockHandlerType>;
 | 
			
		||||
 
 | 
			
		||||
@@ -23,7 +23,6 @@
 | 
			
		||||
#include <etl/SystemState.h>
 | 
			
		||||
#include <feed/SubscriptionManager.h>
 | 
			
		||||
#include <util/LedgerUtils.h>
 | 
			
		||||
#include <util/Profiler.h>
 | 
			
		||||
#include <util/log/Logger.h>
 | 
			
		||||
 | 
			
		||||
#include <ripple/protocol/LedgerHeader.h>
 | 
			
		||||
@@ -44,7 +43,7 @@ namespace etl::detail {
 | 
			
		||||
 * includes reading all of the transactions from the database) is done from the application wide asio io_service, and a
 | 
			
		||||
 * strand is used to ensure ledgers are published in order.
 | 
			
		||||
 */
 | 
			
		||||
template <typename SubscriptionManagerType>
 | 
			
		||||
template <typename SubscriptionManagerType, typename CacheType>
 | 
			
		||||
class LedgerPublisher
 | 
			
		||||
{
 | 
			
		||||
    util::Logger log_{"ETL"};
 | 
			
		||||
@@ -52,6 +51,7 @@ class LedgerPublisher
 | 
			
		||||
    boost::asio::strand<boost::asio::io_context::executor_type> publishStrand_;
 | 
			
		||||
 | 
			
		||||
    std::shared_ptr<BackendInterface> backend_;
 | 
			
		||||
    std::reference_wrapper<CacheType> cache_;
 | 
			
		||||
    std::shared_ptr<SubscriptionManagerType> subscriptions_;
 | 
			
		||||
    std::reference_wrapper<SystemState const> state_;  // shared state for ETL
 | 
			
		||||
 | 
			
		||||
@@ -71,10 +71,12 @@ public:
 | 
			
		||||
    LedgerPublisher(
 | 
			
		||||
        boost::asio::io_context& ioc,
 | 
			
		||||
        std::shared_ptr<BackendInterface> backend,
 | 
			
		||||
        std::shared_ptr<feed::SubscriptionManager> subscriptions,
 | 
			
		||||
        CacheType& cache,
 | 
			
		||||
        std::shared_ptr<SubscriptionManagerType> subscriptions,
 | 
			
		||||
        SystemState const& state)
 | 
			
		||||
        : publishStrand_{boost::asio::make_strand(ioc)}
 | 
			
		||||
        , backend_{std::move(backend)}
 | 
			
		||||
        , cache_{cache}
 | 
			
		||||
        , subscriptions_{std::move(subscriptions)}
 | 
			
		||||
        , state_{std::cref(state)}
 | 
			
		||||
    {
 | 
			
		||||
@@ -99,6 +101,7 @@ public:
 | 
			
		||||
 | 
			
		||||
            if (!range || range->maxSequence < ledgerSequence)
 | 
			
		||||
            {
 | 
			
		||||
                ++numAttempts;
 | 
			
		||||
                LOG(log_.debug()) << "Trying to publish. Could not find "
 | 
			
		||||
                                     "ledger with sequence = "
 | 
			
		||||
                                  << ledgerSequence;
 | 
			
		||||
@@ -110,7 +113,6 @@ public:
 | 
			
		||||
                    return false;
 | 
			
		||||
                }
 | 
			
		||||
                std::this_thread::sleep_for(std::chrono::seconds(1));
 | 
			
		||||
                ++numAttempts;
 | 
			
		||||
                continue;
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
@@ -126,7 +128,7 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Publish the passed in ledger
 | 
			
		||||
     * @brief Publish the passed ledger asynchronously.
 | 
			
		||||
     *
 | 
			
		||||
     * All ledgers are published thru publishStrand_ which ensures that all publishes are performed in a serial fashion.
 | 
			
		||||
     *
 | 
			
		||||
@@ -145,34 +147,34 @@ public:
 | 
			
		||||
                std::vector<data::LedgerObject> const diff = data::synchronousAndRetryOnTimeout(
 | 
			
		||||
                    [&](auto yield) { return backend_->fetchLedgerDiff(lgrInfo.seq, yield); });
 | 
			
		||||
 | 
			
		||||
                backend_->cache().update(diff, lgrInfo.seq);  // todo: inject cache to update, don't use backend cache
 | 
			
		||||
                cache_.get().update(diff, lgrInfo.seq);
 | 
			
		||||
                backend_->updateRange(lgrInfo.seq);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            setLastClose(lgrInfo.closeTime);
 | 
			
		||||
            auto age = lastCloseAgeSeconds();
 | 
			
		||||
 | 
			
		||||
            // if the ledger closed over 10 minutes ago, assume we are still catching up and don't publish
 | 
			
		||||
            // if the ledger closed over MAX_LEDGER_AGE_SECONDS ago, assume we are still catching up and don't publish
 | 
			
		||||
            // TODO: this probably should be a strategy
 | 
			
		||||
            static constexpr std::uint32_t MAX_LEDGER_AGE_SECONDS = 600;
 | 
			
		||||
            if (age < MAX_LEDGER_AGE_SECONDS)
 | 
			
		||||
            {
 | 
			
		||||
                std::optional<ripple::Fees> fees = data::synchronousAndRetryOnTimeout(
 | 
			
		||||
                    [&](auto yield) { return backend_->fetchFees(lgrInfo.seq, yield); });
 | 
			
		||||
                assert(fees);
 | 
			
		||||
 | 
			
		||||
                std::vector<data::TransactionAndMetadata> const transactions = data::synchronousAndRetryOnTimeout(
 | 
			
		||||
                    [&](auto yield) { return backend_->fetchAllTransactionsInLedger(lgrInfo.seq, yield); });
 | 
			
		||||
 | 
			
		||||
                auto ledgerRange = backend_->fetchLedgerRange();
 | 
			
		||||
                auto const ledgerRange = backend_->fetchLedgerRange();
 | 
			
		||||
                assert(ledgerRange);
 | 
			
		||||
                assert(fees);
 | 
			
		||||
 | 
			
		||||
                std::string const range =
 | 
			
		||||
                    std::to_string(ledgerRange->minSequence) + "-" + std::to_string(ledgerRange->maxSequence);
 | 
			
		||||
 | 
			
		||||
                subscriptions_->pubLedger(lgrInfo, *fees, range, transactions.size());
 | 
			
		||||
 | 
			
		||||
                for (auto& txAndMeta : transactions)
 | 
			
		||||
                for (auto const& txAndMeta : transactions)
 | 
			
		||||
                    subscriptions_->pubTransaction(txAndMeta, lgrInfo);
 | 
			
		||||
 | 
			
		||||
                subscriptions_->pubBookChanges(lgrInfo, transactions);
 | 
			
		||||
@@ -223,6 +225,10 @@ public:
 | 
			
		||||
        return now - (rippleEpochStart + closeTime);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * @brief Get the sequence of the last schueduled ledger to publish, Be aware that the ledger may not have been
 | 
			
		||||
     * published to network
 | 
			
		||||
     */
 | 
			
		||||
    std::optional<uint32_t>
 | 
			
		||||
    getLastPublishedSequence() const
 | 
			
		||||
    {
 | 
			
		||||
 
 | 
			
		||||
@@ -126,7 +126,7 @@ TEST_F(CacheLoaderTest, FromCache)
 | 
			
		||||
        .WillByDefault(Return(std::vector<Blob>{keysSize - 1, Blob{'s'}}));
 | 
			
		||||
    EXPECT_CALL(*rawBackendPtr, doFetchLedgerObjects).Times(loops);
 | 
			
		||||
 | 
			
		||||
    EXPECT_CALL(cache, update).Times(loops);
 | 
			
		||||
    EXPECT_CALL(cache, updateImp).Times(loops);
 | 
			
		||||
    EXPECT_CALL(cache, isFull).Times(1);
 | 
			
		||||
 | 
			
		||||
    std::mutex m;
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										249
									
								
								unittests/etl/LedgerPublisherTests.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								unittests/etl/LedgerPublisherTests.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
			
		||||
//------------------------------------------------------------------------------
 | 
			
		||||
/*
 | 
			
		||||
    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();
 | 
			
		||||
}
 | 
			
		||||
@@ -25,7 +25,15 @@
 | 
			
		||||
 | 
			
		||||
struct MockCache
 | 
			
		||||
{
 | 
			
		||||
    MOCK_METHOD(void, update, (std::vector<data::LedgerObject> const& a, uint32_t b, bool c), ());
 | 
			
		||||
    virtual ~MockCache() = default;
 | 
			
		||||
 | 
			
		||||
    MOCK_METHOD(void, updateImp, (std::vector<data::LedgerObject> const& a, uint32_t b, bool c), ());
 | 
			
		||||
 | 
			
		||||
    virtual void
 | 
			
		||||
    update(std::vector<data::LedgerObject> const& a, uint32_t b, bool c = false)
 | 
			
		||||
    {
 | 
			
		||||
        updateImp(a, b, c);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    MOCK_METHOD(std::optional<data::Blob>, get, (ripple::uint256 const& a, uint32_t b), (const));
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user