databaseSpanSummaryPage.spec.tsx 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {RouteComponentPropsFixture} from 'sentry-fixture/routeComponentPropsFixture';
  3. import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
  4. import {useLocation} from 'sentry/utils/useLocation';
  5. import useOrganization from 'sentry/utils/useOrganization';
  6. import usePageFilters from 'sentry/utils/usePageFilters';
  7. import {DatabaseSpanSummaryPage} from 'sentry/views/performance/database/databaseSpanSummaryPage';
  8. jest.mock('sentry/utils/useLocation');
  9. jest.mock('sentry/utils/usePageFilters');
  10. jest.mock('sentry/utils/useOrganization');
  11. describe('DatabaseSpanSummaryPage', function () {
  12. const organization = OrganizationFixture();
  13. jest.mocked(usePageFilters).mockReturnValue({
  14. isReady: true,
  15. desyncedFilters: new Set(),
  16. pinnedFilters: new Set(),
  17. shouldPersist: true,
  18. selection: {
  19. datetime: {
  20. period: '10d',
  21. start: null,
  22. end: null,
  23. utc: false,
  24. },
  25. environments: [],
  26. projects: [],
  27. },
  28. });
  29. jest.mocked(useLocation).mockReturnValue({
  30. pathname: '',
  31. search: '',
  32. query: {statsPeriod: '10d', transactionsCursor: '0:25:0'},
  33. hash: '',
  34. state: undefined,
  35. action: 'PUSH',
  36. key: '',
  37. });
  38. jest.mocked(useOrganization).mockReturnValue(organization);
  39. beforeEach(function () {
  40. jest.clearAllMocks();
  41. });
  42. afterAll(function () {
  43. jest.resetAllMocks();
  44. });
  45. it('renders', async function () {
  46. const eventsRequestMock = MockApiClient.addMockResponse({
  47. url: `/organizations/${organization.slug}/events/`,
  48. method: 'GET',
  49. body: {
  50. data: [
  51. {
  52. 'span.op': 'db',
  53. },
  54. ],
  55. },
  56. });
  57. const eventsStatsRequestMock = MockApiClient.addMockResponse({
  58. url: `/organizations/${organization.slug}/events-stats/`,
  59. method: 'GET',
  60. body: {
  61. 'spm()': {
  62. data: [],
  63. },
  64. 'avg(span.self_time)': {
  65. data: [],
  66. },
  67. },
  68. });
  69. const spanDescriptionRequestMock = MockApiClient.addMockResponse({
  70. url: `/organizations/${organization.slug}/events/`,
  71. match: [
  72. MockApiClient.matchQuery({
  73. referrer: 'api.starfish.span-description',
  74. }),
  75. ],
  76. method: 'GET',
  77. body: {
  78. data: [
  79. {
  80. 'span.group': '1756baf8fd19c116',
  81. },
  82. ],
  83. },
  84. });
  85. const transactionListMock = MockApiClient.addMockResponse({
  86. url: `/organizations/${organization.slug}/events/`,
  87. method: 'GET',
  88. match: [
  89. MockApiClient.matchQuery({
  90. referrer: 'api.starfish.span-transaction-metrics',
  91. }),
  92. ],
  93. body: {
  94. data: [
  95. {
  96. transaction: '/api/users',
  97. 'transaction.method': 'GET',
  98. 'span.op': 'db',
  99. 'spm()': 17.88,
  100. 'avg(span.self_time)': 204.5,
  101. 'sum(span.self_time)': 177238,
  102. 'time_spent_percentage()': 0.00341,
  103. },
  104. ],
  105. meta: {
  106. fields: {
  107. 'spm()': 'rate',
  108. 'avg(span.self_time)': 'duration',
  109. 'sum(span.self_time)': 'duration',
  110. 'time_spent_percentage()': 'percentage',
  111. },
  112. },
  113. },
  114. });
  115. render(
  116. <DatabaseSpanSummaryPage
  117. {...RouteComponentPropsFixture({})}
  118. params={{
  119. groupId: '1756baf8fd19c116',
  120. transaction: '',
  121. transactionMethod: '',
  122. transactionsSort: '',
  123. }}
  124. />
  125. );
  126. // Metrics ribbon
  127. expect(eventsRequestMock).toHaveBeenNthCalledWith(
  128. 1,
  129. `/organizations/${organization.slug}/events/`,
  130. expect.objectContaining({
  131. method: 'GET',
  132. query: {
  133. dataset: 'spansMetrics',
  134. environment: [],
  135. field: [
  136. 'span.op',
  137. 'span.description',
  138. 'span.action',
  139. 'span.domain',
  140. 'count()',
  141. 'spm()',
  142. 'sum(span.self_time)',
  143. 'avg(span.self_time)',
  144. 'time_spent_percentage()',
  145. 'http_error_count()',
  146. ],
  147. per_page: 50,
  148. project: [],
  149. query: 'span.group:1756baf8fd19c116',
  150. referrer: 'api.starfish.span-summary-page-metrics',
  151. statsPeriod: '10d',
  152. },
  153. })
  154. );
  155. // Full span description
  156. expect(spanDescriptionRequestMock).toHaveBeenNthCalledWith(
  157. 1,
  158. `/organizations/${organization.slug}/events/`,
  159. expect.objectContaining({
  160. method: 'GET',
  161. query: {
  162. dataset: 'spansIndexed',
  163. environment: [],
  164. field: ['project_id', 'transaction.id', 'span.description'],
  165. per_page: 1,
  166. project: [],
  167. query: 'span.group:1756baf8fd19c116',
  168. referrer: 'api.starfish.span-description',
  169. statsPeriod: '10d',
  170. },
  171. })
  172. );
  173. // SPM Chart
  174. expect(eventsStatsRequestMock).toHaveBeenNthCalledWith(
  175. 1,
  176. `/organizations/${organization.slug}/events-stats/`,
  177. expect.objectContaining({
  178. method: 'GET',
  179. query: {
  180. cursor: undefined,
  181. dataset: 'spansMetrics',
  182. environment: [],
  183. excludeOther: 0,
  184. field: [],
  185. interval: '30m',
  186. orderby: undefined,
  187. partial: 1,
  188. per_page: 50,
  189. project: [],
  190. query: 'span.group:1756baf8fd19c116',
  191. referrer: 'api.starfish.span-summary-page-metrics-chart',
  192. statsPeriod: '10d',
  193. topEvents: undefined,
  194. yAxis: 'spm()',
  195. },
  196. })
  197. );
  198. // Duration Chart
  199. expect(eventsStatsRequestMock).toHaveBeenNthCalledWith(
  200. 2,
  201. `/organizations/${organization.slug}/events-stats/`,
  202. expect.objectContaining({
  203. method: 'GET',
  204. query: {
  205. cursor: undefined,
  206. dataset: 'spansMetrics',
  207. environment: [],
  208. excludeOther: 0,
  209. field: [],
  210. interval: '30m',
  211. orderby: undefined,
  212. partial: 1,
  213. per_page: 50,
  214. project: [],
  215. query: 'span.group:1756baf8fd19c116',
  216. referrer: 'api.starfish.span-summary-page-metrics-chart',
  217. statsPeriod: '10d',
  218. topEvents: undefined,
  219. yAxis: 'avg(span.self_time)',
  220. },
  221. })
  222. );
  223. // Transactions table
  224. expect(transactionListMock).toHaveBeenNthCalledWith(
  225. 1,
  226. `/organizations/${organization.slug}/events/`,
  227. expect.objectContaining({
  228. method: 'GET',
  229. query: {
  230. dataset: 'spansMetrics',
  231. environment: [],
  232. field: [
  233. 'transaction',
  234. 'transaction.method',
  235. 'spm()',
  236. 'sum(span.self_time)',
  237. 'avg(span.self_time)',
  238. 'time_spent_percentage()',
  239. 'http_error_count()',
  240. ],
  241. per_page: 25,
  242. cursor: '0:25:0',
  243. project: [],
  244. query: 'span.group:1756baf8fd19c116',
  245. sort: '-time_spent_percentage()',
  246. referrer: 'api.starfish.span-transaction-metrics',
  247. statsPeriod: '10d',
  248. },
  249. })
  250. );
  251. expect(spanDescriptionRequestMock).toHaveBeenCalledTimes(1);
  252. expect(eventsRequestMock).toHaveBeenCalledTimes(1);
  253. expect(eventsStatsRequestMock).toHaveBeenCalledTimes(2);
  254. expect(transactionListMock).toHaveBeenCalledTimes(1);
  255. await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
  256. // Span details for query source. This runs after the indexed span has loaded
  257. expect(eventsRequestMock).toHaveBeenNthCalledWith(
  258. 2,
  259. `/organizations/${organization.slug}/events/`,
  260. expect.objectContaining({
  261. method: 'GET',
  262. query: {
  263. dataset: 'spansIndexed',
  264. environment: [],
  265. field: [
  266. 'resource.render_blocking_status',
  267. 'http.response_content_length',
  268. 'span.duration',
  269. 'span.self_time',
  270. 'span.group',
  271. 'span.module',
  272. 'span.description',
  273. 'span.op',
  274. 'span_id',
  275. 'span.action',
  276. 'span.ai.pipeline.group',
  277. 'trace',
  278. 'transaction.id',
  279. 'transaction.method',
  280. 'transaction.op',
  281. 'span.domain',
  282. 'timestamp',
  283. 'raw_domain',
  284. 'project',
  285. 'project_id',
  286. ],
  287. per_page: 1,
  288. project: [],
  289. query: 'span.group:1756baf8fd19c116',
  290. sort: '-span.self_time',
  291. referrer: 'api.starfish.full-span-from-trace',
  292. statsPeriod: '10d',
  293. },
  294. })
  295. );
  296. expect(screen.getByRole('table', {name: 'Transactions'})).toBeInTheDocument();
  297. expect(screen.getByRole('columnheader', {name: 'Found In'})).toBeInTheDocument();
  298. expect(
  299. screen.getByRole('columnheader', {name: 'Queries Per Minute'})
  300. ).toBeInTheDocument();
  301. expect(screen.getByRole('columnheader', {name: 'Avg Duration'})).toBeInTheDocument();
  302. expect(screen.getByRole('columnheader', {name: 'Time Spent'})).toBeInTheDocument();
  303. expect(screen.getByRole('cell', {name: 'GET /api/users'})).toBeInTheDocument();
  304. expect(screen.getByRole('link', {name: 'GET /api/users'})).toHaveAttribute(
  305. 'href',
  306. '/organizations/org-slug/performance/database/spans/span/1756baf8fd19c116?statsPeriod=10d&transaction=%2Fapi%2Fusers&transactionMethod=GET&transactionsCursor=0%3A25%3A0'
  307. );
  308. expect(screen.getByRole('cell', {name: '17.9/s'})).toBeInTheDocument();
  309. expect(screen.getByRole('cell', {name: '204.50ms'})).toBeInTheDocument();
  310. expect(screen.getByRole('cell', {name: '2.95min'})).toBeInTheDocument();
  311. });
  312. });