"""Unit tests for status-based task runner.""" from unittest.mock import Mock from unittest.mock import patch from garcon import runner import pytest from feed_ingestion.common import status_runner feed_name = 'feed_name' requisite_status = 'REQUISITE_STATUS' complete_status = 'COMPLETE_STATUS' different_status = 'DIFFERENT_STATUS' date_key = 'date_key' @pytest.fixture def mock_tasks(): """Mock tasks.""" return ['task-1', 'task-2'] @pytest.fixture def create(): """Run create.""" return status_runner.create(feed_name) class TestConstructsStatusRunner(object): """Test status runner construction.""" @pytest.fixture def mock_base_runner_init(self): """Mock BaseRunner constructor.""" with patch( 'feed_ingestion.common.status_runner.' 'runner.BaseRunner.__init__') as init: yield init @pytest.fixture def sync_runner( self, mock_base_runner_init, mock_tasks, create): """Create Sync runner.""" return create( 'Sync', requisite_status, mock_tasks, complete_status=complete_status, date_key=date_key) @pytest.fixture def async_runner(self, create): """Create Async runner.""" return create('Async') def test_return_sync_runner(self, sync_runner): """Return Sync runner.""" assert isinstance(sync_runner, runner.Sync) def test_return_async_runner(self, async_runner): """Return Async runner.""" assert isinstance(async_runner, runner.Async) def test_pass_requisite_status(self, sync_runner): """Pass requisite status.""" assert sync_runner.requisite_status == 'REQUISITE_STATUS' def test_pass_complete_status(self, sync_runner): """Pass complete status.""" assert sync_runner.complete_status == 'COMPLETE_STATUS' def test_pass_date_key(self, sync_runner): """Pass date key.""" assert sync_runner.date_key == 'date_key' def test_pass_tasks(self, mock_base_runner_init, sync_runner): """Pass tasks.""" mock_base_runner_init.assert_called_with(['task-1', 'task-2']) class TestStatusRunnerExecute(object): """Test status runner execute method.""" @pytest.fixture def mock_sync_execute(self): """Mock Sync.execute.""" with patch( 'feed_ingestion.common.status_runner.' 'runner.Sync.execute') as execute: execute.return_value = dict() yield execute @pytest.fixture def mock_get_overall_status_different(self): """Mock feed status different than REQUISITE_STATUS.""" with patch( 'feed_ingestion.common.status_runner.' 'garcon_feed_status.get_overall_status') as get_overall_status: get_overall_status.return_value = different_status yield get_overall_status @pytest.fixture def mock_get_overall_status_same(self): """Mock feed status same as REQUISITE_STATUS.""" with patch( 'feed_ingestion.common.status_runner.' 'garcon_feed_status.get_overall_status') as get_overall_status: get_overall_status.return_value = requisite_status yield get_overall_status @pytest.fixture def mock_set_overall_status(self): """Mock set_overall_status.""" with patch( 'feed_ingestion.common.status_runner.' 'garcon_feed_status.set_overall_status') as set_overall_status: yield set_overall_status @pytest.fixture def mock_activity(self): """Mock activity.""" activity = Mock() activity.name = 'mock-activity' return activity @pytest.fixture def mock_context(self): """Mock context.""" return {'date_key': '2017-01-01'} @pytest.fixture def execute( self, mock_tasks, mock_activity, mock_context, mock_sync_execute, create, mock_set_overall_status): """Run execute method.""" sync_runner = create( 'Sync', requisite_status, mock_tasks, complete_status=complete_status, date_key=date_key) return sync_runner.execute(mock_activity, mock_context) @pytest.fixture def execute_without_complete_status( self, mock_tasks, mock_activity, mock_context, mock_sync_execute, create): """Run execute method without complete status.""" sync_runner = create( 'Sync', requisite_status, mock_tasks, date_key=date_key) return sync_runner.execute(mock_activity, mock_context) def test_return_dict_when_current_status_different( self, mock_get_overall_status_different, execute): """Return dictionary when current status is not requisite status.""" assert isinstance(execute, dict) def test_return_dict_when_current_status_same( self, mock_get_overall_status_same, execute): """Return dictionary when current status is requisite status.""" assert isinstance(execute, dict) def test_execute_not_called_when_current_status_different( self, mock_get_overall_status_different, mock_sync_execute, execute): """Execute not called when current status is not requisite status.""" mock_sync_execute.assert_not_called def test_log_when_current_status_different( self, mock_activity, mock_get_overall_status_different, execute): """Log when current status is not requisite status.""" mock_activity.logger.info.assert_called_with( 'feed_name on 2017-01-01: status is currently DIFFERENT_STATUS, ' 'not REQUISITE_STATUS. Skipping tasks for mock-activity.') def test_execute_called_when_current_status_same( self, mock_activity, mock_context, mock_get_overall_status_same, mock_sync_execute, execute): """Execute called when current status is requisite status.""" mock_sync_execute.assert_called_with(mock_activity, mock_context) def test_log_when_current_status_same( self, mock_activity, mock_get_overall_status_same, execute): """Log when current status is requisite status.""" mock_activity.logger.info.assert_any_call( 'feed_name on 2017-01-01: status matches REQUISITE_STATUS. ' 'Running tasks for mock-activity.') def test_set_overall_status_when_complete_status_set( self, mock_get_overall_status_same, mock_set_overall_status, execute): """Set overall status when complete status is set.""" mock_set_overall_status.assert_called_with( 'feed_name', '2017-01-01', 'COMPLETE_STATUS') def test_log_when_overall_status_set( self, mock_activity, mock_get_overall_status_same, execute): """Log when overall status is set.""" mock_activity.logger.info.assert_any_call( 'feed_name on 2017-01-01: Updating overall status to ' 'COMPLETE_STATUS.') def test_do_not_set_overall_status_when_complete_status_unset( self, mock_get_overall_status_same, mock_set_overall_status, execute_without_complete_status): """Do not set overall status when complete status unset.""" mock_set_overall_status.assert_not_called