#include "feed/FeedTestUtil.hpp" #include "feed/impl/LedgerFeed.hpp" #include "util/Spawn.hpp" #include "util/TestObject.hpp" #include "web/SubscriptionContextInterface.hpp" #include #include #include #include #include #include namespace { constexpr auto kLEDGER_HASH = "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652"; } // namespace using namespace feed::impl; namespace json = boost::json; using namespace testing; using FeedLedgerTest = FeedBaseTest; TEST_F(FeedLedgerTest, SubPub) { backend_->setRange(10, 30); auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(testing::Return(ledgerHeader)); auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0); EXPECT_CALL(*backend_, doFetchLedgerObject).WillOnce(testing::Return(feeBlob)); // check the function response // Information about the ledgers on hand and current fee schedule. This // includes the same fields as a ledger stream message, except that it omits // the type and txn_count fields static constexpr auto kLEDGER_RESPONSE = R"JSON({ "validated_ledgers": "10-30", "ledger_index": 30, "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_time": 0, "fee_base": 1, "reserve_base": 3, "reserve_inc": 2, "network_id": 123 })JSON"; boost::asio::io_context ioContext; util::spawn(ioContext, [this](boost::asio::yield_context yield) { EXPECT_CALL(*mockSessionPtr, onDisconnect); auto res = testFeedPtr->sub(yield, backend_, sessionPtr, networkID); // check the response EXPECT_EQ(res, json::parse(kLEDGER_RESPONSE)); }); ioContext.run(); EXPECT_EQ(testFeedPtr->count(), 1); static constexpr auto kLEDGER_PUB = R"JSON({ "type": "ledgerClosed", "ledger_index": 31, "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_time": 0, "fee_base": 0, "reserve_base": 10, "reserve_inc": 0, "validated_ledgers": "10-31", "txn_count": 8, "network_id": 123 })JSON"; // test publish EXPECT_CALL(*mockSessionPtr, send(sharedStringJsonEq(kLEDGER_PUB))).Times(1); auto const ledgerHeader2 = createLedgerHeader(kLEDGER_HASH, 31); auto fee2 = ripple::Fees(); fee2.reserve = 10; testFeedPtr->pub(ledgerHeader2, fee2, "10-31", 8, networkID); // 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, networkID); } TEST_F(FeedLedgerTest, AutoDisconnect) { backend_->setRange(10, 30); auto const ledgerHeader = createLedgerHeader(kLEDGER_HASH, 30); EXPECT_CALL(*backend_, fetchLedgerBySequence).WillOnce(testing::Return(ledgerHeader)); auto const feeBlob = createLegacyFeeSettingBlob(1, 2, 3, 4, 0); EXPECT_CALL(*backend_, doFetchLedgerObject).WillOnce(testing::Return(feeBlob)); static constexpr auto kLEDGER_RESPONSE = R"JSON({ "validated_ledgers": "10-30", "ledger_index": 30, "ledger_hash": "4BC50C9B0D8515D3EAAE1E74B29A95804346C491EE1A95BF25E4AAB854A6A652", "ledger_time": 0, "fee_base": 1, "reserve_base": 3, "reserve_inc": 2, "network_id": 123 })JSON"; web::SubscriptionContextInterface::OnDisconnectSlot slot; EXPECT_CALL(*mockSessionPtr, onDisconnect).WillOnce(testing::SaveArg<0>(&slot)); boost::asio::io_context ioContext; util::spawn(ioContext, [this](boost::asio::yield_context yield) { auto res = testFeedPtr->sub(yield, backend_, sessionPtr, networkID); // check the response EXPECT_EQ(res, json::parse(kLEDGER_RESPONSE)); }); ioContext.run(); EXPECT_EQ(testFeedPtr->count(), 1); EXPECT_CALL(*mockSessionPtr, send(_)).Times(0); ASSERT_TRUE(slot); slot(sessionPtr.get()); sessionPtr.reset(); EXPECT_EQ(testFeedPtr->count(), 0); auto const ledgerHeader2 = createLedgerHeader(kLEDGER_HASH, 31); auto fee2 = ripple::Fees(); fee2.reserve = 10; // no error testFeedPtr->pub(ledgerHeader2, fee2, "10-31", 8, networkID); }