"""Unit testcases for ledger_contract_flowthrough logic.""" from decimal import Decimal from unittest.mock import MagicMock, patch import pytest from sqlalchemy import exc from werkzeug.exceptions import HTTPException from ledger.logic import ledger_contract_flowthrough as logic from ledger.models.ledger_contract_flowthrough import LedgerContractFlowthrough from ledger.models.ledger_deposit import LedgerDeposit from tests.utils.factories import LedgerContractFlowthroughFactory @patch('ledger.logic.ledger_contract_flowthrough.LedgerContractFlowthrough') def test_get_ledger_flowthrough_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 = LedgerContractFlowthroughFactory.create( contract_id=mock_contract_id, current_balance='150.00' ) mock_all = MagicMock(all=MagicMock(return_value=[mock_response])) mock_model.get_ledger_contract_flowthrough_balance_by_contracts.return_value = ( mock_all ) result = logic.get_ledger_flowthrough_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.ledger_contract_flowthrough.get_ledger_flowthrough_current_balances_by_contracts' ) def test_build_contract_flowthrough_ledger_entries( mock_contract_balances, mock_event_fixtures ): """Test builds ledger_contract_flowthrough 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_contract_balances.return_value = {mock_contract_id: Decimal('100.00')} contract_response = logic.build_contract_flowthrough_ledger_entries([mock_request]) 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') def test_validate_and_group_ledger_entries(): """Test grouping deposit and cpmtract_flowthrough ledger request types.""" mock_contract_flowthrough_request = { 'account_id': 1, 'contract_id': 1, 'abacus_event_id': 1, 'currency_amount': '200.0', 'currency_code': 'USD', 'model_type': 'contract', } mock_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 = [mock_contract_flowthrough_request, mock_deposit_request] account_response, deposit_response = logic.validate_and_group_ledger_entries( mock_request ) mock_contract_flowthrough_request.pop('model_type') mock_deposit_request.pop('model_type') assert account_response == [mock_contract_flowthrough_request] assert deposit_response == [mock_deposit_request] def test_build_ledger_contract_flowthrough_entry(): """Test build ledger_contract_flowthrough entry.""" mock_ledger_contract_flowthrough = { 'account_id': 1, 'contract_id': 1, 'abacus_event_id': 1, 'currency_amount': '200.0', 'currency_code': 'USD', } response = logic.build_ledger_contract_flowthrough_entry( mock_ledger_contract_flowthrough, Decimal('12.90') ) assert response.previous_balance == Decimal('12.90') assert response.current_balance == Decimal('212.90') assert response.currency_amount == '200.0' def test_build_deposit_ledger_entries(): """Test build ledger deposit entry.""" mock_ledger_deposit_records = [ { 'account_id': 2, 'contract_id': 2, 'abacus_event_id': 1, 'remaining_amount': '0.0001442850416000000', 'rounded_amount': '100.90', 'currency_code': 'USD', }, { 'account_id': 2, 'contract_id': 2, 'abacus_event_id': 1, 'remaining_amount': '0.00015', 'rounded_amount': '219.00', 'currency_code': 'USD', }, ] response = logic.build_deposit_ledger_entries(mock_ledger_deposit_records) assert response[0].remaining_amount == Decimal( mock_ledger_deposit_records[0]['remaining_amount'] ) assert response[1].remaining_amount == Decimal( mock_ledger_deposit_records[1]['remaining_amount'] ) @patch('ledger.logic.ledger_contract_flowthrough.build_deposit_ledger_entries') @patch( 'ledger.logic.ledger_contract_flowthrough.build_contract_flowthrough_ledger_entries' ) @patch('ledger.logic.ledger_contract_flowthrough.validate_and_group_ledger_entries') @patch('ledger.logic.ledger_contract_flowthrough.db') def test_handle_bulk_ledger_entries( mock_db, mock_validate_ledger_entries, mock_build_flowthrough_ledger_entries, mock_build_deposit_ledger_entries, mock_event_fixtures, ): """Test handle bulk insert.""" mock_contract_flowthrough_request = { 'account_id': 1, 'contract_id': 1, 'abacus_event_id': 1, 'currency_amount': '200.0', 'currency_code': 'USD', 'model_type': 'contract', } mock_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 = [mock_contract_flowthrough_request, mock_deposit_request] mock_ledger_contract_flowthrough_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_validate_ledger_entries.return_value = ( [mock_contract_flowthrough_request], [mock_deposit_request], ) mock_build_flowthrough_ledger_entries.return_value = [ LedgerContractFlowthrough(**mock_ledger_contract_flowthrough_dict) ] mock_build_deposit_ledger_entries.return_value = [ LedgerDeposit(**mock_ledger_deposit_dict) ] mock_db.session.commit.return_value = None mock_db.session.bulk_save_objects.return_value = None response = logic.handle_bulk_ledger_entries(mock_request) assert response.status == 201 assert response.message == {'message': 'OK'} mock_validate_ledger_entries.assert_called_once_with(mock_request) mock_build_flowthrough_ledger_entries.assert_called_once_with( [mock_contract_flowthrough_request] ) mock_build_deposit_ledger_entries.assert_called_once_with([mock_deposit_request]) mock_db.session.commit.assert_called_once() mock_db.session.bulk_save_objects.call_args_list == [ mock_ledger_contract_flowthrough_dict, [LedgerDeposit(**mock_ledger_deposit_dict)], ] @patch('ledger.logic.ledger_contract_flowthrough.validate_and_group_ledger_entries') @patch('ledger.logic.ledger_contract_flowthrough.db') def test_handle_bulk_ledger_entries_integrity_error( mock_db, mock_validate_ledger_entries, ): """Test IntegrityError returns 409 Conflict.""" mock_validate_ledger_entries.return_value = ( [ { 'contract_id': 1, 'account_id': 1, 'abacus_event_id': 1, 'currency_amount': '1.00', 'currency_code': 'USD', } ], [], ) mock_db.session.commit.side_effect = exc.IntegrityError(None, None, None) with pytest.raises(HTTPException) as exc_info: logic.handle_bulk_ledger_entries([{}]) assert exc_info.value.code == 409 mock_db.session.rollback.assert_called_once() @patch('ledger.logic.ledger_contract_flowthrough.validate_and_group_ledger_entries') @patch('ledger.logic.ledger_contract_flowthrough.db') def test_handle_bulk_ledger_entries_data_error( mock_db, mock_validate_ledger_entries, ): """Test DataError returns 422 Unprocessable Entity.""" mock_validate_ledger_entries.return_value = ( [ { 'contract_id': 1, 'account_id': 1, 'abacus_event_id': 1, 'currency_amount': '1.00', 'currency_code': 'USD', } ], [], ) mock_db.session.commit.side_effect = exc.DataError(None, None, None) with pytest.raises(HTTPException) as exc_info: logic.handle_bulk_ledger_entries([{}]) assert exc_info.value.code == 422 mock_db.session.rollback.assert_called_once()