tests.py 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import
  3. import datetime
  4. import mock
  5. from django.conf import settings as django_settings
  6. from django.core.urlresolvers import reverse
  7. from django.utils import timezone
  8. from raven import Client
  9. from sentry.models import Group, Event, Project, User
  10. from sentry.testutils import TestCase
  11. from sentry.utils.settings import (
  12. validate_settings, ConfigurationError, import_string)
  13. DEPENDENCY_TEST_DATA = {
  14. "postgresql": ('DATABASES', 'psycopg2.extensions', "database engine", "django.db.backends.postgresql_psycopg2", {
  15. 'default': {
  16. 'ENGINE': "django.db.backends.postgresql_psycopg2",
  17. 'NAME': 'test',
  18. 'USER': 'root',
  19. 'PASSWORD': '',
  20. 'HOST': 'localhost',
  21. 'PORT': ''
  22. }
  23. }),
  24. "mysql": ('DATABASES', 'MySQLdb', "database engine", "django.db.backends.mysql", {
  25. 'default': {
  26. 'ENGINE': "django.db.backends.mysql",
  27. 'NAME': 'test',
  28. 'USER': 'root',
  29. 'PASSWORD': '',
  30. 'HOST': 'localhost',
  31. 'PORT': ''
  32. }
  33. }),
  34. "oracle": ('DATABASES', 'cx_Oracle', "database engine", "django.db.backends.oracle", {
  35. 'default': {
  36. 'ENGINE': "django.db.backends.oracle",
  37. 'NAME': 'test',
  38. 'USER': 'root',
  39. 'PASSWORD': '',
  40. 'HOST': 'localhost',
  41. 'PORT': ''
  42. }
  43. }),
  44. "memcache": ('CACHES', 'memcache', "caching backend", "django.core.cache.backends.memcached.MemcachedCache", {
  45. 'default': {
  46. 'BACKEND': "django.core.cache.backends.memcached.MemcachedCache",
  47. 'LOCATION': '127.0.0.1:11211',
  48. }
  49. }),
  50. "pylibmc": ('CACHES', 'pylibmc', "caching backend", "django.core.cache.backends.memcached.PyLibMCCache", {
  51. 'default': {
  52. 'BACKEND': "django.core.cache.backends.memcached.PyLibMCCache",
  53. 'LOCATION': '127.0.0.1:11211',
  54. }
  55. }),
  56. }
  57. class RavenIntegrationTest(TestCase):
  58. """
  59. This mocks the test server and specifically tests behavior that would
  60. happen between Raven <--> Sentry over HTTP communication.
  61. """
  62. def setUp(self):
  63. self.user = User.objects.create(username='coreapi')
  64. self.project = Project.objects.create(owner=self.user, name='Foo', slug='bar')
  65. self.pm = self.project.team.member_set.get_or_create(user=self.user)[0]
  66. self.pk = self.project.key_set.get_or_create(user=self.user)[0]
  67. def sendRemote(self, url, data, headers={}):
  68. # TODO: make this install a temporary handler which raises an assertion error
  69. import logging
  70. sentry_errors = logging.getLogger('sentry.errors')
  71. sentry_errors.addHandler(logging.StreamHandler())
  72. sentry_errors.setLevel(logging.DEBUG)
  73. content_type = headers.pop('Content-Type', None)
  74. headers = dict(('HTTP_' + k.replace('-', '_').upper(), v) for k, v in headers.iteritems())
  75. resp = self.client.post(
  76. reverse('sentry-api-store', args=[self.pk.project_id]),
  77. data=data,
  78. content_type=content_type,
  79. **headers)
  80. self.assertEquals(resp.status_code, 200, resp.content)
  81. @mock.patch('raven.base.Client.send_remote')
  82. def test_basic(self, send_remote):
  83. send_remote.side_effect = self.sendRemote
  84. client = Client(
  85. dsn='http://%s:%s@localhost:8000/%s' % (
  86. self.pk.public_key, self.pk.secret_key, self.pk.project_id)
  87. )
  88. client.capture('Message', message='foo')
  89. send_remote.assert_called_once()
  90. self.assertEquals(Group.objects.count(), 1)
  91. group = Group.objects.get()
  92. self.assertEquals(group.event_set.count(), 1)
  93. instance = group.event_set.get()
  94. self.assertEquals(instance.message, 'foo')
  95. class SentryRemoteTest(TestCase):
  96. def test_correct_data(self):
  97. kwargs = {'message': 'hello', 'server_name': 'not_dcramer.local', 'level': 40, 'site': 'not_a_real_site'}
  98. resp = self._postWithHeader(kwargs)
  99. self.assertEquals(resp.status_code, 200, resp.content)
  100. instance = Event.objects.get()
  101. self.assertEquals(instance.message, 'hello')
  102. self.assertEquals(instance.server_name, 'not_dcramer.local')
  103. self.assertEquals(instance.level, 40)
  104. self.assertEquals(instance.site, 'not_a_real_site')
  105. def test_unicode_keys(self):
  106. kwargs = {u'message': 'hello', u'server_name': 'not_dcramer.local', u'level': 40, u'site': 'not_a_real_site'}
  107. resp = self._postWithSignature(kwargs)
  108. self.assertEquals(resp.status_code, 200, resp.content)
  109. instance = Event.objects.get()
  110. self.assertEquals(instance.message, 'hello')
  111. self.assertEquals(instance.server_name, 'not_dcramer.local')
  112. self.assertEquals(instance.level, 40)
  113. self.assertEquals(instance.site, 'not_a_real_site')
  114. def test_timestamp(self):
  115. timestamp = timezone.now().replace(microsecond=0, tzinfo=timezone.utc) - datetime.timedelta(hours=1)
  116. kwargs = {u'message': 'hello', 'timestamp': timestamp.strftime('%s.%f')}
  117. resp = self._postWithSignature(kwargs)
  118. self.assertEquals(resp.status_code, 200, resp.content)
  119. instance = Event.objects.get()
  120. self.assertEquals(instance.message, 'hello')
  121. self.assertEquals(instance.datetime, timestamp)
  122. group = instance.group
  123. self.assertEquals(group.first_seen, timestamp)
  124. self.assertEquals(group.last_seen, timestamp)
  125. def test_timestamp_as_iso(self):
  126. timestamp = timezone.now().replace(microsecond=0, tzinfo=timezone.utc) - datetime.timedelta(hours=1)
  127. kwargs = {u'message': 'hello', 'timestamp': timestamp.strftime('%Y-%m-%dT%H:%M:%S.%f')}
  128. resp = self._postWithSignature(kwargs)
  129. self.assertEquals(resp.status_code, 200, resp.content)
  130. instance = Event.objects.get()
  131. self.assertEquals(instance.message, 'hello')
  132. self.assertEquals(instance.datetime, timestamp)
  133. group = instance.group
  134. self.assertEquals(group.first_seen, timestamp)
  135. self.assertEquals(group.last_seen, timestamp)
  136. def test_ungzipped_data(self):
  137. kwargs = {'message': 'hello', 'server_name': 'not_dcramer.local', 'level': 40, 'site': 'not_a_real_site'}
  138. resp = self._postWithSignature(kwargs)
  139. self.assertEquals(resp.status_code, 200)
  140. instance = Event.objects.get()
  141. self.assertEquals(instance.message, 'hello')
  142. self.assertEquals(instance.server_name, 'not_dcramer.local')
  143. self.assertEquals(instance.site, 'not_a_real_site')
  144. self.assertEquals(instance.level, 40)
  145. # def test_byte_sequence(self):
  146. # """
  147. # invalid byte sequence for encoding "UTF8": 0xedb7af
  148. # """
  149. # # TODO:
  150. # # add 'site' to data in fixtures/bad_data.json, then assert it's set correctly below
  151. # fname = os.path.join(os.path.dirname(__file__), 'fixtures/bad_data.json')
  152. # data = open(fname).read()
  153. # resp = self.client.post(reverse('sentry-api-store'), {
  154. # 'data': data,
  155. # 'key': settings.KEY,
  156. # })
  157. # self.assertEquals(resp.status_code, 200)
  158. # self.assertEquals(Event.objects.count(), 1)
  159. # instance = Event.objects.get()
  160. # self.assertEquals(instance.message, 'DatabaseError: invalid byte sequence for encoding "UTF8": 0xeda4ac\nHINT: This error can also happen if the byte sequence does not match the encoding expected by the server, which is controlled by "client_encoding".\n')
  161. # self.assertEquals(instance.server_name, 'shilling.disqus.net')
  162. # self.assertEquals(instance.level, 40)
  163. def test_signature(self):
  164. kwargs = {'message': 'hello', 'server_name': 'not_dcramer.local', 'level': 40, 'site': 'not_a_real_site'}
  165. resp = self._postWithSignature(kwargs)
  166. self.assertEquals(resp.status_code, 200, resp.content)
  167. instance = Event.objects.get()
  168. self.assertEquals(instance.message, 'hello')
  169. self.assertEquals(instance.server_name, 'not_dcramer.local')
  170. self.assertEquals(instance.site, 'not_a_real_site')
  171. self.assertEquals(instance.level, 40)
  172. class DepdendencyTest(TestCase):
  173. def raise_import_error(self, package):
  174. def callable(package_name):
  175. if package_name != package:
  176. return import_string(package_name)
  177. raise ImportError("No module named %s" % (package,))
  178. return callable
  179. @mock.patch('django.conf.settings')
  180. @mock.patch('sentry.utils.settings.import_string')
  181. def validate_dependency(self, key, package, dependency_type, dependency,
  182. setting_value, import_string, settings):
  183. import_string.side_effect = self.raise_import_error(package)
  184. with self.Settings(**{key: setting_value}):
  185. with self.assertRaises(ConfigurationError):
  186. validate_settings(django_settings)
  187. def test_validate_fails_on_postgres(self):
  188. self.validate_dependency(*DEPENDENCY_TEST_DATA['postgresql'])
  189. def test_validate_fails_on_mysql(self):
  190. self.validate_dependency(*DEPENDENCY_TEST_DATA['mysql'])
  191. def test_validate_fails_on_oracle(self):
  192. self.validate_dependency(*DEPENDENCY_TEST_DATA['oracle'])
  193. def test_validate_fails_on_memcache(self):
  194. self.validate_dependency(*DEPENDENCY_TEST_DATA['memcache'])
  195. def test_validate_fails_on_pylibmc(self):
  196. self.validate_dependency(*DEPENDENCY_TEST_DATA['pylibmc'])