Skip to content

Commit

Permalink
feat(transactions): all tests
Browse files Browse the repository at this point in the history
  • Loading branch information
tomjeannesson committed Jul 18, 2023
1 parent 32a3e75 commit ad3d19f
Show file tree
Hide file tree
Showing 12 changed files with 141 additions and 65 deletions.
9 changes: 8 additions & 1 deletion django_napse/core/models/accounts/managers/space.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import shortuuid
from django.apps import apps
from django.db import models

from django_napse.utils.errors import NapseError


class NapseSpaceManager(models.Manager):
def create(self, name: str, exchange_account, description: str = ""):
SpaceWallet = apps.get_model("django_napse_core", "SpaceWallet")
attempts = 0
while attempts < 10:
attempts += 1
Expand All @@ -15,9 +17,14 @@ def create(self, name: str, exchange_account, description: str = ""):
else:
error_msg = f"Unable to generate a unique identifier for the space {name}"
raise NapseError.RandomGenrationError(error_msg)
return super().create(
space = self.model(
name=name,
exchange_account=exchange_account,
identifier=f"#{uuid}",
description=description,
)
space.save()

SpaceWallet.objects.create(owner=space, title=f"Main Wallet for space {space.name}")

return space
12 changes: 11 additions & 1 deletion django_napse/core/models/bots/bot.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,17 @@ def space(self):
if self.is_in_simulation:
return self.simulation.space
if self.is_in_fleet:
return self.bot_in_cluster.cluster.fleet.testing
error_msg = "Bot is in a fleet and therefore doesn't have a space."
raise BotError.NoSpace(error_msg)
error_msg = "Bot is not in simulation or fleet."
raise BotError.InvalidSetting(error_msg)

@property
def exchange_account(self):
if self.is_in_simulation:
return self.simulation.space.exchange_account
if self.is_in_fleet:
return self.bot_in_cluster.cluster.fleet.exchange_account
error_msg = "Bot is not in simulation or fleet."
raise BotError.InvalidSetting(error_msg)

Expand Down
4 changes: 2 additions & 2 deletions django_napse/core/models/orders/order.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,8 @@ def testing(self):
return self.bot.testing

@property
def space(self):
return self.bot.space
def exchange_account(self):
return self.bot.exchange_account

def set_status_passed(self):
if self.status == ORDER_STATUS.PENDING:
Expand Down
10 changes: 5 additions & 5 deletions django_napse/core/models/transactions/managers/transaction.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@


class TransactionManager(models.Manager):
def create(self, from_wallet, to_wallet, amount, ticker, transaction_type="TRANSFER"):
def create(self, from_wallet, to_wallet, amount, ticker, transaction_type):
"""Create a Transaction object and update the wallets accordingly."""
if amount == 0:
return None
Expand All @@ -16,14 +16,14 @@ def create(self, from_wallet, to_wallet, amount, ticker, transaction_type="TRANS
ticker=ticker,
transaction_type=transaction_type,
)
if from_wallet.space != to_wallet.space:
if from_wallet.exchange_account != to_wallet.exchange_account:
error_msg = "Wallets must be on the same space."
raise TransactionError.DifferentSpaceError(error_msg)
raise TransactionError.DifferentAccountError(error_msg)
if transaction_type not in TRANSACTION_TYPES:
error_msg = f"Transaction type {transaction_type} not in {TRANSACTION_TYPES}."
raise TransactionError.InvalidTransactionError(error_msg)

from_wallet.spend(amount, ticker)
to_wallet.top_up(amount, ticker, mbp=from_wallet.currencies.get(ticker=ticker).mbp)
from_wallet.spend(amount, ticker, force=True)
to_wallet.top_up(amount, ticker, mbp=from_wallet.currencies.get(ticker=ticker).mbp, force=True)
transaction.save()
return transaction
1 change: 0 additions & 1 deletion django_napse/core/models/transactions/transaction.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
from django.core.exceptions import ValidationError
from django.db import models

from django_napse.core.models.transactions.managers import TransactionManager
Expand Down
13 changes: 11 additions & 2 deletions django_napse/core/models/wallets/wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ def space(self): # pragma: no cover
error_msg = "space() not implemented by default. Please implement in a subclass of Wallet."
raise NotImplementedError(error_msg)

@property
def exchange_account(self): # pragma: no cover
error_msg = "exchange_account() not implemented by default. Please implement in a subclass of Wallet."
raise NotImplementedError(error_msg)

def spend(self, amount: float, ticker: str, recv: int = 3, **kwargs) -> None:
if not kwargs.get("force", False):
error_msg = "DANGEROUS: You should not use this method outside of select circumstances. Use Transactions instead."
Expand Down Expand Up @@ -162,13 +167,17 @@ class SpaceWallet(Wallet):
def space(self):
return self.owner

@property
def exchange_account(self):
return self.owner.exchange_account


class OrderWallet(Wallet):
owner = models.OneToOneField("Order", on_delete=models.CASCADE, related_name="wallet")

@property
def space(self):
return self.owner.space
def exchange_account(self):
return self.owner.exchange_account


class ConnectionWallet(Wallet):
Expand Down
3 changes: 3 additions & 0 deletions django_napse/utils/errors/bots.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ class BotError:
class InvalidSetting(Exception):
"""Raised when a bot is invalid."""

class NoSpace(Exception):
"""Raised when a bot is in a fleet and therefore doesn't have a space."""


class BotConfigError:
"""Base class for bot config errors."""
Expand Down
2 changes: 1 addition & 1 deletion django_napse/utils/errors/transactions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
class TransactionError:
"""Base class for transaction errors."""

class DifferentSpaceError(Exception):
class DifferentAccountError(Exception):
"""Raised when the from and to wallets are on different spaces."""

class InvalidTransactionError(Exception):
Expand Down
2 changes: 1 addition & 1 deletion django_napse/utils/model_test_case.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ def test_info(self):
if self.skip_condition():
return
instance = self.simple_create()
instance.info(verbose=True)
instance.info(verbose=False)

def test_str(self):
if self.skip_condition():
Expand Down
113 changes: 82 additions & 31 deletions test/django_tests/transactions/test_transactions.py
Original file line number Diff line number Diff line change
@@ -1,38 +1,89 @@
from django_napse.core.models import Currency, Exchange, ExchangeAccount, NapseSpace, SpaceWallet, Transaction
from django_napse.core.models import BotConfig, Currency, Exchange, ExchangeAccount, Fleet, NapseSpace, Order, Transaction
from django_napse.utils.constants import TRANSACTION_TYPES
from django_napse.utils.errors import TransactionError
from django_napse.utils.model_test_case import ModelTestCase

"""
python test/test_app/manage.py test test.django_tests.transactions.test_transactions -v2 --keepdb --parallel
"""


# class TransactionTestCase(ModelTestCase):
# model = Transaction

# def setUp(self):
# self.exchange = Exchange.objects.create(
# name="random exchange",
# description="random description",
# )
# self.exchange_account = ExchangeAccount.objects.create(
# exchange=self.exchange,
# testing=True,
# name="random exchange account 1",
# description="random description",
# )
# self.space1 = NapseSpace.objects.create(
# name="Test Space 1",
# exchange_account=self.exchange_account,
# description="This is a test space",
# )
# self.space2 = NapseSpace.objects.create(
# name="Test Space 2",
# exchange_account=self.exchange_account,
# description="This is a test space",
# )
# self.from_wallet = SpaceWallet.objects.create(owner=self.space1, title="Test Wallet")
# Currency.objects.create(wallet=self.from_wallet, ticker="BTC", amount=1, mbp=20000)
# self.to_wallet = SpaceWallet.objects.create(owner=self.space2, title="Test Wallet")

# def simple_create(self):
# return Transaction.objects.create(from_wallet=self.from_wallet, to_wallet=self.to_wallet, amount=1, ticker="BTC")
class TransactionTestCase(ModelTestCase):
model = Transaction

def setUp(self):
self.exchange = Exchange.objects.create(
name="random exchange",
description="random description",
)
self.exchange_account = ExchangeAccount.objects.create(
exchange=self.exchange,
testing=True,
name="random exchange account 1",
description="random description",
)
self.space1 = NapseSpace.objects.create(
name="Test Space 1",
exchange_account=self.exchange_account,
description="This is a test space",
)

self.from_wallet = self.space1.wallet
Currency.objects.create(wallet=self.from_wallet, ticker="BTC", amount=1, mbp=20000)

config = BotConfig.objects.create(bot_type="Bot", name="test_bot", pair="MATICUSDT", interval="1m", space=self.space1)
fleet = Fleet.objects.create(name="test_fleet", configs=[config], exchange_account=self.exchange_account)
bot = fleet.bots.first()
order = Order.objects.create(bot=bot, buy_amount=100, sell_amount=100, price=1)
self.to_wallet = order.wallet

def simple_create(self):
return Transaction.objects.create(
from_wallet=self.from_wallet,
to_wallet=self.to_wallet,
amount=1,
ticker="BTC",
transaction_type=TRANSACTION_TYPES.TRANSFER,
)

def test_error_same_space(self):
exchange_account2 = ExchangeAccount.objects.create(
exchange=self.exchange,
testing=True,
name="random exchange account 2",
description="random description",
)
space2 = NapseSpace.objects.create(
name="Test Space 2",
exchange_account=exchange_account2,
description="This is a test space",
)
wallet = space2.wallet
with self.assertRaises(TransactionError.DifferentAccountError):
Transaction.objects.create(
from_wallet=self.from_wallet,
to_wallet=wallet,
amount=1,
ticker="BTC",
transaction_type=TRANSACTION_TYPES.TRANSFER,
)

def test_amount_zero(self):
transaction = Transaction.objects.create(
from_wallet=self.from_wallet,
to_wallet=self.to_wallet,
amount=0,
ticker="BTC",
transaction_type=TRANSACTION_TYPES.TRANSFER,
)
self.assertIsNone(transaction)

def test_error_transaction_type(self):
with self.assertRaises(TransactionError.InvalidTransactionError):
Transaction.objects.create(
from_wallet=self.from_wallet,
to_wallet=self.to_wallet,
amount=1,
ticker="BTC",
transaction_type="random transaction type",
)
4 changes: 2 additions & 2 deletions test/django_tests/wallets/test_currency.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from django.db.utils import IntegrityError

from django_napse.core.models import Currency, Exchange, ExchangeAccount, NapseSpace, SpaceWallet
from django_napse.core.models import Currency, Exchange, ExchangeAccount, NapseSpace
from django_napse.utils.model_test_case import ModelTestCase


Expand All @@ -19,7 +19,7 @@ def setUp(self) -> None:
description="random description",
)
self.space = NapseSpace.objects.create(name="Test Space", exchange_account=self.exchange_account, description="This is a test space")
self.wallet = SpaceWallet.objects.create(title="Test Wallet", owner=self.space)
self.wallet = self.space.wallet

