"""Unit tests for tasks of MRC Workflow.""" from unittest.mock import MagicMock from unittest.mock import patch from boto3.exceptions import S3UploadFailedError from garcon_contrib.dynamo_feed_status import garcon_feed_status import pytest from feed_ingestion.flows.mrc import config from feed_ingestion.flows.mrc import tasks _date = '2021-03-24' def mock_remove_files_from_path(activity, path, return_deleted_files=False): """Return a deleted file.""" files = { 's3.files_removed': ['filename.xlsx', ], } return files @pytest.fixture def expected_bootstrap_response(): """Response for bootstrap task.""" return { 'feed_name': config.feed_name, 'secrets_path': config.secrets_path, 'date': _date, 'file': {'files': [{'file_name': 'Orchard_AR_Daily_20210324.xlsx'}]}, 's3_archive_path': 's3://dev-cucumbers/MRC/archives/2021-03-24/', 's3_mapping_archive_path': 's3://dev-cucumbers/MRC/archives/{mapping}/{date}', 's3_preprocessed_path': 's3://dev-cucumbers/MRC/preprocessed/2021-03-24/', 'processed_filename': 'Orchard_AR_Daily_20210324.xlsx', 'reload': None, 'file_pattern': 'Orchard_AR_Daily_{date}.xlsx', 'temp_staging_raw_table': ( 'temp_staging_raw_mrc_20210324'), 's3_temp_staging_raw_bucket': ( 's3://dev-cucumbers/MRC/preprocessed/' '2021-03-24/Orchard_AR_Daily_20210324.csv.gz'), 's3_mapping_temp_staging_raw_bucket': 's3://dev-cucumbers/MRC/preprocessed/{mapping}/{date}/', 'mapping_staging_raw_tables': { 'Artist_Metadata': 'staging_raw_mrc_artist_metadata', 'Collection_Metadata': 'staging_raw_mrc_collection_metadata', 'Song_Mapping': 'staging_raw_mrc_song_mapping', 'Song_Metadata': 'staging_raw_mrc_song_metadata'}, 'mapping_temp_staging_raw_tables': { 'Artist_Metadata': 'temp_staging_raw_mrc_artist_metadata_20210324', 'Collection_Metadata': 'temp_staging_raw_mrc_collection_metadata_20210324', 'Song_Mapping': 'temp_staging_raw_mrc_song_mapping_20210324', 'Song_Metadata': 'temp_staging_raw_mrc_song_metadata_20210324'}, 'staging_raw_table': config.snowflake_table_names['staging_raw'], 'temp_table_pattern': 'temp_staging_raw_mrc'} def test_bootstrap( expected_bootstrap_response): """Test bootstrap task.""" result = tasks.bootstrap( activity=MagicMock(), date=_date, reload=None) assert result == expected_bootstrap_response @patch('feed_ingestion.flows.mrc.tasks.' 'garcon_feed_status.delete_status') @patch('feed_ingestion.flows.mrc.tasks.remove_files_from_path', mock_remove_files_from_path) def test_clear_s3_folders_if_reload( mock_delete_status, expected_bootstrap_response): """The feed status should be deleted.""" tasks.clear_s3_folders( activity=MagicMock(), reload='True', date=_date) mock_delete_status.assert_called_with( config.feed_name, _date) @pytest.fixture def mock_set_overall_status(): """Yield overall status.""" overall_status_path = ( 'feed_ingestion.flows.mrc.tasks.' 'garcon_feed_status.set_overall_status') with patch(overall_status_path) as overall_status: yield overall_status @pytest.fixture def process_drop_files_context(expected_bootstrap_response): """Context for task process_drop_files.""" file = { 'files': [ { 'file_name': ( 'Orchard_AR_Daily_20210324.xlsx'), 'found': True, 'file_size': 1}]} return { 'activity': MagicMock(), 'feed_name': config.feed_name, 'date': _date, 's3_archive_path': expected_bootstrap_response['s3_archive_path'], 's3_preprocessed_path': expected_bootstrap_response[ 's3_preprocessed_path'], 'processed_filename': expected_bootstrap_response[ 'processed_filename'], 'file': file} @patch('feed_ingestion.flows.mrc.tasks._process_data') @patch('feed_ingestion.flows.mrc.tasks.s3utils') def test_process_drop_files( mock_s3utils, mock_process_data, process_drop_files_context): """Should process and upload files.""" tasks.process_drop_files(**process_drop_files_context) assert mock_process_data.called assert mock_s3utils.upload_processed_to_s3.called @patch('feed_ingestion.flows.mrc.tasks._process_data') @patch('feed_ingestion.flows.mrc.tasks.s3utils') def test_process_drop_files_with_upload_error( mock_s3utils, mock_process_data, process_drop_files_context, mock_set_overall_status): """Should set status NOT_AVAILABLE.""" def sf_raise_error(csvfile, s3_path, expected_bucket_owner=config.expected_bucket_owner): raise S3UploadFailedError(500, 'Service error.') mock_s3utils.upload_processed_to_s3.side_effect = sf_raise_error with pytest.raises(S3UploadFailedError): tasks.process_drop_files(**process_drop_files_context) assert mock_process_data.called assert mock_s3utils.upload_processed_to_s3.called mock_set_overall_status.assert_called_with( '_'.join([config.feed_name, config.feed_name]), _date, garcon_feed_status.STATUS_NOT_AVAILABLE)