123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260 |
- import hashlib
- from unittest import mock
- from django.urls import reverse
- from sentry.api.endpoints.organization_spans_aggregation import NULL_GROUP
- from sentry.testutils.cases import APITestCase, SnubaTestCase
- MOCK_SNUBA_RESPONSE = {
- "data": [
- {
- "transaction_id": "80fe542aea4945ffbe612646987ee449",
- "count": 71,
- "spans": [
- [
- "root_1",
- 1,
- "parent_1",
- "A",
- "A",
- "bind_organization_context",
- "other",
- "2023-09-13 17:12:19",
- 100,
- 0,
- 0.0,
- ],
- [
- "B1",
- 0,
- "root_1",
- "B",
- "B",
- "connect",
- "db",
- "2023-09-13 17:12:19",
- 150,
- 50,
- 50.0,
- ],
- [
- "C1",
- 0,
- "root_1",
- "C",
- "C",
- "resolve_conditions",
- "discover.endpoint",
- "2023-09-13 17:12:19",
- 155,
- 0,
- 10.0,
- ],
- [
- "D1",
- 0,
- "C1",
- "D",
- "D",
- "resolve_orderby",
- "discover.snql",
- "2023-09-13 17:12:19",
- 157,
- 0,
- 20.0,
- ],
- [
- "E1",
- 0,
- "C1",
- NULL_GROUP,
- "E",
- "resolve_columns",
- "discover.snql",
- "2023-09-13 17:12:19",
- 157,
- 0,
- 20.0,
- ],
- ],
- },
- {
- "transaction_id": "86b21833d1854d9b811000b91e7fccfa",
- "count": 71,
- "spans": [
- [
- "root_2",
- 1,
- "parent_2",
- "A",
- "A",
- "bind_organization_context",
- "other",
- "2023-09-13 17:12:39",
- 100,
- 0,
- 0.0,
- ],
- [
- "B2",
- 0,
- "root_2",
- "B",
- "B",
- "connect",
- "db",
- "2023-09-13 17:12:39",
- 110,
- 10,
- 30.0,
- ],
- [
- "C2",
- 0,
- "root_2",
- "C",
- "C",
- "resolve_conditions",
- "discover.endpoint",
- "2023-09-13 17:12:39",
- 115,
- 0,
- 40.0,
- ],
- [
- "D2",
- 0,
- "C2",
- "D",
- "D",
- "resolve_orderby",
- "discover.snql",
- "2023-09-13 17:12:39",
- 150,
- 0,
- 10.0,
- ],
- [
- "D2-duplicate",
- 0,
- "C2",
- "D",
- "D",
- "resolve_orderby",
- "discover.snql",
- "2023-09-13 17:12:40",
- 155,
- 0,
- 20.0,
- ],
- [
- "E2",
- 0,
- "C2",
- NULL_GROUP,
- "E",
- "resolve_columns",
- "discover.snql",
- "2023-09-13 17:12:39",
- 157,
- 0,
- 20.0,
- ],
- ],
- },
- ]
- }
- class OrganizationSpansAggregationTest(APITestCase, SnubaTestCase):
- url_name = "sentry-api-0-organization-spans-aggregation"
- FEATURES = [
- "organizations:starfish-aggregate-span-waterfall",
- "organizations:performance-view",
- ]
- def setUp(self):
- super().setUp()
- self.login_as(user=self.user)
- self.url = reverse(
- self.url_name,
- kwargs={"organization_slug": self.project.organization.slug},
- )
- @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
- def test_simple(self, mock_query):
- mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
- with self.feature(self.FEATURES):
- response = self.client.get(
- self.url,
- data={"transaction": "foo"},
- format="json",
- )
- assert response.data
- data = response.data
- root_fingerprint = hashlib.md5(b"A").hexdigest()[:16]
- assert root_fingerprint in data
- assert data[root_fingerprint]["description"] == "bind_organization_context"
- assert data[root_fingerprint]["count()"] == 2
- fingerprint = hashlib.md5(b"A-B").hexdigest()[:16]
- assert data[fingerprint]["description"] == "connect"
- assert data[fingerprint]["avg(duration)"] == 30.0
- fingerprint = hashlib.md5(b"A-C-D").hexdigest()[:16]
- assert data[fingerprint]["description"] == "resolve_orderby"
- assert data[fingerprint]["avg(exclusive_time)"] == 15.0
- assert data[fingerprint]["count()"] == 2
- fingerprint = hashlib.md5(b"A-C-D2").hexdigest()[:16]
- assert data[fingerprint]["description"] == "resolve_orderby"
- assert data[fingerprint]["avg(exclusive_time)"] == 20.0
- assert data[fingerprint]["count()"] == 1
- @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
- def test_offset_logic(self, mock_query):
- mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
- with self.feature(self.FEATURES):
- response = self.client.get(
- self.url,
- data={"transaction": "foo"},
- format="json",
- )
- assert response.data
- data = response.data
- root_fingerprint = hashlib.md5(b"A").hexdigest()[:16]
- assert root_fingerprint in data
- assert data[root_fingerprint]["avg(absolute_offset)"] == 0.0
- fingerprint = hashlib.md5(b"A-B").hexdigest()[:16]
- assert data[fingerprint]["avg(absolute_offset)"] == 30.0
- fingerprint = hashlib.md5(b"A-C").hexdigest()[:16]
- assert data[fingerprint]["avg(absolute_offset)"] == 35.0
- fingerprint = hashlib.md5(b"A-C-D").hexdigest()[:16]
- assert data[fingerprint]["avg(absolute_offset)"] == 53.5
- fingerprint = hashlib.md5(b"A-C-D2").hexdigest()[:16]
- assert data[fingerprint]["avg(absolute_offset)"] == 1075.0
- @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
- def test_null_group_falls_back_to_span_op(self, mock_query):
- mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
- with self.feature(self.FEATURES):
- response = self.client.get(
- self.url,
- data={"transaction": "foo"},
- format="json",
- )
- assert response.data
- data = response.data
- root_fingerprint = hashlib.md5(b"A-C-discover.snql").hexdigest()[:16]
- assert root_fingerprint in data
- assert data[root_fingerprint]["description"] == "<<unparametrized>> resolve_columns"
- assert data[root_fingerprint]["count()"] == 2
|