Esempio n. 1
0
 def setUp(self):
     super(SentryExceptionHandlerTest, self).setUp()
     self.client = get_client()
     self.handler = SentryDjangoHandler(self.client)
Esempio n. 2
0
 def setUp(self):
     self.raven = get_client()
     self.handler = SentryDjangoHandler(self.raven)
     self.handler.install()
     self.addCleanup(self.handler.uninstall)
Esempio n. 3
0
class SentryExceptionHandlerTest(TestCase):
    @fixture
    def request(self):
        return make_request()

    @fixture
    def exc_info(self):
        return (ValueError, ValueError('lol world'), None)

    def setUp(self):
        super(SentryExceptionHandlerTest, self).setUp()
        self.client = get_client()
        self.handler = SentryDjangoHandler(self.client)

    @mock.patch.object(MockClient, 'captureException')
    @mock.patch('sys.exc_info')
    def test_does_capture_exception(self, exc_info, captureException):
        exc_info.return_value = self.exc_info
        self.handler.exception_handler(request=self.request)

        captureException.assert_called_once_with(exc_info=self.exc_info, request=self.request)

    @mock.patch.object(MockClient, 'send')
    @mock.patch('sys.exc_info')
    def test_does_exclude_filtered_types(self, exc_info, mock_send):
        exc_info.return_value = self.exc_info
        try:
            self.client.ignore_exceptions = set(['ValueError'])

            self.handler.exception_handler(request=self.request)
        finally:
            self.client.ignore_exceptions.clear()

        assert not mock_send.called

    @mock.patch.object(MockClient, 'send')
    @mock.patch('sys.exc_info')
    def test_ignore_exceptions_with_expression_match(self, exc_info, mock_send):
        exc_info.return_value = self.exc_info

        try:
            if not PY2:
                self.client.ignore_exceptions = set(['builtins.*'])
            else:
                self.client.ignore_exceptions = set(['exceptions.*'])
            self.handler.exception_handler(request=self.request)
        finally:
            self.client.ignore_exceptions.clear()

        assert not mock_send.called

    @mock.patch.object(MockClient, 'send')
    @mock.patch('sys.exc_info')
    def test_ignore_exceptions_with_module_match(self, exc_info, mock_send):
        exc_info.return_value = self.exc_info

        try:
            if not PY2:
                self.client.ignore_exceptions = set(['builtins.ValueError'])
            else:
                self.client.ignore_exceptions = set(['exceptions.ValueError'])
            self.handler.exception_handler(request=self.request)
        finally:
            self.client.ignore_exceptions.clear()

        assert not mock_send.called