def simple_create(self):
return Currency.objects.create(wallet=self.wallet, ticker="BTC", amount=1, mbp=20000)
Expand Down
33 changes: 15 additions & 18 deletions test/django_tests/wallets/test_wallet.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,9 +118,9 @@ def test_topup_negative(self):
wallet.top_up(ticker="BTC", amount=-2, mbp=1, force=True)

# Other
def test_space(self): # pragma: no cover
error_msg = "Please implement this test in the child class."
raise NotImplementedError(error_msg)
def test_exchange_account(self):
wallet = self.simple_create()
self.assertEqual(wallet.exchange_account, self.owner.exchange_account)

def test_has_funds(self):
wallet = self.simple_create()
Expand Down Expand Up @@ -167,12 +167,12 @@ def setUp(self):
exchange_account=self.exchange_account,
description="This is a test space",
)
self.owner.wallet.delete()

def test_with_real_exchange(self):
for account in BinanceAccount.objects.all():
space = account.spaces.first()
wallet = self.model.objects.create(title="Test Wallet", owner=space)
wallet.top_up(ticker="BTC", amount=0.5, force=True)
space.wallet.top_up(ticker="BTC", amount=0.5, force=True)

def test_space(self):
wallet = self.simple_create()
Expand All @@ -182,26 +182,23 @@ def test_space(self):
def test_value_market_BTC(self):
for account in BinanceAccount.objects.all():
space = account.spaces.first()
wallet = self.model.objects.create(title="Test Wallet", owner=space)
Currency.objects.create(wallet=wallet, ticker="BTC", amount=1, mbp=20000)
self.assertTrue(wallet.value_market() > 0)
Currency.objects.create(wallet=space.wallet, ticker="BTC", amount=1, mbp=20000)
self.assertTrue(space.wallet.value_market() > 0)

