2022-07-15 10:09:54 +00:00
|
|
|
import logging
|
|
|
|
from copy import deepcopy
|
2022-02-24 21:32:32 +00:00
|
|
|
from unittest import TestCase
|
|
|
|
from unittest.mock import MagicMock
|
|
|
|
|
2022-05-05 17:16:56 +00:00
|
|
|
import lib.antifraud
|
2022-07-15 10:09:54 +00:00
|
|
|
import lib.money
|
|
|
|
import lib.transactions
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
|
|
|
|
class TestTransactions(TestCase):
|
|
|
|
def setUp(self):
|
2022-05-24 07:38:37 +00:00
|
|
|
logging.disable(logging.CRITICAL)
|
2022-05-05 17:16:56 +00:00
|
|
|
self.transactions = lib.transactions.Transactions()
|
2022-02-24 21:32:32 +00:00
|
|
|
self.test_data = {
|
2022-03-14 20:55:40 +00:00
|
|
|
"timestamp": "2022-03-14T19:34:13.501Z",
|
|
|
|
"description": "Received Rebiere Matthieu",
|
|
|
|
"transaction_type": "CREDIT",
|
|
|
|
"transaction_category": "CREDIT",
|
|
|
|
"transaction_classification": [],
|
|
|
|
"amount": 1,
|
|
|
|
"currency": "GBP",
|
|
|
|
"transaction_id": "ec4df5248c750c30301a1da71024ac0b",
|
|
|
|
"provider_transaction_id": "27373011.TU9ORVRBUllfQUNUSVZJVFk6OjI1MDE4MDQyOjpUUkFOU0ZFUjo6MzgwMDM2NDY2",
|
|
|
|
"normalised_provider_transaction_id": "txn-c8c12c308789bd980",
|
|
|
|
"meta": {
|
|
|
|
"provider_reference": "TEST-1",
|
|
|
|
"transaction_type": "Credit",
|
|
|
|
"provider_id": "27373011.TU9ORVRBUllfQUNUSVZJVFk6OjI1MDE4MDQyOjpUUkFOU0ZFUjo6MzgwMDM2NDY2",
|
|
|
|
"counter_party_preferred_name": "Rebiere Matthieu",
|
2022-02-24 21:32:32 +00:00
|
|
|
},
|
2022-04-04 16:23:09 +00:00
|
|
|
"subclass": "truelayer",
|
2022-02-24 21:32:32 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
# Mock redis calls
|
2022-05-05 17:16:56 +00:00
|
|
|
lib.transactions.db.r.hgetall = self.mock_hgetall
|
|
|
|
lib.transactions.db.r.hmset = self.mock_hmset
|
|
|
|
lib.transactions.db.r.keys = self.mock_keys
|
|
|
|
lib.transactions.db.r.get = self.mock_get
|
2022-02-24 21:32:32 +00:00
|
|
|
|
2022-02-24 22:27:04 +00:00
|
|
|
# Mock some callbacks
|
2022-02-24 21:32:32 +00:00
|
|
|
self.transactions.irc = MagicMock()
|
|
|
|
self.transactions.irc.sendmsg = MagicMock()
|
|
|
|
self.transactions.release_funds = MagicMock()
|
2022-03-04 22:47:06 +00:00
|
|
|
self.transactions.ux = MagicMock()
|
|
|
|
self.transactions.ux.notify = MagicMock()
|
|
|
|
self.transactions.ux.notify.notify_complete_trade = MagicMock()
|
2022-05-24 07:38:37 +00:00
|
|
|
self.transactions.antifraud = lib.antifraud.AntiFraud()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
2022-02-24 22:27:04 +00:00
|
|
|
# Mock the rates
|
|
|
|
self.transactions.money = MagicMock()
|
2022-04-10 14:54:59 +00:00
|
|
|
self.transactions.money.to_usd = self.mock_to_usd
|
2022-02-24 22:27:04 +00:00
|
|
|
self.transactions.money.get_rates_all = MagicMock()
|
|
|
|
self.transactions.money.get_rates_all.return_value = {"GBP": 0.8}
|
|
|
|
|
2022-02-28 20:22:11 +00:00
|
|
|
# Don't mock the functions we want to test
|
2022-05-05 17:16:56 +00:00
|
|
|
self.money = lib.money.Money()
|
2022-02-28 20:22:11 +00:00
|
|
|
self.money.get_rates_all = MagicMock()
|
|
|
|
self.money.get_rates_all.return_value = {"GBP": 0.8}
|
2022-07-15 10:09:54 +00:00
|
|
|
self.transactions.money.get_acceptable_margins = (
|
|
|
|
self.money.get_acceptable_margins
|
|
|
|
)
|
2022-02-28 20:22:11 +00:00
|
|
|
|
2022-02-24 22:27:04 +00:00
|
|
|
self.trades = {
|
|
|
|
1: {
|
|
|
|
"id": "uuid1",
|
|
|
|
"buyer": "test_buyer_1",
|
|
|
|
"currency": "GBP",
|
|
|
|
"amount": "1",
|
|
|
|
"amount_xmr": "0.3",
|
|
|
|
"reference": "TEST-1",
|
2022-04-13 17:29:17 +00:00
|
|
|
"subclass": "agora",
|
2022-02-24 22:27:04 +00:00
|
|
|
},
|
|
|
|
2: {
|
|
|
|
"id": "uuid2",
|
|
|
|
"buyer": "test_buyer_2",
|
|
|
|
"currency": "GBP",
|
|
|
|
"amount": "1",
|
|
|
|
"amount_xmr": "0.3",
|
|
|
|
"reference": "TEST-2",
|
2022-04-13 17:29:17 +00:00
|
|
|
"subclass": "agora",
|
2022-02-24 22:27:04 +00:00
|
|
|
},
|
|
|
|
3: {
|
|
|
|
"id": "uuid3",
|
|
|
|
"buyer": "test_buyer_3",
|
|
|
|
"currency": "GBP",
|
|
|
|
"amount": "1000",
|
|
|
|
"amount_xmr": "3",
|
|
|
|
"reference": "TEST-3",
|
2022-04-13 17:29:17 +00:00
|
|
|
"subclass": "agora",
|
2022-02-24 22:27:04 +00:00
|
|
|
},
|
|
|
|
4: {
|
|
|
|
"id": "uuid4",
|
|
|
|
"buyer": "test_buyer_4",
|
|
|
|
"currency": "GBP",
|
|
|
|
"amount": "10",
|
|
|
|
"amount_xmr": "0.5",
|
|
|
|
"reference": "TEST-4",
|
2022-04-13 17:29:17 +00:00
|
|
|
"subclass": "agora",
|
2022-02-24 22:27:04 +00:00
|
|
|
},
|
2022-02-24 21:32:32 +00:00
|
|
|
}
|
|
|
|
|
2022-02-24 22:27:04 +00:00
|
|
|
self.return_trades = [1, 2, 3]
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
@property
|
|
|
|
def test_data_copy(self):
|
|
|
|
return deepcopy(self.test_data)
|
|
|
|
|
2022-02-24 22:27:04 +00:00
|
|
|
def data_custom(self, amount, currency, reference):
|
|
|
|
test_data = self.test_data_copy
|
2022-03-14 20:55:40 +00:00
|
|
|
test_data["meta"]["provider_reference"] = reference
|
|
|
|
test_data["amount"] = amount
|
|
|
|
test_data["currency"] = currency
|
2022-02-24 22:27:04 +00:00
|
|
|
return test_data
|
|
|
|
|
2022-02-24 21:32:32 +00:00
|
|
|
def mock_hgetall(self, string):
|
2022-02-24 22:27:04 +00:00
|
|
|
ref = string.split(".")[1]
|
|
|
|
for num, trade in self.trades.items():
|
|
|
|
if trade["reference"] == ref:
|
|
|
|
return trade
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def mock_hmset(self, string, data):
|
2022-03-04 22:17:17 +00:00
|
|
|
pass
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def mock_keys(self, string):
|
2022-02-24 22:27:04 +00:00
|
|
|
return [v["id"] for k, v in self.trades.items() if k in self.return_trades]
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def mock_get(self, string):
|
2022-02-24 22:27:04 +00:00
|
|
|
for num, trade in self.trades.items():
|
|
|
|
if trade["id"] == string:
|
|
|
|
return trade["reference"]
|
2022-02-24 21:32:32 +00:00
|
|
|
|
2022-04-10 14:54:59 +00:00
|
|
|
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
|
|
|
|
|
2022-02-24 21:32:32 +00:00
|
|
|
def test_transaction(self):
|
|
|
|
self.transactions.transaction(self.test_data)
|
|
|
|
self.transactions.release_funds.assert_called_once_with("uuid1", "TEST-1")
|
|
|
|
|
|
|
|
self.transactions.release_funds = MagicMock()
|
|
|
|
ref_2 = self.test_data_copy
|
2022-03-14 20:55:40 +00:00
|
|
|
ref_2["meta"]["provider_reference"] = "TEST-2"
|
2022-02-24 21:32:32 +00:00
|
|
|
self.transactions.transaction(ref_2)
|
|
|
|
self.transactions.release_funds.assert_called_once_with("uuid2", "TEST-2")
|
|
|
|
|
|
|
|
def test_transaction_invalid(self):
|
|
|
|
invalid_data = self.test_data_copy
|
2022-02-24 22:27:04 +00:00
|
|
|
invalid_data = self.data_custom(2000, "SEK", "sss")
|
2022-02-24 21:32:32 +00:00
|
|
|
self.transactions.transaction(invalid_data)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
|
|
|
|
|
|
|
def test_transaction_malformed(self):
|
2022-02-24 22:27:04 +00:00
|
|
|
malformed_data = self.test_data_copy
|
2022-03-14 20:55:40 +00:00
|
|
|
del malformed_data["amount"]
|
2022-02-24 22:27:04 +00:00
|
|
|
self.transactions.transaction(malformed_data)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
|
|
|
|
|
|
|
malformed_data = self.test_data_copy
|
2022-03-14 20:55:40 +00:00
|
|
|
del malformed_data["currency"]
|
2022-02-24 22:27:04 +00:00
|
|
|
self.transactions.transaction(malformed_data)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def test_transaction_no_reference_fail(self):
|
2022-02-24 22:27:04 +00:00
|
|
|
no_reference_fail = self.data_custom(1, "GBP", "none")
|
2022-03-14 20:55:40 +00:00
|
|
|
no_reference_fail["meta"]["provider_reference"] = "none"
|
2022-02-24 22:27:04 +00:00
|
|
|
self.transactions.transaction(no_reference_fail)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def test_transaction_no_reference_pass(self):
|
2022-02-24 22:27:04 +00:00
|
|
|
no_reference_pass = self.data_custom(1, "GBP", "none")
|
2022-04-10 14:54:59 +00:00
|
|
|
no_reference_pass["meta"]["provider_reference"] = "THIS_ONE_FAILS"
|
2022-02-24 22:27:04 +00:00
|
|
|
self.return_trades = [1]
|
|
|
|
self.transactions.transaction(no_reference_pass)
|
|
|
|
self.transactions.release_funds.assert_called_with("uuid1", "TEST-1")
|
|
|
|
|
|
|
|
def test_transaction_large(self):
|
|
|
|
exceeds_max = self.data_custom(1000, "GBP", "TEST-3")
|
|
|
|
self.transactions.transaction(exceeds_max)
|
|
|
|
self.transactions.release_funds.assert_called_once_with("uuid3", "TEST-3")
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def test_transaction_no_reference_exceeds_max(self):
|
2022-02-24 22:27:04 +00:00
|
|
|
exceeds_max = self.data_custom(1000, "GBP", "noref")
|
|
|
|
self.transactions.transaction(exceeds_max)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def test_transaction_wrong_currency(self):
|
2022-02-24 22:27:04 +00:00
|
|
|
wrong_currency = self.data_custom(1, "EUR", "TEST-1")
|
|
|
|
self.transactions.transaction(wrong_currency)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
|
|
|
|
|
|
|
wrong_currency = self.data_custom(1, "EUR", "none")
|
|
|
|
self.transactions.transaction(wrong_currency)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def test_transaction_wrong_amount(self):
|
2022-02-24 22:27:04 +00:00
|
|
|
self.transactions.money.get_acceptable_margins = MagicMock()
|
|
|
|
self.transactions.money.get_acceptable_margins.return_value = (0.8, 1.8)
|
|
|
|
wrong_amount = self.data_custom(10, "GBP", "TEST-1")
|
|
|
|
self.transactions.transaction(wrong_amount)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
|
|
|
|
|
|
|
wrong_amount = self.data_custom(10, "GBP", "none")
|
|
|
|
self.transactions.transaction(wrong_amount)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
2022-03-14 20:55:40 +00:00
|
|
|
# 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()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def test_transaction_too_low(self):
|
2022-02-24 22:27:04 +00:00
|
|
|
too_low = self.data_custom(5, "GBP", "TEST-1")
|
|
|
|
self.transactions.transaction(too_low)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
def test_transaction_too_high(self):
|
2022-02-24 22:27:04 +00:00
|
|
|
too_high = self.data_custom(15, "GBP", "TEST-1")
|
|
|
|
self.transactions.transaction(too_high)
|
|
|
|
self.transactions.release_funds.assert_not_called()
|
2022-02-24 21:32:32 +00:00
|
|
|
|
|
|
|
# def test_transaction_pending_then_completed(self):
|
|
|
|
# pass
|
|
|
|
|
|
|
|
def test_transaction_store_incomplete_trade(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_transaction_release_incomplete_trade(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_transaction_card_payment(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_transaction_negative_amount(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_release_funds(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_new_trade(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_find_trade(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_get_refs(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_get_ref_map(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_get_ref(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_del_ref(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_cleanup(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_tx_to_ref(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_ref_to_tx(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_get_total_usd(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_get_total(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_write_to_es(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_get_remaining(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_get_open_trades_usd(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def test_get_total_remaining(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def get_total_with_trades(self):
|
|
|
|
pass
|