mirror of
https://github.com/XRPLF/clio.git
synced 2025-11-20 11:45:53 +00:00
Add time/uptime/amendment_blocked to server_info (#775)
This commit is contained in:
@@ -79,8 +79,7 @@ ETLService::monitor()
|
|||||||
auto rng = backend_->hardFetchLedgerRangeNoThrow();
|
auto rng = backend_->hardFetchLedgerRangeNoThrow();
|
||||||
if (!rng)
|
if (!rng)
|
||||||
{
|
{
|
||||||
log_.info() << "Database is empty. Will download a ledger "
|
log_.info() << "Database is empty. Will download a ledger from the network.";
|
||||||
"from the network.";
|
|
||||||
std::optional<ripple::LedgerInfo> ledger;
|
std::optional<ripple::LedgerInfo> ledger;
|
||||||
|
|
||||||
try
|
try
|
||||||
@@ -98,23 +97,22 @@ ETLService::monitor()
|
|||||||
|
|
||||||
if (mostRecentValidated)
|
if (mostRecentValidated)
|
||||||
{
|
{
|
||||||
log_.info() << "Ledger " << *mostRecentValidated << " has been validated. "
|
log_.info() << "Ledger " << *mostRecentValidated << " has been validated. Downloading...";
|
||||||
<< "Downloading...";
|
|
||||||
ledger = ledgerLoader_.loadInitialLedger(*mostRecentValidated);
|
ledger = ledgerLoader_.loadInitialLedger(*mostRecentValidated);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
log_.info() << "The wait for the next validated "
|
log_.info() << "The wait for the next validated ledger has been aborted. Exiting monitor loop";
|
||||||
<< "ledger has been aborted. "
|
|
||||||
<< "Exiting monitor loop";
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (std::runtime_error const& e)
|
catch (std::runtime_error const& e)
|
||||||
{
|
{
|
||||||
|
setAmendmentBlocked();
|
||||||
|
|
||||||
log_.fatal()
|
log_.fatal()
|
||||||
<< "Failed to load initial ledger, Exiting monitor loop : " << e.what()
|
<< "Failed to load initial ledger, Exiting monitor loop: " << e.what()
|
||||||
<< " Possible cause: The ETL node is not compatible with the version of the rippled lib Clio is using.";
|
<< " Possible cause: The ETL node is not compatible with the version of the rippled lib Clio is using.";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -152,6 +152,17 @@ public:
|
|||||||
return ledgerPublisher_.lastCloseAgeSeconds();
|
return ledgerPublisher_.lastCloseAgeSeconds();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Check for the amendment blocked state.
|
||||||
|
*
|
||||||
|
* @return true if currently amendment blocked; false otherwise
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
isAmendmentBlocked() const
|
||||||
|
{
|
||||||
|
return state_.isAmendmentBlocked;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Get state of ETL as a JSON object
|
* @brief Get state of ETL as a JSON object
|
||||||
*/
|
*/
|
||||||
@@ -236,4 +247,13 @@ private:
|
|||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
doWork();
|
doWork();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sets amendment blocked flag
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
setAmendmentBlocked()
|
||||||
|
{
|
||||||
|
state_.isAmendmentBlocked = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -47,4 +47,9 @@ struct SystemState
|
|||||||
* @brief Whether a write conflict was detected
|
* @brief Whether a write conflict was detected
|
||||||
*/
|
*/
|
||||||
std::atomic_bool writeConflict = false;
|
std::atomic_bool writeConflict = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Whether we detected an amendment block
|
||||||
|
*/
|
||||||
|
std::atomic_bool isAmendmentBlocked = false;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -186,8 +186,10 @@ private:
|
|||||||
}
|
}
|
||||||
catch (std::runtime_error const& e)
|
catch (std::runtime_error const& e)
|
||||||
{
|
{
|
||||||
|
setAmendmentBlocked();
|
||||||
|
|
||||||
log_.fatal()
|
log_.fatal()
|
||||||
<< "Failed to build next ledger : " << e.what()
|
<< "Failed to build next ledger: " << e.what()
|
||||||
<< " Possible cause: The ETL node is not compatible with the version of the rippled lib Clio is using.";
|
<< " Possible cause: The ETL node is not compatible with the version of the rippled lib Clio is using.";
|
||||||
return {ripple::LedgerInfo{}, false};
|
return {ripple::LedgerInfo{}, false};
|
||||||
}
|
}
|
||||||
@@ -414,6 +416,12 @@ private:
|
|||||||
{
|
{
|
||||||
state_.get().writeConflict = conflict;
|
state_.get().writeConflict = conflict;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
setAmendmentBlocked()
|
||||||
|
{
|
||||||
|
state_.get().isAmendmentBlocked = true;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace clio::detail
|
} // namespace clio::detail
|
||||||
|
|||||||
@@ -97,6 +97,12 @@ Counters::onInternalError()
|
|||||||
++internalErrorCounter_;
|
++internalErrorCounter_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::chrono::seconds
|
||||||
|
Counters::uptime() const
|
||||||
|
{
|
||||||
|
return std::chrono::duration_cast<std::chrono::seconds>(std::chrono::system_clock::now() - startupTime_);
|
||||||
|
}
|
||||||
|
|
||||||
boost::json::object
|
boost::json::object
|
||||||
Counters::report() const
|
Counters::report() const
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -54,9 +54,10 @@ class Counters
|
|||||||
std::atomic_uint64_t internalErrorCounter_;
|
std::atomic_uint64_t internalErrorCounter_;
|
||||||
|
|
||||||
std::reference_wrapper<const WorkQueue> workQueue_;
|
std::reference_wrapper<const WorkQueue> workQueue_;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> startupTime_;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Counters(WorkQueue const& wq) : workQueue_(std::cref(wq)){};
|
Counters(WorkQueue const& wq) : workQueue_(std::cref(wq)), startupTime_{std::chrono::system_clock::now()} {};
|
||||||
|
|
||||||
static Counters
|
static Counters
|
||||||
make_Counters(WorkQueue const& wq)
|
make_Counters(WorkQueue const& wq)
|
||||||
@@ -94,6 +95,9 @@ public:
|
|||||||
void
|
void
|
||||||
onInternalError();
|
onInternalError();
|
||||||
|
|
||||||
|
std::chrono::seconds
|
||||||
|
uptime() const;
|
||||||
|
|
||||||
boost::json::object
|
boost::json::object
|
||||||
report() const;
|
report() const;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -27,6 +27,9 @@
|
|||||||
#include <rpc/common/Types.h>
|
#include <rpc/common/Types.h>
|
||||||
#include <rpc/common/Validators.h>
|
#include <rpc/common/Validators.h>
|
||||||
|
|
||||||
|
#include <ripple/basics/chrono.h>
|
||||||
|
|
||||||
|
#include <chrono>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
|
||||||
class SubscriptionManager;
|
class SubscriptionManager;
|
||||||
@@ -78,10 +81,13 @@ public:
|
|||||||
std::optional<AdminSection> adminSection = std::nullopt;
|
std::optional<AdminSection> adminSection = std::nullopt;
|
||||||
std::string completeLedgers = {};
|
std::string completeLedgers = {};
|
||||||
uint32_t loadFactor = 1u;
|
uint32_t loadFactor = 1u;
|
||||||
|
std::chrono::time_point<std::chrono::system_clock> time = std::chrono::system_clock::now();
|
||||||
|
std::chrono::seconds uptime = {};
|
||||||
std::string clioVersion = Build::getClioVersionString();
|
std::string clioVersion = Build::getClioVersionString();
|
||||||
std::optional<boost::json::object> rippledInfo = std::nullopt;
|
std::optional<boost::json::object> rippledInfo = std::nullopt;
|
||||||
ValidatedLedgerSection validatedLedger = {};
|
ValidatedLedgerSection validatedLedger = {};
|
||||||
CacheSection cache = {};
|
CacheSection cache = {};
|
||||||
|
bool isAmendmentBlocked = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Output
|
struct Output
|
||||||
@@ -155,6 +161,8 @@ public:
|
|||||||
output.info.cache.latestLedgerSeq = backend_->cache().latestLedgerSequence();
|
output.info.cache.latestLedgerSeq = backend_->cache().latestLedgerSequence();
|
||||||
output.info.cache.objectHitRate = backend_->cache().getObjectHitRate();
|
output.info.cache.objectHitRate = backend_->cache().getObjectHitRate();
|
||||||
output.info.cache.successorHitRate = backend_->cache().getSuccessorHitRate();
|
output.info.cache.successorHitRate = backend_->cache().getSuccessorHitRate();
|
||||||
|
output.info.uptime = counters_.get().uptime();
|
||||||
|
output.info.isAmendmentBlocked = etl_->isAmendmentBlocked();
|
||||||
|
|
||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
@@ -172,14 +180,21 @@ private:
|
|||||||
friend void
|
friend void
|
||||||
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, InfoSection const& info)
|
tag_invoke(boost::json::value_from_tag, boost::json::value& jv, InfoSection const& info)
|
||||||
{
|
{
|
||||||
|
using ripple::to_string;
|
||||||
|
|
||||||
jv = {
|
jv = {
|
||||||
{JS(complete_ledgers), info.completeLedgers},
|
{JS(complete_ledgers), info.completeLedgers},
|
||||||
{JS(load_factor), info.loadFactor},
|
{JS(load_factor), info.loadFactor},
|
||||||
|
{JS(time), to_string(std::chrono::floor<std::chrono::microseconds>(info.time))},
|
||||||
|
{JS(uptime), info.uptime.count()},
|
||||||
{"clio_version", info.clioVersion},
|
{"clio_version", info.clioVersion},
|
||||||
{JS(validated_ledger), info.validatedLedger},
|
{JS(validated_ledger), info.validatedLedger},
|
||||||
{"cache", info.cache},
|
{"cache", info.cache},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (info.isAmendmentBlocked)
|
||||||
|
jv.as_object()[JS(amendment_blocked)] = true;
|
||||||
|
|
||||||
if (info.rippledInfo)
|
if (info.rippledInfo)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
|||||||
@@ -72,6 +72,8 @@ protected:
|
|||||||
EXPECT_TRUE(info.contains("load_factor"));
|
EXPECT_TRUE(info.contains("load_factor"));
|
||||||
EXPECT_TRUE(info.contains("clio_version"));
|
EXPECT_TRUE(info.contains("clio_version"));
|
||||||
EXPECT_TRUE(info.contains("validated_ledger"));
|
EXPECT_TRUE(info.contains("validated_ledger"));
|
||||||
|
EXPECT_TRUE(info.contains("time"));
|
||||||
|
EXPECT_TRUE(info.contains("uptime"));
|
||||||
|
|
||||||
auto const& validated = info.at("validated_ledger").as_object();
|
auto const& validated = info.at("validated_ledger").as_object();
|
||||||
EXPECT_TRUE(validated.contains("age"));
|
EXPECT_TRUE(validated.contains("age"));
|
||||||
@@ -188,6 +190,8 @@ TEST_F(RPCServerInfoHandlerTest, DefaultOutputIsPresent)
|
|||||||
{
|
{
|
||||||
MockBackend* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
MockBackend* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
MockLoadBalancer* rawBalancerPtr = static_cast<MockLoadBalancer*>(mockLoadBalancerPtr.get());
|
MockLoadBalancer* rawBalancerPtr = static_cast<MockLoadBalancer*>(mockLoadBalancerPtr.get());
|
||||||
|
MockCounters* rawCountersPtr = static_cast<MockCounters*>(mockCountersPtr.get());
|
||||||
|
MockETLService* rawETLServicePtr = static_cast<MockETLService*>(mockETLServicePtr.get());
|
||||||
|
|
||||||
mockBackendPtr->updateRange(10); // min
|
mockBackendPtr->updateRange(10); // min
|
||||||
mockBackendPtr->updateRange(30); // max
|
mockBackendPtr->updateRange(30); // max
|
||||||
@@ -203,6 +207,12 @@ TEST_F(RPCServerInfoHandlerTest, DefaultOutputIsPresent)
|
|||||||
ON_CALL(*rawBalancerPtr, forwardToRippled).WillByDefault(Return(std::nullopt));
|
ON_CALL(*rawBalancerPtr, forwardToRippled).WillByDefault(Return(std::nullopt));
|
||||||
EXPECT_CALL(*rawBalancerPtr, forwardToRippled(testing::_, testing::Eq(CLIENTIP), testing::_)).Times(1);
|
EXPECT_CALL(*rawBalancerPtr, forwardToRippled(testing::_, testing::Eq(CLIENTIP), testing::_)).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawCountersPtr, uptime).WillByDefault(Return(std::chrono::seconds{1234}));
|
||||||
|
EXPECT_CALL(*rawCountersPtr, uptime).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawETLServicePtr, isAmendmentBlocked).WillByDefault(Return(false));
|
||||||
|
EXPECT_CALL(*rawETLServicePtr, isAmendmentBlocked).Times(1);
|
||||||
|
|
||||||
auto const handler = AnyHandler{TestServerInfoHandler{
|
auto const handler = AnyHandler{TestServerInfoHandler{
|
||||||
mockBackendPtr, mockSubscriptionManagerPtr, mockLoadBalancerPtr, mockETLServicePtr, *mockCountersPtr}};
|
mockBackendPtr, mockSubscriptionManagerPtr, mockLoadBalancerPtr, mockETLServicePtr, *mockCountersPtr}};
|
||||||
|
|
||||||
@@ -220,6 +230,48 @@ TEST_F(RPCServerInfoHandlerTest, DefaultOutputIsPresent)
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(RPCServerInfoHandlerTest, AmendmentBlockedIsPresentIfSet)
|
||||||
|
{
|
||||||
|
MockBackend* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
|
MockLoadBalancer* rawBalancerPtr = static_cast<MockLoadBalancer*>(mockLoadBalancerPtr.get());
|
||||||
|
MockCounters* rawCountersPtr = static_cast<MockCounters*>(mockCountersPtr.get());
|
||||||
|
MockETLService* rawETLServicePtr = static_cast<MockETLService*>(mockETLServicePtr.get());
|
||||||
|
|
||||||
|
mockBackendPtr->updateRange(10); // min
|
||||||
|
mockBackendPtr->updateRange(30); // max
|
||||||
|
|
||||||
|
auto const ledgerinfo = CreateLedgerInfo(LEDGERHASH, 30, 3); // 3 seconds old
|
||||||
|
ON_CALL(*rawBackendPtr, fetchLedgerBySequence).WillByDefault(Return(ledgerinfo));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, fetchLedgerBySequence).Times(1);
|
||||||
|
|
||||||
|
auto const feeBlob = CreateFeeSettingBlob(1, 2, 3, 4, 0);
|
||||||
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject).WillByDefault(Return(feeBlob));
|
||||||
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawBalancerPtr, forwardToRippled).WillByDefault(Return(std::nullopt));
|
||||||
|
EXPECT_CALL(*rawBalancerPtr, forwardToRippled(testing::_, testing::Eq(CLIENTIP), testing::_)).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawCountersPtr, uptime).WillByDefault(Return(std::chrono::seconds{1234}));
|
||||||
|
EXPECT_CALL(*rawCountersPtr, uptime).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawETLServicePtr, isAmendmentBlocked).WillByDefault(Return(true));
|
||||||
|
EXPECT_CALL(*rawETLServicePtr, isAmendmentBlocked).Times(1);
|
||||||
|
|
||||||
|
auto const handler = AnyHandler{TestServerInfoHandler{
|
||||||
|
mockBackendPtr, mockSubscriptionManagerPtr, mockLoadBalancerPtr, mockETLServicePtr, *mockCountersPtr}};
|
||||||
|
|
||||||
|
runSpawn([&](auto& yield) {
|
||||||
|
auto const req = json::parse("{}");
|
||||||
|
auto const output = handler.process(req, Context{std::ref(yield), {}, false, CLIENTIP});
|
||||||
|
|
||||||
|
validateNormalOutput(output);
|
||||||
|
|
||||||
|
auto const& info = output.value().as_object().at("info").as_object();
|
||||||
|
EXPECT_TRUE(info.contains("amendment_blocked"));
|
||||||
|
EXPECT_EQ(info.at("amendment_blocked").as_bool(), true);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(RPCServerInfoHandlerTest, AdminSectionPresentWhenAdminFlagIsSet)
|
TEST_F(RPCServerInfoHandlerTest, AdminSectionPresentWhenAdminFlagIsSet)
|
||||||
{
|
{
|
||||||
MockBackend* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
MockBackend* rawBackendPtr = static_cast<MockBackend*>(mockBackendPtr.get());
|
||||||
@@ -244,6 +296,12 @@ TEST_F(RPCServerInfoHandlerTest, AdminSectionPresentWhenAdminFlagIsSet)
|
|||||||
ON_CALL(*rawBalancerPtr, forwardToRippled).WillByDefault(Return(empty));
|
ON_CALL(*rawBalancerPtr, forwardToRippled).WillByDefault(Return(empty));
|
||||||
EXPECT_CALL(*rawBalancerPtr, forwardToRippled).Times(1);
|
EXPECT_CALL(*rawBalancerPtr, forwardToRippled).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawCountersPtr, uptime).WillByDefault(Return(std::chrono::seconds{1234}));
|
||||||
|
EXPECT_CALL(*rawCountersPtr, uptime).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawETLServicePtr, isAmendmentBlocked).WillByDefault(Return(false));
|
||||||
|
EXPECT_CALL(*rawETLServicePtr, isAmendmentBlocked).Times(1);
|
||||||
|
|
||||||
// admin calls
|
// admin calls
|
||||||
ON_CALL(*rawCountersPtr, report).WillByDefault(Return(empty));
|
ON_CALL(*rawCountersPtr, report).WillByDefault(Return(empty));
|
||||||
EXPECT_CALL(*rawCountersPtr, report).Times(1);
|
EXPECT_CALL(*rawCountersPtr, report).Times(1);
|
||||||
@@ -287,6 +345,12 @@ TEST_F(RPCServerInfoHandlerTest, RippledForwardedValuesPresent)
|
|||||||
ON_CALL(*rawBackendPtr, doFetchLedgerObject).WillByDefault(Return(feeBlob));
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject).WillByDefault(Return(feeBlob));
|
||||||
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawCountersPtr, uptime).WillByDefault(Return(std::chrono::seconds{1234}));
|
||||||
|
EXPECT_CALL(*rawCountersPtr, uptime).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawETLServicePtr, isAmendmentBlocked).WillByDefault(Return(false));
|
||||||
|
EXPECT_CALL(*rawETLServicePtr, isAmendmentBlocked).Times(1);
|
||||||
|
|
||||||
auto const rippledObj = boost::json::parse(R"({
|
auto const rippledObj = boost::json::parse(R"({
|
||||||
"result": {
|
"result": {
|
||||||
"info": {
|
"info": {
|
||||||
@@ -344,10 +408,15 @@ TEST_F(RPCServerInfoHandlerTest, RippledForwardedValuesMissingNoExceptionThrown)
|
|||||||
ON_CALL(*rawBackendPtr, doFetchLedgerObject).WillByDefault(Return(feeBlob));
|
ON_CALL(*rawBackendPtr, doFetchLedgerObject).WillByDefault(Return(feeBlob));
|
||||||
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
EXPECT_CALL(*rawBackendPtr, doFetchLedgerObject).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawCountersPtr, uptime).WillByDefault(Return(std::chrono::seconds{1234}));
|
||||||
|
EXPECT_CALL(*rawCountersPtr, uptime).Times(1);
|
||||||
|
|
||||||
|
ON_CALL(*rawETLServicePtr, isAmendmentBlocked).WillByDefault(Return(false));
|
||||||
|
EXPECT_CALL(*rawETLServicePtr, isAmendmentBlocked).Times(1);
|
||||||
|
|
||||||
auto const rippledObj = boost::json::parse(R"({
|
auto const rippledObj = boost::json::parse(R"({
|
||||||
"result": {
|
"result": {
|
||||||
"info": {
|
"info": {}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})");
|
})");
|
||||||
ON_CALL(*rawBalancerPtr, forwardToRippled).WillByDefault(Return(rippledObj.as_object()));
|
ON_CALL(*rawBalancerPtr, forwardToRippled).WillByDefault(Return(rippledObj.as_object()));
|
||||||
|
|||||||
@@ -37,4 +37,5 @@ struct MockCounters
|
|||||||
MOCK_METHOD(void, onUnknownCommand, (), ());
|
MOCK_METHOD(void, onUnknownCommand, (), ());
|
||||||
MOCK_METHOD(void, onInternalError, (), ());
|
MOCK_METHOD(void, onInternalError, (), ());
|
||||||
MOCK_METHOD(boost::json::object, report, (), (const));
|
MOCK_METHOD(boost::json::object, report, (), (const));
|
||||||
|
MOCK_METHOD(std::chrono::seconds, uptime, (), (const));
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -30,4 +30,5 @@ struct MockETLService
|
|||||||
MOCK_METHOD(std::chrono::time_point<std::chrono::system_clock>, getLastPublish, (), (const));
|
MOCK_METHOD(std::chrono::time_point<std::chrono::system_clock>, getLastPublish, (), (const));
|
||||||
MOCK_METHOD(std::uint32_t, lastPublishAgeSeconds, (), (const));
|
MOCK_METHOD(std::uint32_t, lastPublishAgeSeconds, (), (const));
|
||||||
MOCK_METHOD(std::uint32_t, lastCloseAgeSeconds, (), (const));
|
MOCK_METHOD(std::uint32_t, lastCloseAgeSeconds, (), (const));
|
||||||
|
MOCK_METHOD(bool, isAmendmentBlocked, (), (const));
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user