"""Unit tests for tax reset logic.""" from unittest.mock import call, MagicMock, patch from abacus_common_logic.connectors.database import db import pytest from payee.constants.constants import TAX_TYPES from payee.logic import tax_reset as logic from payee.logic.exceptions import LogicError from payee.models.account_payee import AccountPayee @patch('payee.logic.tax_reset.delete_tax_details_record') @patch('payee.logic.tax_reset.AccountPayee') @patch('payee.logic.tax_reset.ReportsTaxInfo') def test_reset_by_account_payee_id_success_details( mock_reports_tax_info, mock_account_payee, mock_delete_tax_details_record, account_fixtures, account_payee_fixtures, account_tax_info_fixtures, mocker, ): account_payee_id = 1 account_payee = MagicMock(values={'account_id': account_payee_id}) account_payee.account_tax_info = MagicMock( account_tax_info_history=[MagicMock(account_tax_info_history_id=10)] ) tax_details_deleted_revision = 'rev-1' tax_details_deleted_document = MagicMock(values={'field1': 'v1', 'field2': 'v2'}) mock_account_payee.get_by_id_or_error.return_value = account_payee mock_delete_tax_details_record.return_value = ( tax_details_deleted_revision, tax_details_deleted_document, ) # Spy on db methods mocker.spy(db.session, 'flush') mocker.spy(db.session, 'commit') logic.reset_by_account_payee_id(account_payee_id) assert mock_account_payee.get_by_id_or_error.call_args_list == [ call(account_payee_id) ] assert mock_delete_tax_details_record.call_args_list == [call(account_payee_id)] assert account_payee.account_tax_info.update_attributes.call_args_list == [ call( country_of_tax_residence=None, is_sba_signed=False, is_tax_treaty_claimed=False, tax_employment_type=None, certificate_of_residence_expiration_date=None, is_wht_applicable=True, is_resident_of_spanish_islands=None, wht_rate_override=None, ) ] assert db.session.flush.called assert mock_reports_tax_info.build.call_args_list == [ call( account_tax_info_history_id=10, tax_type=TAX_TYPES.DETAILS, revision_id=tax_details_deleted_revision, ) ] assert db.session.commit.called @patch('payee.logic.tax_reset.g') @patch('payee.logic.tax_reset.delete_tax_form_record') @patch('payee.logic.tax_reset.delete_tax_details_record') @patch('payee.logic.tax_reset.AccountPayee') def test_reset_by_account_payee_id_no_secure_doc_details( mock_account_payee, mock_delete_tax_details_record, mock_delete_tax_form_record, account_fixtures, account_payee_fixtures, account_tax_info_fixtures, ): account_payee_id = 1 account_payee = MagicMock(values={'account_payee_id': account_payee_id}) mock_account_payee.get_by_id_or_error.return_value = account_payee account_payee.account_tax_info.account_tax_info_history = [ MagicMock(account_tax_info_history_id=10) ] mock_delete_tax_details_record.side_effect = LogicError('no document') mock_delete_tax_form_record.side_effect = LogicError('no document') logic.reset_by_account_payee_id(account_payee_id) assert mock_account_payee.get_by_id_or_error.call_args_list == [ call(account_payee_id) ] assert mock_delete_tax_details_record.call_args_list == [call(account_payee_id)] assert account_payee.account_tax_info.update_attributes.called @patch('payee.logic.tax_reset.g') @patch('payee.logic.tax_reset.delete_tax_details_record') @patch('payee.logic.tax_reset.AccountPayee') def test_reset_by_account_payee_id_no_tax_info_details( mock_account_payee, mock_delete_tax_details_record, account_fixtures, account_payee_fixtures, mocker, ): account_payee_id = 1 account_payee = AccountPayee.get_payee_by_id(account_payee_id) mock_account_payee.get_by_id_or_error.return_value = account_payee tax_details_deleted_revision = 'rev-1' tax_details_deleted_document = MagicMock(values={'field1': 'v1'}) mock_delete_tax_details_record.return_value = ( tax_details_deleted_revision, tax_details_deleted_document, ) # Spy on db methods mocker.spy(db.session, 'flush') mocker.spy(db.session, 'commit') logic.reset_by_account_payee_id(account_payee_id) assert mock_account_payee.get_by_id_or_error.call_args_list == [ call(account_payee_id) ] assert mock_delete_tax_details_record.call_args_list == [call(account_payee_id)] # No DB operations when no tax_info assert not db.session.flush.called assert not db.session.commit.called @patch('payee.logic.tax_reset.save_secure_document_details_by_payee_id') @patch('payee.logic.tax_reset.ReportsTaxInfo') @patch('payee.logic.tax_reset.delete_tax_details_record') @patch('payee.logic.tax_reset.AccountPayee') def test_reset_by_account_payee_id_db_failure_rollback_details( mock_account_payee, mock_delete_tax_details_record, mock_reports_tax_info, mock_save_secure_document_details_by_payee_id, account_fixtures, account_payee_fixtures, account_tax_info_fixtures, mocker, ): account_payee_id = 1 account_payee = AccountPayee.get_payee_by_id(account_payee_id) tax_details_deleted_revision = 'rev-1' tax_details_deleted_values = {'field1': 'v1', 'field2': 'v2'} tax_details_deleted_document = MagicMock(values=tax_details_deleted_values) mock_account_payee.get_by_id_or_error.return_value = account_payee mock_delete_tax_details_record.return_value = ( tax_details_deleted_revision, tax_details_deleted_document, ) mock_reports_tax_info.build.side_effect = Exception('DB error') # Spy on db methods mocker.spy(db.session, 'flush') mocker.spy(db.session, 'rollback') mocker.spy(db.session, 'commit') with pytest.raises(Exception, match='DB error'): logic.reset_by_account_payee_id(account_payee_id) assert db.session.flush.called assert db.session.rollback.called assert not db.session.commit.called assert mock_save_secure_document_details_by_payee_id.call_args_list == [ call( account_payee_id, tax_details_deleted_document.__class__, **tax_details_deleted_values, ) ] @patch('payee.logic.tax_reset.TaxFormInfo') @patch( 'payee.logic.tax_reset.delete_tax_details_record', side_effect=LogicError('No details'), ) @patch('payee.logic.tax_reset.delete_tax_form_record') @patch('payee.logic.tax_reset.AccountPayee') @patch('payee.logic.tax_reset.ReportsTaxInfo') def test_reset_by_account_payee_id_success_form( mock_reports_tax_info, mock_account_payee, mock_delete_tax_form_record, mock_delete_tax_details_record, mock_tax_form_info, account_fixtures, account_payee_fixtures, account_tax_info_fixtures, mocker, ): account_payee_id = 1 account_payee_tax_form_info_id = 11 account_payee = MagicMock(values={'account_id': account_payee_id}) account_payee.account_tax_info = MagicMock( account_tax_info_history=[MagicMock(account_tax_info_history_id=10)] ) tax_form_deleted_revision = 'rev-2' tax_form_deleted_document = MagicMock(values={'field1': 'v1', 'field2': 'v2'}) mock_tax_form_info.get_by_account_payee_id.return_value.account_payee_tax_form_info_id = account_payee_tax_form_info_id mock_account_payee.get_by_id_or_error.return_value = account_payee mock_delete_tax_form_record.return_value = ( tax_form_deleted_revision, tax_form_deleted_document, ) mocker.spy(db.session, 'flush') mocker.spy(db.session, 'commit') logic.reset_by_account_payee_id(account_payee_id) assert mock_account_payee.get_by_id_or_error.call_args_list == [ call(account_payee_id) ] assert mock_tax_form_info.get_by_account_payee_id.call_args_list == [ call(account_payee_id) ] assert mock_delete_tax_form_record.call_args_list == [ call(account_payee_tax_form_info_id, False) ] assert account_payee.account_tax_info.update_attributes.call_args_list == [ call( country_of_tax_residence=None, is_sba_signed=False, is_tax_treaty_claimed=False, tax_employment_type=None, certificate_of_residence_expiration_date=None, is_wht_applicable=True, is_resident_of_spanish_islands=None, wht_rate_override=None, ) ] assert db.session.flush.called assert mock_reports_tax_info.build.call_args_list == [ call( account_tax_info_history_id=10, tax_type=TAX_TYPES.FORM, revision_id=tax_form_deleted_revision, ) ] assert db.session.commit.called @patch('payee.logic.tax_reset.TaxFormInfo') @patch('payee.logic.tax_reset.delete_tax_details_record') @patch('payee.logic.tax_reset.delete_tax_form_record') @patch('payee.logic.tax_reset.AccountPayee') def test_reset_by_account_payee_id_no_secure_doc_form( mock_account_payee, mock_delete_tax_form_record, mock_delete_tax_details_record, mock_tax_form_info, account_fixtures, account_payee_fixtures, account_tax_info_fixtures, ): account_payee_id = 1 account_payee_tax_form_info_id = 11 account_payee = MagicMock(values={'account_payee_id': account_payee_id}) mock_tax_form_info.get_by_account_payee_id.return_value.account_payee_tax_form_info_id = account_payee_tax_form_info_id mock_account_payee.get_by_id_or_error.return_value = account_payee account_payee.account_tax_info.account_tax_info_history = [ MagicMock(account_tax_info_history_id=10) ] mock_delete_tax_form_record.side_effect = LogicError('no document') mock_delete_tax_details_record.side_effect = LogicError('no document') logic.reset_by_account_payee_id(account_payee_id) assert mock_tax_form_info.get_by_account_payee_id.call_args_list == [ call(account_payee_id) ] assert mock_account_payee.get_by_id_or_error.call_args_list == [ call(account_payee_id) ] assert mock_delete_tax_form_record.call_args_list == [ call(account_payee_tax_form_info_id, False) ] assert account_payee.account_tax_info.update_attributes.called @patch('payee.logic.tax_reset.TaxFormInfo') @patch('payee.logic.tax_reset.delete_tax_form_record') @patch('payee.logic.tax_reset.AccountPayee') def test_reset_by_account_payee_id_no_tax_info_form( mock_account_payee, mock_delete_tax_form_record, mock_tax_form_info, account_fixtures, account_payee_fixtures, mocker, ): account_payee_id = 1 account_payee = AccountPayee.get_payee_by_id(account_payee_id) tax_form_deleted_revision = 'rev-2' tax_form_deleted_document = MagicMock(values={'field1': 'v1'}) mock_tax_form_info.get_by_account_payee_id.return_value = None mock_account_payee.get_by_id_or_error.return_value = account_payee mock_delete_tax_form_record.return_value = ( tax_form_deleted_revision, tax_form_deleted_document, ) mocker.spy(db.session, 'flush') mocker.spy(db.session, 'commit') logic.reset_by_account_payee_id(account_payee_id) assert mock_account_payee.get_by_id_or_error.call_args_list == [ call(account_payee_id) ] assert not mock_delete_tax_form_record.called # No DB operations when no tax_info assert not db.session.flush.called assert not db.session.commit.called @patch('payee.logic.tax_reset.TaxFormInfo') @patch('payee.logic.tax_reset.save_secure_document_details_by_payee_id') @patch('payee.logic.tax_reset.ReportsTaxInfo') @patch('payee.logic.tax_reset.delete_tax_form_record') @patch('payee.logic.tax_reset.AccountPayee') def test_reset_by_account_payee_id_db_failure_rollback_form( mock_account_payee, mock_delete_tax_form_record, mock_reports_tax_info, mock_save_secure_document_details_by_payee_id, mock_tax_form_info, account_fixtures, account_payee_fixtures, account_tax_info_fixtures, mocker, ): account_payee_id = 1 account_payee_tax_form_info_id = 11 account_payee = AccountPayee.get_payee_by_id(account_payee_id) tax_form_deleted_revision = 'rev-2' tax_form_deleted_values = {'field1': 'v1', 'field2': 'v2'} tax_form_deleted_document = MagicMock(values=tax_form_deleted_values) mock_tax_form_info.get_by_account_payee_id.return_value.account_payee_tax_form_info_id = account_payee_tax_form_info_id mock_account_payee.get_by_id_or_error.return_value = account_payee mock_delete_tax_form_record.return_value = ( tax_form_deleted_revision, tax_form_deleted_document, ) mock_reports_tax_info.build.side_effect = Exception('DB error') mocker.spy(db.session, 'flush') mocker.spy(db.session, 'rollback') mocker.spy(db.session, 'commit') with pytest.raises(Exception, match='DB error'): logic.reset_by_account_payee_id(account_payee_id) assert db.session.flush.called assert db.session.rollback.called assert not db.session.commit.called assert mock_save_secure_document_details_by_payee_id.call_args_list == [ call( account_payee_id, tax_form_deleted_document.__class__, **tax_form_deleted_values, ) ]