databaseSpanSummaryPage.spec.tsx 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313
  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 transactionListMock = MockApiClient.addMockResponse({
  83. url: `/organizations/${organization.slug}/events/`,
  84. method: 'GET',
  85. match: [
  86. MockApiClient.matchQuery({
  87. referrer: 'api.starfish.span-transaction-metrics',
  88. }),
  89. ],
  90. body: {
  91. data: [
  92. {
  93. transaction: '/api/users',
  94. 'transaction.method': 'GET',
  95. 'span.op': 'db',
  96. 'spm()': 17.88,
  97. 'avg(span.self_time)': 204.5,
  98. 'sum(span.self_time)': 177238,
  99. 'time_spent_percentage()': 0.00341,
  100. },
  101. ],
  102. meta: {
  103. fields: {
  104. 'spm()': 'rate',
  105. 'avg(span.self_time)': 'duration',
  106. 'sum(span.self_time)': 'duration',
  107. 'time_spent_percentage()': 'percentage',
  108. },
  109. },
  110. },
  111. });
  112. render(
  113. <DatabaseSpanSummaryPage
  114. {...RouteComponentPropsFixture({})}
  115. params={{
  116. groupId: '1756baf8fd19c116',
  117. transaction: '',
  118. transactionMethod: '',
  119. transactionsSort: '',
  120. }}
  121. />,
  122. {organization}
  123. );
  124. // Metrics ribbon
  125. expect(eventsRequestMock).toHaveBeenNthCalledWith(
  126. 1,
  127. `/organizations/${organization.slug}/events/`,
  128. expect.objectContaining({
  129. method: 'GET',
  130. query: {
  131. dataset: 'spansMetrics',
  132. environment: [],
  133. field: [
  134. 'span.op',
  135. 'span.description',
  136. 'span.action',
  137. 'span.domain',
  138. 'count()',
  139. 'spm()',
  140. 'sum(span.self_time)',
  141. 'avg(span.self_time)',
  142. 'time_spent_percentage()',
  143. 'http_error_count()',
  144. ],
  145. per_page: 50,
  146. project: [],
  147. query: 'span.group:1756baf8fd19c116',
  148. referrer: 'api.starfish.span-summary-page-metrics',
  149. statsPeriod: '10d',
  150. },
  151. })
  152. );
  153. // Full span description
  154. expect(spanDescriptionRequestMock).toHaveBeenNthCalledWith(
  155. 1,
  156. `/organizations/${organization.slug}/events/`,
  157. expect.objectContaining({
  158. method: 'GET',
  159. query: {
  160. dataset: 'spansIndexed',
  161. environment: [],
  162. field: ['project_id', 'transaction.id', 'span.description'],
  163. per_page: 1,
  164. project: [],
  165. query: 'span.group:1756baf8fd19c116',
  166. referrer: 'api.starfish.span-description',
  167. statsPeriod: '10d',
  168. },
  169. })
  170. );
  171. // SPM Chart
  172. expect(eventsStatsRequestMock).toHaveBeenNthCalledWith(
  173. 1,
  174. `/organizations/${organization.slug}/events-stats/`,
  175. expect.objectContaining({
  176. method: 'GET',
  177. query: {
  178. cursor: undefined,
  179. dataset: 'spansMetrics',
  180. environment: [],
  181. excludeOther: 0,
  182. field: [],
  183. interval: '30m',
  184. orderby: undefined,
  185. partial: 1,
  186. per_page: 50,
  187. project: [],
  188. query: 'span.group:1756baf8fd19c116',
  189. referrer: 'api.starfish.span-summary-page-metrics-chart',
  190. statsPeriod: '10d',
  191. topEvents: undefined,
  192. yAxis: 'spm()',
  193. },
  194. })
  195. );
  196. // Duration Chart
  197. expect(eventsStatsRequestMock).toHaveBeenNthCalledWith(
  198. 2,
  199. `/organizations/${organization.slug}/events-stats/`,
  200. expect.objectContaining({
  201. method: 'GET',
  202. query: {
  203. cursor: undefined,
  204. dataset: 'spansMetrics',
  205. environment: [],
  206. excludeOther: 0,
  207. field: [],
  208. interval: '30m',
  209. orderby: undefined,
  210. partial: 1,
  211. per_page: 50,
  212. project: [],
  213. query: 'span.group:1756baf8fd19c116',
  214. referrer: 'api.starfish.span-summary-page-metrics-chart',
  215. statsPeriod: '10d',
  216. topEvents: undefined,
  217. yAxis: 'avg(span.self_time)',
  218. },
  219. })
  220. );
  221. // Transactions table
  222. expect(transactionListMock).toHaveBeenNthCalledWith(
  223. 1,
  224. `/organizations/${organization.slug}/events/`,
  225. expect.objectContaining({
  226. method: 'GET',
  227. query: {
  228. dataset: 'spansMetrics',
  229. environment: [],
  230. field: [
  231. 'transaction',
  232. 'transaction.method',
  233. 'spm()',
  234. 'sum(span.self_time)',
  235. 'avg(span.self_time)',
  236. 'time_spent_percentage()',
  237. 'http_error_count()',
  238. ],
  239. per_page: 25,
  240. cursor: '0:25:0',
  241. project: [],
  242. query: 'span.group:1756baf8fd19c116',
  243. sort: '-time_spent_percentage()',
  244. referrer: 'api.starfish.span-transaction-metrics',
  245. statsPeriod: '10d',
  246. },
  247. })
  248. );
  249. expect(spanDescriptionRequestMock).toHaveBeenCalledTimes(1);
  250. expect(eventsRequestMock).toHaveBeenCalledTimes(1);
  251. expect(eventsStatsRequestMock).toHaveBeenCalledTimes(2);
  252. expect(transactionListMock).toHaveBeenCalledTimes(1);
  253. await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
  254. // Span details for query source. This runs after the indexed span has loaded
  255. expect(eventsRequestMock).toHaveBeenNthCalledWith(
  256. 2,
  257. `/organizations/${organization.slug}/events/`,
  258. expect.objectContaining({
  259. method: 'GET',
  260. query: {
  261. dataset: 'spansIndexed',
  262. environment: [],
  263. field: ['transaction.id', 'project', 'span_id', 'span.self_time'],
  264. per_page: 1,
  265. project: [],
  266. query: 'span.group:1756baf8fd19c116',
  267. sort: '-span.self_time',
  268. referrer: 'api.starfish.full-span-from-trace',
  269. statsPeriod: '10d',
  270. },
  271. })
  272. );
  273. expect(screen.getByRole('table', {name: 'Transactions'})).toBeInTheDocument();
  274. expect(screen.getByRole('columnheader', {name: 'Found In'})).toBeInTheDocument();
  275. expect(
  276. screen.getByRole('columnheader', {name: 'Queries Per Minute'})
  277. ).toBeInTheDocument();
  278. expect(screen.getByRole('columnheader', {name: 'Avg Duration'})).toBeInTheDocument();
  279. expect(screen.getByRole('columnheader', {name: 'Time Spent'})).toBeInTheDocument();
  280. expect(screen.getByRole('cell', {name: 'GET /api/users'})).toBeInTheDocument();
  281. expect(screen.getByRole('link', {name: 'GET /api/users'})).toHaveAttribute(
  282. 'href',
  283. '/organizations/org-slug/performance/database/spans/span/1756baf8fd19c116?statsPeriod=10d&transaction=%2Fapi%2Fusers&transactionMethod=GET&transactionsCursor=0%3A25%3A0'
  284. );
  285. expect(screen.getByRole('cell', {name: '17.9/s'})).toBeInTheDocument();
  286. expect(screen.getByRole('cell', {name: '204.50ms'})).toBeInTheDocument();
  287. expect(screen.getByRole('cell', {name: '2.95min'})).toBeInTheDocument();
  288. });
  289. });