"""Generate tax form resport processor tests.""" from unittest.mock import call, Mock, patch from faker import Faker import pytest from src.constants import DEFAULT_BATCH_SIZE from src.models import TaxFormInfoDetailsBulk from src.processors.exceptions import ProcessingError from src.processors.reports.generate_report_tax_form import ( GenerateReportTaxFormProcessor, ) from tests.unit.factories import TaxFormInfoDetailsItemFactory from tests.utils import create_sample_csv_buffer class TestGenerateReportTaxFormProcessor: """GenerateReportTaxFormProcessor test suite.""" @patch.object(GenerateReportTaxFormProcessor, '_load_csv_accounts') @patch.object(GenerateReportTaxFormProcessor, '_load_payees_by_accounts') @patch.object(GenerateReportTaxFormProcessor, '_load_tax_form_data') def test_process( self, mock_load_tax_form_data: Mock, mock_load_payees_by_accounts: Mock, mock_load_csv_accounts: Mock, faker: Faker, ) -> None: """Test process method.""" processor = GenerateReportTaxFormProcessor(faker.pystr(), faker.pystr()) processor.process() mock_load_tax_form_data.assert_called_once_with() mock_load_payees_by_accounts.assert_called_once_with() mock_load_csv_accounts.assert_called_once_with() @patch('src.processors.reports.generate_report_tax_form.capture_message') @patch('src.processors.reports.generate_report_tax_form.logger') def test_capture_error( self, mock_logger: Mock, mock_capture_message: Mock, faker: Faker ) -> None: """Test capture_error.""" message = faker.pystr() processor = GenerateReportTaxFormProcessor(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_data_success( self, file_content: list[str | int], faker: Faker ) -> None: """Test loading data from CSV file success.""" processor = GenerateReportTaxFormProcessor(faker.pystr(), faker.pystr()) 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:] def test_load_data_failure_no_file(self, faker: Faker) -> None: """Test loading data from CSV file failure.""" processor = GenerateReportTaxFormProcessor(faker.pystr(), faker.pystr()) with pytest.raises(ProcessingError, match='Unable to read the file'): processor._load_csv_accounts() def test_load_data_failure_invalid_content(self, faker: Faker) -> None: """Test loading data from CSV file failure.""" processor = GenerateReportTaxFormProcessor(faker.pystr(), faker.pystr()) 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, faker: Faker) -> None: """Test loading data from CSV file failure.""" processor = GenerateReportTaxFormProcessor(faker.pystr(), faker.pystr()) processor._input_file_buffer = create_sample_csv_buffer((('vendor_id',),)) with pytest.raises(ProcessingError, match='No items in file to process'): processor._load_csv_accounts() @patch.object(GenerateReportTaxFormProcessor, 'capture_error') @patch('src.processors.reports.generate_report_tax_form.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 = GenerateReportTaxFormProcessor(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_payee_id_account_id == {1050: 105, 152: 15} mock_capture_error.assert_called_once_with('Accounts not found: {81}') @patch( 'src.processors.reports.generate_report_tax_form.get_tax_form_info_details_bulk' ) def test_load_tax_form_data( self, mock_get_tax_form_info_details_bulk: Mock, faker: Faker ) -> None: """Test _load_tax_form_data method.""" account_payee_id_account_id = { i * 10: i for i in range(DEFAULT_BATCH_SIZE * 2 + DEFAULT_BATCH_SIZE // 2) } account_payee_ids = list(account_payee_id_account_id.keys()) items = [ TaxFormInfoDetailsItemFactory.build(account_payee_id=i * 10) for i in range(8) ] mock_get_tax_form_info_details_bulk.side_effect = [ TaxFormInfoDetailsBulk(items=items[:3], total_count=DEFAULT_BATCH_SIZE), TaxFormInfoDetailsBulk(items=items[3:6], total_count=DEFAULT_BATCH_SIZE), TaxFormInfoDetailsBulk( items=items[6:], total_count=DEFAULT_BATCH_SIZE // 2 ), ] processor = GenerateReportTaxFormProcessor(faker.pystr(), faker.pystr()) processor._account_payee_id_account_id = account_payee_id_account_id processor._load_tax_form_data() assert mock_get_tax_form_info_details_bulk.call_args_list == [ call( account_payee_ids[:DEFAULT_BATCH_SIZE], limit=DEFAULT_BATCH_SIZE, ), call( account_payee_ids[DEFAULT_BATCH_SIZE : DEFAULT_BATCH_SIZE * 2], limit=DEFAULT_BATCH_SIZE, ), call( account_payee_ids[DEFAULT_BATCH_SIZE * 2 :], limit=DEFAULT_BATCH_SIZE, ), ] assert processor._output_data == [ {'vendor_id': index, **dict(item)} for index, item in enumerate(items) ]