tests.py 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. import json
  2. import random
  3. from unittest.mock import patch
  4. from django.shortcuts import reverse
  5. from rest_framework.test import APITestCase
  6. from model_bakery import baker
  7. from glitchtip import test_utils # pylint: disable=unused-import
  8. from issues.models import Issue, Event, EventStatus
  9. from ..test_data.csp import mdn_sample_csp
  10. class EventStoreTestCase(APITestCase):
  11. def setUp(self):
  12. self.project = baker.make("projects.Project")
  13. self.projectkey = self.project.projectkey_set.first()
  14. self.params = f"?sentry_key={self.projectkey.public_key}"
  15. self.url = reverse("event_store", args=[self.project.id]) + self.params
  16. def test_store_api(self):
  17. with open("event_store/test_data/py_hi_event.json") as json_file:
  18. data = json.load(json_file)
  19. res = self.client.post(self.url, data, format="json")
  20. self.assertEqual(res.status_code, 200)
  21. def test_store_duplicate(self):
  22. with open("event_store/test_data/py_hi_event.json") as json_file:
  23. data = json.load(json_file)
  24. self.client.post(self.url, data, format="json")
  25. res = self.client.post(self.url, data, format="json")
  26. self.assertContains(res, "ID already exist", status_code=403)
  27. def test_store_invalid_key(self):
  28. with open("event_store/test_data/py_hi_event.json") as json_file:
  29. data = json.load(json_file)
  30. self.client.post(self.url, data, format="json")
  31. res = self.client.post(self.url, data, format="json")
  32. self.assertContains(res, "ID already exist", status_code=403)
  33. def test_store_api_auth_failure(self):
  34. url = "/api/1/store/"
  35. with open("event_store/test_data/py_hi_event.json") as json_file:
  36. data = json.load(json_file)
  37. params = f"?sentry_key=aaa"
  38. url = reverse("event_store", args=[self.project.id]) + params
  39. res = self.client.post(url, data, format="json")
  40. self.assertEqual(res.status_code, 401)
  41. params = f"?sentry_key=238df2aac6331578a16c14bcb3db5259"
  42. url = reverse("event_store", args=[self.project.id]) + params
  43. res = self.client.post(url, data, format="json")
  44. self.assertContains(res, "Invalid api key", status_code=401)
  45. url = reverse("event_store", args=[10000]) + self.params
  46. res = self.client.post(url, data, format="json")
  47. self.assertContains(res, "Invalid project_id", status_code=400)
  48. def test_error_event(self):
  49. with open("event_store/test_data/py_error.json") as json_file:
  50. data = json.load(json_file)
  51. res = self.client.post(self.url, data, format="json")
  52. self.assertEqual(res.status_code, 200)
  53. def test_csp_event(self):
  54. url = reverse("csp_store", args=[self.project.id]) + self.params
  55. data = mdn_sample_csp
  56. res = self.client.post(url, data, format="json")
  57. self.assertEqual(res.status_code, 200)
  58. expected_title = "Blocked 'style' from 'example.com'"
  59. issue = Issue.objects.get(title=expected_title)
  60. event = Event.objects.get()
  61. self.assertEqual(event.data["csp"]["effective_directive"], "style-src")
  62. self.assertTrue(issue)
  63. def test_reopen_resolved_issue(self):
  64. with open("event_store/test_data/py_hi_event.json") as json_file:
  65. data = json.load(json_file)
  66. self.client.post(self.url, data, format="json")
  67. issue = Issue.objects.all().first()
  68. issue.status = EventStatus.RESOLVED
  69. issue.save()
  70. data["event_id"] = "6600a066e64b4caf8ed7ec5af64ac4ba"
  71. self.client.post(self.url, data, format="json")
  72. issue.refresh_from_db()
  73. self.assertEqual(issue.status, EventStatus.UNRESOLVED)
  74. def test_performance(self):
  75. with open("event_store/test_data/py_hi_event.json") as json_file:
  76. data = json.load(json_file)
  77. with self.assertNumQueries(14):
  78. res = self.client.post(self.url, data, format="json")
  79. self.assertEqual(res.status_code, 200)
  80. # Second event should have less queries
  81. data["event_id"] = "6600a066e64b4caf8ed7ec5af64ac4bb"
  82. with self.assertNumQueries(7):
  83. res = self.client.post(self.url, data, format="json")
  84. self.assertEqual(res.status_code, 200)
  85. def test_throttle_organization(self):
  86. organization = self.project.organization
  87. organization.is_accepting_events = False
  88. organization.save()
  89. with open("event_store/test_data/py_hi_event.json") as json_file:
  90. data = json.load(json_file)
  91. res = self.client.post(self.url, data, format="json")
  92. self.assertEqual(res.status_code, 429)
  93. def test_project_first_event(self):
  94. with open("event_store/test_data/py_error.json") as json_file:
  95. data = json.load(json_file)
  96. self.assertFalse(self.project.first_event)
  97. self.client.post(self.url, data, format="json")
  98. self.project.refresh_from_db()
  99. self.assertTrue(self.project.first_event)
  100. def test_null_character_event(self):
  101. """
  102. Unicode null characters \u0000 are not supported by Postgres JSONB
  103. NUL \x00 characters are not supported by Postgres string types
  104. They should be filtered out
  105. """
  106. with open("event_store/test_data/py_error.json") as json_file:
  107. data = json.load(json_file)
  108. data["exception"]["values"][0]["stacktrace"]["frames"][0][
  109. "function"
  110. ] = "a\u0000a"
  111. data["exception"]["values"][0]["value"] = "\x00\u0000"
  112. res = self.client.post(self.url, data, format="json")
  113. self.assertEqual(res.status_code, 200)
  114. def test_header_value_array(self):
  115. """
  116. Request Header values are both strings and arrays (sentry-php uses arrays)
  117. """
  118. with open("event_store/test_data/py_error.json") as json_file:
  119. data = json.load(json_file)
  120. data["request"]["headers"]["Content-Type"] = ["text/plain"]
  121. res = self.client.post(self.url, data, format="json")
  122. self.assertEqual(res.status_code, 200)
  123. event = Event.objects.first()
  124. header = next(
  125. x for x in event.data["request"]["headers"] if x[0] == "Content-Type"
  126. )
  127. self.assertTrue(isinstance(header[1], str))
  128. def test_anonymize_ip(self):
  129. """ ip address should get masked because default project settings are to scrub ip address """
  130. with open("event_store/test_data/py_hi_event.json") as json_file:
  131. data = json.load(json_file)
  132. test_ip = "123.168.29.14"
  133. res = self.client.post(self.url, data, format="json", REMOTE_ADDR=test_ip)
  134. self.assertEqual(res.status_code, 200)
  135. event = Event.objects.first()
  136. self.assertNotEqual(event.data["user"]["ip_address"], test_ip)
  137. def test_csp_event_anonymize_ip(self):
  138. url = reverse("csp_store", args=[self.project.id]) + self.params
  139. test_ip = "123.168.29.14"
  140. data = mdn_sample_csp
  141. res = self.client.post(url, data, format="json", REMOTE_ADDR=test_ip)
  142. self.assertEqual(res.status_code, 200)
  143. event = Event.objects.first()
  144. self.assertNotEqual(event.data["user"]["ip_address"], test_ip)
  145. def test_store_very_large_data(self):
  146. """
  147. This test is expected to exceed the 1mb limit of a postgres tsvector
  148. """
  149. with open("event_store/test_data/py_hi_event.json") as json_file:
  150. data = json.load(json_file)
  151. data["platform"] = " ".join([str(random.random()) for _ in range(50000)])
  152. res = self.client.post(self.url, data, format="json")
  153. self.assertEqual(res.status_code, 200)
  154. self.assertEqual(
  155. Issue.objects.first().search_vector,
  156. None,
  157. "No tsvector is expected as it would exceed the Postgres limit",
  158. )
  159. @patch("event_store.views.logger")
  160. def test_invalid_event(self, mock_logger):
  161. with open("event_store/test_data/py_hi_event.json") as json_file:
  162. data = json.load(json_file)
  163. data["transaction"] = True
  164. res = self.client.post(self.url, data, format="json")
  165. self.assertEqual(res.status_code, 200)
  166. mock_logger.warning.assert_called()
  167. def test_breadcrumbs_object(self):
  168. """ Event breadcrumbs may be sent as an array or a object. """
  169. with open("event_store/test_data/py_hi_event.json") as json_file:
  170. data = json.load(json_file)
  171. data["breadcrumbs"] = {
  172. "values": [
  173. {
  174. "timestamp": "2020-01-20T20:00:00.000Z",
  175. "message": "Something",
  176. "category": "log",
  177. "data": {"foo": "bar"},
  178. },
  179. ]
  180. }
  181. res = self.client.post(self.url, data, format="json")
  182. self.assertEqual(res.status_code, 200)
  183. self.assertTrue(Issue.objects.exists())
  184. def test_event_release(self):
  185. with open("event_store/test_data/py_hi_event.json") as json_file:
  186. data = json.load(json_file)
  187. self.client.post(self.url, data, format="json")
  188. event = Event.objects.first()
  189. self.assertTrue(event.release)