Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Split exchanges orchestrator tests in smaller files for better clarity #319

Merged
merged 1 commit into from
Sep 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -258,3 +258,4 @@ add_subdirectory(src/objects)
add_subdirectory(src/api-objects)
add_subdirectory(src/api)
add_subdirectory(src/engine)
add_subdirectory(src/main)
82 changes: 34 additions & 48 deletions src/engine/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,65 +1,51 @@
aux_source_directory(src ENGINE_SRC)

if (CCT_BUILD_EXEC)
add_executable (coincenter ${ENGINE_SRC})
add_library(coincenter_engine STATIC ${ENGINE_SRC})

# Enable LTO with coincenter in Release mode
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set_property(TARGET coincenter PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
message(STATUS "Activate LTO for coincenter")
endif()
target_link_libraries(coincenter_engine PUBLIC coincenter_exchangeapi)
target_link_libraries(coincenter_engine PUBLIC coincenter_objects)
target_include_directories(coincenter_engine PUBLIC include)

else()

list(REMOVE_ITEM ENGINE_SRC "src/main.cpp")
add_library(coincenter STATIC ${ENGINE_SRC})

endif()

target_link_libraries(coincenter PUBLIC coincenter_exchangeapi)
target_link_libraries(coincenter PUBLIC coincenter_objects)
target_include_directories(coincenter PUBLIC include)
add_unit_test(
commandlineoptionsparser_test
test/commandlineoptionsparser_test.cpp
DEFINITIONS
CCT_DISABLE_SPDLOG
LIBRARIES
coincenter_tech
)

set_target_properties(coincenter PROPERTIES
VERSION ${PROJECT_VERSION}
COMPILE_DEFINITIONS_DEBUG "JSON_DEBUG;JSON_SAFE;JSON_ISO_STRICT"
RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}
)
add_unit_test(
exchangesorchestrator_private_test
test/exchangesorchestrator_private_test.cpp
LIBRARIES
coincenter_engine
)

add_unit_test(
commandlineoptionsparser_test
test/commandlineoptionsparser_test.cpp
DEFINITIONS
CCT_DISABLE_SPDLOG
LIBRARIES
coincenter_tech
exchangesorchestrator_public_test
test/exchangesorchestrator_public_test.cpp
LIBRARIES
coincenter_engine
)

add_unit_test(
exchangesorchestrator_test
src/exchangesorchestrator.cpp
test/exchangesorchestrator_test.cpp
LIBRARIES
coincenter_exchangeapi
coincenter_objects
exchangesorchestrator_trade_test
test/exchangesorchestrator_trade_test.cpp
LIBRARIES
coincenter_engine
)

add_unit_test(
queryresultprinter_test
src/balanceperexchangeportfolio.cpp
src/coincentercommandtype.cpp
src/queryresultprinter.cpp
test/queryresultprinter_test.cpp
LIBRARIES
coincenter_exchangeapi
coincenter_objects
queryresultprinter_test
test/queryresultprinter_test.cpp
LIBRARIES
coincenter_engine
)

add_unit_test(
stringoptionparser_test
src/stringoptionparser.cpp
test/stringoptionparser_test.cpp
LIBRARIES
coincenter_objects
coincenter_tools
stringoptionparser_test
test/stringoptionparser_test.cpp
LIBRARIES
coincenter_engine
)
243 changes: 243 additions & 0 deletions src/engine/test/exchangesorchestrator_private_test.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
#include <gtest/gtest.h>

#include "cct_const.hpp"
#include "exchangedata_test.hpp"
#include "exchangesorchestrator.hpp"

