databaseSpanSummaryPage.spec.tsx 9.7 KB

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