useDiscoverSeries.spec.tsx 7.5 KB

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