// Copyright (c) 2011-2016 The Cryptonote developers // Distributed under the MIT/X11 software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include "gtest/gtest.h" #include #include "CryptoNoteCore/TransactionApi.h" #include "Logging/ConsoleLogger.h" #include "Transfers/TransfersSubscription.h" #include "Transfers/TypeHelpers.h" #include "ITransfersContainer.h" #include "TransactionApiHelpers.h" #include "TransfersObserver.h" using namespace CryptoNote; namespace { const uint32_t UNCONFIRMED_TRANSACTION_HEIGHT = std::numeric_limits::max(); const uint32_t UNCONFIRMED = std::numeric_limits::max(); std::error_code createError() { return std::make_error_code(std::errc::invalid_argument); } class TransfersSubscriptionTest : public ::testing::Test { public: TransfersSubscriptionTest() : currency(CurrencyBuilder(m_logger).currency()), account(generateAccountKeys()), syncStart(SynchronizationStart{ 0, 0 }), sub(currency, AccountSubscription{ account, syncStart, 10 }) { sub.addObserver(&observer); } std::shared_ptr addTransaction(uint64_t amount, uint32_t height, uint32_t outputIndex) { TestTransactionBuilder b1; auto unknownSender = generateAccountKeys(); b1.addTestInput(amount, unknownSender); auto outInfo = b1.addTestKeyOutput(amount, outputIndex, account); auto tx = std::shared_ptr(b1.build().release()); std::vector outputs = { outInfo }; sub.addTransaction(TransactionBlockInfo{ height, 100000 }, *tx, outputs); return tx; } Logging::ConsoleLogger m_logger; Currency currency; AccountKeys account; SynchronizationStart syncStart; TransfersSubscription sub; TransfersObserver observer; }; } TEST_F(TransfersSubscriptionTest, getInitParameters) { ASSERT_EQ(syncStart.height, sub.getSyncStart().height); ASSERT_EQ(syncStart.timestamp, sub.getSyncStart().timestamp); ASSERT_EQ(account.address, sub.getAddress()); ASSERT_EQ(account, sub.getKeys()); } TEST_F(TransfersSubscriptionTest, addTransaction) { auto tx1 = addTransaction(10000, 1, 0); auto tx2 = addTransaction(10000, 2, 1); // this transaction should not be added, so no notification auto tx = createTransaction(); addTestInput(*tx, 20000); sub.addTransaction(TransactionBlockInfo{ 2, 100000 }, *tx, {}); ASSERT_EQ(2, sub.getContainer().transactionsCount()); ASSERT_EQ(2, observer.updated.size()); ASSERT_EQ(tx1->getTransactionHash(), observer.updated[0]); ASSERT_EQ(tx2->getTransactionHash(), observer.updated[1]); } TEST_F(TransfersSubscriptionTest, onBlockchainDetach) { addTransaction(10000, 10, 0); auto txHash = addTransaction(10000, 11, 1)->getTransactionHash(); ASSERT_EQ(2, sub.getContainer().transactionsCount()); sub.onBlockchainDetach(11); ASSERT_EQ(1, sub.getContainer().transactionsCount()); ASSERT_EQ(1, observer.deleted.size()); ASSERT_EQ(txHash, observer.deleted[0]); } TEST_F(TransfersSubscriptionTest, onError) { auto err = createError(); addTransaction(10000, 10, 0); addTransaction(10000, 11, 1); ASSERT_EQ(2, sub.getContainer().transactionsCount()); sub.onError(err, 12); ASSERT_EQ(2, sub.getContainer().transactionsCount()); ASSERT_EQ(1, observer.errors.size()); ASSERT_EQ(std::make_tuple(12, err), observer.errors[0]); sub.onError(err, 11); ASSERT_EQ(1, sub.getContainer().transactionsCount()); // one transaction should be detached ASSERT_EQ(2, observer.errors.size()); ASSERT_EQ(std::make_tuple(12, err), observer.errors[0]); ASSERT_EQ(std::make_tuple(11, err), observer.errors[1]); } TEST_F(TransfersSubscriptionTest, advanceHeight) { ASSERT_TRUE(sub.advanceHeight(10)); ASSERT_FALSE(sub.advanceHeight(9)); // can't go backwards } TEST_F(TransfersSubscriptionTest, markTransactionConfirmed) { auto txHash = addTransaction(10000, UNCONFIRMED_TRANSACTION_HEIGHT, UNCONFIRMED)->getTransactionHash(); ASSERT_EQ(1, sub.getContainer().transactionsCount()); ASSERT_EQ(1, observer.updated.size()); // added sub.markTransactionConfirmed(TransactionBlockInfo{ 10, 100000 }, txHash, { 1 }); ASSERT_EQ(2, observer.updated.size()); // added + updated ASSERT_EQ(txHash, observer.updated[0]); } TEST_F(TransfersSubscriptionTest, deleteUnconfirmedTransaction) { auto txHash = addTransaction(10000, UNCONFIRMED_TRANSACTION_HEIGHT, UNCONFIRMED)->getTransactionHash(); ASSERT_EQ(1, sub.getContainer().transactionsCount()); sub.deleteUnconfirmedTransaction(txHash); ASSERT_EQ(0, sub.getContainer().transactionsCount()); ASSERT_EQ(1, observer.deleted.size()); ASSERT_EQ(txHash, observer.deleted[0]); }