test_organization_event_details.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. from datetime import timedelta
  2. import pytest
  3. from django.urls import NoReverseMatch, reverse
  4. from sentry.models import Group
  5. from sentry.testutils import APITestCase, SnubaTestCase
  6. from sentry.testutils.helpers.datetime import before_now, iso_format
  7. from sentry.testutils.silo import region_silo_test
  8. from sentry.utils.samples import load_data
  9. def format_project_event(project_slug, event_id):
  10. return f"{project_slug}:{event_id}"
  11. @region_silo_test
  12. class OrganizationEventDetailsEndpointTest(APITestCase, SnubaTestCase):
  13. def setUp(self):
  14. super().setUp()
  15. min_ago = iso_format(before_now(minutes=1))
  16. two_min_ago = iso_format(before_now(minutes=2))
  17. three_min_ago = iso_format(before_now(minutes=3))
  18. self.login_as(user=self.user)
  19. self.project = self.create_project()
  20. self.project_2 = self.create_project()
  21. self.store_event(
  22. data={
  23. "event_id": "a" * 32,
  24. "message": "oh no",
  25. "timestamp": three_min_ago,
  26. "fingerprint": ["group-1"],
  27. },
  28. project_id=self.project.id,
  29. )
  30. self.store_event(
  31. data={
  32. "event_id": "b" * 32,
  33. "message": "very bad",
  34. "timestamp": two_min_ago,
  35. "fingerprint": ["group-1"],
  36. },
  37. project_id=self.project.id,
  38. )
  39. self.store_event(
  40. data={
  41. "event_id": "c" * 32,
  42. "message": "very bad",
  43. "timestamp": min_ago,
  44. "fingerprint": ["group-2"],
  45. },
  46. project_id=self.project.id,
  47. )
  48. self.groups = list(Group.objects.all().order_by("id"))
  49. def test_performance_flag(self):
  50. url = reverse(
  51. "sentry-api-0-organization-event-details",
  52. kwargs={
  53. "organization_slug": self.project.organization.slug,
  54. "project_slug": self.project.slug,
  55. "event_id": "a" * 32,
  56. },
  57. )
  58. with self.feature(
  59. {"organizations:discover-basic": False, "organizations:performance-view": True}
  60. ):
  61. response = self.client.get(url, format="json")
  62. assert response.status_code == 200, response.content
  63. assert response.data["id"] == "a" * 32
  64. assert response.data["projectSlug"] == self.project.slug
  65. def test_simple(self):
  66. url = reverse(
  67. "sentry-api-0-organization-event-details",
  68. kwargs={
  69. "organization_slug": self.project.organization.slug,
  70. "project_slug": self.project.slug,
  71. "event_id": "a" * 32,
  72. },
  73. )
  74. with self.feature("organizations:discover-basic"):
  75. response = self.client.get(url, format="json")
  76. assert response.status_code == 200, response.content
  77. assert response.data["id"] == "a" * 32
  78. assert response.data["projectSlug"] == self.project.slug
  79. def test_simple_transaction(self):
  80. min_ago = iso_format(before_now(minutes=1))
  81. event = self.store_event(
  82. data={
  83. "event_id": "d" * 32,
  84. "type": "transaction",
  85. "transaction": "api.issue.delete",
  86. "spans": [],
  87. "contexts": {"trace": {"op": "foobar", "trace_id": "a" * 32, "span_id": "a" * 16}},
  88. "start_timestamp": iso_format(before_now(minutes=1, seconds=5)),
  89. "timestamp": min_ago,
  90. },
  91. project_id=self.project.id,
  92. )
  93. url = reverse(
  94. "sentry-api-0-organization-event-details",
  95. kwargs={
  96. "organization_slug": self.project.organization.slug,
  97. "project_slug": self.project.slug,
  98. "event_id": event.event_id,
  99. },
  100. )
  101. with self.feature("organizations:discover-basic"):
  102. response = self.client.get(url, format="json")
  103. assert response.status_code == 200
  104. assert response.data["id"] == "d" * 32
  105. assert response.data["type"] == "transaction"
  106. def test_no_access_missing_feature(self):
  107. with self.feature({"organizations:discover-basic": False}):
  108. url = reverse(
  109. "sentry-api-0-organization-event-details",
  110. kwargs={
  111. "organization_slug": self.project.organization.slug,
  112. "project_slug": self.project.slug,
  113. "event_id": "a" * 32,
  114. },
  115. )
  116. response = self.client.get(url, format="json")
  117. assert response.status_code == 404, response.content
  118. def test_access_non_member_project(self):
  119. # Add a new user to a project and then access events on project they are not part of.
  120. member_user = self.create_user()
  121. team = self.create_team(members=[member_user])
  122. self.create_project(organization=self.organization, teams=[team])
  123. # Enable open membership
  124. self.organization.flags.allow_joinleave = True
  125. self.organization.save()
  126. self.login_as(member_user)
  127. url = reverse(
  128. "sentry-api-0-organization-event-details",
  129. kwargs={
  130. "organization_slug": self.organization.slug,
  131. "project_slug": self.project.slug,
  132. "event_id": "a" * 32,
  133. },
  134. )
  135. with self.feature("organizations:discover-basic"):
  136. response = self.client.get(url, format="json")
  137. assert response.status_code == 200, response.content
  138. # When open membership is off, access should be denied to non owner users
  139. self.organization.flags.allow_joinleave = False
  140. self.organization.save()
  141. with self.feature("organizations:discover-basic"):
  142. response = self.client.get(url, format="json")
  143. assert response.status_code == 404, response.content
  144. def test_no_event(self):
  145. url = reverse(
  146. "sentry-api-0-organization-event-details",
  147. kwargs={
  148. "organization_slug": self.project.organization.slug,
  149. "project_slug": self.project.slug,
  150. "event_id": "d" * 32,
  151. },
  152. )
  153. with self.feature("organizations:discover-basic"):
  154. response = self.client.get(url, format="json")
  155. assert response.status_code == 404, response.content
  156. def test_invalid_event_id(self):
  157. with pytest.raises(NoReverseMatch):
  158. reverse(
  159. "sentry-api-0-organization-event-details",
  160. kwargs={
  161. "organization_slug": self.project.organization.slug,
  162. "project_slug": self.project.slug,
  163. "event_id": "not-an-event",
  164. },
  165. )
  166. def test_long_trace_description(self):
  167. data = load_data("transaction")
  168. data["event_id"] = "d" * 32
  169. data["timestamp"] = iso_format(before_now(minutes=1))
  170. data["start_timestamp"] = iso_format(before_now(minutes=1) - timedelta(seconds=5))
  171. data["contexts"]["trace"]["description"] = "b" * 512
  172. self.store_event(data=data, project_id=self.project.id)
  173. url = reverse(
  174. "sentry-api-0-organization-event-details",
  175. kwargs={
  176. "organization_slug": self.project.organization.slug,
  177. "project_slug": self.project.slug,
  178. "event_id": "d" * 32,
  179. },
  180. )
  181. with self.feature("organizations:discover-basic"):
  182. response = self.client.get(url, format="json")
  183. assert response.status_code == 200, response.content
  184. trace = response.data["contexts"]["trace"]
  185. original_trace = data["contexts"]["trace"]
  186. assert trace["trace_id"] == original_trace["trace_id"]
  187. assert trace["span_id"] == original_trace["span_id"]
  188. assert trace["parent_span_id"] == original_trace["parent_span_id"]
  189. assert trace["description"][:-3] in original_trace["description"]
  190. def test_blank_fields(self):
  191. url = reverse(
  192. "sentry-api-0-organization-event-details",
  193. kwargs={
  194. "organization_slug": self.project.organization.slug,
  195. "project_slug": self.project.slug,
  196. "event_id": "a" * 32,
  197. },
  198. )
  199. with self.feature("organizations:discover-basic"):
  200. response = self.client.get(
  201. url,
  202. data={"field": ["", " "], "statsPeriod": "24h"},
  203. format="json",
  204. )
  205. assert response.status_code == 200, response.content
  206. assert response.data["id"] == "a" * 32
  207. assert response.data["projectSlug"] == self.project.slug
  208. def test_out_of_retention(self):
  209. self.store_event(
  210. data={
  211. "event_id": "d" * 32,
  212. "message": "oh no",
  213. "timestamp": iso_format(before_now(days=2)),
  214. "fingerprint": ["group-1"],
  215. },
  216. project_id=self.project.id,
  217. )
  218. url = reverse(
  219. "sentry-api-0-organization-event-details",
  220. kwargs={
  221. "organization_slug": self.project.organization.slug,
  222. "project_slug": self.project.slug,
  223. "event_id": "d" * 32,
  224. },
  225. )
  226. with self.options({"system.event-retention-days": 1}):
  227. response = self.client.get(
  228. url,
  229. format="json",
  230. )
  231. assert response.status_code == 404, response.content