"""Generate report banking details processor tests.""" from unittest.mock import call, Mock, patch from faker import Faker import pytest from src.processors.exceptions import ProcessingError from src.processors.reports.generate_report_banking_details import ( GenerateReportBankingDetailsProcessor, ) from tests.unit.factories import PayeeDetailsFactory from tests.utils import create_sample_csv_buffer class TestGenerateReportBankingDetailsProcessor: """GenerateReportBankingDetailsProcessor test suite.""" @patch.object(GenerateReportBankingDetailsProcessor, '_prepare_data') @patch.object(GenerateReportBankingDetailsProcessor, '_load_bank_details') @patch.object(GenerateReportBankingDetailsProcessor, '_load_payees_by_client_refs') @patch.object(GenerateReportBankingDetailsProcessor, '_load_payees_by_accounts') @patch.object(GenerateReportBankingDetailsProcessor, '_load_csv_accounts') def test_process( self, mock_load_csv_accounts: Mock, mock_load_payees_by_accounts: Mock, mock_load_payees_by_client_refs: Mock, mock_load_bank_details: Mock, mock_prepare_data: Mock, faker: Faker, ) -> None: """Test process method.""" processor = GenerateReportBankingDetailsProcessor(faker.pystr(), faker.pystr()) processor.process() mock_prepare_data.assert_called_once() mock_load_bank_details.assert_called_once() mock_load_payees_by_client_refs.assert_called_once() mock_load_payees_by_accounts.assert_called_once() mock_load_csv_accounts.assert_called_once() @patch('src.processors.reports.generate_report_banking_details.capture_message') @patch('src.processors.reports.generate_report_banking_details.logger') def test_capture_error( self, mock_logger: Mock, mock_capture_message: Mock, faker: Faker ) -> None: """Test capture_error.""" message = faker.pystr() processor = GenerateReportBankingDetailsProcessor(faker.pystr(), faker.pystr()) processor.capture_error(message) mock_logger.error.assert_called_once_with(message) mock_capture_message.assert_called_once_with(message, level='error') @pytest.mark.parametrize('file_content', (['vendor_id', 52, 180], ['vendor_id', 3])) def test_load_account_payee_data_success( self, file_content: list[str | int] ) -> None: """Test loading data from CSV file success.""" processor = GenerateReportBankingDetailsProcessor('', '') processor._input_file_buffer = create_sample_csv_buffer( ((i,) for i in file_content) ) processor._load_csv_accounts() assert processor._account_ids == file_content[1:] @pytest.mark.parametrize( 'file_content', ( [ 'vendor_id', '253a90d9-483e-11f0-9627-0e1655f1d811', '253b582f-483e-11f0-9627-0e1655f1d811', ], ['vendor_id', '253f83a6-483e-11f0-9627-0e1655f1d811'], ), ) def test_load_client_refs_data_success(self, file_content: list[str | int]) -> None: """Test loading data from CSV file success.""" processor = GenerateReportBankingDetailsProcessor('', '') processor._input_file_buffer = create_sample_csv_buffer( ((i,) for i in file_content) ) processor._load_csv_accounts() assert processor._client_reference_ids == file_content[1:] def test_load_data_failure_no_file(self) -> None: """Test loading data from CSV file failure.""" processor = GenerateReportBankingDetailsProcessor('', '') with pytest.raises(ProcessingError, match='Unable to read the file'): processor._load_csv_accounts() def test_load_data_failure_invalid_content(self) -> None: """Test loading data from CSV file failure.""" processor = GenerateReportBankingDetailsProcessor('', '') processor._input_file_buffer = create_sample_csv_buffer( (('header',), ('row1',), ('row2',)) ) with pytest.raises(ProcessingError, match='Invalid file content'): processor._load_csv_accounts() def test_load_data_failure_no_items(self) -> None: """Test loading data from CSV file failure.""" processor = GenerateReportBankingDetailsProcessor('', '') processor._input_file_buffer = create_sample_csv_buffer((('vendor_id',),)) with pytest.raises( ProcessingError, match='No valid IDs found in the CSV file.' ): processor._load_csv_accounts() @patch.object(GenerateReportBankingDetailsProcessor, 'capture_error') @patch( 'src.processors.reports.generate_report_banking_details.get_payees_by_accounts' ) def test_load_payees_by_accounts( self, mock_get_payees_by_accounts: Mock, mock_capture_error: Mock, faker: Faker ) -> None: """Test _load_payees_by_accounts method.""" account_ids = [105, 81, 15] account_id_to_payee_id = {105: 1050, 15: 152} mock_get_payees_by_accounts.return_value = account_id_to_payee_id processor = GenerateReportBankingDetailsProcessor(faker.pystr(), faker.pystr()) processor._account_ids = account_ids processor._load_payees_by_accounts() mock_get_payees_by_accounts.assert_called_once_with(account_ids) assert processor._account_id_to_account_payee_id == account_id_to_payee_id mock_capture_error.assert_called_once_with('Accounts not found: {81}') @patch.object(GenerateReportBankingDetailsProcessor, 'capture_error') @patch( 'src.processors.reports.generate_report_banking_details.get_payees_by_client_refs' ) def test_load_payees_by_client_refs( self, mock_get_payees_by_client_refs: Mock, mock_capture_error: Mock, faker: Faker, ) -> None: """Test _load_payees_by_client_refs method.""" client_reference_ids = [ '253a90d9-483e-11f0-9627-0e1655f1d811', '253b582f-483e-11f0-9627-0e1655f1d811', '2541df66-483e-11f0-9627-0e1655f1d811', ] client_ref_to_payee_id = { '253a90d9-483e-11f0-9627-0e1655f1d811': 101, '253b582f-483e-11f0-9627-0e1655f1d811': 207, } mock_get_payees_by_client_refs.return_value = client_ref_to_payee_id processor = GenerateReportBankingDetailsProcessor(faker.pystr(), faker.pystr()) processor._client_reference_ids = client_reference_ids processor._load_payees_by_client_refs() mock_get_payees_by_client_refs.assert_called_once_with(client_reference_ids) assert processor._client_reference_id_to_payee_id == client_ref_to_payee_id mock_capture_error.assert_called_once_with( "Client reference IDs not found: {'2541df66-483e-11f0-9627-0e1655f1d811'}" ) @patch( 'src.processors.reports.generate_report_banking_details.get_bank_details_by_account_payee' ) def test_generate_report_banking_details_by_account_payee( self, mock_get_bank_details: Mock, faker: Faker ) -> None: """Test _generate_report_banking_details method.""" account_id_to_payee_id = {1: 101, 2: 202} processor = GenerateReportBankingDetailsProcessor(faker.pystr(), faker.pystr()) processor._account_id_to_account_payee_id = account_id_to_payee_id processor._load_bank_details() assert mock_get_bank_details.call_args_list == [ call(payee_id) for payee_id in account_id_to_payee_id.values() ] @patch( 'src.processors.reports.generate_report_banking_details.get_bank_details_by_payee' ) def test_generate_report_banking_details_by_payee( self, mock_get_bank_details: Mock, faker: Faker ) -> None: """Test _generate_report_banking_details method.""" client_ref_to_payee_id = { '253a90d9-483e-11f0-9627-0e1655f1d811': 303, '253b582f-483e-11f0-9627-0e1655f1d811': 404, } processor = GenerateReportBankingDetailsProcessor(faker.pystr(), faker.pystr()) processor._client_reference_id_to_payee_id = client_ref_to_payee_id processor._load_bank_details() assert mock_get_bank_details.call_args_list == [ call(payee_id) for payee_id in client_ref_to_payee_id.values() ] @patch('src.processors.reports.generate_report_banking_details.flatten_model') @patch( 'src.processors.reports.generate_report_banking_details.map_bank_details_fields' ) def test_prepare_data_success( self, mock_map_fields: Mock, mock_flatten: Mock, faker: Faker ) -> None: processor = GenerateReportBankingDetailsProcessor(faker.pystr(), faker.pystr()) processor._raw_data = [ PayeeDetailsFactory.build(account_payee_id=1), PayeeDetailsFactory.build(account_payee_id=2), PayeeDetailsFactory.build( account_payee_id='253a90d9-483e-11f0-9627-0e1655f1d811' ), PayeeDetailsFactory.build( account_payee_id='253b582f-483e-11f0-9627-0e1655f1d811' ), ] processor._account_id_to_account_payee_id = {1: 101, 2: 202} processor._client_reference_id_to_payee_id = { '253a90d9-483e-11f0-9627-0e1655f1d811': 303, '253b582f-483e-11f0-9627-0e1655f1d811': 404, } mock_flatten.side_effect = [ {'account_payee_id': 101, 'field_a': 'x'}, {'account_payee_id': 202, 'field_b': 'y'}, {'account_payee_id': 303, 'field_c': 'z'}, {'account_payee_id': 404, 'field_d': 'w'}, ] mock_map_fields.side_effect = [ {'mapped_field_a': 'x', 'account_id': 1}, {'mapped_field_b': 'y', 'account_id': 2}, {'mapped_field_c': 'z', 'account_id': 303}, {'mapped_field_d': 'w', 'account_id': 404}, ] processor._prepare_data() assert mock_flatten.call_count == 4 assert mock_map_fields.call_count == 4 expected_output = [ {'mapped_field_a': 'x', 'account_id': 1}, {'mapped_field_b': 'y', 'account_id': 2}, {'mapped_field_c': 'z', 'account_id': 303}, {'mapped_field_d': 'w', 'account_id': 404}, ] assert processor._output_data == expected_output