debian-python-raven/tests/base/tests.py

543 lines
19 KiB
Python

# -*- coding: utf-8 -*-
from __future__ import unicode_literals
import inspect
import mock
import raven
import time
import six
from raven.base import Client, ClientState
from raven.exceptions import RateLimited
from raven.transport import AsyncTransport
from raven.transport.http import HTTPTransport
from raven.utils.stacks import iter_stack_frames
from raven.utils.testutils import TestCase
class TempStoreClient(Client):
def __init__(self, **kwargs):
self.events = []
super(TempStoreClient, self).__init__(**kwargs)
def is_enabled(self):
return True
def send(self, **kwargs):
self.events.append(kwargs)
class ClientStateTest(TestCase):
def test_should_try_online(self):
state = ClientState()
self.assertEquals(state.should_try(), True)
def test_should_try_new_error(self):
state = ClientState()
state.status = state.ERROR
state.last_check = time.time()
state.retry_number = 1
self.assertEquals(state.should_try(), False)
def test_should_try_time_passed_error(self):
state = ClientState()
state.status = state.ERROR
state.last_check = time.time() - 10
state.retry_number = 1
self.assertEquals(state.should_try(), True)
def test_set_fail(self):
state = ClientState()
state.set_fail()
self.assertEquals(state.status, state.ERROR)
self.assertNotEquals(state.last_check, None)
self.assertEquals(state.retry_number, 1)
def test_set_success(self):
state = ClientState()
state.status = state.ERROR
state.last_check = 'foo'
state.retry_number = 0
state.set_success()
self.assertEquals(state.status, state.ONLINE)
self.assertEquals(state.last_check, None)
self.assertEquals(state.retry_number, 0)
def test_should_try_retry_after(self):
state = ClientState()
state.status = state.ERROR
state.last_check = time.time()
state.retry_number = 1
state.retry_after = 1
self.assertFalse(state.should_try())
def test_should_try_retry_after_passed(self):
state = ClientState()
state.status = state.ERROR
state.last_check = time.time() - 1
state.retry_number = 1
state.retry_after = 1
self.assertTrue(state.should_try())
class ClientTest(TestCase):
def setUp(self):
self.client = TempStoreClient()
def test_first_client_is_singleton(self):
from raven import base
base.Raven = None
client = Client()
client2 = Client()
assert base.Raven is client
assert client is not client2
@mock.patch('raven.transport.http.HTTPTransport.send')
@mock.patch('raven.base.ClientState.should_try')
def test_send_remote_failover(self, should_try, send):
should_try.return_value = True
client = Client(
dsn='sync+http://public:secret@example.com/1'
)
# test error
send.side_effect = Exception()
client.send_remote('sync+http://example.com/api/store', client.encode({}))
self.assertEquals(client.state.status, client.state.ERROR)
# test recovery
send.side_effect = None
client.send_remote('sync+http://example.com/api/store', client.encode({}))
self.assertEquals(client.state.status, client.state.ONLINE)
@mock.patch('raven.transport.http.HTTPTransport.send')
@mock.patch('raven.base.ClientState.should_try')
def test_send_remote_failover_with_retry_after(self, should_try, send):
should_try.return_value = True
client = Client(
dsn='sync+http://public:secret@example.com/1'
)
# test error
send.side_effect = RateLimited('foo', 5)
client.send_remote('sync+http://example.com/api/1/store/', client.encode({}))
self.assertEquals(client.state.status, client.state.ERROR)
self.assertEqual(client.state.retry_after, 5)
# test recovery
send.side_effect = None
client.send_remote('sync+http://example.com/api/1/store/', client.encode({}))
self.assertEquals(client.state.status, client.state.ONLINE)
self.assertEqual(client.state.retry_after, 0)
@mock.patch('raven.conf.remote.RemoteConfig.get_transport')
@mock.patch('raven.base.ClientState.should_try')
def test_async_send_remote_failover(self, should_try, get_transport):
should_try.return_value = True
async_transport = AsyncTransport()
async_transport.async_send = async_send = mock.Mock()
get_transport.return_value = async_transport
client = Client(
dsn='http://public:secret@example.com/1',
)
# test immediate raise of error
async_send.side_effect = Exception()
client.send_remote('http://example.com/api/1/store/', client.encode({}))
self.assertEquals(client.state.status, client.state.ERROR)
# test recovery
client.send_remote('http://example.com/api/1/store/', client.encode({}))
success_cb = async_send.call_args[0][2]
success_cb()
self.assertEquals(client.state.status, client.state.ONLINE)
# test delayed raise of error
client.send_remote('http://example.com/api/1/store/', client.encode({}))
failure_cb = async_send.call_args[0][3]
failure_cb(Exception())
self.assertEquals(client.state.status, client.state.ERROR)
@mock.patch('raven.base.Client.send_remote')
@mock.patch('raven.base.time.time')
def test_send(self, time, send_remote):
time.return_value = 1328055286.51
client = Client(
dsn='http://public:secret@example.com/1',
)
client.send(**{
'foo': 'bar',
})
send_remote.assert_called_once_with(
url='http://example.com/api/1/store/',
data=client.encode({'foo': 'bar'}),
headers={
'User-Agent': 'raven-python/%s' % (raven.VERSION,),
'Content-Type': 'application/octet-stream',
'Content-Encoding': client.get_content_encoding(),
'X-Sentry-Auth': (
'Sentry sentry_timestamp=1328055286.51, '
'sentry_client=raven-python/%s, sentry_version=6, '
'sentry_key=public, '
'sentry_secret=secret' % (raven.VERSION,))
},
)
@mock.patch('raven.base.Client.send_remote')
@mock.patch('raven.base.time.time')
def test_send_with_auth_header(self, time, send_remote):
time.return_value = 1328055286.51
client = Client(
dsn='http://public:secret@example.com/1',
)
client.send(auth_header='foo', **{
'foo': 'bar',
})
send_remote.assert_called_once_with(
url='http://example.com/api/1/store/',
data=client.encode({'foo': 'bar'}),
headers={
'User-Agent': 'raven-python/%s' % (raven.VERSION,),
'Content-Type': 'application/octet-stream',
'Content-Encoding': client.get_content_encoding(),
'X-Sentry-Auth': 'foo',
},
)
@mock.patch('raven.transport.http.HTTPTransport.send')
@mock.patch('raven.base.ClientState.should_try')
def test_raise_exception_on_send_error(self, should_try, _send_remote):
should_try.return_value = True
client = Client(
dsn='sync+http://public:secret@example.com/1',
)
# Test for the default behaviour in which a send error is handled by the client
_send_remote.side_effect = Exception()
client.capture('Message', data={}, date=None, time_spent=10,
extra={}, stack=None, tags=None, message='Test message')
assert client.state.status == client.state.ERROR
# Test for the case in which a send error is raised to the calling frame.
client = Client(
dsn='sync+http://public:secret@example.com/1',
raise_send_errors=True,
)
with self.assertRaises(Exception):
client.capture('Message', data={}, date=None, time_spent=10,
extra={}, stack=None, tags=None, message='Test message')
def test_encode_decode(self):
data = {'foo': 'bar'}
encoded = self.client.encode(data)
self.assertTrue(type(encoded), str)
self.assertEquals(data, self.client.decode(encoded))
def test_get_public_dsn(self):
client = Client('http://public:secret@example.com/1')
public_dsn = client.get_public_dsn()
self.assertEquals(public_dsn, '//public@example.com/1')
def test_explicit_message_on_message_event(self):
self.client.captureMessage(message='test', data={
'message': 'foo'
})
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'foo')
def test_message_from_kwargs(self):
try:
raise ValueError('foo')
except ValueError:
self.client.captureException(message='test', data={})
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'test')
def test_explicit_message_on_exception_event(self):
try:
raise ValueError('foo')
except ValueError:
self.client.captureException(data={'message': 'foobar'})
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'foobar')
def test_exception_event(self):
try:
raise ValueError('foo')
except ValueError:
self.client.captureException()
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'ValueError: foo')
self.assertTrue('exception' in event)
exc = event['exception']['values'][0]
self.assertEquals(exc['type'], 'ValueError')
self.assertEquals(exc['value'], 'foo')
self.assertEquals(exc['module'], ValueError.__module__) # this differs in some Python versions
assert 'stacktrace' not in event
stacktrace = exc['stacktrace']
self.assertEquals(len(stacktrace['frames']), 1)
frame = stacktrace['frames'][0]
self.assertEquals(frame['abs_path'], __file__.replace('.pyc', '.py'))
self.assertEquals(frame['filename'], 'tests/base/tests.py')
self.assertEquals(frame['module'], __name__)
self.assertEquals(frame['function'], 'test_exception_event')
self.assertTrue('timestamp' in event)
def test_decorator_preserves_function(self):
@self.client.capture_exceptions
def test1():
return 'foo'
self.assertEquals(test1(), 'foo')
class DecoratorTestException(Exception):
pass
def test_decorator_functionality(self):
@self.client.capture_exceptions
def test2():
raise self.DecoratorTestException()
try:
test2()
except self.DecoratorTestException:
pass
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'DecoratorTestException')
exc = event['exception']['values'][0]
self.assertEquals(exc['type'], 'DecoratorTestException')
self.assertEquals(exc['module'], self.DecoratorTestException.__module__)
stacktrace = exc['stacktrace']
# this is a wrapped class object with __call__ so three frames are expected
self.assertEquals(len(stacktrace['frames']), 3)
frame = stacktrace['frames'][-1]
self.assertEquals(frame['module'], __name__)
self.assertEquals(frame['function'], 'test2')
def test_decorator_filtering(self):
@self.client.capture_exceptions(self.DecoratorTestException)
def test3():
raise Exception()
try:
test3()
except Exception:
pass
self.assertEquals(len(self.client.events), 0)
def test_context_manager_functionality(self):
def test4():
raise self.DecoratorTestException()
try:
with self.client.capture_exceptions():
test4()
except self.DecoratorTestException:
pass
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'DecoratorTestException')
exc = event['exception']['values'][0]
self.assertEquals(exc['type'], 'DecoratorTestException')
self.assertEquals(exc['module'], self.DecoratorTestException.__module__)
stacktrace = exc['stacktrace']
# three frames are expected: test4, `with` block and context manager internals
self.assertEquals(len(stacktrace['frames']), 3)
frame = stacktrace['frames'][-1]
self.assertEquals(frame['module'], __name__)
self.assertEquals(frame['function'], 'test4')
def test_content_manager_filtering(self):
def test5():
raise Exception()
try:
with self.client.capture_exceptions(self.DecoratorTestException):
test5()
except Exception:
pass
self.assertEquals(len(self.client.events), 0)
def test_message_event(self):
self.client.captureMessage(message='test')
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'test')
assert 'stacktrace' not in event
self.assertTrue('timestamp' in event)
def test_fingerprint(self):
self.client.captureMessage(
message='test',
fingerprint=['{{ default }}', 'foobar'],
)
assert len(self.client.events) == 1
event = self.client.events.pop(0)
assert event['fingerprint'] == ['{{ default }}', 'foobar']
def test_context(self):
self.client.context.merge({
'tags': {'foo': 'bar'},
})
try:
raise ValueError('foo')
except ValueError:
self.client.captureException()
else:
self.fail('Exception should have been raised')
assert len(self.client.events) == 1
event = self.client.events.pop(0)
assert event['tags'] == {'foo': 'bar'}
def test_stack_explicit_frames(self):
def bar():
return inspect.stack()
frames = bar()
self.client.captureMessage('test', stack=iter_stack_frames(frames))
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'test')
assert 'stacktrace' in event
self.assertEquals(len(frames), len(event['stacktrace']['frames']))
for frame, frame_i in zip(frames, event['stacktrace']['frames']):
self.assertEquals(frame[0].f_code.co_filename, frame_i['abs_path'])
self.assertEquals(frame[0].f_code.co_name, frame_i['function'])
def test_stack_auto_frames(self):
self.client.captureMessage('test', stack=True)
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['message'], 'test')
self.assertTrue('stacktrace' in event)
self.assertTrue('timestamp' in event)
def test_site(self):
self.client.captureMessage(message='test', data={'site': 'test'})
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
assert 'site' in event['tags']
assert event['tags']['site'] == 'test'
def test_implicit_site(self):
self.client = TempStoreClient(site='foo')
self.client.captureMessage(message='test')
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
assert 'site' in event['tags']
assert event['tags']['site'] == 'foo'
def test_logger(self):
self.client.captureMessage(message='test', data={'logger': 'test'})
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['logger'], 'test')
self.assertTrue('timestamp' in event)
def test_tags(self):
self.client.captureMessage(message='test', tags={'logger': 'test'})
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
self.assertEquals(event['tags'], {'logger': 'test'})
def test_client_extra_context(self):
self.client.extra = {
'foo': 'bar',
'logger': 'baz',
}
self.client.captureMessage(message='test', extra={'logger': 'test'})
self.assertEquals(len(self.client.events), 1)
event = self.client.events.pop(0)
if six.PY3:
expected = {'logger': "'test'", 'foo': "'bar'"}
else:
expected = {'logger': "u'test'", 'foo': "u'bar'"}
self.assertEquals(event['extra'], expected)
def test_transport_registration(self):
client = Client('http://public:secret@example.com/1',
transport=HTTPTransport)
assert type(client.remote.get_transport()) is HTTPTransport
client = Client('sync+http://public:secret@example.com/1')
assert type(client.remote.get_transport()) is HTTPTransport
def test_marks_in_app_frames_for_stacktrace(self):
client = TempStoreClient(
include_paths=['foo'],
exclude_paths=['foo.bar'],
)
client.captureMessage('hello', data={
'stacktrace': {
'frames': [
{'module': 'foo'},
{'module': 'bar'},
{'module': 'foo.bar'},
{'module': 'foo.baz'},
]
}
})
event = client.events.pop(0)
frames = event['stacktrace']['frames']
assert frames[0]['in_app']
assert not frames[1]['in_app']
assert not frames[2]['in_app']
assert frames[3]['in_app']
def test_marks_in_app_frames_for_exception(self):
client = TempStoreClient(
include_paths=['foo'],
exclude_paths=['foo.bar'],
)
client.captureMessage('hello', data={
'exception': {
'values': [{
'stacktrace': {
'frames': [
{'module': 'foo'},
{'module': 'bar'},
{'module': 'foo.bar'},
{'module': 'foo.baz'},
]
}
}]
}
})
event = client.events.pop(0)
frames = event['exception']['values'][0]['stacktrace']['frames']
assert frames[0]['in_app']
assert not frames[1]['in_app']
assert not frames[2]['in_app']
assert frames[3]['in_app']