"""Tests for the Flow.""" import datetime from unittest.mock import ANY from unittest.mock import MagicMock from unittest.mock import Mock from unittest.mock import call from garcon.contrib.dynamo_feed_status import \ feed_status_ingestion as feed_status import pytest from analytics_aggregation.flows.apple_music_sos import flow as apple_flow @pytest.fixture def mock_feed_status(monkeypatch): """Mock get_overall_status function.""" status_getter = MagicMock(name='status_getter') monkeypatch.setattr(feed_status, 'get_overall_status', status_getter) monkeypatch.setattr( 'analytics_aggregation.flows.apple_music_sos.tasks' '._find_range_to_process', status_getter.find_range_to_process) monkeypatch.setattr( 'analytics_aggregation.flows.apple_music_sos.utils.' 'is_apple_music_ingested', status_getter.is_apple_music_ingested) def get_status(*args, **kwargs): if status_getter.is_aggregated: return feed_status.STATUS_INGESTED return 'NOT_INGESTED' def is_apple_music_ingested(day): return status_getter.has_raw_data def find_range_to_process(*args, **kwargs): today = datetime.date.today() return { 'found_days': status_getter.has_raw_data, 'week_range': '{}_{}'.format( (today - datetime.timedelta(days=7)).isoformat(), today.isoformat()), 'start_date': (today - datetime.timedelta(days=7)).isoformat(), 'end_date': today.isoformat() } status_getter.side_effect = get_status status_getter.is_apple_music_ingested = is_apple_music_ingested status_getter.find_range_to_process.side_effect = find_range_to_process status_getter.has_raw_data = True status_getter.is_aggregated = False return status_getter def test_decider_if_reload_is_true(monkeypatch): """Test Flow decider if 'bootstrap.reload' is True.""" activity_creator = Mock() activity_creator.return_value.return_value = 'activity' monkeypatch.setattr('garcon.activity.create', activity_creator) scheduler = Mock(name='sheduler') scheduler.return_value.result = {'bootstrap.reload': True} flow = apple_flow.Flow() flow.decider(scheduler) scheduler.assert_has_calls([ call('bootstrap', 'activity'), call('check_sos_reports_ingested', 'activity', requires=[ANY]), call('populate_staging_sos', 'activity', requires=[ANY]), call('set_status_to_ingested', 'activity', requires=[ANY])]) def test_decider_if_reload_is_false(monkeypatch): """Test Flow decider if 'bootstrap.reload' is False.""" activity_creator = Mock() activity_creator.return_value.return_value = 'activity' monkeypatch.setattr('garcon.activity.create', activity_creator) scheduler = Mock(name='sheduler') scheduler.return_value.result = {'bootstrap.reload': False} flow = apple_flow.Flow() flow.decider(scheduler) scheduler.assert_has_calls([ call('bootstrap', 'activity'), call('check_sos_reports_ingested', 'activity', requires=[ANY]), call('populate_staging_sos', 'activity', requires=[ANY]), call('set_status_to_ingested', 'activity', requires=[ANY])]) def test_for_decider_stop_after_bootstrap(): """Test for Flow decider.""" activity = Mock() activity.result = {'bootstrap.stop': True} scheduler = Mock(return_value=activity) flow = apple_flow.Flow() flow.create = Mock(return_value='activity') flow.decider(scheduler) assert scheduler.call_count == 1 scheduler.assert_has_calls([call('bootstrap', 'activity')]) def test_for_decider_stop_after_check_apple_music_ingested(): """Test for Flow decider.""" activity = Mock() activity.result = {'check_apple_music_ingested.stop': True} scheduler = Mock(return_value=activity) flow = apple_flow.Flow() flow.create = Mock(return_value='activity') flow.decider(scheduler) assert scheduler.call_count == 2 scheduler.assert_has_calls([ call('bootstrap', 'activity'), call('check_sos_reports_ingested', 'activity', requires=[ANY])]) @pytest.mark.parametrize( 'reload', [ True, 'true', 'True', 'TRUE', False, 'false', 'False', 'FALSE', None ]) @pytest.mark.usefixtures('mock_feed_status') def test_reload_flag_is_always_boolean(reload, mock_garcon): """Test if reload flag is always boolean.""" flow = apple_flow.Flow() mock_garcon.run_flow(flow, reload=reload, stop_after='bootstrap') assert isinstance(mock_garcon.context['reload'], bool) def test_flow_fails_to_reload_without_date_range(mock_garcon): """Test flow fails to reload without date_range.""" flow = apple_flow.Flow() mock_garcon.run_flow(flow, reload=True, stop_after='bootstrap') assert mock_garcon.context == { 'bootstrap.message': "'reload' flag passed but date range is not specified.", 'bootstrap.stop': True, 'reload': True } def test_flow_fails_to_process_date_range_without_reload(mock_garcon): """Test flow fails to process date range without reload.""" flow = apple_flow.Flow() context_date_range = '2016-09-28_2016-10-05' mock_garcon.run_flow( flow, context_date_range=context_date_range, stop_after='bootstrap') assert mock_garcon.context == { 'bootstrap.message': "'explicit_date_range' specified but 'reload' flag omitted.", 'bootstrap.stop': True, 'context_date_range': context_date_range, } def test_flow_stops_if_already_aggregated(mock_feed_status, mock_garcon): """Test flow stops if already aggregated.""" flow = apple_flow.Flow() mock_feed_status.is_aggregated = True range_to_process = mock_feed_status.find_range_to_process() mock_garcon.run_flow(flow) assert mock_garcon.context == { 'bootstrap.date_range': { 'end_date': range_to_process['end_date'], 'start_date': range_to_process['start_date'], }, 'bootstrap.date_range_as_str': range_to_process['week_range'], 'bootstrap.message': 'Data was already aggregated for {} range'.format( range_to_process['week_range']), 'bootstrap.reload': False, 'bootstrap.stop': True } def test_flow_stops_if_there_is_no_data(mock_feed_status, mock_garcon): """Test flow stops if there is no data.""" flow = apple_flow.Flow() mock_feed_status.has_raw_data = False range_to_process = mock_feed_status.find_range_to_process() mock_garcon.run_flow(flow) assert mock_garcon.context == { 'bootstrap.message': 'There is no data to aggregate for {} range'.format( range_to_process['week_range']), 'bootstrap.stop': True } @pytest.mark.usefixtures('mock_feed_status') def test_checks_pass_without_reload_and_range(mock_garcon): """Test checks pass if no args specified and data is ok.""" flow = apple_flow.Flow() mock_garcon.run_flow(flow, stop_after='check_sos_reports_ingested') assert mock_garcon.context == { 'bootstrap.date_range': ANY, 'bootstrap.date_range_as_str': ANY, 'bootstrap.reload': False } @pytest.mark.usefixtures('mock_feed_status') def test_checks_pass_with_reload_and_range(mock_garcon): """Test checks pass if both reload and range specified and data is ok.""" flow = apple_flow.Flow() context_date_range = '2016-09-28_2016-10-05' mock_garcon.run_flow( flow, reload=True, context_date_range=context_date_range, stop_after='check_sos_reports_ingested') assert mock_garcon.context == { 'reload': True, 'bootstrap.reload': True, 'bootstrap.date_range': { 'start_date': '2016-09-28', 'end_date': '2016-10-05'}, 'bootstrap.date_range_as_str': context_date_range, 'context_date_range': context_date_range }