"""Tests for payment group logic.""" from unittest.mock import call, MagicMock, patch import pytest from payment.constants.constants import SPECIFIC_ACCOUNTS_PAYMENT_GROUP_NAME from payment.constants.error import ( ERROR_ALREADY_EXISTS, ERROR_CANNOT_REUSE, ERROR_ONLY_ONE_REFERENCE_PAYMENT_ENTITY, # noqa: E501 ERROR_PAYMENT_NAME_MISSING, ERROR_REUSE, ERROR_UNKNOWN_CURRENCY, ERROR_UPDATE_NON_REUSABLE_PAYMENT_GROUP, ) from payment.logic.exceptions import LogicError from payment.logic.payment_group import ( _validate_payment_group, create_payment_group, get_reusable_payment_groups, update_payment_group, ) from payment.models import Items from payment.schemas.payment_group import PaymentGroupDetailSchema from payment.utils.models import as_dict from tests.utils.factories import PaymentGroupFactory, PaymentGroupPaymentFactory @patch('payment.logic.payment_group.create_payment_group_payment') @patch('payment.logic.payment_group.PaymentGroup') @patch('payment.logic.payment_group._validate_payment_group') def test_create_payment_group_success( mock_validation, mock_model, mock_create_group_payment ): """Test successful creation of payment group.""" payment_group = PaymentGroupFactory.build(is_reusable=True) payment_name = 'Name of Payment from FE Form' params = PaymentGroupDetailSchema().dump(payment_group) params.update({'payment_name': payment_name}) mock_validation.return_value = None mock_model.create.return_value = payment_group res = create_payment_group(**params) assert res == as_dict(payment_group) mock_validation.assert_called_once_with(**params) mock_create_group_payment.assert_not_called() @patch('payment.logic.payment_group.create_payment_group_payment') @patch('payment.logic.payment_group.PaymentGroup') @patch('payment.logic.payment_group._validate_payment_group') def test_create_payment_group_for_specific_accounts_success( mock_validation, mock_model, mock_create_group_payment ): """Test successful creation of a payment_group for specific accounts.""" group_criteria = {'account_ids': [1, 2, 3, 4]} payment_group = PaymentGroupFactory.build( group_criteria=group_criteria, group_name=SPECIFIC_ACCOUNTS_PAYMENT_GROUP_NAME ) payment_group_payment = PaymentGroupPaymentFactory.build( payment_group_id=payment_group.payment_group_id ) params = PaymentGroupDetailSchema().dump(payment_group) params.update({'payment_name': 'Name of Payment from FE Form'}) mock_validation.return_value = None mock_model.create.return_value = payment_group mock_create_group_payment.return_value = payment_group_payment res = create_payment_group(**params) assert res['group_criteria'] == group_criteria assert res['group_name'] == SPECIFIC_ACCOUNTS_PAYMENT_GROUP_NAME mock_validation.assert_called_once_with(**params) mock_create_group_payment.assert_called_once_with( payment_group.payment_group_id, params['payment_name'] ) @patch('payment.logic.payment_group.create_payment_group_payment') @patch('payment.logic.payment_group.PaymentGroup') @patch('payment.logic.payment_group._validate_payment_group') def test_create_payment_group_for_specific_account_success( mock_validation, mock_model, mock_create_group_payment ): """Test successful creation of a payment_group for a specific account.""" group_criteria = {'account_id': 321} payment_group = PaymentGroupFactory.build(group_criteria=group_criteria) payment_group_payment = PaymentGroupPaymentFactory.build( payment_group_id=payment_group.payment_group_id ) params = PaymentGroupDetailSchema().dump(payment_group) params.update({'payment_name': 'Name of Payment from FE Form'}) mock_validation.return_value = None mock_model.create.return_value = payment_group mock_create_group_payment.return_value = payment_group_payment res = create_payment_group(**params) assert res['group_criteria'] == group_criteria mock_validation.assert_called_once_with(**params) mock_create_group_payment.assert_called_once_with( payment_group.payment_group_id, params['payment_name'] ) @patch('payment.logic.payment_group.create_payment_group_payment') @patch('payment.logic.payment_group.PaymentGroup') @patch('payment.logic.payment_group._validate_payment_group') def test_create_payment_group_payment_entities_provided( mock_validation, mock_model, mock_create_group_payment ): """Test successful creation of a payment_group for when reference_payment_entities provided.""" # noqa: E501 group_criteria = {'account_id': 321, 'reference_payment_entities': [1]} payment_group = PaymentGroupFactory.build(group_criteria=group_criteria) payment_group_payment = PaymentGroupPaymentFactory.build( payment_group_id=payment_group.payment_group_id ) params = PaymentGroupDetailSchema().dump(payment_group) params.update({'payment_name': 'Name of Payment from FE Form'}) mock_validation.return_value = None mock_model.create.return_value = payment_group mock_create_group_payment.return_value = payment_group_payment create_payment_group(**params) mock_validation.assert_called_once_with(**params) mock_create_group_payment.assert_called_once() args, kwargs = mock_model.create.call_args expected_group_criteria = kwargs['group_criteria'] expected_group_criteria['account_id'] = 321 expected_group_criteria['reference_payment_entities'] = [1] @patch('payment.logic.payment_group.PaymentGroup') @patch('payment.logic.payment_group._validate_payment_group') def test_create_payment_group_error(mock_validation, mock_model): """Test failed creation of payment group.""" payment_group = PaymentGroupFactory.build(is_reusable=True) params = PaymentGroupDetailSchema().dump(payment_group) mock_validation.side_effect = LogicError('test') with pytest.raises(LogicError, match='test'): create_payment_group(**params) mock_model.create.assert_not_called() def test_create_payment_group_duplicate_name(): """Test error when creating reusable payment group with an existing group name.""" existing = PaymentGroupFactory.create(is_reusable=True) params = { 'group_criteria': {}, 'group_name': existing.group_name, 'is_reusable': True, } with pytest.raises( LogicError, match=ERROR_ALREADY_EXISTS.format(object_type='Payment Group') ): create_payment_group(**params) def test_validate_payment_group_no_error(): """Test validation of payment group params passes.""" payment_group = PaymentGroupFactory.build(is_reusable=True) params = PaymentGroupDetailSchema().dump(payment_group) res = _validate_payment_group(**params) assert res is None def test_validate_payment_group_error(): """Test validation returns error. When payment group is not an account specific and it is not reusable. """ payment_group = PaymentGroupFactory.build() params = PaymentGroupDetailSchema().dump(payment_group) with pytest.raises(LogicError, match=ERROR_REUSE): _validate_payment_group(**params) def test_validate_payment_group_validation_account_id_errors(): """Test validation returns appropriate errors for invalid payment group configurations.""" params = { 'group_criteria': {'account_id': 1}, 'group_name': 'Test Group', 'is_reusable': 0, } with pytest.raises(LogicError, match=ERROR_PAYMENT_NAME_MISSING): _validate_payment_group(**params) def test_validate_payment_group_invalid_currency_codes(): """Test validation returns error for invalid currency code.""" params = { 'group_criteria': {'currency_codes': ['GBP', 'LOL', 'JPY']}, 'is_reusable': 1, } with pytest.raises(LogicError, match=ERROR_UNKNOWN_CURRENCY.format(code='LOL')): _validate_payment_group(**params) def test_validate_payment_group_invalid_ref_payment_entities(): """Test validation returns error when there is more than one reference_payment_entity.""" # noqa: E501 params = { 'group_criteria': {'reference_payment_entities': [1, 2]}, 'is_reusable': 1, } with pytest.raises(LogicError, match=ERROR_ONLY_ONE_REFERENCE_PAYMENT_ENTITY): _validate_payment_group(**params) def test_validate_payment_group_duplicate_name(): """Test validation error when a group_name is already used by a reusable group.""" payment_group = PaymentGroupFactory.create(is_reusable=True) params = {'group_name': payment_group.group_name, 'is_reusable': True} with pytest.raises( LogicError, match=ERROR_ALREADY_EXISTS.format(object_type='Payment Group') ): _validate_payment_group(**params) def test_validate_payment_group_cannot_reuse(): """Test validation error when account_id is given and group is marked reusable.""" group_criteria = {'account_id': 321} payment_group = PaymentGroupFactory.build( group_criteria=group_criteria, is_reusable=True ) params = PaymentGroupDetailSchema().dump(payment_group) with pytest.raises(LogicError, match=ERROR_CANNOT_REUSE): _validate_payment_group(**params) def test_get_reusable_payment_groups(): """Test to get reusable payment groups.""" payment_group = PaymentGroupFactory.create( group_name='M/Q 45 days', is_reusable=True ) PaymentGroupFactory.create(group_name='M/Q 90 days', is_reusable=False) limit = 1 offset = 0 response = get_reusable_payment_groups(limit, offset) assert response == Items([payment_group], 1) @patch('payment.logic.payment_group.PaymentGroup') def test_update_payment_group_success(mock_model): """Test successful update of payment group.""" payment_group = MagicMock() payment_group.is_reusable = True new_name = 'Updated name' params = {'group_name': new_name} mock_model.get_by_id_or_error.return_value = payment_group mock_model.find_by_name.return_value = None res = update_payment_group(payment_group.payment_group_id, **params) assert res == payment_group assert mock_model.get_by_id_or_error.call_args_list == [ call(payment_group.payment_group_id) ] assert payment_group.update_attributes.call_args_list == [call(**params)] assert payment_group.commit_changes.called @patch('payment.logic.payment_group.PaymentGroup') def test_update_payment_group_failure(mock_model): """Test error updating of payment group.""" payment_group = MagicMock() payment_group.is_reusable = False new_name = 'Updated name' params = {'group_name': new_name} mock_model.get_by_id_or_error.return_value = payment_group with pytest.raises(LogicError, match=ERROR_UPDATE_NON_REUSABLE_PAYMENT_GROUP): update_payment_group(payment_group.payment_group_id, **params) assert mock_model.get_by_id_or_error.call_args_list == [ call(payment_group.payment_group_id) ] assert not payment_group.update_attributes.called assert not payment_group.commit_changes.called def test_update_payment_group_duplicate_name(): """Test error when updating payment group to an already used group name.""" existing = PaymentGroupFactory.create(group_name='Existing Name', is_reusable=True) payment_group = PaymentGroupFactory.create( group_name='Original Name', is_reusable=True ) with pytest.raises( LogicError, match=ERROR_ALREADY_EXISTS.format(object_type='Payment Group') ): update_payment_group( payment_group.payment_group_id, group_name=existing.group_name )