"""Unit tests for Bulk Ledgers Logic.""" from decimal import Decimal from unittest.mock import MagicMock, patch from ledger.logic import bulk_ledgers as logic from ledger.models.ledger_account import LedgerAccount from ledger.models.ledger_account_contract import LedgerAccountContract from ledger.models.ledger_deposit import LedgerDeposit from tests.utils.factories import LedgerAccountContractFactory, LedgerAccountFactory def test_calculate_account_contract_balances_first_time(): """Test previous balance is set to 0 when calculating first time.""" result = logic.calculate_account_contract_balances(Decimal('1.33'), 3) assert result == { 'previous_balance': Decimal('0'), 'current_balance': Decimal('1.33'), } def test_calculate_account_contract_balances_empty_and_negative(): """Test negative current balance can be set when calculating first time.""" result = logic.calculate_account_contract_balances(Decimal('-1.33'), 3) assert result == { 'previous_balance': Decimal('0'), 'current_balance': Decimal('-1.33'), } def test_calculate_account_contract_balance(mock_event_fixtures): """Test new current balance is calculated on existing contract balance.""" LedgerAccountContractFactory.create(current_balance=492.11) result = logic.calculate_account_contract_balances(Decimal('132.98'), 1) assert result == { 'previous_balance': Decimal('492.11'), 'current_balance': Decimal('625.09'), } def test_calculate_account_contract_balance_negative(mock_event_fixtures): """Test negative balance is calculated on existing contract balance.""" LedgerAccountContractFactory.create(current_balance=492.11) result = logic.calculate_account_contract_balances(Decimal('-532.98'), 1) assert result == { 'previous_balance': Decimal('492.11'), 'current_balance': Decimal('-40.87'), } def test_get_contracts_balance(mock_event_fixtures): """Test get_contracts_balance.""" test_item = LedgerAccountContractFactory.create(current_balance=492.11) result = logic.get_contracts_balance( contract_ids=[test_item.contract_id], account_ids=[test_item.account_id] ) assert result.status_code == 200 assert result.json == [ { 'ledger_account_contract_id': test_item.ledger_account_contract_id, 'account_id': test_item.account_id, 'contract_id': test_item.contract_id, 'currency_code': test_item.currency_code, 'current_balance': str(test_item.current_balance), } ] @patch('ledger.logic.bulk_ledgers.LedgerAccount') def test_get_ledger_current_balances_by_accounts(mock_model, mock_event_fixtures): """Test get current balances for given account_ids.""" mock_account_id = 1 mock_account_no_ledger = 2 mock_response = LedgerAccountFactory( account_id=mock_account_id, current_balance='100.00' ) mock_model.get_ledger_account_balances.return_value = MagicMock( all=MagicMock(return_value=[mock_response]) ) result = logic.get_ledger_current_balances_by_accounts( account_ids=[mock_account_id, mock_account_no_ledger] ) assert result == {mock_account_id: Decimal(mock_response.current_balance)} @patch('ledger.logic.bulk_ledgers.LedgerAccountContract') def test_get_ledger_current_balances_by_contracts(mock_model, mock_event_fixtures): """Test get current balances by given contract_ids.""" mock_contract_id = 1 mock_contract_no_ledger_entry = 2 mock_response = LedgerAccountContractFactory( contract_id=mock_contract_id, current_balance='150.00' ) mock_all = MagicMock(all=MagicMock(return_value=[mock_response])) mock_model.get_ledger_account_contract_balance_by_contracts.return_value = mock_all result = logic.get_ledger_current_balances_by_contracts( contract_ids=[mock_contract_id, mock_contract_no_ledger_entry] ) assert result == {mock_contract_id: Decimal(mock_response.current_balance)} @patch('ledger.logic.bulk_ledgers.get_ledger_current_balances_by_contracts') @patch('ledger.logic.bulk_ledgers.get_ledger_current_balances_by_accounts') def test_build_account_ledger_entries( mock_account_balances, mock_contract_balances, mock_event_fixtures ): """Test successfully builds ledger_account and ledger_account_contract entries.""" mock_account_id = 1 mock_contract_id = 2 mock_request = { 'account_id': mock_account_id, 'contract_id': mock_contract_id, 'abacus_event_id': 1, 'currency_amount': '250.0', 'currency_code': 'USD', } mock_account_balances.return_value = {} mock_contract_balances.return_value = {mock_contract_id: Decimal('100.00')} account_response, contract_response = logic.build_account_ledger_entries( [mock_request] ) assert account_response[0].account_id == mock_account_id assert account_response[0].contract_id == mock_contract_id assert account_response[0].currency_amount == mock_request['currency_amount'] assert account_response[0].current_balance == Decimal( mock_request['currency_amount'] ) assert account_response[0].previous_balance == Decimal('0.0') assert contract_response[0].account_id == mock_account_id assert contract_response[0].contract_id == mock_contract_id assert contract_response[0].currency_amount == mock_request['currency_amount'] assert contract_response[0].current_balance == Decimal('350.0') assert contract_response[0].previous_balance == Decimal('100.0') @patch('ledger.logic.bulk_ledgers.get_ledger_current_balances_by_contracts') @patch('ledger.logic.bulk_ledgers.get_ledger_current_balances_by_accounts') def test_build_account_ledger_entries_same_account( mock_account_balances, mock_contract_balances, mock_event_fixtures ): """Test current/prev balance calculated correctly for same account.""" mock_account_id = 1 mock_request = [ { 'account_id': mock_account_id, 'contract_id': 1, 'abacus_event_id': 1, 'currency_amount': '250.0', 'currency_code': 'USD', }, { 'account_id': mock_account_id, 'contract_id': 2, 'abacus_event_id': 1, 'currency_amount': '300.0', 'currency_code': 'USD', }, ] mock_account_balances.return_value = {mock_account_id: Decimal('100.0')} mock_contract_balances.return_value = {} account_response, __ = logic.build_account_ledger_entries(mock_request) len(account_response) == 2 account_response[0].currency_amount == Decimal(250.0) account_response[0].current_balance == Decimal(350.0) account_response[0].previous_balance == Decimal(100.0) account_response[1].currency_amount == Decimal(300.0) account_response[1].current_balance == Decimal(650.0) account_response[1].previous_balance == Decimal(350.0) def test_validate_and_group_ledger_entries(): """Test grouping deposit and account ledger request types.""" account_request = { 'account_id': 1, 'contract_id': 1, 'abacus_event_id': 1, 'currency_amount': '200.0', 'currency_code': 'USD', 'model_type': 'account', } deposit_request = { 'account_id': 2, 'contract_id': 2, 'abacus_event_id': 1, 'remaining_amount': '0.0001442850416000000', 'rounded_amount': '100.90', 'currency_code': 'USD', 'model_type': 'deposit', } mock_request = [account_request, deposit_request] account_response, deposit_response = logic.validate_and_group_ledger_entries( mock_request ) account_request.pop('model_type') deposit_request.pop('model_type') assert account_response == [account_request] assert deposit_response == [deposit_request] @patch('ledger.logic.bulk_ledgers.validate_request') @patch('ledger.logic.bulk_ledgers.build_account_ledger_entries') @patch('ledger.logic.bulk_ledgers.db') def test_handle_bulk_insert( mock_db, mock_build_account_ledger_entries, mock_validate_request, mock_event_fixtures, ): """Test handle bulk insert.""" account_request = { 'account_id': 1, 'contract_id': 1, 'abacus_event_id': 1, 'currency_amount': '200.0', 'currency_code': 'USD', 'model_type': 'account', } deposit_request = { 'account_id': 2, 'contract_id': 2, 'abacus_event_id': 1, 'remaining_amount': '0.0001442850416000000', 'rounded_amount': '100.90', 'currency_code': 'USD', 'model_type': 'deposit', } mock_request = [account_request, deposit_request] mock_ledger_account_dict = { 'account_id': 1, 'contract_id': 1, 'abacus_event_id': 1, 'currency_amount': '200.0', 'currency_code': 'USD', 'current_balance': '200.0', 'previous_balance': '0.0', } mock_ledger_deposit_dict = { 'account_id': 2, 'contract_id': 2, 'abacus_event_id': 1, 'remaining_amount': '0.0001442850416000000', 'rounded_amount': '100.90', 'currency_code': 'USD', } mock_ledger_acc = [LedgerAccount(**mock_ledger_account_dict)] mock_ledger_acc_contract = [LedgerAccountContract(**mock_ledger_account_dict)] mock_validate_request.return_value = True mock_build_account_ledger_entries.return_value = ( mock_ledger_acc, mock_ledger_acc_contract, ) mock_db.session.commit.return_value = None mock_db.session.bulk_save_objects.return_value = None logic.handle_bulk_insert(mock_request) mock_db.session.commit.assert_called_once() mock_db.session.bulk_save_objects.call_args_list == [ mock_ledger_acc, mock_ledger_acc_contract, [LedgerDeposit(**mock_ledger_deposit_dict)], ]