tests.py 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. # -*- coding: utf-8 -*-
  2. from __future__ import absolute_import, print_function
  3. import datetime
  4. import json
  5. import mock
  6. import zlib
  7. from django.conf import settings as django_settings
  8. from django.core.urlresolvers import reverse
  9. from django.test.utils import override_settings
  10. from django.utils import timezone
  11. from gzip import GzipFile
  12. from exam import fixture
  13. from raven import Client
  14. from sentry.models import Group, Event, User
  15. from sentry.testutils import TestCase
  16. from sentry.testutils.helpers import get_auth_header
  17. from sentry.utils.compat import StringIO
  18. from sentry.utils.settings import (
  19. validate_settings, ConfigurationError, import_string)
  20. DEPENDENCY_TEST_DATA = {
  21. "postgresql": ('DATABASES', 'psycopg2.extensions', "database engine", "django.db.backends.postgresql_psycopg2", {
  22. 'default': {
  23. 'ENGINE': "django.db.backends.postgresql_psycopg2",
  24. 'NAME': 'test',
  25. 'USER': 'root',
  26. 'PASSWORD': '',
  27. 'HOST': 'localhost',
  28. 'PORT': ''
  29. }
  30. }),
  31. "mysql": ('DATABASES', 'MySQLdb', "database engine", "django.db.backends.mysql", {
  32. 'default': {
  33. 'ENGINE': "django.db.backends.mysql",
  34. 'NAME': 'test',
  35. 'USER': 'root',
  36. 'PASSWORD': '',
  37. 'HOST': 'localhost',
  38. 'PORT': ''
  39. }
  40. }),
  41. "oracle": ('DATABASES', 'cx_Oracle', "database engine", "django.db.backends.oracle", {
  42. 'default': {
  43. 'ENGINE': "django.db.backends.oracle",
  44. 'NAME': 'test',
  45. 'USER': 'root',
  46. 'PASSWORD': '',
  47. 'HOST': 'localhost',
  48. 'PORT': ''
  49. }
  50. }),
  51. "memcache": ('CACHES', 'memcache', "caching backend", "django.core.cache.backends.memcached.MemcachedCache", {
  52. 'default': {
  53. 'BACKEND': "django.core.cache.backends.memcached.MemcachedCache",
  54. 'LOCATION': '127.0.0.1:11211',
  55. }
  56. }),
  57. "pylibmc": ('CACHES', 'pylibmc', "caching backend", "django.core.cache.backends.memcached.PyLibMCCache", {
  58. 'default': {
  59. 'BACKEND': "django.core.cache.backends.memcached.PyLibMCCache",
  60. 'LOCATION': '127.0.0.1:11211',
  61. }
  62. }),
  63. }
  64. class RavenIntegrationTest(TestCase):
  65. """
  66. This mocks the test server and specifically tests behavior that would
  67. happen between Raven <--> Sentry over HTTP communication.
  68. """
  69. def setUp(self):
  70. self.user = User.objects.create(username='coreapi')
  71. self.project = self.create_project(owner=self.user, slug='bar')
  72. self.pm = self.project.team.member_set.get_or_create(user=self.user)[0]
  73. self.pk = self.project.key_set.get_or_create(user=self.user)[0]
  74. def sendRemote(self, url, data, headers={}):
  75. # TODO: make this install a temporary handler which raises an assertion error
  76. import logging
  77. sentry_errors = logging.getLogger('sentry.errors')
  78. sentry_errors.addHandler(logging.StreamHandler())
  79. sentry_errors.setLevel(logging.DEBUG)
  80. content_type = headers.pop('Content-Type', None)
  81. headers = dict(('HTTP_' + k.replace('-', '_').upper(), v) for k, v in headers.iteritems())
  82. resp = self.client.post(
  83. reverse('sentry-api-store', args=[self.pk.project_id]),
  84. data=data,
  85. content_type=content_type,
  86. **headers)
  87. self.assertEquals(resp.status_code, 200, resp.content)
  88. @mock.patch('raven.base.Client.send_remote')
  89. def test_basic(self, send_remote):
  90. send_remote.side_effect = self.sendRemote
  91. client = Client(
  92. dsn='http://%s:%s@localhost:8000/%s' % (
  93. self.pk.public_key, self.pk.secret_key, self.pk.project_id)
  94. )
  95. client.capture('Message', message='foo')
  96. send_remote.assert_called_once()
  97. self.assertEquals(Group.objects.count(), 1)
  98. group = Group.objects.get()
  99. self.assertEquals(group.event_set.count(), 1)
  100. instance = group.event_set.get()
  101. self.assertEquals(instance.message, 'foo')
  102. class SentryRemoteTest(TestCase):
  103. @fixture
  104. def path(self):
  105. return reverse('sentry-api-store')
  106. def test_minimal(self):
  107. kwargs = {'message': 'hello'}
  108. resp = self._postWithHeader(kwargs)
  109. assert resp.status_code == 200, resp.content
  110. event_id = json.loads(resp.content)['id']
  111. instance = Event.objects.get(event_id=event_id)
  112. assert instance.message == 'hello'
  113. def test_timestamp(self):
  114. timestamp = timezone.now().replace(microsecond=0, tzinfo=timezone.utc) - datetime.timedelta(hours=1)
  115. kwargs = {u'message': 'hello', 'timestamp': timestamp.strftime('%s.%f')}
  116. resp = self._postWithSignature(kwargs)
  117. self.assertEquals(resp.status_code, 200, resp.content)
  118. instance = Event.objects.get()
  119. self.assertEquals(instance.message, 'hello')
  120. self.assertEquals(instance.datetime, timestamp)
  121. group = instance.group
  122. self.assertEquals(group.first_seen, timestamp)
  123. self.assertEquals(group.last_seen, timestamp)
  124. def test_timestamp_as_iso(self):
  125. timestamp = timezone.now().replace(microsecond=0, tzinfo=timezone.utc) - datetime.timedelta(hours=1)
  126. kwargs = {u'message': 'hello', 'timestamp': timestamp.strftime('%Y-%m-%dT%H:%M:%S.%f')}
  127. resp = self._postWithSignature(kwargs)
  128. self.assertEquals(resp.status_code, 200, resp.content)
  129. instance = Event.objects.get()
  130. self.assertEquals(instance.message, 'hello')
  131. self.assertEquals(instance.datetime, timestamp)
  132. group = instance.group
  133. self.assertEquals(group.first_seen, timestamp)
  134. self.assertEquals(group.last_seen, timestamp)
  135. def test_ungzipped_data(self):
  136. kwargs = {'message': 'hello'}
  137. resp = self._postWithSignature(kwargs)
  138. self.assertEquals(resp.status_code, 200)
  139. instance = Event.objects.get()
  140. self.assertEquals(instance.message, 'hello')
  141. @override_settings(SENTRY_ALLOW_ORIGIN='getsentry.com')
  142. def test_correct_data_with_get(self):
  143. kwargs = {'message': 'hello'}
  144. resp = self._getWithReferer(kwargs)
  145. self.assertEquals(resp.status_code, 200, resp.content)
  146. instance = Event.objects.get()
  147. self.assertEquals(instance.message, 'hello')
  148. @override_settings(SENTRY_ALLOW_ORIGIN='getsentry.com')
  149. def test_get_without_referer(self):
  150. kwargs = {'message': 'hello'}
  151. resp = self._getWithReferer(kwargs, referer=None, protocol='4')
  152. self.assertEquals(resp.status_code, 400, resp.content)
  153. @override_settings(SENTRY_ALLOW_ORIGIN='*')
  154. def test_get_without_referer_allowed(self):
  155. kwargs = {'message': 'hello'}
  156. resp = self._getWithReferer(kwargs, referer=None, protocol='4')
  157. self.assertEquals(resp.status_code, 200, resp.content)
  158. def test_signature(self):
  159. kwargs = {'message': 'hello'}
  160. resp = self._postWithSignature(kwargs)
  161. self.assertEquals(resp.status_code, 200, resp.content)
  162. instance = Event.objects.get()
  163. self.assertEquals(instance.message, 'hello')
  164. def test_content_encoding_deflate(self):
  165. kwargs = {'message': 'hello'}
  166. message = zlib.compress(json.dumps(kwargs))
  167. key = self.projectkey.public_key
  168. secret = self.projectkey.secret_key
  169. resp = self.client.post(
  170. self.path, message,
  171. content_type='application/octet-stream',
  172. HTTP_CONTENT_ENCODING='deflate',
  173. HTTP_X_SENTRY_AUTH=get_auth_header('_postWithHeader', key, secret),
  174. )
  175. assert resp.status_code == 200, resp.content
  176. event_id = json.loads(resp.content)['id']
  177. instance = Event.objects.get(event_id=event_id)
  178. assert instance.message == 'hello'
  179. def test_content_encoding_gzip(self):
  180. kwargs = {'message': 'hello'}
  181. message = json.dumps(kwargs)
  182. fp = StringIO()
  183. try:
  184. f = GzipFile(fileobj=fp, mode='w')
  185. f.write(message)
  186. finally:
  187. f.close()
  188. key = self.projectkey.public_key
  189. secret = self.projectkey.secret_key
  190. resp = self.client.post(
  191. self.path, fp.getvalue(),
  192. content_type='application/octet-stream',
  193. HTTP_CONTENT_ENCODING='gzip',
  194. HTTP_X_SENTRY_AUTH=get_auth_header('_postWithHeader', key, secret),
  195. )
  196. assert resp.status_code == 200, resp.content
  197. event_id = json.loads(resp.content)['id']
  198. instance = Event.objects.get(event_id=event_id)
  199. assert instance.message == 'hello'
  200. class DepdendencyTest(TestCase):
  201. def raise_import_error(self, package):
  202. def callable(package_name):
  203. if package_name != package:
  204. return import_string(package_name)
  205. raise ImportError("No module named %s" % (package,))
  206. return callable
  207. @mock.patch('django.conf.settings')
  208. @mock.patch('sentry.utils.settings.import_string')
  209. def validate_dependency(self, key, package, dependency_type, dependency,
  210. setting_value, import_string, settings):
  211. import_string.side_effect = self.raise_import_error(package)
  212. with self.settings(**{key: setting_value}):
  213. with self.assertRaises(ConfigurationError):
  214. validate_settings(django_settings)
  215. def test_validate_fails_on_postgres(self):
  216. self.validate_dependency(*DEPENDENCY_TEST_DATA['postgresql'])
  217. def test_validate_fails_on_mysql(self):
  218. self.validate_dependency(*DEPENDENCY_TEST_DATA['mysql'])
  219. def test_validate_fails_on_oracle(self):
  220. self.validate_dependency(*DEPENDENCY_TEST_DATA['oracle'])
  221. def test_validate_fails_on_memcache(self):
  222. self.validate_dependency(*DEPENDENCY_TEST_DATA['memcache'])
  223. def test_validate_fails_on_pylibmc(self):
  224. self.validate_dependency(*DEPENDENCY_TEST_DATA['pylibmc'])