useSpanMetricsSeries.spec.tsx 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267
  1. import type {ReactNode} from 'react';
  2. import {OrganizationFixture} from 'sentry-fixture/organization';
  3. import {makeTestQueryClient} from 'sentry-test/queryClient';
  4. import {reactHooks} from 'sentry-test/reactTestingLibrary';
  5. import {QueryClientProvider} from 'sentry/utils/queryClient';
  6. import {MutableSearch} from 'sentry/utils/tokenizeSearch';
  7. import {useLocation} from 'sentry/utils/useLocation';
  8. import useOrganization from 'sentry/utils/useOrganization';
  9. import usePageFilters from 'sentry/utils/usePageFilters';
  10. import {useSpanMetricsSeries} from 'sentry/views/starfish/queries/useSpanMetricsSeries';
  11. import type {MetricsProperty} from 'sentry/views/starfish/types';
  12. jest.mock('sentry/utils/useLocation');
  13. jest.mock('sentry/utils/usePageFilters');
  14. jest.mock('sentry/utils/useOrganization');
  15. function Wrapper({children}: {children?: ReactNode}) {
  16. return (
  17. <QueryClientProvider client={makeTestQueryClient()}>{children}</QueryClientProvider>
  18. );
  19. }
  20. describe('useSpanMetricsSeries', () => {
  21. const organization = OrganizationFixture();
  22. jest.mocked(usePageFilters).mockReturnValue({
  23. isReady: true,
  24. desyncedFilters: new Set(),
  25. pinnedFilters: new Set(),
  26. shouldPersist: true,
  27. selection: {
  28. datetime: {
  29. period: '10d',
  30. start: null,
  31. end: null,
  32. utc: false,
  33. },
  34. environments: [],
  35. projects: [],
  36. },
  37. });
  38. jest.mocked(useLocation).mockReturnValue({
  39. pathname: '',
  40. search: '',
  41. query: {},
  42. hash: '',
  43. state: undefined,
  44. action: 'PUSH',
  45. key: '',
  46. });
  47. jest.mocked(useOrganization).mockReturnValue(organization);
  48. it('respects the `enabled` prop', () => {
  49. const eventsRequest = MockApiClient.addMockResponse({
  50. url: `/organizations/${organization.slug}/events-stats/`,
  51. method: 'GET',
  52. body: {},
  53. });
  54. const {result} = reactHooks.renderHook(
  55. ({filters, enabled}) =>
  56. useSpanMetricsSeries({
  57. search: MutableSearch.fromQueryObject(filters),
  58. enabled,
  59. }),
  60. {
  61. wrapper: Wrapper,
  62. initialProps: {
  63. filters: {
  64. 'span.group': '221aa7ebd216',
  65. },
  66. enabled: false,
  67. },
  68. }
  69. );
  70. expect(result.current.isFetching).toEqual(false);
  71. expect(eventsRequest).not.toHaveBeenCalled();
  72. });
  73. it('queries for current selection', async () => {
  74. const eventsRequest = MockApiClient.addMockResponse({
  75. url: `/organizations/${organization.slug}/events-stats/`,
  76. method: 'GET',
  77. body: {
  78. 'spm()': {
  79. data: [
  80. [1699907700, [{count: 7810.2}]],
  81. [1699908000, [{count: 1216.8}]],
  82. ],
  83. },
  84. },
  85. });
  86. const {result, waitFor} = reactHooks.renderHook(
  87. ({filters, yAxis}) =>
  88. useSpanMetricsSeries({search: MutableSearch.fromQueryObject(filters), yAxis}),
  89. {
  90. wrapper: Wrapper,
  91. initialProps: {
  92. filters: {
  93. 'span.group': '221aa7ebd216',
  94. transaction: '/api/details',
  95. release: '0.0.1',
  96. 'resource.render_blocking_status': 'blocking' as const,
  97. environment: undefined,
  98. },
  99. yAxis: ['spm()'] as MetricsProperty[],
  100. },
  101. }
  102. );
  103. expect(result.current.isLoading).toEqual(true);
  104. expect(eventsRequest).toHaveBeenCalledWith(
  105. '/organizations/org-slug/events-stats/',
  106. expect.objectContaining({
  107. method: 'GET',
  108. query: expect.objectContaining({
  109. query: `span.group:221aa7ebd216 transaction:/api/details release:0.0.1 resource.render_blocking_status:blocking`,
  110. dataset: 'spansMetrics',
  111. statsPeriod: '10d',
  112. referrer: 'span-metrics-series',
  113. interval: '30m',
  114. yAxis: 'spm()',
  115. }),
  116. })
  117. );
  118. await waitFor(() => expect(result.current.isLoading).toEqual(false));
  119. });
  120. it('adjusts interval based on the yAxis', async () => {
  121. const eventsRequest = MockApiClient.addMockResponse({
  122. url: `/organizations/${organization.slug}/events-stats/`,
  123. method: 'GET',
  124. body: {},
  125. });
  126. const {rerender, waitFor} = reactHooks.renderHook(
  127. ({yAxis}) => useSpanMetricsSeries({yAxis}),
  128. {
  129. wrapper: Wrapper,
  130. initialProps: {
  131. yAxis: ['avg(span.self_time)', 'spm()'] as MetricsProperty[],
  132. },
  133. }
  134. );
  135. expect(eventsRequest).toHaveBeenLastCalledWith(
  136. '/organizations/org-slug/events-stats/',
  137. expect.objectContaining({
  138. method: 'GET',
  139. query: expect.objectContaining({
  140. interval: '30m',
  141. yAxis: ['avg(span.self_time)', 'spm()'] as MetricsProperty[],
  142. }),
  143. })
  144. );
  145. rerender({
  146. yAxis: ['p95(span.self_time)', 'spm()'] as MetricsProperty[],
  147. });
  148. await waitFor(() =>
  149. expect(eventsRequest).toHaveBeenLastCalledWith(
  150. '/organizations/org-slug/events-stats/',
  151. expect.objectContaining({
  152. method: 'GET',
  153. query: expect.objectContaining({
  154. interval: '1h',
  155. yAxis: ['p95(span.self_time)', 'spm()'] as MetricsProperty[],
  156. }),
  157. })
  158. )
  159. );
  160. });
  161. it('rolls single-axis responses up into a series', async () => {
  162. MockApiClient.addMockResponse({
  163. url: `/organizations/${organization.slug}/events-stats/`,
  164. method: 'GET',
  165. body: {
  166. data: [
  167. [1699907700, [{count: 7810.2}]],
  168. [1699908000, [{count: 1216.8}]],
  169. ],
  170. },
  171. });
  172. const {result, waitFor} = reactHooks.renderHook(
  173. ({yAxis}) => useSpanMetricsSeries({yAxis}),
  174. {
  175. wrapper: Wrapper,
  176. initialProps: {
  177. yAxis: ['spm()'] as MetricsProperty[],
  178. },
  179. }
  180. );
  181. await waitFor(() => expect(result.current.isLoading).toEqual(false));
  182. expect(result.current.data).toEqual({
  183. 'spm()': {
  184. data: [
  185. {name: '2023-11-13T20:35:00+00:00', value: 7810.2},
  186. {name: '2023-11-13T20:40:00+00:00', value: 1216.8},
  187. ],
  188. seriesName: 'spm()',
  189. },
  190. });
  191. });
  192. it('rolls multi-axis responses up into multiple series', async () => {
  193. MockApiClient.addMockResponse({
  194. url: `/organizations/${organization.slug}/events-stats/`,
  195. method: 'GET',
  196. body: {
  197. 'http_response_rate(3)': {
  198. data: [
  199. [1699907700, [{count: 10.1}]],
  200. [1699908000, [{count: 11.2}]],
  201. ],
  202. },
  203. 'http_response_rate(4)': {
  204. data: [
  205. [1699907700, [{count: 12.6}]],
  206. [1699908000, [{count: 13.8}]],
  207. ],
  208. },
  209. },
  210. });
  211. const {result, waitFor} = reactHooks.renderHook(
  212. ({yAxis}) => useSpanMetricsSeries({yAxis}),
  213. {
  214. wrapper: Wrapper,
  215. initialProps: {
  216. yAxis: ['http_response_rate(3)', 'http_response_rate(4)'] as MetricsProperty[],
  217. },
  218. }
  219. );
  220. await waitFor(() => expect(result.current.isLoading).toEqual(false));
  221. expect(result.current.data).toEqual({
  222. 'http_response_rate(3)': {
  223. data: [
  224. {name: '2023-11-13T20:35:00+00:00', value: 10.1},
  225. {name: '2023-11-13T20:40:00+00:00', value: 11.2},
  226. ],
  227. seriesName: 'http_response_rate(3)',
  228. },
  229. 'http_response_rate(4)': {
  230. data: [
  231. {name: '2023-11-13T20:35:00+00:00', value: 12.6},
  232. {name: '2023-11-13T20:40:00+00:00', value: 13.8},
  233. ],
  234. seriesName: 'http_response_rate(4)',
  235. },
  236. });
  237. });
  238. });