Esempio n. 4
0
class DjangoClientTest(TestCase):
    # Fixture setup/teardown
    urls = 'tests.contrib.django.urls'

    def setUp(self):
        self.raven = get_client()
        self.handler = SentryDjangoHandler(self.raven)
        self.handler.install()
        self.addCleanup(self.handler.uninstall)

    def test_basic(self):
        self.raven.captureMessage(message='foo')
        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert 'sentry.interfaces.Message' in event
        message = event['sentry.interfaces.Message']
        assert message['message'] == 'foo'
        assert event['level'] == logging.ERROR
        assert event['message'] == 'foo'
        assert isinstance(event['timestamp'], datetime.datetime)

    def test_signal_integration(self):
        try:
            int(None)
        except Exception:
            got_request_exception.send(sender=self.__class__, request=None)
        else:
            self.fail('Expected an exception.')

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert 'exception' in event
        exc = event['exception']['values'][-1]
        assert exc['type'] == 'TypeError'
        assert exc['value'], "int() argument must be a string or a number == not 'NoneType'"
        assert event['level'] == logging.ERROR
        assert event['message'], "TypeError: int() argument must be a string or a number == not 'NoneType'"

    @pytest.mark.skipif(sys.version_info[:2] == (2, 6), reason='Python 2.6')
    def test_view_exception(self):
        path = reverse('sentry-raise-exc')
        self.assertRaises(Exception, self.client.get, path)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert 'exception' in event
        exc = event['exception']['values'][-1]
        assert exc['type'] == 'Exception'
        assert exc['value'] == 'view exception'
        assert event['level'] == logging.ERROR
        assert event['message'] == 'Exception: view exception'
        assert 'request' in event
        assert event['request']['url'] == 'http://testserver{}'.format(path)

    def test_request_data_unavailable_if_request_is_read(self):
        with Settings(**{MIDDLEWARE_ATTR: []}):
            path = reverse('sentry-readrequest-raise-exc')
            self.assertRaises(
                AppError,
                self.client.post,
                path,
                '{"a":"b"}',
                content_type='application/json')
        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert event['request']['data'] == '<unavailable>'

    def test_djangorestframeworkcompatmiddleware_fills_request_data(self):
        with Settings(**{MIDDLEWARE_ATTR: [
            'raven.contrib.django.middleware.DjangoRestFrameworkCompatMiddleware']}):
            path = reverse('sentry-readrequest-raise-exc')
            self.assertRaises(
                AppError,
                self.client.post,
                path,
                '{"a":"b"}',
                content_type='application/json')
        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert event['request']['data'] == '{"a":"b"}'

    def test_capture_event_with_request_middleware(self):
        path = reverse('sentry-trigger-event')
        resp = self.client.get(path)
        assert resp.status_code == 200

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert event['message'] == 'test'
        assert 'request' in event
        assert event['request']['url'] == 'http://testserver{}'.format(path)

    def test_user_info(self):
        with Settings(**{MIDDLEWARE_ATTR: [
                'django.contrib.sessions.middleware.SessionMiddleware',
                'django.contrib.auth.middleware.AuthenticationMiddleware']}):

            self.assertRaises(Exception, self.client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)
            assert 'user' in event

            user_info = event['user']
            assert user_info == {'ip_address': '127.0.0.1'}

            assert self.client.login(username='******', password='******')

            self.assertRaises(Exception, self.client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)
            assert 'user' in event
            user_info = event['user']
            assert user_info == {
                'ip_address': '127.0.0.1',
                'username': self.user.username,
                'id': self.user.id,
                'email': self.user.email,
            }

    @pytest.mark.skipif(not DJANGO_15, reason='< Django 1.5')
    def test_get_user_info_abstract_user(self):

        from django.db import models
        from django.http import HttpRequest
        from django.contrib.auth.models import AbstractBaseUser

        class MyUser(AbstractBaseUser):
            USERNAME_FIELD = 'username'

            username = models.CharField(max_length=32)
            email = models.EmailField()

        user = MyUser(
            username='******',
            email='*****@*****.**',
            id=1,
        )

        request = HttpRequest()
        request.META['REMOTE_ADDR'] = '127.0.0.1'
        request.user = user
        user_info = self.raven.get_user_info(request)
        assert user_info == {
            'ip_address': '127.0.0.1',
            'username': user.username,
            'id': user.id,
            'email': user.email,
        }

        request = HttpRequest()
        request.META['REMOTE_ADDR'] = '127.0.0.1'
        request.META['HTTP_X_FORWARDED_FOR'] = '1.1.1.1, 2.2.2.2'
        request.user = user
        user_info = self.raven.get_user_info(request)
        assert user_info == {
            'ip_address': '1.1.1.1',
            'username': user.username,
            'id': user.id,
            'email': user.email,
        }

    @pytest.mark.skipif(not DJANGO_110, reason='< Django 1.10')
    def test_get_user_info_is_authenticated_property(self):
        from django.db import models
        from django.http import HttpRequest
        from django.contrib.auth.models import AbstractBaseUser

        class MyUser(AbstractBaseUser):
            USERNAME_FIELD = 'username'
            username = models.CharField(max_length=32)
            email = models.EmailField()

            @property
            def is_authenticated(self):
                return True

        user = MyUser(
            username='******',
            email='*****@*****.**',
            id=1,
        )

        request = HttpRequest()
        request.META['REMOTE_ADDR'] = '127.0.0.1'
        request.user = user
        user_info = self.raven.get_user_info(request)
        assert user_info == {
            'ip_address': '127.0.0.1',
            'username': user.username,
            'id': user.id,
            'email': user.email,
        }

        request = HttpRequest()
        request.META['REMOTE_ADDR'] = '127.0.0.1'
        request.META['HTTP_X_FORWARDED_FOR'] = '1.1.1.1, 2.2.2.2'
        request.user = user
        user_info = self.raven.get_user_info(request)
        assert user_info == {
            'ip_address': '1.1.1.1',
            'username': user.username,
            'id': user.id,
            'email': user.email,
        }

    def test_request_middleware_exception(self):
        with Settings(**{MIDDLEWARE_ATTR: ['tests.contrib.django.middleware.BrokenRequestMiddleware']}):
            self.assertRaises(ImportError, self.client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'ImportError'
            assert exc['value'] == 'request'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'ImportError: request'

    def test_response_middlware_exception(self):
        if django.VERSION[:2] < (1, 3):
            return
        with Settings(**{MIDDLEWARE_ATTR: ['tests.contrib.django.middleware.BrokenResponseMiddleware']}):
            self.assertRaises(ImportError, self.client.get, reverse('sentry-no-error'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'ImportError'
            assert exc['value'] == 'response'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'ImportError: response'

    def test_broken_500_handler_with_middleware(self):
        with Settings(BREAK_THAT_500=True, INSTALLED_APPS=['raven.contrib.django']):
            client = DjangoTestClient(REMOTE_ADDR='127.0.0.1')
            client.handler = MockSentryMiddleware(MockClientHandler())

            self.assertRaises(Exception, client.get, reverse('sentry-raise-exc'))
            assert len(self.raven.events) == 2 or 4  # TODO: ash remove duplicate client events
            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'Exception'
            assert exc['value'] == 'view exception'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'Exception: view exception'

            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'ValueError'
            assert exc['value'] == 'handler500'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'ValueError: handler500'

    def test_view_middleware_exception(self):
        with Settings(**{MIDDLEWARE_ATTR: ['tests.contrib.django.middleware.BrokenViewMiddleware']}):
            self.assertRaises(ImportError, self.client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'ImportError'
            assert exc['value'] == 'view'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'ImportError: view'

    @pytest.mark.skipif(DJANGO_18, reason='Django 1.8+ not supported')
    def test_template_name_as_view(self):
        self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        assert event['culprit'] == 'error.html'

    # def test_request_in_logging(self):
    #     resp = self.client.get(reverse('sentry-log-request-exc'))
    #     assert resp.status_code == 200

    #     assert len(self.raven.events) == 1
    #     event = self.raven.events.pop(0)

    #     assert event['culprit'] == 'tests.contrib.django.views in logging_request_exc'
    #     assert event['data']['META']['REMOTE_ADDR'] == '127.0.0.1'

    # TODO: Python bug #10805
    @pytest.mark.skipif(not PY2, reason='Python 2')
    def test_record_none_exc_info(self):
        # sys.exc_info can return (None, None, None) if no exception is being
        # handled anywhere on the stack. See:
        #  http://docs.python.org/library/sys.html#sys.exc_info
        record = logging.LogRecord(
            'foo',
            logging.INFO,
            pathname=None,
            lineno=None,
            msg='test',
            args=(),
            exc_info=(None, None, None),
        )
        handler = SentryHandler()
        handler.emit(record)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert event['message'] == 'test'

    def test_404_middleware(self):
        with Settings(**{MIDDLEWARE_ATTR: ['raven.contrib.django.middleware.Sentry404CatchMiddleware']}):
            resp = self.client.get('/non-existent-page')
            assert resp.status_code == 404

            assert len(self.raven.events) == 1, [e['message'] for e in self.raven.events]
            event = self.raven.events.pop(0)

            assert event['level'] == logging.INFO
            assert event['logger'] == 'http404'

            assert 'request' in event
            http = event['request']
            assert http['url'] == 'http://testserver/non-existent-page'
            assert http['method'] == 'GET'
            assert http['query_string'] == ''
            assert http['data'] is None

    def test_404_middleware_when_disabled(self):
        extra_settings = {
            MIDDLEWARE_ATTR: ['raven.contrib.django.middleware.Sentry404CatchMiddleware'],
            'SENTRY_CLIENT': 'tests.contrib.django.tests.DisabledMockClient',
        }
        with Settings(**extra_settings):
            resp = self.client.get('/non-existent-page')
            assert resp.status_code == 404
            assert self.raven.events == []

    def test_invalid_client(self):
        extra_settings = {
            'SENTRY_CLIENT': 'raven.contrib.django.DjangoClient',  # default
        }
        # Should return fallback client (MockClient)
        client = get_client('nonexistent.and.invalid')

        # client should be valid, and the same as with the next call.
        assert client is get_client()

        with Settings(**extra_settings):
            assert isinstance(get_client(), DjangoClient)

    def test_transport_specification(self):
        extra_settings = {
            'SENTRY_TRANSPORT': 'raven.transport.HTTPTransport',
            'SENTRY_DSN': 'http://*****:*****@example.com/1',
        }
        with Settings(**extra_settings):
            client = get_client(reset=True)
            assert type(client.remote.get_transport()) is HTTPTransport

    def test_response_error_id_middleware(self):
        # TODO: test with 500s
        with Settings(**{MIDDLEWARE_ATTR: [
                'raven.contrib.django.middleware.SentryResponseErrorIdMiddleware',
                'raven.contrib.django.middleware.Sentry404CatchMiddleware']}):
            resp = self.client.get('/non-existent-page')
            assert resp.status_code == 404
            headers = dict(resp.items())
            assert 'X-Sentry-ID' in headers
            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)
            assert event['event_id'] == headers['X-Sentry-ID']

    def test_get_client(self):
        assert get_client() == get_client()
        assert get_client('raven.base.Client').__class__ == Client
        assert get_client() == self.raven

        assert get_client('%s.%s' % (type(self.raven).__module__, type(self.raven).__name__)) == self.raven
        assert get_client() == self.raven

    def test_raw_post_data_partial_read(self):
        v = '{"foo": "bar"}'
        request = make_request()
        request.environ.update({
            'wsgi.input': StringIO(v + '\r\n\r\n'),
            'CONTENT_TYPE': 'application/octet-stream',
            'CONTENT_LENGTH': len(v),
        })
        request.read(1)

        self.raven.captureMessage(message='foo', request=request)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        assert 'request' in event
        http = event['request']
        assert http['method'] == 'POST'
        assert http['data'] == '<unavailable>'

    def test_read_post_data(self):
        request = make_request()
        request.POST = QueryDict("foo=bar&ham=spam")
        request.read(1)

        self.raven.captureMessage(message='foo', request=request)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        assert 'request' in event
        http = event['request']
        assert http['method'] == 'POST'
        assert http['data'] == {'foo': 'bar', 'ham': 'spam'}

    def test_request_capture(self):
        request = make_request()
        request.read(1)

        self.raven.captureMessage(message='foo', request=request)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        assert 'request' in event
        http = event['request']
        assert http['method'] == 'POST'
        assert http['data'] == '<unavailable>'
        assert 'headers' in http
        headers = http['headers']
        assert 'Content-Type' in headers, headers.keys()
        assert headers['Content-Type'] == 'text/html'
        env = http['env']
        assert 'SERVER_NAME' in env, env.keys()
        assert env['SERVER_NAME'] == 'testserver'
        assert 'SERVER_PORT' in env, env.keys()
        assert env['SERVER_PORT'] == '80'

    def test_marks_django_frames_correctly(self):
        self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        frames = event['exception']['values'][-1]['stacktrace']['frames']
        for frame in frames:
            if frame['module'].startswith('django.'):
                assert frame.get('in_app') is False

    def test_adds_site_to_tags(self):
        self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        tags = event['tags']
        assert 'site' in event['tags']
        assert tags['site'] == 'example.com'

    def test_adds_site_to_tags_fallback(self):
        with Settings(SITE_ID=12345):  # nonexistent site, should fallback to SITE_ID
            self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)

            tags = event['tags']
            assert 'site' in event['tags']
            assert tags['site'] == 12345

    def test_settings_site_overrides_contrib(self):
        self.raven.site = 'FOO'
        self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        tags = event['tags']
        assert 'site' in event['tags']
        assert tags['site'] == 'FOO'

    @mock.patch.object(WSGIRequest, 'build_absolute_uri')
    def test_suspicious_operation_in_build_absolute_uri(self, build_absolute_uri):
        build_absolute_uri.side_effect = SuspiciousOperation()
        request = make_request()
        request.META['HTTP_HOST'] = 'example.com'
        result = self.raven.get_data_from_request(request)
        build_absolute_uri.assert_called_once_with()
        assert 'request' in result
        assert result['request']['url'] == 'http://example.com/'
Esempio n. 5
0
 def setUp(self):
     super(SentryExceptionHandlerTest, self).setUp()
     self.client = get_client()
     self.handler = SentryDjangoHandler(self.client)
Esempio n. 6
0
class SentryExceptionHandlerTest(TestCase):
    @fixture
    def request(self):
        return make_request()

    @fixture
    def exc_info(self):
        return (ValueError, ValueError('lol world'), None)

    def setUp(self):
        super(SentryExceptionHandlerTest, self).setUp()
        self.client = get_client()
        self.handler = SentryDjangoHandler(self.client)

    @mock.patch.object(TempStoreClient, 'captureException')
    @mock.patch('sys.exc_info')
    def test_does_capture_exception(self, exc_info, captureException):
        exc_info.return_value = self.exc_info
        self.handler.exception_handler(request=self.request)

        captureException.assert_called_once_with(exc_info=self.exc_info, request=self.request)

    @mock.patch.object(TempStoreClient, 'send')
    @mock.patch('sys.exc_info')
    def test_does_exclude_filtered_types(self, exc_info, mock_send):
        exc_info.return_value = self.exc_info
        try:
            self.client.ignore_exceptions = set(['ValueError'])

            self.handler.exception_handler(request=self.request)
        finally:
            self.client.ignore_exceptions.clear()

        assert not mock_send.called

    @mock.patch.object(TempStoreClient, 'send')
    @mock.patch('sys.exc_info')
    def test_ignore_exceptions_with_expression_match(self, exc_info, mock_send):
        exc_info.return_value = self.exc_info

        try:
            if not PY2:
                self.client.ignore_exceptions = set(['builtins.*'])
            else:
                self.client.ignore_exceptions = set(['exceptions.*'])
            self.handler.exception_handler(request=self.request)
        finally:
            self.client.ignore_exceptions.clear()

        assert not mock_send.called

    @mock.patch.object(TempStoreClient, 'send')
    @mock.patch('sys.exc_info')
    def test_ignore_exceptions_with_module_match(self, exc_info, mock_send):
        exc_info.return_value = self.exc_info

        try:
            if not PY2:
                self.client.ignore_exceptions = set(['builtins.ValueError'])
            else:
                self.client.ignore_exceptions = set(['exceptions.ValueError'])
            self.handler.exception_handler(request=self.request)
        finally:
            self.client.ignore_exceptions.clear()

        assert not mock_send.called
Esempio n. 7
0
 def setUp(self):
     self.raven = get_client()
     self.handler = SentryDjangoHandler(self.raven)
     self.handler.install()
     self.addCleanup(self.handler.uninstall)
Esempio n. 8
0
class DjangoClientTest(TestCase):
    # Fixture setup/teardown
    urls = 'tests.contrib.django.urls'

    def setUp(self):
        self.raven = get_client()
        self.handler = SentryDjangoHandler(self.raven)
        self.handler.install()
        self.addCleanup(self.handler.uninstall)

    def test_basic(self):
        self.raven.captureMessage(message='foo')
        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert 'sentry.interfaces.Message' in event
        message = event['sentry.interfaces.Message']
        assert message['message'] == 'foo'
        assert event['level'] == logging.ERROR
        assert event['message'] == 'foo'
        assert isinstance(event['timestamp'], datetime.datetime)

    def test_signal_integration(self):
        try:
            int(None)
        except Exception:
            got_request_exception.send(sender=self.__class__, request=None)
        else:
            self.fail('Expected an exception.')

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert 'exception' in event
        exc = event['exception']['values'][-1]
        assert exc['type'] == 'TypeError'
        assert exc['value'], "int() argument must be a string or a number == not 'NoneType'"
        assert event['level'] == logging.ERROR
        assert event['message'], "TypeError: int() argument must be a string or a number == not 'NoneType'"

    @pytest.mark.skipif(sys.version_info[:2] == (2, 6), reason='Python 2.6')
    def test_view_exception(self):
        self.assertRaises(Exception, self.client.get, reverse('sentry-raise-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert 'exception' in event
        exc = event['exception']['values'][-1]
        assert exc['type'] == 'Exception'
        assert exc['value'] == 'view exception'
        assert event['level'] == logging.ERROR
        assert event['message'] == 'Exception: view exception'

    def test_user_info(self):
        with Settings(MIDDLEWARE_CLASSES=[
                'django.contrib.sessions.middleware.SessionMiddleware',
                'django.contrib.auth.middleware.AuthenticationMiddleware']):
            user = User(username='******', email='*****@*****.**')
            user.set_password('admin')
            user.save()

            self.assertRaises(Exception, self.client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)
            assert 'user' not in event

            assert self.client.login(username='******', password='******')

            self.assertRaises(Exception, self.client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)
            assert 'user' in event
            user_info = event['user']
            assert user_info == {
                'username': user.username,
                'id': user.id,
                'email': user.email,
            }

    @pytest.mark.skipif(not DJANGO_15, reason='< Django 1.5')
    def test_get_user_info_abstract_user(self):
        from django.db import models
        from django.contrib.auth.models import AbstractBaseUser

        class MyUser(AbstractBaseUser):
            USERNAME_FIELD = 'username'

            username = models.CharField(max_length=32)
            email = models.EmailField()

        user = MyUser(
            username='******',
            email='*****@*****.**',
            id=1,
        )
        user_info = self.raven.get_user_info(user)
        assert user_info == {
            'username': user.username,
            'id': user.id,
            'email': user.email,
        }

    @pytest.mark.skipif(not DJANGO_110, reason='< Django 1.10')
    def test_get_user_info_is_authenticated_property(self):
        from django.db import models
        from django.contrib.auth.models import AbstractBaseUser

        class MyUser(AbstractBaseUser):
            USERNAME_FIELD = 'username'
            username = models.CharField(max_length=32)
            email = models.EmailField()

            @property
            def is_authenticated(self):
                return True

        user = MyUser(
            username='******',
            email='*****@*****.**',
            id=1,
        )
        user_info = self.raven.get_user_info(user)
        assert user_info == {
            'username': user.username,
            'id': user.id,
            'email': user.email,
        }

    def test_request_middleware_exception(self):
        with Settings(MIDDLEWARE_CLASSES=['tests.contrib.django.middleware.BrokenRequestMiddleware']):
            self.assertRaises(ImportError, self.client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'ImportError'
            assert exc['value'] == 'request'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'ImportError: request'

    def test_response_middlware_exception(self):
        if django.VERSION[:2] < (1, 3):
            return
        with Settings(MIDDLEWARE_CLASSES=['tests.contrib.django.middleware.BrokenResponseMiddleware']):
            self.assertRaises(ImportError, self.client.get, reverse('sentry-no-error'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'ImportError'
            assert exc['value'] == 'response'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'ImportError: response'

    def test_broken_500_handler_with_middleware(self):
        with Settings(BREAK_THAT_500=True, INSTALLED_APPS=['raven.contrib.django']):
            client = DjangoTestClient(REMOTE_ADDR='127.0.0.1')
            client.handler = MockSentryMiddleware(MockClientHandler())

            self.assertRaises(Exception, client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 2
            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'Exception'
            assert exc['value'] == 'view exception'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'Exception: view exception'

            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'ValueError'
            assert exc['value'] == 'handler500'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'ValueError: handler500'

    def test_view_middleware_exception(self):
        with Settings(MIDDLEWARE_CLASSES=['tests.contrib.django.middleware.BrokenViewMiddleware']):
            self.assertRaises(ImportError, self.client.get, reverse('sentry-raise-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)

            assert 'exception' in event
            exc = event['exception']['values'][-1]
            assert exc['type'] == 'ImportError'
            assert exc['value'] == 'view'
            assert event['level'] == logging.ERROR
            assert event['message'] == 'ImportError: view'

    @pytest.mark.skipif(DJANGO_18, reason='Django 1.8+ not supported')
    def test_template_name_as_view(self):
        self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        assert event['culprit'] == 'error.html'

    # def test_request_in_logging(self):
    #     resp = self.client.get(reverse('sentry-log-request-exc'))
    #     assert resp.status_code == 200

    #     assert len(self.raven.events) == 1
    #     event = self.raven.events.pop(0)

    #     assert event['culprit'] == 'tests.contrib.django.views in logging_request_exc'
    #     assert event['data']['META']['REMOTE_ADDR'] == '127.0.0.1'

    # TODO: Python bug #10805
    @pytest.mark.skipif(not PY2, reason='Python 2')
    def test_record_none_exc_info(self):
        # sys.exc_info can return (None, None, None) if no exception is being
        # handled anywhere on the stack. See:
        #  http://docs.python.org/library/sys.html#sys.exc_info
        record = logging.LogRecord(
            'foo',
            logging.INFO,
            pathname=None,
            lineno=None,
            msg='test',
            args=(),
            exc_info=(None, None, None),
        )
        handler = SentryHandler()
        handler.emit(record)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)
        assert event['message'] == 'test'

    def test_404_middleware(self):
        with Settings(MIDDLEWARE_CLASSES=['raven.contrib.django.middleware.Sentry404CatchMiddleware']):
            resp = self.client.get('/non-existent-page')
            assert resp.status_code == 404

            assert len(self.raven.events) == 1, [e['message'] for e in self.raven.events]
            event = self.raven.events.pop(0)

            assert event['level'] == logging.INFO
            assert event['logger'] == 'http404'

            assert 'request' in event
            http = event['request']
            assert http['url'] == 'http://testserver/non-existent-page'
            assert http['method'] == 'GET'
            assert http['query_string'] == ''
            assert http['data'] is None

    def test_404_middleware_when_disabled(self):
        extra_settings = {
            'MIDDLEWARE_CLASSES': ['raven.contrib.django.middleware.Sentry404CatchMiddleware'],
            'SENTRY_CLIENT': 'tests.contrib.django.tests.DisabledTempStoreClient',
        }
        with Settings(**extra_settings):
            resp = self.client.get('/non-existent-page')
            assert resp.status_code == 404
            assert self.raven.events == []

    def test_invalid_client(self):
        extra_settings = {
            'SENTRY_CLIENT': 'raven.contrib.django.DjangoClient',  # default
        }
        # Should return fallback client (TempStoreClient)
        client = get_client('nonexistent.and.invalid')

        # client should be valid, and the same as with the next call.
        assert client is get_client()

        with Settings(**extra_settings):
            assert isinstance(get_client(), DjangoClient)

    def test_transport_specification(self):
        extra_settings = {
            'SENTRY_TRANSPORT': 'raven.transport.HTTPTransport',
            'SENTRY_DSN': 'http://*****:*****@example.com/1',
        }
        with Settings(**extra_settings):
            client = get_client(reset=True)
            assert type(client.remote.get_transport()) is HTTPTransport

    def test_response_error_id_middleware(self):
        # TODO: test with 500s
        with Settings(MIDDLEWARE_CLASSES=[
                'raven.contrib.django.middleware.SentryResponseErrorIdMiddleware',
                'raven.contrib.django.middleware.Sentry404CatchMiddleware']):
            resp = self.client.get('/non-existent-page')
            assert resp.status_code == 404
            headers = dict(resp.items())
            assert 'X-Sentry-ID' in headers
            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)
            assert event['event_id'] == headers['X-Sentry-ID']

    def test_get_client(self):
        assert get_client() == get_client()
        assert get_client('raven.base.Client').__class__ == Client
        assert get_client() == self.raven

        assert get_client('%s.%s' % (type(self.raven).__module__, type(self.raven).__name__)) == self.raven
        assert get_client() == self.raven

    def test_raw_post_data_partial_read(self):
        v = '{"foo": "bar"}'
        request = make_request()
        request.environ.update({
            'wsgi.input': StringIO(v + '\r\n\r\n'),
            'CONTENT_TYPE': 'application/octet-stream',
            'CONTENT_LENGTH': len(v),
        })
        request.read(1)

        self.raven.captureMessage(message='foo', request=request)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        assert 'request' in event
        http = event['request']
        assert http['method'] == 'POST'
        assert http['data'] == '<unavailable>'

    def test_read_post_data(self):
        request = make_request()
        request.POST = QueryDict("foo=bar&ham=spam")
        request.read(1)

        self.raven.captureMessage(message='foo', request=request)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        assert 'request' in event
        http = event['request']
        assert http['method'] == 'POST'
        assert http['data'] == {'foo': 'bar', 'ham': 'spam'}

    def test_request_capture(self):
        request = make_request()
        request.read(1)

        self.raven.captureMessage(message='foo', request=request)

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        assert 'request' in event
        http = event['request']
        assert http['method'] == 'POST'
        assert http['data'] == '<unavailable>'
        assert 'headers' in http
        headers = http['headers']
        assert 'Content-Type' in headers, headers.keys()
        assert headers['Content-Type'] == 'text/html'
        env = http['env']
        assert 'SERVER_NAME' in env, env.keys()
        assert env['SERVER_NAME'] == 'testserver'
        assert 'SERVER_PORT' in env, env.keys()
        assert env['SERVER_PORT'] == '80'

    def test_marks_django_frames_correctly(self):
        self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        frames = event['exception']['values'][-1]['stacktrace']['frames']
        for frame in frames:
            if frame['module'].startswith('django.'):
                assert frame.get('in_app') is False

    def test_adds_site_to_tags(self):
        self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        tags = event['tags']
        assert 'site' in event['tags']
        assert tags['site'] == 'example.com'

    def test_adds_site_to_tags_fallback(self):
        with Settings(SITE_ID=12345):  # nonexistent site, should fallback to SITE_ID
            self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

            assert len(self.raven.events) == 1
            event = self.raven.events.pop(0)

            tags = event['tags']
            assert 'site' in event['tags']
            assert tags['site'] == 12345

    def test_settings_site_overrides_contrib(self):
        self.raven.site = 'FOO'
        self.assertRaises(TemplateSyntaxError, self.client.get, reverse('sentry-template-exc'))

        assert len(self.raven.events) == 1
        event = self.raven.events.pop(0)

        tags = event['tags']
        assert 'site' in event['tags']
        assert tags['site'] == 'FOO'

    @mock.patch.object(WSGIRequest, 'build_absolute_uri')
    def test_suspicious_operation_in_build_absolute_uri(self, build_absolute_uri):
        build_absolute_uri.side_effect = SuspiciousOperation()
        request = make_request()
        request.META['HTTP_HOST'] = 'example.com'
        result = self.raven.get_data_from_request(request)
        build_absolute_uri.assert_called_once_with()
        assert 'request' in result
        assert result['request']['url'] == 'http://example.com/'