def setup(self):
     """Setup before each method"""
     self._app = AppIntegration(AppConfig(get_valid_config_dict('duo_admin')))
class TestAppIntegration(object):
    """Test class for the AppIntegration"""

    @patch.object(AppIntegration, 'type', Mock(return_value='type'))
    def __init__(self):
        self._app = None

    # Remove all abstractmethods so we can instantiate AppIntegration for testing
    # Also patch some abstractproperty attributes
    @patch.object(AppIntegration, '__abstractmethods__', frozenset())
    def setup(self):
        """Setup before each method"""
        self._app = AppIntegration(AppConfig(get_valid_config_dict('duo_admin')))

    @patch('logging.Logger.debug')
    def test_no_sleep(self, log_mock):
        """App Integration - App Base, No Sleep on First Poll"""
        self._app._sleep()
        log_mock.assert_called_with('Skipping sleep for first poll')

    @patch('time.sleep')
    @patch('app_integrations.apps.app_base.AppIntegration._sleep_seconds', Mock(return_value=1))
    def test_sleep(self, time_mock):
        """App Integration - App Base, Sleep"""
        self._app._poll_count = 1
        self._app._sleep()
        time_mock.assert_called_with(1)

    def test_validate_auth_(self):
        """App Integration - Validate Authentication Info"""
        assert_is_none(self._app._validate_auth())

    @raises(AppIntegrationConfigError)
    def test_validate_auth_empty(self):
        """App Integration - Validate Authentication Info, No Config Exception"""
        self._app._config.clear()
        self._app._validate_auth()

    @raises(AppIntegrationConfigError)
    def test_validate_auth_no_auth(self):
        """App Integration - Validate Authentication Info, No Auth Exception"""
        del self._app._config['auth']
        self._app._validate_auth()

    @raises(AppIntegrationConfigError)
    def test_validate_auth_missing_auth(self):
        """App Integration - Validate Authentication Info, Missing Auth Key Exception"""
        with patch.object(AppIntegration, 'required_auth_info') as auth_keys_mock:
            auth_keys_mock.return_value = {'new_auth_key'}
            self._app._validate_auth()

    def test_check_http_response_good(self):
        """App Integration - Check HTTP Response, Success"""
        response = Mock(status_code=200)
        assert_true(self._app._check_http_response(response))

    @patch('logging.Logger.error')
    def test_check_http_response_bad(self, log_mock):
        """App Integration - Check HTTP Response, Failure"""
        response = Mock(status_code=404, json=Mock(side_effect=[{'message': 'hey'}]))

        # Check to make sure this resulted in a return of False
        assert_false(self._app._check_http_response(response))

        # Make sure the logger was called with the proper info
        log_mock.assert_called_with('HTTP request failed for service \'%s\': [%d] %s',
                                    'type', 404, 'hey')

    def test_initialize(self):
        """App Integration - Initialize, Valid"""
        assert_true(self._app._initialize())

    @patch('logging.Logger.error')
    def test_initialize_running(self, log_mock):
        """App Integration - Initialize, Already Running"""
        self._app._config['current_state'] = 'running'
        assert_false(self._app._initialize())
        log_mock.assert_called_with('App already running for service \'%s\'.', 'type')

    def test_finalize(self):
        """App Integration - Finalize, Valid"""
        test_new_time = 50000000
        self._app._last_timestamp = test_new_time
        self._app._finalize()
        assert_equal(self._app._config.last_timestamp, test_new_time)

    @patch('logging.Logger.error')
    def test_finalize_zero_time(self, log_mock):
        """App Integration - Finalize, Zero Time Error"""
        self._app._finalize()
        log_mock.assert_called_with('Ending last timestamp is 0. This should not happen and '
                                    'is likely due to the subclass not setting this value.')

    @patch('logging.Logger.error')
    def test_finalize_same_time(self, log_mock):
        """App Integration - Finalize, Same Time Error"""
        self._app._last_timestamp = self._app._config.start_last_timestamp
        self._app._finalize()
        log_mock.assert_called_with('Ending last timestamp is the same as '
                                    'the beginning last timestamp')

    @patch('logging.Logger.debug')
    def test_gather_success(self, log_mock):
        """App Integration - Gather, Success"""
        with patch.object(AppIntegration, '_gather_logs') as subclass_gather_mock:
            subclass_gather_mock.return_value = ['log01', 'log02', 'log03']
            self._app._gather()
            log_mock.assert_called()
            assert_equal(log_mock.call_args_list[-1][0][0],
                         'Gather process for \'%s\' executed in %f seconds.')

    @patch('logging.Logger.error')
    def test_gather_no_logs(self, log_mock):
        """App Integration - Gather, Success"""
        with patch.object(AppIntegration, '_gather_logs') as subclass_gather_mock:
            subclass_gather_mock.return_value = []
            self._app._gather()
            log_mock.assert_called_with('Gather process for service \'%s\' was not able '
                                        'to poll any logs', 'type')

    @patch('app_integrations.apps.app_base.AppIntegration._finalize')
    @patch('app_integrations.apps.app_base.AppIntegration._sleep_seconds', Mock(return_value=1))
    @patch('app_integrations.config.AppConfig.remaining_ms', Mock(return_value=5000))
    def test_gather_entry(self, finalize_mock):
        """App Integration - Gather, Entry Point"""
        self._app.gather()
        finalize_mock.assert_called()

    @patch('app_integrations.apps.app_base.AppIntegration._gather')
    @patch('app_integrations.apps.app_base.AppIntegration._sleep_seconds', Mock(return_value=1))
    @patch('app_integrations.config.AppConfig.remaining_ms', Mock(side_effect=[5000, 2000]))
    def test_gather_multiple(self, gather_mock):
        """App Integration - Gather, Entry Point, Multiple Calls"""
        # 3 == number of 'seconds' this ran for. This is compared against the remaining_ms mock
        gather_mock.side_effect = [3, 3]
        self._app._more_to_poll = True
        self._app.gather()
        assert_equal(gather_mock.call_count, 2)

    @patch('app_integrations.apps.app_base.AppIntegration._finalize')
    def test_gather_running(self, finalize_mock):
        """App Integration - Gather, Entry Point, Already Running"""
        self._app._config['current_state'] = 'running'
        self._app.gather()
        finalize_mock.assert_not_called()