test_organization_spans_aggregation.py 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. import hashlib
  2. from unittest import mock
  3. from django.urls import reverse
  4. from sentry.api.endpoints.organization_spans_aggregation import NULL_GROUP
  5. from sentry.testutils.cases import APITestCase, SnubaTestCase
  6. MOCK_SNUBA_RESPONSE = {
  7. "data": [
  8. {
  9. "transaction_id": "80fe542aea4945ffbe612646987ee449",
  10. "count": 71,
  11. "spans": [
  12. [
  13. "root_1",
  14. 1,
  15. "parent_1",
  16. "A",
  17. "A",
  18. "bind_organization_context",
  19. "other",
  20. 100,
  21. 0,
  22. 0.0,
  23. ],
  24. ["B1", 0, "root_1", "B", "B", "connect", "db", 150, 50, 50.0],
  25. [
  26. "C1",
  27. 0,
  28. "root_1",
  29. "C",
  30. "C",
  31. "resolve_conditions",
  32. "discover.endpoint",
  33. 155,
  34. 0,
  35. 10.0,
  36. ],
  37. ["D1", 0, "C1", "D", "D", "resolve_orderby", "discover.snql", 157, 0, 20.0],
  38. ["E1", 0, "C1", NULL_GROUP, "E", "resolve_columns", "discover.snql", 157, 0, 20.0],
  39. ],
  40. },
  41. {
  42. "transaction_id": "86b21833d1854d9b811000b91e7fccfa",
  43. "count": 71,
  44. "spans": [
  45. [
  46. "root_2",
  47. 1,
  48. "parent_2",
  49. "A",
  50. "A",
  51. "bind_organization_context",
  52. "other",
  53. 100,
  54. 0,
  55. 0.0,
  56. ],
  57. ["B2", 0, "root_2", "B", "B", "connect", "db", 110, 10, 30.0],
  58. [
  59. "C2",
  60. 0,
  61. "root_2",
  62. "C",
  63. "C",
  64. "resolve_conditions",
  65. "discover.endpoint",
  66. 115,
  67. 0,
  68. 40.0,
  69. ],
  70. ["D2", 0, "C2", "D", "D", "resolve_orderby", "discover.snql", 150, 0, 10.0],
  71. [
  72. "D2-duplicate",
  73. 0,
  74. "C2",
  75. "D",
  76. "D",
  77. "resolve_orderby",
  78. "discover.snql",
  79. 155,
  80. 0,
  81. 20.0,
  82. ],
  83. ["E2", 0, "C2", NULL_GROUP, "E", "resolve_columns", "discover.snql", 157, 0, 20.0],
  84. ],
  85. },
  86. ]
  87. }
  88. class OrganizationSpansAggregationTest(APITestCase, SnubaTestCase):
  89. url_name = "sentry-api-0-organization-spans-aggregation"
  90. FEATURES = [
  91. "organizations:starfish-aggregate-span-waterfall",
  92. "organizations:performance-view",
  93. ]
  94. def setUp(self):
  95. super().setUp()
  96. self.login_as(user=self.user)
  97. self.url = reverse(
  98. self.url_name,
  99. kwargs={"organization_slug": self.project.organization.slug},
  100. )
  101. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  102. def test_simple(self, mock_query):
  103. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  104. with self.feature(self.FEATURES):
  105. response = self.client.get(
  106. self.url,
  107. data={"transaction": "foo"},
  108. format="json",
  109. )
  110. assert response.data
  111. data = response.data
  112. root_fingerprint = hashlib.md5(b"A").hexdigest()[:16]
  113. assert root_fingerprint in data
  114. assert data[root_fingerprint]["description"] == "bind_organization_context"
  115. assert data[root_fingerprint]["count()"] == 2
  116. fingerprint = hashlib.md5(b"A-B").hexdigest()[:16]
  117. assert data[fingerprint]["description"] == "connect"
  118. assert data[fingerprint]["avg(duration)"] == 30.0
  119. fingerprint = hashlib.md5(b"A-C-D").hexdigest()[:16]
  120. assert data[fingerprint]["description"] == "resolve_orderby"
  121. assert data[fingerprint]["avg(exclusive_time)"] == 15.0
  122. assert data[fingerprint]["count()"] == 2
  123. fingerprint = hashlib.md5(b"A-C-D2").hexdigest()[:16]
  124. assert data[fingerprint]["description"] == "resolve_orderby"
  125. assert data[fingerprint]["avg(exclusive_time)"] == 20.0
  126. assert data[fingerprint]["count()"] == 1
  127. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  128. def test_offset_logic(self, mock_query):
  129. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  130. with self.feature(self.FEATURES):
  131. response = self.client.get(
  132. self.url,
  133. data={"transaction": "foo"},
  134. format="json",
  135. )
  136. assert response.data
  137. data = response.data
  138. root_fingerprint = hashlib.md5(b"A").hexdigest()[:16]
  139. assert root_fingerprint in data
  140. assert data[root_fingerprint]["avg(absolute_offset)"] == 0.0
  141. fingerprint = hashlib.md5(b"A-B").hexdigest()[:16]
  142. assert data[fingerprint]["avg(absolute_offset)"] == 30.0
  143. fingerprint = hashlib.md5(b"A-C").hexdigest()[:16]
  144. assert data[fingerprint]["avg(absolute_offset)"] == 35.0
  145. fingerprint = hashlib.md5(b"A-C-D").hexdigest()[:16]
  146. assert data[fingerprint]["avg(absolute_offset)"] == 53.5
  147. fingerprint = hashlib.md5(b"A-C-D2").hexdigest()[:16]
  148. assert data[fingerprint]["avg(absolute_offset)"] == 75.0
  149. @mock.patch("sentry.api.endpoints.organization_spans_aggregation.raw_snql_query")
  150. def test_null_group_falls_back_to_span_op(self, mock_query):
  151. mock_query.side_effect = [MOCK_SNUBA_RESPONSE]
  152. with self.feature(self.FEATURES):
  153. response = self.client.get(
  154. self.url,
  155. data={"transaction": "foo"},
  156. format="json",
  157. )
  158. assert response.data
  159. data = response.data
  160. root_fingerprint = hashlib.md5(b"A-C-discover.snql").hexdigest()[:16]
  161. assert root_fingerprint in data
  162. assert data[root_fingerprint]["description"] == "<<unparametrized>> resolve_columns"
  163. assert data[root_fingerprint]["count()"] == 2