useDiscoverSeries.spec.tsx 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  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 useOrganization from 'sentry/utils/useOrganization';
  9. import usePageFilters from 'sentry/utils/usePageFilters';
  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. 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} = renderHook(
  55. ({filters, enabled}) =>
  56. useSpanMetricsSeries(
  57. {
  58. search: MutableSearch.fromQueryObject(filters),
  59. enabled,
  60. },
  61. 'span-metrics-series'
  62. ),
  63. {
  64. wrapper: Wrapper,
  65. initialProps: {
  66. filters: {
  67. 'span.group': '221aa7ebd216',
  68. },
  69. enabled: false,
  70. },
  71. }
  72. );
  73. expect(result.current.isFetching).toEqual(false);
  74. expect(eventsRequest).not.toHaveBeenCalled();
  75. });
  76. it('queries for current selection', async () => {
  77. const eventsRequest = MockApiClient.addMockResponse({
  78. url: `/organizations/${organization.slug}/events-stats/`,
  79. method: 'GET',
  80. body: {
  81. 'spm()': {
  82. data: [
  83. [1699907700, [{count: 7810.2}]],
  84. [1699908000, [{count: 1216.8}]],
  85. ],
  86. },
  87. },
  88. });
  89. const {result} = renderHook(
  90. ({filters, yAxis}) =>
  91. useSpanMetricsSeries(
  92. {search: MutableSearch.fromQueryObject(filters), yAxis},
  93. 'span-metrics-series'
  94. ),
  95. {
  96. wrapper: Wrapper,
  97. initialProps: {
  98. filters: {
  99. 'span.group': '221aa7ebd216',
  100. transaction: '/api/details',
  101. release: '0.0.1',
  102. 'resource.render_blocking_status': 'blocking' as const,
  103. environment: undefined,
  104. },
  105. yAxis: ['spm()'] as SpanMetricsProperty[],
  106. },
  107. }
  108. );
  109. expect(result.current.isLoading).toEqual(true);
  110. expect(eventsRequest).toHaveBeenCalledWith(
  111. '/organizations/org-slug/events-stats/',
  112. expect.objectContaining({
  113. method: 'GET',
  114. query: expect.objectContaining({
  115. query: `span.group:221aa7ebd216 transaction:/api/details release:0.0.1 resource.render_blocking_status:blocking`,
  116. dataset: 'spansMetrics',
  117. statsPeriod: '10d',
  118. referrer: 'span-metrics-series',
  119. interval: '30m',
  120. yAxis: 'spm()',
  121. }),
  122. })
  123. );
  124. await waitFor(() => expect(result.current.isLoading).toEqual(false));
  125. });
  126. it('adjusts interval based on the yAxis', async () => {
  127. const eventsRequest = MockApiClient.addMockResponse({
  128. url: `/organizations/${organization.slug}/events-stats/`,
  129. method: 'GET',
  130. body: {},
  131. });
  132. const {rerender} = renderHook(
  133. ({yAxis}) => useSpanMetricsSeries({yAxis}, 'span-metrics-series'),
  134. {
  135. wrapper: Wrapper,
  136. initialProps: {
  137. yAxis: ['avg(span.self_time)', 'spm()'] as SpanMetricsProperty[],
  138. },
  139. }
  140. );
  141. expect(eventsRequest).toHaveBeenLastCalledWith(
  142. '/organizations/org-slug/events-stats/',
  143. expect.objectContaining({
  144. method: 'GET',
  145. query: expect.objectContaining({
  146. interval: '30m',
  147. yAxis: ['avg(span.self_time)', 'spm()'] as SpanMetricsProperty[],
  148. }),
  149. })
  150. );
  151. rerender({
  152. yAxis: ['p95(span.self_time)', 'spm()'] as SpanMetricsProperty[],
  153. });
  154. await waitFor(() =>
  155. expect(eventsRequest).toHaveBeenLastCalledWith(
  156. '/organizations/org-slug/events-stats/',
  157. expect.objectContaining({
  158. method: 'GET',
  159. query: expect.objectContaining({
  160. interval: '1h',
  161. yAxis: ['p95(span.self_time)', 'spm()'] as SpanMetricsProperty[],
  162. }),
  163. })
  164. )
  165. );
  166. });
  167. it('rolls single-axis responses up into a series', async () => {
  168. MockApiClient.addMockResponse({
  169. url: `/organizations/${organization.slug}/events-stats/`,
  170. method: 'GET',
  171. body: {
  172. data: [
  173. [1699907700, [{count: 7810.2}]],
  174. [1699908000, [{count: 1216.8}]],
  175. ],
  176. },
  177. });
  178. const {result} = renderHook(
  179. ({yAxis}) => useSpanMetricsSeries({yAxis}, 'span-metrics-series'),
  180. {
  181. wrapper: Wrapper,
  182. initialProps: {
  183. yAxis: ['spm()'] as SpanMetricsProperty[],
  184. },
  185. }
  186. );
  187. await waitFor(() => expect(result.current.isLoading).toEqual(false));
  188. expect(result.current.data).toEqual({
  189. 'spm()': {
  190. data: [
  191. {name: '2023-11-13T20:35:00+00:00', value: 7810.2},
  192. {name: '2023-11-13T20:40:00+00:00', value: 1216.8},
  193. ],
  194. seriesName: 'spm()',
  195. },
  196. });
  197. });
  198. it('rolls multi-axis responses up into multiple series', async () => {
  199. MockApiClient.addMockResponse({
  200. url: `/organizations/${organization.slug}/events-stats/`,
  201. method: 'GET',
  202. body: {
  203. 'http_response_rate(3)': {
  204. data: [
  205. [1699907700, [{count: 10.1}]],
  206. [1699908000, [{count: 11.2}]],
  207. ],
  208. },
  209. 'http_response_rate(4)': {
  210. data: [
  211. [1699907700, [{count: 12.6}]],
  212. [1699908000, [{count: 13.8}]],
  213. ],
  214. },
  215. },
  216. });
  217. const {result} = renderHook(
  218. ({yAxis}) => useSpanMetricsSeries({yAxis}, 'span-metrics-series'),
  219. {
  220. wrapper: Wrapper,
  221. initialProps: {
  222. yAxis: [
  223. 'http_response_rate(3)',
  224. 'http_response_rate(4)',
  225. ] as SpanMetricsProperty[],
  226. },
  227. }
  228. );
  229. await waitFor(() => expect(result.current.isLoading).toEqual(false));
  230. expect(result.current.data).toEqual({
  231. 'http_response_rate(3)': {
  232. data: [
  233. {name: '2023-11-13T20:35:00+00:00', value: 10.1},
  234. {name: '2023-11-13T20:40:00+00:00', value: 11.2},
  235. ],
  236. seriesName: 'http_response_rate(3)',
  237. },
  238. 'http_response_rate(4)': {
  239. data: [
  240. {name: '2023-11-13T20:35:00+00:00', value: 12.6},
  241. {name: '2023-11-13T20:40:00+00:00', value: 13.8},
  242. ],
  243. seriesName: 'http_response_rate(4)',
  244. },
  245. });
  246. });
  247. });