namespace cct {

using Deposit = CurrencyExchange::Deposit;
using Withdraw = CurrencyExchange::Withdraw;
using Type = CurrencyExchange::Type;

class ExchangeOrchestratorTest : public ExchangesBaseTest {
protected:
ExchangesOrchestrator exchangesOrchestrator{std::span<Exchange>(&this->exchange1, 8)};
};

TEST_F(ExchangeOrchestratorTest, BalanceNoEquivalentCurrencyUniqueExchange) {
CurrencyCode equiCurrency;

EXPECT_CALL(exchangePrivate1, queryAccountBalance(equiCurrency)).WillOnce(testing::Return(balancePortfolio1));

const ExchangeName privateExchangeNames[1] = {ExchangeName(exchange1.name(), exchange1.keyName())};
BalancePerExchange ret{{&exchange1, balancePortfolio1}};
EXPECT_EQ(exchangesOrchestrator.getBalance(privateExchangeNames, equiCurrency), ret);
}

TEST_F(ExchangeOrchestratorTest, BalanceNoEquivalentCurrencySeveralExchanges) {
CurrencyCode equiCurrency;

EXPECT_CALL(exchangePrivate1, queryAccountBalance(equiCurrency)).WillOnce(testing::Return(balancePortfolio1));
EXPECT_CALL(exchangePrivate3, queryAccountBalance(equiCurrency)).WillOnce(testing::Return(balancePortfolio2));
EXPECT_CALL(exchangePrivate4, queryAccountBalance(equiCurrency)).WillOnce(testing::Return(balancePortfolio3));

const ExchangeName privateExchangeNames[] = {ExchangeName(exchange3.name(), exchange3.keyName()),
ExchangeName(exchange1.name(), exchange1.keyName()),
ExchangeName(exchange4.name(), exchange4.keyName())};
BalancePerExchange ret{
{&exchange1, balancePortfolio1}, {&exchange3, balancePortfolio2}, {&exchange4, balancePortfolio3}};
EXPECT_EQ(exchangesOrchestrator.getBalance(privateExchangeNames, equiCurrency), ret);
}

TEST_F(ExchangeOrchestratorTest, DepositInfoUniqueExchanges) {
CurrencyCode depositCurrency{"ETH"};

const ExchangeName privateExchangeNames[] = {ExchangeName(exchange2.name(), exchange2.keyName())};

CurrencyExchangeFlatSet tradableCurrencies2{CurrencyExchangeVector{
CurrencyExchange(depositCurrency, Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto),
CurrencyExchange("XRP", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};
EXPECT_CALL(exchangePrivate2, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies2));

Wallet wallet2{privateExchangeNames[0], depositCurrency, "address1", "", WalletCheck()};
EXPECT_CALL(exchangePrivate2, queryDepositWallet(depositCurrency)).WillOnce(testing::Return(wallet2));

WalletPerExchange ret{{&exchange2, wallet2}};
EXPECT_EQ(exchangesOrchestrator.getDepositInfo(privateExchangeNames, depositCurrency), ret);
}

TEST_F(ExchangeOrchestratorTest, DepositInfoSeveralExchangesWithUnavailableDeposits) {
CurrencyCode depositCurrency{"XRP"};

const ExchangeName privateExchangeNames[] = {
ExchangeName(exchange3.name(), exchange3.keyName()), ExchangeName(exchange1.name(), exchange1.keyName()),
ExchangeName(exchange2.name(), exchange2.keyName()), ExchangeName(exchange4.name(), exchange4.keyName())};

CurrencyExchangeFlatSet tradableCurrencies1{CurrencyExchangeVector{
CurrencyExchange(depositCurrency, Deposit::kUnavailable, Withdraw::kAvailable, Type::kCrypto),
CurrencyExchange("SHIB", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};
EXPECT_CALL(exchangePrivate1, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies1));

CurrencyExchangeFlatSet tradableCurrencies2{
CurrencyExchangeVector{CurrencyExchange("XLM", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};
EXPECT_CALL(exchangePrivate2, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies2));

CurrencyExchangeFlatSet tradableCurrencies3{CurrencyExchangeVector{
CurrencyExchange("BTC", Deposit::kUnavailable, Withdraw::kUnavailable, Type::kCrypto),
CurrencyExchange("SOL", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto),
CurrencyExchange(depositCurrency, Deposit::kAvailable, Withdraw::kUnavailable, Type::kCrypto),
CurrencyExchange("EUR", Deposit::kAvailable, Withdraw::kAvailable, Type::kFiat),
}};
EXPECT_CALL(exchangePrivate3, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies3));
EXPECT_CALL(exchangePrivate4, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies3));

Wallet wallet31{privateExchangeNames[2], depositCurrency, "address2", "tag2", WalletCheck()};
EXPECT_CALL(exchangePrivate3, queryDepositWallet(depositCurrency)).WillOnce(testing::Return(wallet31));

Wallet wallet32{privateExchangeNames[3], depositCurrency, "address3", "tag3", WalletCheck()};
EXPECT_CALL(exchangePrivate4, queryDepositWallet(depositCurrency)).WillOnce(testing::Return(wallet32));

WalletPerExchange ret{{&exchange3, wallet31}, {&exchange4, wallet32}};
EXPECT_EQ(exchangesOrchestrator.getDepositInfo(privateExchangeNames, depositCurrency), ret);
}

TEST_F(ExchangeOrchestratorTest, GetOpenedOrders) {
OrdersConstraints noConstraints;

const ExchangeName privateExchangeNames[] = {ExchangeName(exchange3.name(), exchange3.keyName()),
ExchangeName(exchange2.name(), exchange2.keyName()),
ExchangeName(exchange4.name(), exchange4.keyName())};

Orders orders2{Order("Id1", MonetaryAmount("0.1ETH"), MonetaryAmount("0.9ETH"), MonetaryAmount("0.14BTC"),
Clock::now(), TradeSide::kBuy),
Order("Id2", MonetaryAmount("15XLM"), MonetaryAmount("76XLM"), MonetaryAmount("0.5EUR"), Clock::now(),
TradeSide::kSell)};
EXPECT_CALL(exchangePrivate2, queryOpenedOrders(noConstraints)).WillOnce(testing::Return(orders2));

Orders orders3{};
EXPECT_CALL(exchangePrivate3, queryOpenedOrders(noConstraints)).WillOnce(testing::Return(orders3));

Orders orders4{Order("Id37", MonetaryAmount("0.7ETH"), MonetaryAmount("0.9ETH"), MonetaryAmount("0.14BTC"),
Clock::now(), TradeSide::kSell),
Order("Id2", MonetaryAmount("15XLM"), MonetaryAmount("19XLM"), MonetaryAmount("0.5EUR"), Clock::now(),
TradeSide::kBuy)};
EXPECT_CALL(exchangePrivate4, queryOpenedOrders(noConstraints)).WillOnce(testing::Return(orders4));

OpenedOrdersPerExchange ret{{&exchange2, OrdersSet(orders2.begin(), orders2.end())},
{&exchange3, OrdersSet(orders3.begin(), orders3.end())},
{&exchange4, OrdersSet(orders4.begin(), orders4.end())}};
EXPECT_EQ(exchangesOrchestrator.getOpenedOrders(privateExchangeNames, noConstraints), ret);
}

TEST_F(ExchangeOrchestratorTest, WithdrawSameAccountImpossible) {
MonetaryAmount grossAmount{1000, "XRP"};
ExchangeName fromExchange(exchange1.name(), exchange1.keyName());
const ExchangeName &toExchange = fromExchange;
EXPECT_THROW(exchangesOrchestrator.withdraw(grossAmount, false, fromExchange, toExchange), exception);
}

TEST_F(ExchangeOrchestratorTest, WithdrawImpossibleFrom) {
MonetaryAmount grossAmount{1000, "XRP"};
ExchangeName fromExchange(exchange1.name(), exchange1.keyName());
ExchangeName toExchange(exchange2.name(), exchange2.keyName());

CurrencyExchangeFlatSet tradableCurrencies1{CurrencyExchangeVector{
CurrencyExchange(grossAmount.currencyCode(), Deposit::kAvailable, Withdraw::kUnavailable, Type::kCrypto),
CurrencyExchange("SHIB", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};
EXPECT_CALL(exchangePrivate1, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies1));
CurrencyExchangeFlatSet tradableCurrencies2{CurrencyExchangeVector{
CurrencyExchange(grossAmount.currencyCode(), Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto),
CurrencyExchange("SHIB", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};
EXPECT_CALL(exchangePrivate2, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies2));

EXPECT_FALSE(exchangesOrchestrator.withdraw(grossAmount, false, fromExchange, toExchange).hasBeenInitiated());
}

TEST_F(ExchangeOrchestratorTest, WithdrawImpossibleTo) {
MonetaryAmount grossAmount{1000, "XRP"};
ExchangeName fromExchange(exchange1.name(), exchange1.keyName());
ExchangeName toExchange(exchange2.name(), exchange2.keyName());

CurrencyExchangeFlatSet tradableCurrencies1{CurrencyExchangeVector{
CurrencyExchange(grossAmount.currencyCode(), Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto),
CurrencyExchange("SHIB", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};
EXPECT_CALL(exchangePrivate1, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies1));
CurrencyExchangeFlatSet tradableCurrencies2{CurrencyExchangeVector{
CurrencyExchange(grossAmount.currencyCode(), Deposit::kUnavailable, Withdraw::kAvailable, Type::kCrypto),
CurrencyExchange("SHIB", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};
EXPECT_CALL(exchangePrivate2, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies2));

EXPECT_FALSE(exchangesOrchestrator.withdraw(grossAmount, false, fromExchange, toExchange).hasBeenInitiated());
}

inline bool operator==(const WithdrawInfo &lhs, const WithdrawInfo &rhs) {
return lhs.withdrawId() == rhs.withdrawId();
}

namespace api {
inline bool operator==(const InitiatedWithdrawInfo &lhs, const InitiatedWithdrawInfo &rhs) {
return lhs.withdrawId() == rhs.withdrawId();
}

inline bool operator==(const SentWithdrawInfo &lhs, const SentWithdrawInfo &rhs) {
return lhs.isWithdrawSent() == rhs.isWithdrawSent() && lhs.netEmittedAmount() == rhs.netEmittedAmount();
}
} // namespace api

class ExchangeOrchestratorWithdrawTest : public ExchangeOrchestratorTest {
protected:
ExchangeOrchestratorWithdrawTest() {
CurrencyExchangeFlatSet tradableCurrencies1{
CurrencyExchangeVector{CurrencyExchange(cur, Deposit::kUnavailable, Withdraw::kAvailable, Type::kCrypto),
CurrencyExchange("SHIB", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};
CurrencyExchangeFlatSet tradableCurrencies2{
CurrencyExchangeVector{CurrencyExchange(cur, Deposit::kAvailable, Withdraw::kUnavailable, Type::kCrypto),
CurrencyExchange("SHIB", Deposit::kAvailable, Withdraw::kAvailable, Type::kCrypto)}};

EXPECT_CALL(exchangePrivate1, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies1));
EXPECT_CALL(exchangePrivate2, queryTradableCurrencies()).WillOnce(testing::Return(tradableCurrencies2));
}

WithdrawInfo createWithdrawInfo(MonetaryAmount grossAmount, bool isPercentageWithdraw) {
if (isPercentageWithdraw) {
EXPECT_CALL(exchangePrivate1, queryAccountBalance(CurrencyCode())).WillOnce(testing::Return(balancePortfolio1));
grossAmount = (grossAmount.toNeutral() * balancePortfolio1.get(cur)) / 100;
} else {
EXPECT_CALL(exchangePrivate1, queryAccountBalance(testing::_)).Times(0);
}
MonetaryAmount netEmittedAmount = grossAmount - fee;
Wallet receivingWallet{toExchange, cur, address, tag, WalletCheck()};
EXPECT_CALL(exchangePrivate2, queryDepositWallet(cur)).WillOnce(testing::Return(receivingWallet));

api::InitiatedWithdrawInfo initiatedWithdrawInfo{receivingWallet, withdrawIdView, grossAmount};
EXPECT_CALL(exchangePrivate1, launchWithdraw(grossAmount, std::move(receivingWallet)))
.WillOnce(testing::Return(initiatedWithdrawInfo));
api::SentWithdrawInfo sentWithdrawInfo{netEmittedAmount, true};
EXPECT_CALL(exchangePrivate1, isWithdrawSuccessfullySent(initiatedWithdrawInfo))
.WillOnce(testing::Return(sentWithdrawInfo));
EXPECT_CALL(exchangePrivate2, isWithdrawReceived(initiatedWithdrawInfo, sentWithdrawInfo))
.WillOnce(testing::Return(true));
return WithdrawInfo(initiatedWithdrawInfo, sentWithdrawInfo);
}

CurrencyCode cur{"XRP"};
ExchangeName fromExchange{exchange1.name(), exchange1.keyName()};
ExchangeName toExchange{exchange2.name(), exchange2.keyName()};

std::string_view address{"TestAddress"};
std::string_view tag{"TestTag"};

WithdrawIdView withdrawIdView{"WithdrawId"};

MonetaryAmount fee{"0.02", cur};
};

TEST_F(ExchangeOrchestratorWithdrawTest, WithdrawPossible) {
MonetaryAmount grossAmount{1000, cur};
bool isPercentageWithdraw = false;
auto exp = createWithdrawInfo(grossAmount, isPercentageWithdraw);
auto ret =
exchangesOrchestrator.withdraw(grossAmount, isPercentageWithdraw, fromExchange, toExchange, Duration::zero());
EXPECT_EQ(exp, ret);
}

TEST_F(ExchangeOrchestratorWithdrawTest, WithdrawPossiblePercentage) {
MonetaryAmount grossAmount{25, cur};
bool isPercentageWithdraw = true;
auto exp = createWithdrawInfo(grossAmount, isPercentageWithdraw);
auto ret =
exchangesOrchestrator.withdraw(grossAmount, isPercentageWithdraw, fromExchange, toExchange, Duration::zero());
EXPECT_EQ(exp, ret);
}
} // namespace cct
Loading