test_group_details.py 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224
  1. from unittest import mock
  2. from rest_framework.exceptions import ErrorDetail
  3. from sentry.models import Environment, GroupInboxReason, Release
  4. from sentry.models.groupinbox import add_group_to_inbox, remove_group_from_inbox
  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. @region_silo_test
  9. class GroupDetailsTest(APITestCase, SnubaTestCase):
  10. def test_multiple_environments(self):
  11. group = self.create_group()
  12. self.login_as(user=self.user)
  13. environment = Environment.get_or_create(group.project, "production")
  14. environment2 = Environment.get_or_create(group.project, "staging")
  15. url = f"/api/0/issues/{group.id}/"
  16. from sentry.api.endpoints.group_details import tsdb
  17. with mock.patch(
  18. "sentry.api.endpoints.group_details.tsdb.get_range", side_effect=tsdb.get_range
  19. ) as get_range:
  20. response = self.client.get(
  21. f"{url}?environment=production&environment=staging", format="json"
  22. )
  23. assert response.status_code == 200
  24. assert get_range.call_count == 2
  25. for args, kwargs in get_range.call_args_list:
  26. assert kwargs["environment_ids"] == [environment.id, environment2.id]
  27. response = self.client.get(f"{url}?environment=invalid", format="json")
  28. assert response.status_code == 404
  29. def test_with_first_last_release(self):
  30. self.login_as(user=self.user)
  31. first_release = {
  32. "firstEvent": before_now(minutes=3),
  33. "lastEvent": before_now(minutes=2, seconds=30),
  34. }
  35. last_release = {
  36. "firstEvent": before_now(minutes=1, seconds=30),
  37. "lastEvent": before_now(minutes=1),
  38. }
  39. for timestamp in first_release.values():
  40. self.store_event(
  41. data={"release": "1.0", "timestamp": iso_format(timestamp)},
  42. project_id=self.project.id,
  43. )
  44. self.store_event(
  45. data={"release": "1.1", "timestamp": iso_format(before_now(minutes=2))},
  46. project_id=self.project.id,
  47. )
  48. event = None
  49. for timestamp in last_release.values():
  50. event = self.store_event(
  51. data={"release": "1.0a", "timestamp": iso_format(timestamp)},
  52. project_id=self.project.id,
  53. )
  54. group = event.group
  55. url = f"/api/0/issues/{group.id}/"
  56. response = self.client.get(url, format="json")
  57. assert response.status_code == 200, response.content
  58. assert response.data["id"] == str(group.id)
  59. release = response.data["firstRelease"]
  60. assert release["version"] == "1.0"
  61. for event, timestamp in first_release.items():
  62. assert release[event].ctime() == timestamp.ctime()
  63. release = response.data["lastRelease"]
  64. assert release["version"] == "1.0a"
  65. for event, timestamp in last_release.items():
  66. assert release[event].ctime() == timestamp.ctime()
  67. def test_first_last_only_one_tagstore(self):
  68. self.login_as(user=self.user)
  69. event = self.store_event(
  70. data={"release": "1.0", "timestamp": iso_format(before_now(days=3))},
  71. project_id=self.project.id,
  72. )
  73. self.store_event(
  74. data={"release": "1.1", "timestamp": iso_format(before_now(minutes=3))},
  75. project_id=self.project.id,
  76. )
  77. group = event.group
  78. url = f"/api/0/issues/{group.id}/"
  79. with mock.patch(
  80. "sentry.api.endpoints.group_details.tagstore.get_release_tags"
  81. ) as get_release_tags:
  82. response = self.client.get(url, format="json")
  83. assert response.status_code == 200
  84. assert get_release_tags.call_count == 1
  85. def test_first_release_only(self):
  86. self.login_as(user=self.user)
  87. first_event = before_now(days=3)
  88. self.store_event(
  89. data={"release": "1.0", "timestamp": iso_format(first_event)},
  90. project_id=self.project.id,
  91. )
  92. event = self.store_event(
  93. data={"release": "1.1", "timestamp": iso_format(before_now(days=1))},
  94. project_id=self.project.id,
  95. )
  96. # Forcibly remove one of the releases
  97. Release.objects.get(version="1.1").delete()
  98. group = event.group
  99. url = f"/api/0/issues/{group.id}/"
  100. response = self.client.get(url, format="json")
  101. assert response.status_code == 200, response.content
  102. assert response.data["firstRelease"]["version"] == "1.0"
  103. # only one event
  104. assert (
  105. response.data["firstRelease"]["firstEvent"]
  106. == response.data["firstRelease"]["lastEvent"]
  107. )
  108. assert response.data["firstRelease"]["firstEvent"].ctime() == first_event.ctime()
  109. assert response.data["lastRelease"] is None
  110. def test_group_expand_inbox(self):
  111. self.login_as(user=self.user)
  112. event = self.store_event(
  113. data={"timestamp": iso_format(before_now(minutes=3))},
  114. project_id=self.project.id,
  115. )
  116. group = event.group
  117. add_group_to_inbox(group, GroupInboxReason.NEW)
  118. url = f"/api/0/issues/{group.id}/?expand=inbox"
  119. response = self.client.get(url, format="json")
  120. assert response.status_code == 200, response.content
  121. assert response.data["inbox"] is not None
  122. assert response.data["inbox"]["reason"] == GroupInboxReason.NEW.value
  123. assert response.data["inbox"]["reason_details"] is None
  124. remove_group_from_inbox(event.group)
  125. response = self.client.get(url, format="json")
  126. assert response.status_code == 200, response.content
  127. assert response.data["inbox"] is None
  128. def test_assigned_to_unknown(self):
  129. self.login_as(user=self.user)
  130. event = self.store_event(
  131. data={"timestamp": iso_format(before_now(minutes=3))},
  132. project_id=self.project.id,
  133. )
  134. group = event.group
  135. url = f"/api/0/issues/{group.id}/"
  136. response = self.client.put(
  137. url, {"assignedTo": "admin@localhost", "status": "unresolved"}, format="json"
  138. )
  139. assert response.status_code == 200
  140. response = self.client.put(
  141. url, {"assignedTo": "user@doesnotexist.com", "status": "unresolved"}, format="json"
  142. )
  143. assert response.status_code == 400
  144. assert response.data == {
  145. "assignedTo": [
  146. ErrorDetail(
  147. string="Could not parse actor. Format should be `type:id` where type is `team` or `user`.",
  148. code="invalid",
  149. )
  150. ]
  151. }
  152. def test_collapse_stats_does_not_work(self):
  153. """
  154. 'collapse' param should hide the stats data and not return anything in the response, but the impl
  155. doesn't seem to respect this param.
  156. include this test here in-case the endpoint behavior changes in the future.
  157. """
  158. self.login_as(user=self.user)
  159. event = self.store_event(
  160. data={"timestamp": iso_format(before_now(minutes=3))},
  161. project_id=self.project.id,
  162. )
  163. group = event.group
  164. url = f"/api/0/issues/{group.id}/"
  165. response = self.client.get(url, {"collapse": ["stats"]}, format="json")
  166. assert response.status_code == 200
  167. assert int(response.data["id"]) == event.group.id
  168. assert response.data["stats"] # key shouldn't be present
  169. assert response.data["count"] is not None # key shouldn't be present
  170. assert response.data["userCount"] is not None # key shouldn't be present
  171. assert response.data["firstSeen"] is not None # key shouldn't be present
  172. assert response.data["lastSeen"] is not None # key shouldn't be present
  173. def test_issue_type_category(self):
  174. """Test that the issue's type and category is returned in the results"""
  175. self.login_as(user=self.user)
  176. event = self.store_event(
  177. data={"timestamp": iso_format(before_now(minutes=3))},
  178. project_id=self.project.id,
  179. )
  180. url = f"/api/0/issues/{event.group.id}/"
  181. response = self.client.get(url, format="json")
  182. assert response.status_code == 200
  183. assert int(response.data["id"]) == event.group.id
  184. assert response.data["issueType"] == "error"
  185. assert response.data["issueCategory"] == "error"