@skipIf(napse_settings.IS_IN_PIPELINE, "IP will be refused")
def test_value_market_USDT(self):
for account in BinanceAccount.objects.all():
space = account.spaces.first()
wallet = self.model.objects.create(title="Test Wallet", owner=space)
Currency.objects.create(wallet=wallet, ticker="USDT", amount=1, mbp=1)
self.assertEqual(wallet.value_market(), 1)
Currency.objects.create(wallet=space.wallet, ticker="USDT", amount=1, mbp=1)
self.assertEqual(space.wallet.value_market(), 1)

@skipIf(napse_settings.IS_IN_PIPELINE, "IP will be refused")
def test_value_zero(self):
for account in BinanceAccount.objects.all():
space = account.spaces.first()
wallet = self.model.objects.create(title="Test Wallet", owner=space)
Currency.objects.create(wallet=wallet, ticker="BTC", amount=0, mbp=1)
Currency.objects.create(wallet=wallet, ticker="USDT", amount=0, mbp=1)
self.assertEqual(wallet.value_market(), 0)
Currency.objects.create(wallet=space.wallet, ticker="BTC", amount=0, mbp=1)
Currency.objects.create(wallet=space.wallet, ticker="USDT", amount=0, mbp=1)
self.assertEqual(space.wallet.value_market(), 0)


class OrderWalletTestCase(BaseWalletTestCase, TestCase):
Expand All @@ -216,9 +213,9 @@ def setUp(self) -> None:
self.owner = Order.objects.create(bot=bot, buy_amount=100, sell_amount=100, price=1)
self.owner.wallet.delete()

def test_space(self):
wallet = self.simple_create()
self.assertEqual(wallet.space, self.owner.space)
# def test_space(self):
# wallet = self.simple_create()
# self.assertEqual(wallet.space, self.owner.space)


# class ConnectionWalletTestCase(BaseWalletTestCase, TestCase):
Expand Down

0 comments on commit ad3d19f

Please sign in to comment.