"""Unit tests for tasks of YouTube Facts Workflow.""" from datetime import date from unittest.mock import MagicMock from unittest.mock import patch from garcon_contrib.dynamo_feed_status import garcon_feed_status import pytest from feed_ingestion.flows.youtube_facts import config from feed_ingestion.flows.youtube_facts import tasks TASKS_PATH = 'feed_ingestion.flows.youtube_facts.tasks' @pytest.fixture def mock_activity(): """Return mock activity.""" return MagicMock() @pytest.fixture def mock_task_status(): """Yield task status.""" task_status_path = 'feed_ingestion.tasks.task_status' with patch(task_status_path) as task_status: task_status.is_completed_task.return_value = False task_status.mark_completed_task = MagicMock() yield task_status @pytest.fixture def mock_executor_context(): """Yield executor context.""" reg_executors_class_path = ( TASKS_PATH + '.registered_executors') with patch(reg_executors_class_path) as reg_executors: mock_executor = MagicMock() reg_executors.get = mock_executor yield mock_executor.return_value.return_value.__enter__.return_value @pytest.fixture(params=[ ('asset', 'theorchard'), ('asset', 'sme'), ('video', 'theorchard'), ('video', 'sme'), ]) def context_and_params(request): """Yield context, report_type, licensor.""" report_type = request.param[0] licensor = request.param[1] context = { 'activity': MagicMock(), 'date': '2019-11-01', 'licensor': licensor, 'report_type': report_type, 'reload': False} yield context, report_type, licensor @pytest.fixture def expected_bootstrap_response(context_and_params): """Response for bootstrap task.""" context, report_type, licensor = context_and_params yield { 'date': '2019-11-01', 'feed_name': 'youtube_facts_{}'.format(licensor), 'kwargs': {'licensor': licensor}, 'licensor': licensor, 'report_status_name': 'youtube_facts_{}_{}'.format( licensor, report_type), 'report_type': report_type} @patch(TASKS_PATH + '.garcon_feed_status.' 'get_overall_status', return_value=garcon_feed_status.STATUS_NOT_INGESTED) def test_bootstrap(mock_get_overall_status, context_and_params, expected_bootstrap_response): """Test bootstrap task.""" context, report_type, licensor = context_and_params result = tasks.bootstrap(**context) assert result == expected_bootstrap_response @patch(TASKS_PATH + '.date_module') @patch(TASKS_PATH + '.garcon_feed_status.' 'get_overall_status', return_value=garcon_feed_status.STATUS_NOT_INGESTED) def test_bootstrap_no_date_is_passed( mock_get_overall_status, mock_date_module, context_and_params): """Date should be set to today if it is not provided.""" context, report_type, licensor = context_and_params context['date'] = None mock_date_module.today.return_value = date(2000, 1, 1) assert tasks.bootstrap(**context)['date'] == '2000-01-01' @patch(TASKS_PATH + '.garcon_feed_status.' 'get_overall_status', return_value=garcon_feed_status.STATUS_NOT_INGESTED) def test_bootstrap_date_is_passed( mock_get_overall_status, context_and_params, expected_bootstrap_response): """Date should be returned if it is provided.""" context, report_type, licensor = context_and_params context['date'] = '2019-11-01' expected_bootstrap_response['date'] = '2019-11-01' assert tasks.bootstrap(**context) == expected_bootstrap_response def test_bootstrap_incorrect_licensor(context_and_params): """Incorrect licensor shall rise.""" context, report_type, licensor = context_and_params context['licensor'] = 'some_unrelated' with pytest.raises(ValueError): tasks.bootstrap(**context) def test_bootstrap_no_licensor(context_and_params): """Undefined licensor shall rise.""" context, report_type, licensor = context_and_params del context['licensor'] with pytest.raises(TypeError): tasks.bootstrap(**context) def test_bootstrap_incorrect_report_type(context_and_params): """Incorrect report_type shall rise.""" context, report_type, licensor = context_and_params context['report_type'] = 'something_unusual' with pytest.raises(ValueError): tasks.bootstrap(**context) def test_bootstrap_no_report_type(context_and_params): """Undefined report_type shall rise.""" context, report_type, licensor = context_and_params del context['report_type'] with pytest.raises(TypeError): tasks.bootstrap(**context) @patch(TASKS_PATH + '.garcon_feed_status.' 'get_overall_status', return_value=garcon_feed_status.STATUS_INGESTED) def test_bootstrap_should_return_stop_if_already_ingested( mock_get_overall_status, context_and_params): """Return stop when overall feed status is already ingested.""" context, report_type, licensor = context_and_params expected_bootstrap_response = {'stop': True} result = tasks.bootstrap(**context) mock_get_overall_status.assert_called_with( '_'.join([config.feed_name, licensor, report_type]), context['date']) assert result == expected_bootstrap_response @patch(TASKS_PATH + '.garcon_feed_status.get_overall_status') def test_check_staging_status(get_overall_status_mock, context_and_params): """Test check_staging_status_task.""" context, report_type, licensor = context_and_params get_overall_status_mock.return_value = 'INGESTED' result = tasks.check_staging_status(MagicMock(), '2019-11-01', report_type, licensor) expected_result = {'statuses': { report: 'INGESTED' for report in config.report_dynamo_status_names[report_type][licensor] }} assert result == expected_result @patch(TASKS_PATH + '.garcon_feed_status.get_overall_status') def test_check_staging_status_not_available( get_overall_status_mock, context_and_params): """Test check_staging_status_task.""" context, report_type, licensor = context_and_params get_overall_status_mock.return_value = False result = tasks.check_staging_status(MagicMock(), '2019-11-01', report_type, licensor) not_ingested = { report: False for report in config.report_dynamo_status_names[report_type][licensor] } expected_result = {'stop': True, 'message': f'Some dependant reports are not ready: ' f'{not_ingested}'} assert result == expected_result def test_load_demographics_table( context_and_params, mock_executor_context, mock_task_status, mock_activity): """Test test_load_demographics_table.""" context, report_type, licensor = context_and_params _date = '2019-11-01' tasks.load_demographics_table( activity=mock_activity, date=_date, feed_name='youtube_video_{}'.format(licensor), sfdb_params={}, kwargs={'licensor': licensor}) mock_executor_context.delete_from_demographics_table.assert_called_with( _date, licensor=licensor) mock_executor_context.load_demographics_data.assert_called_with( _date, licensor=licensor) def test_load_mapping_table_with_asset_report( context_and_params, mock_executor_context, mock_task_status, mock_activity): """Test test_load_demographics_table.""" context, report_type, licensor = context_and_params _date = '2019-11-01' tasks.load_mapping_table( activity=mock_activity, date=_date, feed_name='youtube_video_{}'.format(licensor), sfdb_params={}, report_type='asset') mock_executor_context.load_youtube_video_asset_type_mapping.\ assert_called_with(_date) def test_load_mapping_table_with_video_report( context_and_params, mock_executor_context, mock_task_status, mock_activity): """Test test_load_demographics_table.""" context, report_type, licensor = context_and_params _date = '2019-11-01' response = tasks.load_mapping_table( activity=mock_activity, date=_date, feed_name='youtube_video_{}'.format(licensor), sfdb_params={}, report_type='video') assert response == {'stop': True} mock_executor_context.load_youtube_video_asset_type_mapping. \ assert_not_called()