from unittest.mock import MagicMock from unittest.mock import patch import pytest from ows_accounting import config from ows_accounting import response from ows_accounting.constants import error from ows_accounting.constants import feature from ows_accounting.logic import reports from ows_accounting.models import accounting_statement_export from ows_accounting.response import Response from ows_accounting.utils import s3 @pytest.fixture def mock_records(): return { 'items': [{'generation_total_time': '438.7435712814331', 'transaction_types': 'all', 'user_id_type': '18805L', 'lines_scanned': '2009222', 'download_avro_file_end': '2016-06-01 17:35:58', 'generation_start': '2016-06-01 17:35:28', 'user_params': '206__all__en_US__txt', 'status': 'GENERATED', 'account_id': '18805', 'download_avro_file_start': '2016-06-01 17:35:28', 'generation_end': '2016-06-01 17:42:46', 'number_format': 'en_US', 'number_of_partitions': '3', 'file_type': 'txt', 'payment_interval': 'month', 'account_type': 'label', 'period_ids': '206'}, {'generation_total_time': '258.09422302246094', 'transaction_types': 'all', 'user_id_type': '18805L', 'lines_scanned': '2009222', 'download_avro_file_end': '2016-05-26 17:45:57', 'generation_start': '2016-05-26 17:45:45', 'user_params': '206__all__en_US__xls', 'status': 'GENERATED', 'account_id': '18805', 'download_avro_file_start': '2016-05-26 17:45:45', 'generation_end': '2016-05-26 17:50:03', 'number_format': 'en_US', 'number_of_partitions': '3', 'file_type': 'xls', 'payment_interval': 'month', 'account_type': 'label', 'period_ids': '206'}, {'generation_total_time': '438.7435712814331', 'transaction_types': 'all', 'user_id_type': '18805L', 'lines_scanned': '2009222', 'download_avro_file_end': '2016-06-01 17:35:58', 'generation_start': '2016-06-01 17:35:28', 'user_params': '206__all__en_US__txt', 'status': 'GENERATED', 'account_id': '18805', 'download_avro_file_start': '2016-06-01 17:35:28', 'generation_end': '2016-06-01 17:42:46', 'number_format': 'en_US', 'number_of_partitions': '3', 'file_type': 'txt', 'payment_interval': 'month', 'account_type': 'label', 'period_ids': '206'}, {'generation_total_time': '438.7435712814331', 'transaction_types': 'all', 'user_id_type': '18805L', 'lines_scanned': '2009222', 'download_avro_file_end': '2016-06-01 17:35:58', 'generation_start': '2016-06-01 17:35:28', 'user_params': '206__all__en_US__txt', 'status': 'GENERATED', 'account_id': '18805', 'download_avro_file_start': '2016-06-01 17:35:28', 'generation_end': '2016-06-01 17:42:46', 'number_format': 'en_US', 'number_of_partitions': '3', 'file_type': 'txt', 'payment_interval': 'month', 'account_type': 'label', 'period_ids': '206'}], 'pagination': { 'type': 'standard', 'offset': 1, 'limit': 1, 'total_records': 4 } } def test_get_reports(monkeypatch, mock_records): """Test the index method. """ mock_model = MagicMock() mock_model.get_records = lambda *params: mock_records monkeypatch.setattr( accounting_statement_export, 'AccountingStatementExport', MagicMock( return_value=mock_model)) assert reports.get_reports( 18805, 'L', ['199'], 0, 10).message == mock_records @patch('ows_accounting.logic.reports.accounting_statement_export') def test_get_report(mock_model, monkeypatch): """Test the get method. """ monkeypatch.setattr( s3, 'object_exists', MagicMock(return_value=True)) monkeypatch.setattr( s3, 'get_presigned_url', MagicMock(return_value=('http://test_presigned_url'))) mock_model_obj = MagicMock() mock_model_obj.get_item.return_value = Response( message={'s3_path': 's3://test_bucket/test_key'}, status=200) mock_model.AccountingStatementExport.return_value = mock_model_obj response = reports.get_report( '18805', 'vendor', '202,201,200', 'DT,DA', 'txt', 'en_US') s3.get_presigned_url.assert_any_call( 'test_bucket', 'test_key', config.PRESIGNED_URL_EXPIRES) mock_model_obj.get_item.assert_any_call( '18805L', '200,201,202', 'DA,DT', 'txt', 'en_US' ) assert response @patch('ows_accounting.logic.reports.accounting_statement_export') def test_get_report_object_not_found(mock_model, monkeypatch): """Test the get method with object not found. """ monkeypatch.setattr( s3, 'object_exists', MagicMock(return_value=False)) monkeypatch.setattr( s3, 'get_presigned_url', MagicMock(return_value=('http://test_presigned_url'))) mock_model_obj = MagicMock() mock_model_obj.get_item.return_value = Response( message="not found", status=404) mock_model.AccountingStatementExport.return_value = mock_model_obj response = reports.get_report( '18805', 'vendor', '200', 'DA,DT', 'txt', 'en_US') assert not s3.get_presigned_url.called assert response.status == 404 def test_add_report(): """Test the add_report method. """ response = reports.add_report( 'test_correlation_id', '18805', 'vendo', ['202', '203', '204'], ['DA', 'DT'], 'txt', 'en_US', 'test@examole.com', 1111, 'first_name', 'last_name', '2016-01-01 12:00:12') assert response.errors.get('message') == 'Invalid account_type: vendo.' @patch('ows_accounting.logic.reports.workflow_service') @patch('ows_accounting.logic.reports.accounting_statement_export') @patch('ows_accounting.logic.reports.response') def test_add_report_doesnotexist( mock_response, mock_report_model, mock_swf, add_report_data): """Test add_report with status DOESNOTEXIST """ mock_response_obj = MagicMock() mock_response_obj.message = {'status': config.STATUS_DOESNOTEXIST} mock_response_obj.status = 400 mock_response_obj.__bool__.return_value = False mock_response.Response.return_value = mock_response_obj mock_model = MagicMock() mock_model.get_item.return_value = mock_response_obj mock_model.put_item.return_value = None mock_report_model.AccountingStatementExport.return_value = mock_model mock_workflow_id_resp = MagicMock() mock_workflow_id_resp.message = 'test_workflow_id' mock_workflow_id_resp.status = 200 mock_exe_workflow_resp = MagicMock() mock_exe_workflow_resp.message = 'Workflow executed.' mock_exe_workflow_resp.status = 200 mock_swf_obj = MagicMock() mock_swf_obj.generate_workflow_id.return_value = mock_workflow_id_resp mock_swf_obj.execute_workflow.return_value = mock_exe_workflow_resp mock_swf.WorkflowService.return_value = mock_swf_obj reports.add_report(*add_report_data) params = dict( user_id='18805', user_type='label', period_ids='202,203,204', transaction_types='DA,DT', file_format='txt', locale='en_US', client_email='test@example.com', report_version=feature.DEFAULT_REPORT_VERSION) mock_swf_obj.generate_workflow_id.assert_any_call( config.WORKFLOW_ID_PREFIX, **params) mock_model.put_item.assert_any_call( '18805L', '202,203,204__DA,DT__en_US__txt', status='PENDING', requested_by='first_name last_name', requested_datetime='2016-01-01 12:00:12') assert mock_model.get_item.called assert mock_swf_obj.generate_workflow_id.called @patch('ows_accounting.logic.reports.workflow_service') @patch('ows_accounting.logic.reports.accounting_statement_export') @patch('ows_accounting.logic.reports.response') def test_add_report_snowflake( mock_response, mock_report_model, mock_swf, add_report_data): """Test add_report using Snowflake as a data warehouse """ mock_response_obj = MagicMock() mock_response_obj.message = {'status': config.STATUS_DOESNOTEXIST} mock_response_obj.status = 400 mock_response_obj.__bool__.return_value = False mock_response.Response.return_value = mock_response_obj mock_model = MagicMock() mock_model.get_item.return_value = mock_response_obj mock_model.put_item.return_value = None mock_report_model.AccountingStatementExport.return_value = mock_model mock_workflow_id_resp = MagicMock() mock_workflow_id_resp.message = 'test_workflow_id' mock_workflow_id_resp.status = 200 mock_exe_workflow_resp = MagicMock() mock_exe_workflow_resp.message = 'Workflow executed.' mock_exe_workflow_resp.status = 200 mock_swf_obj = MagicMock() mock_swf_obj.generate_workflow_id.return_value = mock_workflow_id_resp mock_swf_obj.execute_workflow.return_value = mock_exe_workflow_resp mock_swf.WorkflowService.return_value = mock_swf_obj params = dict( user_id='18805', user_type='label', period_ids='202,203,204', transaction_types='DA,DT', file_format='txt', locale='en_US', client_email='test@example.com', report_version=feature.DEFAULT_REPORT_VERSION) reports.add_report(*add_report_data) mock_swf_obj.generate_workflow_id.assert_any_call( config.WORKFLOW_ID_PREFIX, **params) assert mock_swf_obj.generate_workflow_id.called @patch('ows_accounting.logic.reports.workflow_service') @patch('ows_accounting.logic.reports.accounting_statement_export') @patch('ows_accounting.logic.reports.response') def test_add_report_pending( mock_response, mock_report_model, mock_swf, add_report_data): """Test the add_report method. """ mock_response_obj = MagicMock() mock_response_obj.message = {'status': config.STATUS_PENDING} mock_response_obj.__bool__.return_value = True mock_response_obj.status = 302 mock_response.Response.return_value = mock_response_obj mock_model = MagicMock() mock_model.get_item.return_value = mock_response mock_report_model.AccountingStatementExport.return_value = mock_model mock_swf.generate_workflow_id.return_value = 'test_workflow_id' response = reports.add_report(*add_report_data) assert response.status == 302 assert not mock_swf.execute_workflow.called @pytest.mark.parametrize('report_version, expected_report_version', [ (None, 2), (1, 1), (2, 2), ]) @patch('ows_accounting.logic.reports.workflow_service') @patch('ows_accounting.logic.reports.accounting_statement_export') def test_add_report_version( mock_report_model, mock_swf, report_version, expected_report_version, add_report_data): """Test the add_report method with different report_versions. """ # correlation_id is defined in add_report_data as first element correlation_id = 'test_correlation_id' workflow_id = 'test_workflow_id' get_item_response = response.create_error_response( code='Error', message={'status': config.STATUS_DOESNOTEXIST}) mock_model = MagicMock() mock_model.get_item.return_value = get_item_response mock_model.put_item.return_value = None mock_model.get_avro_item.return_value = response.Response(message='OK') mock_report_model.AccountingStatementExport.return_value = mock_model workflow_id_resp = response.Response(message=workflow_id) exe_workflow_resp = response.Response(message='Workflow executed.') mock_swf_obj = MagicMock() mock_swf_obj.generate_workflow_id.return_value = workflow_id_resp mock_swf_obj.execute_workflow.return_value = exe_workflow_resp mock_swf.WorkflowService.return_value = mock_swf_obj add_report_args = list(add_report_data) if report_version: add_report_args.append(report_version) resp = reports.add_report(*add_report_args) assert resp.status == 200 assert mock_swf_obj.execute_workflow.called params = dict( user_id='18805', user_type='label', period_ids='202,203,204', transaction_types='DA,DT', file_format='txt', locale='en_US', client_email='test@example.com', report_version=expected_report_version) mock_swf_obj.generate_workflow_id.assert_any_call( config.WORKFLOW_ID_PREFIX, **params) mock_swf_obj.execute_workflow.assert_any_call( workflow_id, correlation_id, **params) @patch('ows_accounting.logic.reports.workflow_service') @patch('ows_accounting.logic.reports.accounting_statement_export') @patch('ows_accounting.logic.reports.response') def test_add_report_generated( mock_response, mock_report_model, mock_swf, add_report_data): """Test the add_report method. """ mock_response_obj = MagicMock() mock_response_obj.message = {'status': config.STATUS_GENERATED} mock_response_obj.__bool__.return_value = True mock_response_obj.status = 304 mock_response.Response.return_value = mock_response_obj mock_model = MagicMock() mock_model.get_item.return_value = mock_response mock_report_model.AccountingStatementExport.return_value = mock_model mock_swf.generate_workflow_id.return_value = 'test_workflow_id' response = reports.add_report(*add_report_data) assert response.status == 304 assert not mock_swf.execute_workflow.called @patch('ows_accounting.logic.reports.workflow_service') @patch('ows_accounting.logic.reports.accounting_statement_export') @patch('ows_accounting.logic.reports.response') def test_add_report_generating( mock_response, mock_report_model, mock_swf, add_report_data): """Test the add_report method. """ mock_response_obj = MagicMock() mock_response_obj.message = {'status': config.STATUS_GENERATING} mock_response_obj.__bool__.return_value = True mock_response_obj.status = 302 mock_response.Response.return_value = mock_response_obj mock_model = MagicMock() mock_model.get_item.return_value = mock_response mock_report_model.AccountingStatementExport.return_value = mock_model mock_swf.generate_workflow_id.return_value = 'test_workflow_id' response = reports.add_report(*add_report_data) assert response assert not mock_swf.execute_workflow.called assert response.status == 302 @patch('ows_accounting.logic.reports.workflow_service') @patch('ows_accounting.logic.reports.accounting_statement_export') @patch('ows_accounting.logic.reports.response') def test_add_report_no_avro( mock_response, mock_report_model, mock_swf, add_report_data): """Test add_report with status DOESNOTEXIST and avro file is not available. """ mock_response_obj = MagicMock() mock_response_obj.status = 400 mock_response_obj.message = None mock_response_obj.__bool__.return_value = False mock_avro_response_obj = MagicMock() mock_avro_response_obj.message = {'status': config.STATUS_DOESNOTEXIST} mock_avro_response_obj.status = 400 mock_avro_response_obj.__bool__.return_value = False mock_response.Response.return_value = mock_response_obj mock_model = MagicMock() mock_model.get_item.return_value = mock_response_obj mock_model.get_avro_item.return_value = mock_response_obj mock_model.put_item.return_value = None mock_report_model.AccountingStatementExport.return_value = mock_model resp = reports.add_report(*add_report_data) assert not mock_model.put_item.called assert mock_model.get_item.called assert mock_model.get_avro_item.called assert resp.status == 400 def test_get_user_params(): """Test _get_user_params. """ response = reports._get_user_params('205', 'DA,DT', 'txt', 'es_ES') assert response == '205__DA,DT__es_ES__txt' response = reports._get_user_params('205', 'v', 'txt', 'es_ES') assert response == '205__es_ES__txt__v' def test_get_user_id_type(): """Test _get_user_id_type """ response = reports._get_user_id_type('18805', 'vendor') assert response == '18805L' response = reports._get_user_id_type('2222', 'subaccount') assert response == '2222S' response = reports._get_user_id_type('8869', 'garbage') assert response == '8869L' @patch('ows_accounting.logic.reports.workflow_service') @patch('ows_accounting.logic.reports.accounting_statement_export') @patch('ows_accounting.logic.reports.response.send_to_sentry') @patch('ows_accounting.logic.reports.response.create_error_response') def test_add_report_conflict( mock_create_error_response, mock_send_to_sentry, mock_report_model, mock_swf, add_report_data): """Test a workflow conflict returns a 409 error Response. """ conflict_response_obj = MagicMock() conflict_response_obj.status = 409 mock_response_obj = MagicMock() mock_response_obj.message = {'status': config.STATUS_DOESNOTEXIST} mock_response_obj.__bool__.return_value = False mock_response_obj.status = 400 mock_create_error_response.return_value = conflict_response_obj mock_model = MagicMock() mock_model.get_item.return_value = mock_response_obj mock_model.put_item.return_value = None mock_report_model.AccountingStatementExport.return_value = mock_model mock_workflow_id_resp = MagicMock() mock_workflow_id_resp.message = 'test_workflow_id' mock_workflow_id_resp.status = 200 mock_exe_workflow_resp = MagicMock() mock_exe_workflow_resp.__bool__.return_value = False mock_exe_workflow_resp.message = 'Workflow executed already!' mock_exe_workflow_resp.status = 500 mock_swf_obj = MagicMock() mock_swf_obj.generate_workflow_id.return_value = mock_workflow_id_resp mock_swf_obj.execute_workflow.return_value = mock_exe_workflow_resp mock_swf.WorkflowService.return_value = mock_swf_obj response = reports.add_report(*add_report_data) params = dict( user_id='18805', user_type='label', period_ids='202,203,204', transaction_types='DA,DT', file_format='txt', locale='en_US', client_email='test@example.com', report_version=feature.DEFAULT_REPORT_VERSION) mock_swf_obj.generate_workflow_id.assert_any_call( config.WORKFLOW_ID_PREFIX, **params) assert not mock_model.put_item.called assert mock_model.get_item.called assert mock_swf_obj.generate_workflow_id.called mock_send_to_sentry.assert_called_with( conflict_response_obj, error.ERROR_CODE_CONFLICT) assert response.status == 409 @patch('ows_accounting.logic.reports.contact') def test_get_email(mock_contact): """Test _get_email """ mock_contact.get_email_by_vend_contact_id.return_value = response.Response( 'from_contact_id') mock_contact.get_email_by_subaccount_id.return_value = response.Response( 'from_subaccount_id') mock_contact.get_email_by_vendor_id.return_value = response.Response( 'from_vendor_id') email = reports._get_email(8869, 'label', 12312) assert email == 'from_contact_id' email = reports._get_email(8869, 'label', None) assert email == 'from_vendor_id' email = reports._get_email(8869, 'subaccount', None) assert email == 'from_subaccount_id' @patch('ows_accounting.logic.reports.contact') def test_get_email_no_email_found(mock_contact): """Test _get_email which returns None. """ mock_contact.get_email_by_vend_contact_id.return_value = False mock_contact.get_email_by_subaccount_id.return_value = False mock_contact.get_email_by_vendor_id.return_value = False email = reports._get_email(8869, 'label', 12312) assert not email email = reports._get_email(8869, 'label', None) assert not email email = reports._get_email(8869, 'subaccount', None) assert not email