test_project_event_details.py 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348
  1. from unittest import mock
  2. from django.urls import reverse
  3. from sentry.event_manager import _pull_out_data
  4. from sentry.testutils import APITestCase, SnubaTestCase
  5. from sentry.testutils.helpers.datetime import before_now, iso_format
  6. from sentry.testutils.silo import region_silo_test
  7. from sentry.types.issues import GroupType
  8. @region_silo_test
  9. class ProjectEventDetailsTest(APITestCase, SnubaTestCase):
  10. def setUp(self):
  11. super().setUp()
  12. self.login_as(user=self.user)
  13. project = self.create_project()
  14. one_min_ago = iso_format(before_now(minutes=1))
  15. two_min_ago = iso_format(before_now(minutes=2))
  16. three_min_ago = iso_format(before_now(minutes=3))
  17. four_min_ago = iso_format(before_now(minutes=4))
  18. self.prev_event = self.store_event(
  19. data={"event_id": "a" * 32, "timestamp": four_min_ago, "fingerprint": ["group-1"]},
  20. project_id=project.id,
  21. )
  22. self.cur_event = self.store_event(
  23. data={"event_id": "b" * 32, "timestamp": three_min_ago, "fingerprint": ["group-1"]},
  24. project_id=project.id,
  25. )
  26. self.next_event = self.store_event(
  27. data={
  28. "event_id": "c" * 32,
  29. "timestamp": two_min_ago,
  30. "fingerprint": ["group-1"],
  31. "environment": "production",
  32. "tags": {"environment": "production"},
  33. },
  34. project_id=project.id,
  35. )
  36. # Event in different group
  37. self.store_event(
  38. data={
  39. "event_id": "d" * 32,
  40. "timestamp": one_min_ago,
  41. "fingerprint": ["group-2"],
  42. "environment": "production",
  43. "tags": {"environment": "production"},
  44. },
  45. project_id=project.id,
  46. )
  47. def test_simple(self):
  48. url = reverse(
  49. "sentry-api-0-project-event-details",
  50. kwargs={
  51. "event_id": self.cur_event.event_id,
  52. "project_slug": self.cur_event.project.slug,
  53. "organization_slug": self.cur_event.project.organization.slug,
  54. },
  55. )
  56. response = self.client.get(url, format="json")
  57. assert response.status_code == 200, response.content
  58. assert response.data["id"] == str(self.cur_event.event_id)
  59. assert response.data["nextEventID"] == str(self.next_event.event_id)
  60. assert response.data["previousEventID"] == str(self.prev_event.event_id)
  61. assert response.data["groupID"] == str(self.cur_event.group.id)
  62. def test_snuba_no_prev(self):
  63. url = reverse(
  64. "sentry-api-0-project-event-details",
  65. kwargs={
  66. "event_id": self.prev_event.event_id,
  67. "project_slug": self.prev_event.project.slug,
  68. "organization_slug": self.prev_event.project.organization.slug,
  69. },
  70. )
  71. response = self.client.get(url, format="json")
  72. assert response.status_code == 200, response.content
  73. assert response.data["id"] == str(self.prev_event.event_id)
  74. assert response.data["previousEventID"] is None
  75. assert response.data["nextEventID"] == self.cur_event.event_id
  76. assert response.data["groupID"] == str(self.prev_event.group.id)
  77. def test_snuba_with_environment(self):
  78. url = reverse(
  79. "sentry-api-0-project-event-details",
  80. kwargs={
  81. "event_id": self.cur_event.event_id,
  82. "project_slug": self.cur_event.project.slug,
  83. "organization_slug": self.cur_event.project.organization.slug,
  84. },
  85. )
  86. response = self.client.get(
  87. url, format="json", data={"environment": ["production", "staging"]}
  88. )
  89. assert response.status_code == 200, response.content
  90. assert response.data["id"] == str(self.cur_event.event_id)
  91. assert response.data["previousEventID"] is None
  92. assert response.data["nextEventID"] == self.next_event.event_id
  93. assert response.data["groupID"] == str(self.prev_event.group.id)
  94. def test_ignores_different_group(self):
  95. url = reverse(
  96. "sentry-api-0-project-event-details",
  97. kwargs={
  98. "event_id": self.next_event.event_id,
  99. "project_slug": self.next_event.project.slug,
  100. "organization_slug": self.next_event.project.organization.slug,
  101. },
  102. )
  103. response = self.client.get(url, format="json")
  104. assert response.status_code == 200, response.content
  105. assert response.data["id"] == str(self.next_event.event_id)
  106. assert response.data["nextEventID"] is None
  107. @region_silo_test
  108. class ProjectEventDetailsTransactionTest(APITestCase, SnubaTestCase):
  109. def setUp(self):
  110. super().setUp()
  111. self.login_as(user=self.user)
  112. project = self.create_project()
  113. one_min_ago = iso_format(before_now(minutes=1))
  114. two_min_ago = iso_format(before_now(minutes=2))
  115. three_min_ago = iso_format(before_now(minutes=3))
  116. four_min_ago = iso_format(before_now(minutes=4))
  117. transaction_event_data = {
  118. "level": "info",
  119. "message": "ayoo",
  120. "type": "transaction",
  121. "culprit": "app/components/events/eventEntries in map",
  122. "contexts": {"trace": {"trace_id": "b" * 32, "span_id": "c" * 16, "op": ""}},
  123. }
  124. def hack_pull_out_data(jobs, projects):
  125. _pull_out_data(jobs, projects)
  126. for job in jobs:
  127. job["event"].groups = [self.group]
  128. return jobs, projects
  129. with mock.patch("sentry.event_manager._pull_out_data", hack_pull_out_data):
  130. self.prev_transaction_event = self.store_event(
  131. data={
  132. **transaction_event_data,
  133. "event_id": "a" * 32,
  134. "timestamp": four_min_ago,
  135. "start_timestamp": four_min_ago,
  136. },
  137. project_id=project.id,
  138. )
  139. with mock.patch("sentry.event_manager._pull_out_data", hack_pull_out_data):
  140. self.cur_transaction_event = self.store_event(
  141. data={
  142. **transaction_event_data,
  143. "event_id": "b" * 32,
  144. "timestamp": three_min_ago,
  145. "start_timestamp": three_min_ago,
  146. },
  147. project_id=project.id,
  148. )
  149. with mock.patch("sentry.event_manager._pull_out_data", hack_pull_out_data):
  150. self.next_transaction_event = self.store_event(
  151. data={
  152. **transaction_event_data,
  153. "event_id": "c" * 32,
  154. "timestamp": two_min_ago,
  155. "start_timestamp": two_min_ago,
  156. "environment": "production",
  157. "tags": {"environment": "production"},
  158. },
  159. project_id=project.id,
  160. )
  161. # Event in different group
  162. self.store_event(
  163. data={
  164. **transaction_event_data,
  165. "event_id": "d" * 32,
  166. "timestamp": one_min_ago,
  167. "start_timestamp": one_min_ago,
  168. "environment": "production",
  169. "tags": {"environment": "production"},
  170. },
  171. project_id=project.id,
  172. )
  173. self.group.update(type=GroupType.PERFORMANCE_SLOW_SPAN.value)
  174. self.prev_transaction_event.group = self.group
  175. self.cur_transaction_event.group = self.group
  176. self.next_transaction_event.group = self.group
  177. def test_transaction_event(self):
  178. """Test that you can look up a transaction event w/ a prev and next event"""
  179. url = reverse(
  180. "sentry-api-0-project-event-details",
  181. kwargs={
  182. "event_id": self.cur_transaction_event.event_id,
  183. "project_slug": self.cur_transaction_event.project.slug,
  184. "organization_slug": self.cur_transaction_event.project.organization.slug,
  185. },
  186. )
  187. with self.feature("organizations:performance-issues"):
  188. response = self.client.get(url, format="json", data={"group_id": self.group.id})
  189. assert response.status_code == 200, response.content
  190. assert response.data["id"] == str(self.cur_transaction_event.event_id)
  191. assert response.data["nextEventID"] == str(self.next_transaction_event.event_id)
  192. assert response.data["previousEventID"] == str(self.prev_transaction_event.event_id)
  193. assert response.data["groupID"] == str(self.cur_transaction_event.group.id)
  194. def test_no_previous_event(self):
  195. """Test the case in which there is no previous event"""
  196. url = reverse(
  197. "sentry-api-0-project-event-details",
  198. kwargs={
  199. "event_id": self.prev_transaction_event.event_id,
  200. "project_slug": self.prev_transaction_event.project.slug,
  201. "organization_slug": self.prev_transaction_event.project.organization.slug,
  202. },
  203. )
  204. with self.feature("organizations:performance-issues"):
  205. response = self.client.get(url, format="json", data={"group_id": self.group.id})
  206. assert response.status_code == 200, response.content
  207. assert response.data["id"] == str(self.prev_transaction_event.event_id)
  208. assert response.data["previousEventID"] is None
  209. assert response.data["nextEventID"] == self.cur_transaction_event.event_id
  210. assert response.data["groupID"] == str(self.prev_transaction_event.group.id)
  211. def test_ignores_different_group(self):
  212. """Test that a different group's events aren't attributed to the one that was passed"""
  213. url = reverse(
  214. "sentry-api-0-project-event-details",
  215. kwargs={
  216. "event_id": self.next_transaction_event.event_id,
  217. "project_slug": self.next_transaction_event.project.slug,
  218. "organization_slug": self.next_transaction_event.project.organization.slug,
  219. },
  220. )
  221. with self.feature("organizations:performance-issues"):
  222. response = self.client.get(url, format="json", data={"group_id": self.group.id})
  223. assert response.status_code == 200, response.content
  224. assert response.data["id"] == str(self.next_transaction_event.event_id)
  225. assert response.data["nextEventID"] is None
  226. def test_no_group_id(self):
  227. """Test the case where a group_id was not passed"""
  228. url = reverse(
  229. "sentry-api-0-project-event-details",
  230. kwargs={
  231. "event_id": self.cur_transaction_event.event_id,
  232. "project_slug": self.cur_transaction_event.project.slug,
  233. "organization_slug": self.cur_transaction_event.project.organization.slug,
  234. },
  235. )
  236. with self.feature("organizations:performance-issues"):
  237. response = self.client.get(url, format="json")
  238. assert response.status_code == 200, response.content
  239. assert response.data["id"] == str(self.cur_transaction_event.event_id)
  240. assert response.data["previousEventID"] is None
  241. assert response.data["nextEventID"] is None
  242. assert response.data["groupID"] is None
  243. @region_silo_test
  244. class ProjectEventJsonEndpointTest(APITestCase, SnubaTestCase):
  245. def setUp(self):
  246. super().setUp()
  247. self.login_as(user=self.user)
  248. self.event_id = "c" * 32
  249. self.fingerprint = ["group_2"]
  250. self.min_ago = iso_format(before_now(minutes=1))
  251. self.event = self.store_event(
  252. data={
  253. "event_id": self.event_id,
  254. "timestamp": self.min_ago,
  255. "fingerprint": self.fingerprint,
  256. "user": {"email": self.user.email},
  257. },
  258. project_id=self.project.id,
  259. )
  260. self.url = reverse(
  261. "sentry-api-0-event-json",
  262. kwargs={
  263. "organization_slug": self.organization.slug,
  264. "project_slug": self.project.slug,
  265. "event_id": self.event_id,
  266. },
  267. )
  268. def assert_event(self, data):
  269. assert data["event_id"] == self.event_id
  270. assert data["user"]["email"] == self.user.email
  271. assert data["datetime"][:19] == self.min_ago
  272. assert data["fingerprint"] == self.fingerprint
  273. def test_simple(self):
  274. response = self.client.get(self.url, format="json")
  275. assert response.status_code == 200, response.content
  276. self.assert_event(response.data)
  277. def test_event_does_not_exist(self):
  278. self.url = reverse(
  279. "sentry-api-0-event-json",
  280. kwargs={
  281. "organization_slug": self.organization.slug,
  282. "project_slug": self.project.slug,
  283. "event_id": "no" * 16,
  284. },
  285. )
  286. response = self.client.get(self.url, format="json")
  287. assert response.status_code == 404, response.content
  288. assert response.data == {"detail": "Event not found"}
  289. def test_user_unauthorized(self):
  290. user = self.create_user()
  291. self.login_as(user)
  292. response = self.client.get(self.url, format="json")
  293. assert response.status_code == 403, response.content
  294. assert response.data == {"detail": "You do not have permission to perform this action."}
  295. def test_project_not_associated_with_event(self):
  296. project2 = self.create_project(organization=self.organization)
  297. url = reverse(
  298. "sentry-api-0-event-json",
  299. kwargs={
  300. "organization_slug": self.organization.slug,
  301. "project_slug": project2.slug,
  302. "event_id": self.event_id,
  303. },
  304. )
  305. response = self.client.get(url, format="json")
  306. assert response.status_code == 404, response.content
  307. assert response.data == {"detail": "Event not found"}