"""Unit tests for tasks of Membran Workflow.""" from datetime import date as date_module, timedelta from importlib import reload import os import shutil from tempfile import gettempdir from unittest.mock import MagicMock from unittest.mock import Mock from unittest.mock import patch from garcon_contrib.aws.utils import garcon_s3 from garcon_contrib.dynamo_feed_status import garcon_feed_status import pytest from feed_ingestion import tasks as common_tasks from feed_ingestion.flows.physical_warehouse_reports import tasks from feed_ingestion.tasks import s3_tasks from feed_ingestion.util import os_tools from feed_ingestion.util.aws import s3 as s3utils _date = '2018-05-01' @pytest.fixture def expected_bootstrap_response(): """Response for bootstrap task.""" return { 'date': '2018-05-01', 'source_files_dict': {'files': ['LabelStockReport.csv.gz']}, 'date_as_in_uuid': '20180501', 'feed_name': 'physical-warehouse-reports', 'secrets_path': 'physical-warehouse-reports', 'source_path': 'ftp/physical-reporting/membran/', 's3_drop_bucket': 'prod-orcd-ftp', 's3_archive_bucket': 's3://dev-cucumbers/physical-warehouse-reports/' 'archives/2018-05-01/', 's3_temp_staging_raw_bucket': 's3://dev-cucumbers/physical-warehouse-reports/' 'temp/2018-05-01/', 'drop_file_name': 'Label Stock Report.csv', 'staging_raw_date_col': 'download_date', 'staging_raw_table': 'staging_raw_membran', 'fact_analytics_table': 'fact_analytics', 'fact_analytics_error_table': 'fact_analytics_error', 'use_s3': False, 'date_format': 'DD-MM-YYYY', } @pytest.fixture(autouse=True) def mock_check_status(request, monkeypatch): """Mock check_status decorator. Mock so that the decorated function is kept intact and always get called. """ mock = MagicMock() # just return the function without any modifications mock.return_value = lambda f: f monkeypatch.setattr(common_tasks, 'check_status', value=mock) reload(tasks) # redecorate tasks yield monkeypatch.undo() reload(tasks) @pytest.fixture def mock_set_overall_status(): """Yield overall status.""" overall_status_path = ( 'feed_ingestion.flows.physical_warehouse_reports.' 'tasks.garcon_feed_status.set_overall_status') with patch(overall_status_path) as overall_status: yield overall_status @pytest.fixture def mock_get_overall_status(): """Yield overall status.""" overall_status_path = ( 'feed_ingestion.flows.physical_warehouse_reports.' 'tasks.garcon_feed_status.get_overall_status') with patch(overall_status_path) as overall_status: yield overall_status @pytest.fixture def mock_set_missing_files(): """Yield set_missing_files.""" path = ( 'feed_ingestion.flows.physical_warehouse_reports.' 'tasks.garcon_feed_status.set_missing_files') with patch(path) as set_missing_files: yield set_missing_files @pytest.fixture(params=[ ('2015-02-20', '2015-02-20'), ('2015-11-20', '2015-11-20'), ('', (date_module.today() - timedelta(days=1)).strftime('%Y-%m-%d')) ]) def context_date(request): """Fixture returning different dates. Fixture returning different dates as input and corresponding expected dates coming back from the bootstrap task. """ return request.param @patch('feed_ingestion.flows.physical_warehouse_reports.' 'tasks.garcon_feed_status') def test_bootstrap( garcon_feed_status_mock, expected_bootstrap_response): """Test bootstrap task.""" result = tasks.bootstrap( activity=MagicMock(), date=_date, reload=None) assert result == expected_bootstrap_response @patch('feed_ingestion.flows.physical_warehouse_reports.' 'tasks.garcon_feed_status') def test_grab_drop_files_s3_files_exist( garcon_feed_status_mock, monkeypatch, context_date, mock_set_missing_files, mock_set_overall_status): """Test grab_drop_files_s3: files exist.""" activity_mock = MagicMock() monkeypatch.setattr( garcon_feed_status, 'get_overall_status', value=MagicMock()) input_date, expected_date = context_date context = tasks.bootstrap(activity_mock, input_date, 'True') extract_bucket_path_mock = Mock() extract_bucket_path_mock.return_value = ( 'key_name', 'cucumbers/physical-warehouse-reports/archives/' 'date/LabelStockReport.csv.gz') copy_file_from_ftp_s3_to_theocrhard_mock = Mock() copy_file_from_ftp_s3_to_theocrhard_mock.return_value = \ {context.get('source_files_dict').get('files')[0]: True} monkeypatch.setattr( garcon_s3, 'extract_bucket_path', extract_bucket_path_mock) monkeypatch.setattr( s3_tasks, 'copy_file', copy_file_from_ftp_s3_to_theocrhard_mock) result = tasks.grab_drop_files_s3( activity_mock, context.get('feed_name'), context.get('date'), context.get('drop_file_name'), context.get('s3_drop_bucket'), context.get('source_path'), context.get('s3_archive_bucket') ) assert copy_file_from_ftp_s3_to_theocrhard_mock.called mock_set_missing_files.assert_not_called() assert result is None def test_grab_drop_files_s3_files_do_not_exist( monkeypatch, context_date, mock_set_missing_files, mock_set_overall_status): """Test grab_drop_files_s3: files doesn't exist.""" activity_mock = MagicMock() monkeypatch.setattr( garcon_feed_status, 'get_overall_status', value=MagicMock()) monkeypatch.setattr( garcon_feed_status, 'delete_status', value=MagicMock()) input_date, expected_date = context_date context = tasks.bootstrap(activity_mock, input_date, 'True') extract_bucket_path_mock = Mock() extract_bucket_path_mock.return_value = ( 'key_name', 'cucumbers/physical-warehouse-reports/archives/' 'date/LabelStockReport.csv.gz') copy_file_from_ftp_s3_to_theocrhard_mock = Mock() copy_file_from_ftp_s3_to_theocrhard_mock.return_value = \ {context.get('source_files_dict').get('files')[0]: False} monkeypatch.setattr( garcon_s3, 'extract_bucket_path', extract_bucket_path_mock) monkeypatch.setattr( s3_tasks, 'copy_file', copy_file_from_ftp_s3_to_theocrhard_mock) result = tasks.grab_drop_files_s3( activity_mock, context.get('feed_name'), context.get('date'), context.get('drop_file_name'), context.get('s3_drop_bucket'), context.get('source_path'), context.get('s3_archive_bucket') ) assert copy_file_from_ftp_s3_to_theocrhard_mock.called garcon_feed_status.set_overall_status.assert_called_with( context.get('feed_name'), expected_date, garcon_feed_status.STATUS_NOT_AVAILABLE) mock_set_missing_files.assert_called() assert result == {'stop': True} @patch('feed_ingestion.flows.physical_warehouse_reports.' 'tasks.garcon_feed_status') @patch.object(tasks, 'os') def test_gzip_and_put( os_mock, garcon_feed_status_mock, monkeypatch, context_date ): """Test gzip_and_put: successful operation.""" activity_mock = MagicMock() input_date, expected_date = context_date context = tasks.bootstrap(activity_mock, input_date, 'True') create_temp_dir = MagicMock(return_value=os.path.join( gettempdir(), str(context.get('feed_name')), str(context.get('date')))) download_from_s3 = MagicMock(return_value='16157363') upload_gzipfile_to_s3 = MagicMock(return_value=10) monkeypatch.setattr( os_tools, 'create_temp_dir', create_temp_dir) monkeypatch.setattr( s3utils, 'download_from_s3', download_from_s3) monkeypatch.setattr( s3utils, 'upload_to_s3', upload_gzipfile_to_s3) monkeypatch.setattr( garcon_feed_status, 'set_overall_status', value=MagicMock()) monkeypatch.setattr( shutil, 'rmtree', MagicMock(return_value='')) _gzip_local_file_mock = MagicMock() monkeypatch.setattr( tasks, '_gzip_local_file', _gzip_local_file_mock) tasks.gzip_and_put( activity_mock, context.get('date'), context.get('feed_name'), context.get('s3_archive_bucket'), context.get('s3_temp_staging_raw_bucket'), context.get('drop_file_name'), ) assert os_tools.create_temp_dir.call_count == 1 assert download_from_s3.call_count == 1 assert _gzip_local_file_mock.call_count == 1 assert upload_gzipfile_to_s3.call_count == 1