from django.urls import reverse from sentry.issues.occurrence_consumer import process_event_and_issue_occurrence from sentry.testutils import APITestCase, SnubaTestCase from sentry.testutils.cases import PerformanceIssueTestCase from sentry.testutils.helpers.datetime import before_now, iso_format from sentry.testutils.silo import region_silo_test from sentry.utils.samples import load_data from tests.sentry.issues.test_utils import OccurrenceTestMixin @region_silo_test class ProjectEventDetailsTest(APITestCase, SnubaTestCase): def setUp(self): super().setUp() self.login_as(user=self.user) self.setup_data() def setup_data(self): one_min_ago = iso_format(before_now(minutes=1)) two_min_ago = iso_format(before_now(minutes=2)) three_min_ago = iso_format(before_now(minutes=3)) four_min_ago = iso_format(before_now(minutes=4)) self.prev_event = self.store_event( data={"event_id": "a" * 32, "timestamp": four_min_ago, "fingerprint": ["group-1"]}, project_id=self.project.id, ) self.cur_event = self.store_event( data={"event_id": "b" * 32, "timestamp": three_min_ago, "fingerprint": ["group-1"]}, project_id=self.project.id, ) self.next_event = self.store_event( data={ "event_id": "c" * 32, "timestamp": two_min_ago, "fingerprint": ["group-1"], "environment": "production", "tags": {"environment": "production"}, }, project_id=self.project.id, ) self.cur_group = self.next_event.group # Event in different group self.store_event( data={ "event_id": "d" * 32, "timestamp": one_min_ago, "fingerprint": ["group-2"], "environment": "production", "tags": {"environment": "production"}, }, project_id=self.project.id, ) def test_simple(self): url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.cur_event.event_id, "project_slug": self.project.slug, "organization_slug": self.project.organization.slug, }, ) response = self.client.get(url, format="json") assert response.status_code == 200, response.content assert response.data["id"] == str(self.cur_event.event_id) assert response.data["nextEventID"] == str(self.next_event.event_id) assert response.data["previousEventID"] == str(self.prev_event.event_id) assert response.data["groupID"] == str(self.cur_group.id) def test_snuba_no_prev(self): url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.prev_event.event_id, "project_slug": self.project.slug, "organization_slug": self.project.organization.slug, }, ) response = self.client.get(url, format="json") assert response.status_code == 200, response.content assert response.data["id"] == str(self.prev_event.event_id) assert response.data["previousEventID"] is None assert response.data["nextEventID"] == self.cur_event.event_id assert response.data["groupID"] == str(self.cur_group.id) def test_snuba_with_environment(self): url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.cur_event.event_id, "project_slug": self.project.slug, "organization_slug": self.project.organization.slug, }, ) response = self.client.get( url, format="json", data={"environment": ["production", "staging"]} ) assert response.status_code == 200, response.content assert response.data["id"] == str(self.cur_event.event_id) assert response.data["previousEventID"] is None assert response.data["nextEventID"] == self.next_event.event_id assert response.data["groupID"] == str(self.cur_group.id) def test_ignores_different_group(self): url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.next_event.event_id, "project_slug": self.project.slug, "organization_slug": self.project.organization.slug, }, ) response = self.client.get(url, format="json") assert response.status_code == 200, response.content assert response.data["id"] == str(self.next_event.event_id) assert response.data["nextEventID"] is None @region_silo_test class ProjectEventDetailsGenericTest(OccurrenceTestMixin, ProjectEventDetailsTest): def setup_data(self): one_min_ago = iso_format(before_now(minutes=1)) two_min_ago = iso_format(before_now(minutes=2)) three_min_ago = iso_format(before_now(minutes=3)) four_min_ago = iso_format(before_now(minutes=4)) prev_event_id = "a" * 32 self.prev_event, prev_group_info = process_event_and_issue_occurrence( self.build_occurrence_data( event_id=prev_event_id, project_id=self.project.id, fingerprint=["group-1"] ), { "event_id": prev_event_id, "project_id": self.project.id, "timestamp": four_min_ago, "message_timestamp": four_min_ago, }, ) cur_event_id = "b" * 32 self.cur_event, cur_group_info = process_event_and_issue_occurrence( self.build_occurrence_data( event_id=cur_event_id, project_id=self.project.id, fingerprint=["group-1"] ), { "event_id": cur_event_id, "project_id": self.project.id, "timestamp": three_min_ago, "message_timestamp": three_min_ago, }, ) self.cur_group = cur_group_info.group next_event_id = "c" * 32 self.next_event, next_group_info = process_event_and_issue_occurrence( self.build_occurrence_data( event_id=next_event_id, project_id=self.project.id, fingerprint=["group-1"] ), { "event_id": next_event_id, "project_id": self.project.id, "timestamp": two_min_ago, "message_timestamp": two_min_ago, "tags": {"environment": "production"}, }, ) unrelated_event_id = "d" * 32 process_event_and_issue_occurrence( self.build_occurrence_data( event_id=unrelated_event_id, project_id=self.project.id, fingerprint=["group-2"] ), { "event_id": unrelated_event_id, "project_id": self.project.id, "timestamp": one_min_ago, "message_timestamp": one_min_ago, "tags": {"environment": "production"}, }, )[0] def test_generic_event_with_occurrence(self): url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.cur_event.event_id, "project_slug": self.project.slug, "organization_slug": self.project.organization.slug, }, ) response = self.client.get(url, format="json", data={"group_id": self.cur_group.id}) assert response.status_code == 200, response.content assert response.data["id"] == self.cur_event.event_id assert response.data["occurrence"] is not None assert response.data["occurrence"]["id"] == self.cur_event.id @region_silo_test class ProjectEventDetailsTransactionTest(APITestCase, SnubaTestCase, PerformanceIssueTestCase): def setUp(self): super().setUp() self.login_as(user=self.user) project = self.create_project() one_min_ago = before_now(minutes=1) two_min_ago = before_now(minutes=2) three_min_ago = before_now(minutes=3) four_min_ago = before_now(minutes=4) self.prev_transaction_event = self.create_performance_issue( event_data=load_data( event_id="a" * 32, platform="transaction-n-plus-one", timestamp=four_min_ago, start_timestamp=four_min_ago, ), project_id=project.id, ) self.group = self.prev_transaction_event.group self.cur_transaction_event = self.create_performance_issue( event_data=load_data( event_id="b" * 32, platform="transaction-n-plus-one", timestamp=three_min_ago, start_timestamp=three_min_ago, ), project_id=project.id, ) self.next_transaction_event = self.create_performance_issue( event_data=load_data( event_id="c" * 32, platform="transaction-n-plus-one", timestamp=two_min_ago, start_timestamp=two_min_ago, ), project_id=project.id, ) self.create_performance_issue( event_data=load_data( event_id="d" * 32, platform="transaction-n-plus-one", timestamp=one_min_ago, start_timestamp=one_min_ago, ), fingerprint="other_group", project_id=project.id, ) def test_transaction_event(self): """Test that you can look up a transaction event w/ a prev and next event""" url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.cur_transaction_event.event_id, "project_slug": self.cur_transaction_event.project.slug, "organization_slug": self.cur_transaction_event.project.organization.slug, }, ) response = self.client.get(url, format="json", data={"group_id": self.group.id}) assert response.status_code == 200, response.content assert response.data["id"] == str(self.cur_transaction_event.event_id) assert response.data["nextEventID"] == str(self.next_transaction_event.event_id) assert response.data["previousEventID"] == str(self.prev_transaction_event.event_id) assert response.data["groupID"] == str(self.cur_transaction_event.group.id) def test_no_previous_event(self): """Test the case in which there is no previous event""" url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.prev_transaction_event.event_id, "project_slug": self.prev_transaction_event.project.slug, "organization_slug": self.prev_transaction_event.project.organization.slug, }, ) response = self.client.get(url, format="json", data={"group_id": self.group.id}) assert response.status_code == 200, response.content assert response.data["id"] == str(self.prev_transaction_event.event_id) assert response.data["previousEventID"] is None assert response.data["nextEventID"] == self.cur_transaction_event.event_id assert response.data["groupID"] == str(self.prev_transaction_event.group.id) def test_ignores_different_group(self): """Test that a different group's events aren't attributed to the one that was passed""" url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.next_transaction_event.event_id, "project_slug": self.next_transaction_event.project.slug, "organization_slug": self.next_transaction_event.project.organization.slug, }, ) response = self.client.get(url, format="json", data={"group_id": self.group.id}) assert response.status_code == 200, response.content assert response.data["id"] == str(self.next_transaction_event.event_id) assert response.data["nextEventID"] is None def test_no_group_id(self): """Test the case where a group_id was not passed""" url = reverse( "sentry-api-0-project-event-details", kwargs={ "event_id": self.cur_transaction_event.event_id, "project_slug": self.cur_transaction_event.project.slug, "organization_slug": self.cur_transaction_event.project.organization.slug, }, ) response = self.client.get(url, format="json") assert response.status_code == 200, response.content assert response.data["id"] == str(self.cur_transaction_event.event_id) assert response.data["previousEventID"] is None assert response.data["nextEventID"] is None assert response.data["groupID"] is None @region_silo_test class ProjectEventJsonEndpointTest(APITestCase, SnubaTestCase): def setUp(self): super().setUp() self.login_as(user=self.user) self.event_id = "c" * 32 self.fingerprint = ["group_2"] self.min_ago = iso_format(before_now(minutes=1)) self.event = self.store_event( data={ "event_id": self.event_id, "timestamp": self.min_ago, "fingerprint": self.fingerprint, "user": {"email": self.user.email}, }, project_id=self.project.id, ) self.url = reverse( "sentry-api-0-event-json", kwargs={ "organization_slug": self.organization.slug, "project_slug": self.project.slug, "event_id": self.event_id, }, ) def assert_event(self, data): assert data["event_id"] == self.event_id assert data["user"]["email"] == self.user.email assert data["datetime"][:19] == self.min_ago assert data["fingerprint"] == self.fingerprint def test_simple(self): response = self.client.get(self.url, format="json") assert response.status_code == 200, response.content self.assert_event(response.data) def test_event_does_not_exist(self): self.url = reverse( "sentry-api-0-event-json", kwargs={ "organization_slug": self.organization.slug, "project_slug": self.project.slug, "event_id": "no" * 16, }, ) response = self.client.get(self.url, format="json") assert response.status_code == 404, response.content assert response.data == {"detail": "Event not found"} def test_user_unauthorized(self): user = self.create_user() self.login_as(user) response = self.client.get(self.url, format="json") assert response.status_code == 403, response.content assert response.data == {"detail": "You do not have permission to perform this action."} def test_project_not_associated_with_event(self): project2 = self.create_project(organization=self.organization) url = reverse( "sentry-api-0-event-json", kwargs={ "organization_slug": self.organization.slug, "project_slug": project2.slug, "event_id": self.event_id, }, ) response = self.client.get(url, format="json") assert response.status_code == 404, response.content assert response.data == {"detail": "Event not found"}