test_performance_trace_detail.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. from datetime import timedelta
  2. from unittest.mock import patch
  3. from uuid import uuid4
  4. import pytz
  5. from sentry.testutils.cases import AcceptanceTestCase, SnubaTestCase
  6. from sentry.testutils.helpers.datetime import before_now, iso_format
  7. from sentry.testutils.silo import no_silo_test
  8. from sentry.utils.samples import load_data
  9. FEATURE_NAMES = ["organizations:performance-view"]
  10. def make_span_id() -> str:
  11. return uuid4().hex[:16]
  12. @no_silo_test(stable=True)
  13. class PerformanceTraceDetailTest(AcceptanceTestCase, SnubaTestCase):
  14. def create_error(self, platform, trace_id, span_id, project_id, timestamp):
  15. data = load_data(platform, timestamp=timestamp)
  16. if "contexts" not in data:
  17. data["contexts"] = {}
  18. data["contexts"]["trace"] = {"type": "trace", "trace_id": trace_id, "span_id": span_id}
  19. return self.store_event(data, project_id=project_id)
  20. def create_transaction(
  21. self,
  22. transaction,
  23. trace_id,
  24. span_id,
  25. parent_span_id,
  26. spans,
  27. project_id,
  28. start_timestamp,
  29. duration,
  30. transaction_id=None,
  31. ):
  32. timestamp = start_timestamp + timedelta(milliseconds=duration)
  33. data = load_data(
  34. "transaction",
  35. trace=trace_id,
  36. span_id=span_id,
  37. spans=spans,
  38. start_timestamp=start_timestamp,
  39. timestamp=timestamp,
  40. )
  41. if transaction_id is not None:
  42. data["event_id"] = transaction_id
  43. data["transaction"] = transaction
  44. data["contexts"]["trace"]["parent_span_id"] = parent_span_id
  45. return self.store_event(data, project_id=project_id)
  46. def setUp(self):
  47. super().setUp()
  48. self.org = self.create_organization(owner=self.user, name="Rowdy Tiger")
  49. self.team = self.create_team(
  50. organization=self.org, name="Mariachi Band", members=[self.user]
  51. )
  52. self.frontend_project = self.create_project(
  53. organization=self.org, teams=[self.team], name="Frontend", platform="javascript"
  54. )
  55. self.backend_project = self.create_project(
  56. organization=self.org, teams=[self.team], name="Backend", platform="python"
  57. )
  58. self.service_project = self.create_project(
  59. organization=self.org, teams=[self.team], name="Service", platform="go"
  60. )
  61. self.task_project = self.create_project(
  62. organization=self.org, teams=[self.team], name="Task", platform="rust"
  63. )
  64. self.login_as(self.user)
  65. self.day_ago = before_now(days=1).replace(hour=10, minute=0, second=0, microsecond=0)
  66. self.trace_id = "a" * 32
  67. self.frontend_transaction_id = "b" * 16
  68. self.frontend_span_ids = [make_span_id() for _ in range(3)]
  69. self.backend_transaction_ids = [make_span_id() for _ in range(3)]
  70. # a chain of transactions that are orphans
  71. self.task_transactions = []
  72. last_transaction_id = make_span_id()
  73. for i in range(3):
  74. transaction_id = make_span_id()
  75. timestamp = self.day_ago + timedelta(seconds=i, microseconds=30000)
  76. self.create_error(
  77. platform="python",
  78. trace_id=self.trace_id,
  79. span_id=transaction_id,
  80. project_id=self.task_project.id,
  81. timestamp=timestamp,
  82. )
  83. self.task_transactions.append(
  84. self.create_transaction(
  85. transaction=f"task_transaction_{i}",
  86. trace_id=self.trace_id,
  87. span_id=transaction_id,
  88. parent_span_id=last_transaction_id,
  89. spans=None,
  90. project_id=self.task_project.id,
  91. start_timestamp=timestamp,
  92. duration=700 + 100 * (i + 1),
  93. )
  94. )
  95. last_transaction_id = transaction_id
  96. # two transactions attached to the same span
  97. self.service_transaction_s = [
  98. self.create_transaction(
  99. transaction=f"service_transaction_{i}",
  100. trace_id=self.trace_id,
  101. span_id=make_span_id(),
  102. parent_span_id=self.backend_transaction_ids[1],
  103. spans=None,
  104. project_id=self.service_project.id,
  105. start_timestamp=self.day_ago
  106. + timedelta(seconds=1, microseconds=100000 + i * 50000),
  107. duration=750 * (i + 1),
  108. )
  109. for i in range(2)
  110. ]
  111. # single transaction attached to the root span
  112. self.service_transaction_2 = self.create_transaction(
  113. transaction="service_transaction_2",
  114. trace_id=self.trace_id,
  115. span_id=make_span_id(),
  116. parent_span_id=self.backend_transaction_ids[2],
  117. spans=None,
  118. project_id=self.service_project.id,
  119. start_timestamp=self.day_ago + timedelta(microseconds=400000),
  120. duration=1000,
  121. )
  122. # 3 transactions attached to 3 different spans on the same transaction
  123. self.backend_transactions = [
  124. self.create_transaction(
  125. transaction=f"backend_transaction_{i}",
  126. trace_id=self.trace_id,
  127. span_id=backend_transaction_id,
  128. parent_span_id=frontend_span_id,
  129. spans=None,
  130. project_id=self.backend_project.id,
  131. start_timestamp=self.day_ago + timedelta(microseconds=100000 + i * 50000),
  132. duration=2500 + i * 500,
  133. )
  134. for i, (frontend_span_id, backend_transaction_id) in enumerate(
  135. zip(self.frontend_span_ids, self.backend_transaction_ids)
  136. )
  137. ]
  138. self.frontend_error = self.create_error(
  139. platform="javascript",
  140. trace_id=self.trace_id,
  141. span_id=self.frontend_span_ids[1],
  142. project_id=self.frontend_project.id,
  143. timestamp=self.day_ago,
  144. )
  145. self.frontend_transaction = self.create_transaction(
  146. transaction="frontend_transaction",
  147. trace_id=self.trace_id,
  148. span_id=self.frontend_transaction_id,
  149. parent_span_id=None,
  150. spans=[
  151. {
  152. "same_process_as_parent": True,
  153. "op": "http",
  154. "description": f"GET gen1-{i}",
  155. "span_id": frontend_span_id,
  156. "trace_id": self.trace_id,
  157. }
  158. for i, frontend_span_id in enumerate(self.frontend_span_ids)
  159. ],
  160. project_id=self.frontend_project.id,
  161. start_timestamp=self.day_ago,
  162. duration=4000,
  163. transaction_id="c" * 32,
  164. )
  165. @property
  166. def path(self):
  167. return "/organizations/{}/performance/trace/{}/?pageStart={}&pageEnd={}".format(
  168. self.org.slug,
  169. self.trace_id,
  170. iso_format(before_now(days=1).replace(hour=9, minute=0, second=0, microsecond=0)),
  171. iso_format(before_now(days=1).replace(hour=11, minute=0, second=0, microsecond=0)),
  172. )
  173. @patch("django.utils.timezone.now")
  174. def test_with_data(self, mock_now):
  175. mock_now.return_value = before_now().replace(tzinfo=pytz.utc)
  176. with self.feature(FEATURE_NAMES):
  177. self.browser.get(self.path)
  178. self.browser.wait_until_not('[data-test-id="loading-indicator"]')
  179. row_title = self.browser.elements('[data-test-id="transaction-row-title"]')[1]
  180. # HACK: Use JavaScript to execute click to avoid click intercepted issues
  181. self.browser.driver.execute_script("arguments[0].click()", row_title)
  182. self.browser.snapshot("performance trace view - with data")