import logging from unittest.mock import patch from django.test import TransactionTestCase from core.clients.aggregator import AggregatorClient from core.models import Aggregator, Platform, Trade, Transaction, User class TestTransactions(TransactionTestCase): def setUp(self): logging.disable(logging.CRITICAL) self.user = User.objects.create_user( username="testuser", email="test@example.com", password="test" ) self.aggregator = Aggregator.objects.create( user=self.user, name="Test", service="nordigen", secret_id="a", secret_key="a", ) self.agg_client = AggregatorClient() self.agg_client.instance = self.aggregator self.platform = Platform.objects.create( user=self.user, name="Test", service="agora", token="a", password="a", otp_token="a", username="myuser", ) self.transaction = Transaction.objects.create( aggregator=self.aggregator, account_id="my account id", transaction_id="BANKTX", amount=1, currency="GBP", note="TEST-1", ) self.trades = { 1: { "contact_id": "uuid1", "buyer": "test_buyer_1", "currency": "GBP", "asset": "XMR", "amount_fiat": 1, "amount_crypto": 0.3, "reference": "TEST-1", "provider": "REVOLUT", }, 2: { "contact_id": "uuid2", "buyer": "test_buyer_2", "currency": "GBP", "asset": "XMR", "amount_fiat": 1, "amount_crypto": 0.3, "reference": "TEST-2", "provider": "REVOLUT", }, 3: { "contact_id": "uuid3", "buyer": "test_buyer_3", "currency": "GBP", "asset": "XMR", "amount_fiat": 1000, "amount_crypto": 3, "reference": "TEST-3", "provider": "REVOLUT", }, 4: { "contact_id": "uuid4", "buyer": "test_buyer_4", "currency": "GBP", "asset": "XMR", "amount_fiat": 10, "amount_crypto": 0.5, "reference": "TEST-4", "provider": "REVOLUT", }, 5: { # to conflict with 1 "contact_id": "uuid1", "buyer": "test_buyer_2", "currency": "GBP", "asset": "XMR", "amount_fiat": 1, "amount_crypto": 0.3, "reference": "TEST-1", "provider": "REVOLUT", }, } def create_trades(self, *numbers): for trade_key in self.trades.keys(): if trade_key in numbers: Trade.objects.create( platform=self.platform, # open=True, **self.trades[trade_key], ) def mock_to_usd(self, amount, currency): if currency == "GBP": return amount * 1.3 elif currency == "USD": return amount # fuck it who cares elif currency == "SEK": return 100 elif currency == "EUR": return 10 @patch("core.lib.notify.sendmsg") async def test_reference_partial_check(self, _): self.create_trades(1, 2, 3, 4) result = await self.agg_client.reference_partial_check( self.platform, "TEST-1", "for notifications only", "GBP", 1, ) self.assertEqual(result, "TEST-1") @patch("core.lib.notify.sendmsg") async def test_reference_partial_check_subset(self, _): self.create_trades(1, 2, 3, 4) result = await self.agg_client.reference_partial_check( self.platform, "the TEST-1 in string", "for notifications only", "GBP", 1, ) self.assertEqual(result, "TEST-1") @patch("core.lib.notify.sendmsg") async def test_reference_partial_check_multiple_match(self, _): self.create_trades(1, 2, 3, 4, 5) result = await self.agg_client.reference_partial_check( self.platform, "TEST-1", "for notifications only", "GBP", 1, ) self.assertEqual(result, False) @patch("core.lib.notify.sendmsg") async def test_reference_partial_check_none(self, _): # self.create_trades(1, 2, 3, 4, 5) result = await self.agg_client.reference_partial_check( self.platform, "TEST-1", "for notifications only", "GBP", 1, ) self.assertEqual(result, None) @patch("core.lib.notify.sendmsg") async def test_reference_partial_check_none_match(self, _): self.create_trades(1, 2, 3, 4, 5) result = await self.agg_client.reference_partial_check( self.platform, "NOWHERE", "for notifications only", "GBP", 1, ) self.assertEqual(result, None) def test_valid_transaction(self): result = self.agg_client.valid_transaction(self.transaction) self.assertEqual(result, True) def test_valid_transaction_fail(self): self.transaction.amount = -100 result = self.agg_client.valid_transaction(self.transaction) self.assertEqual(result, False) self.transaction.amount = 1 @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) async def test_can_alt_lookup(self, *args): result = await self.agg_client.can_alt_lookup( self.platform, 999999999, # ignored "GBP", "IGNORED", ) self.assertEqual(result, True) @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=500) async def test_can_alt_lookup_fail(self, *args): result = await self.agg_client.can_alt_lookup( self.platform, 999999999, # ignored "GBP", "IGNORED", ) self.assertEqual(result, False) def test_find_trade(self): self.create_trades(1, 3, 4) result = self.agg_client.find_trade( self.platform, "BANKTX", "GBP", 1, ) self.assertEqual(result.reference, "TEST-1") self.assertEqual(result.currency, "GBP") self.assertEqual(result.amount_fiat, 1) def test_find_trade_fail_two_match(self): self.create_trades(1, 2, 3, 4) # 2 trades with same amount and currency result = self.agg_client.find_trade( self.platform, "BANKTX", "GBP", 1, ) self.assertEqual(result, False) def test_find_trade_fail_two_match_alt(self): self.create_trades(1, 3, 4) result = self.agg_client.find_trade( self.platform, "BANKTX", "GBP", 88, ) self.assertEqual(result, False) @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) async def test_amount_currency_lookup(self, *args): self.create_trades(1, 3, 4) result = await self.agg_client.amount_currency_lookup( self.platform, 1, "GBP", "BANKTX", "TEST-1", ) self.assertEqual(result.reference, "TEST-1") @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) async def test_amount_currency_lookup_fail(self, *args): self.create_trades(1, 3, 4) result = await self.agg_client.amount_currency_lookup( self.platform, 88, "GBP", "BANKTX", "TEST-1", ) self.assertEqual(result, None) @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=500) async def test_amount_currency_lookup_fail_too_high(self, *args): self.create_trades(1, 3, 4) result = await self.agg_client.amount_currency_lookup( self.platform, 1, "GBP", "BANKTX", "TEST-1", ) self.assertEqual(result, False) @patch("core.lib.notify.sendmsg") async def test_normal_lookup(self, _): self.create_trades(1, 2, 3, 4) result = await self.agg_client.normal_lookup( self.platform, "TEST-1", "BANKTX", "GBP", 1, ) self.assertEqual(result.reference, "TEST-1") self.assertEqual(result.currency, "GBP") self.assertEqual(result.amount_fiat, 1) @patch("core.lib.notify.sendmsg") async def test_currency_check(self, _): self.create_trades(1) trade = Trade.objects.all().first() result = await self.agg_client.currency_check( "GBP", trade, ) self.assertEqual(result, True) @patch("core.lib.notify.sendmsg") async def test_currency_check_fail(self, _): self.create_trades(1) trade = Trade.objects.all().first() result = await self.agg_client.currency_check( "AYZ", trade, ) self.assertEqual(result, False) @patch("core.lib.notify.sendmsg") @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1.5) ) async def test_alt_amount_check(self, *args): self.create_trades(1) trade = Trade.objects.all().first() result = await self.agg_client.alt_amount_check( self.platform, 1.123, "GBP", trade, ) self.assertEqual(result, True) @patch("core.lib.notify.sendmsg") @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1.5) ) async def test_alt_amount_check_fail(self, *args): self.create_trades(1) trade = Trade.objects.all().first() result = await self.agg_client.alt_amount_check( self.platform, 1.501, "GBP", trade, ) self.assertEqual(result, False) @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=1) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1.5) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) await self.agg_client.transaction([self.platform], self.transaction) release.assert_called_once_with("uuid1", "TEST-1") self.assertEqual(self.transaction.reconciled, True) @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=1) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1.5) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_second(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) self.transaction.note = "TEST-2" await self.agg_client.transaction([self.platform], self.transaction) release.assert_called_once_with("uuid2", "TEST-2") self.assertEqual(self.transaction.reconciled, True) self.transaction.note = "TEST-1" @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=1) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1.5) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_invalid(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) self.transaction.amount = -1 await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.amount = 1 # def test_transaction_malformed(self): # malformed_data = self.test_data_copy # del malformed_data["amount"] # self.transactions.transaction(malformed_data) # self.transactions.release_funds.assert_not_called() # malformed_data = self.test_data_copy # del malformed_data["currency"] # self.transactions.transaction(malformed_data) # self.transactions.release_funds.assert_not_called() @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=1) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1.5) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_no_reference_fail(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) self.transaction.note = "none" await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.note = "TEST-1" @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=1) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1.5) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_no_reference_pass(self, release, gam, to_usd, sendmsg): self.create_trades(1) self.transaction.note = "none" await self.agg_client.transaction([self.platform], self.transaction) release.assert_called_with("uuid1", "TEST-1") self.assertEqual(self.transaction.reconciled, True) self.transaction.note = "TEST-1" @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=1000) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_large(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) self.transaction.amount = 1000 self.transaction.note = "TEST-3" await self.agg_client.transaction([self.platform], self.transaction) release.assert_called_once_with("uuid3", "TEST-3") self.assertEqual(self.transaction.reconciled, True) self.transaction_amount_fiat = 1 self.transaction.note = "TEST-1" @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=1000) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_no_reference_exceeds_max( self, release, gam, to_usd, sendmsg ): self.create_trades(1, 2, 3, 4) self.transaction.amount = 1000 self.transaction.note = "noref" await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.amount = 1 self.transaction.note = "TEST-1" @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_wrong_currency(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) self.transaction.currency = "EUR" await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.currency = "GBP" @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_wrong_currency_noref( self, release, gam, to_usd, sendmsg ): self.transaction.currency = "EUR" self.transaction.note = "none" await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.currency = "GBP" self.transaction.note = "TEST-1" @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.8, 1.8) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_wrong_amount(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) self.transaction.amount = 10 await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.amount = 1 @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.8, 1.8) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_wrong_amount_noref(self, release, gam, to_usd, sendmsg): self.transaction.amount = 10 self.transaction.note = "none" await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.amount = 1 self.transaction.note = "TEST-1" # def test_transaction_pending(self): # pending_tx = self.test_data_copy # pending_tx["data"]["state"] = "pending" # self.transactions.transaction(pending_tx) # self.transactions.release_funds.assert_not_called() @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_too_low(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) self.transaction.amount = 5 await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.amount = 1 @patch("core.lib.notify.sendmsg") @patch("core.clients.aggregator.money.to_usd", return_value=100) @patch( "core.clients.aggregator.money.get_acceptable_margins", return_value=(0.5, 1) ) @patch("core.clients.aggregator.AgoraClient.release_trade_escrow") async def test_transaction_too_high(self, release, gam, to_usd, sendmsg): self.create_trades(1, 2, 3, 4) self.transaction.amount = 15 await self.agg_client.transaction([self.platform], self.transaction) release.assert_not_called() self.assertEqual(self.transaction.reconciled, False) self.transaction.amount = 1