messageSpanSamplesPanel.spec.tsx 9.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. import {OrganizationFixture} from 'sentry-fixture/organization';
  2. import {render, screen, waitForElementToBeRemoved} from 'sentry-test/reactTestingLibrary';
  3. import {useLocation} from 'sentry/utils/useLocation';
  4. import useOrganization from 'sentry/utils/useOrganization';
  5. import usePageFilters from 'sentry/utils/usePageFilters';
  6. import {MessageSpanSamplesPanel} from 'sentry/views/performance/queues/destinationSummary/messageSpanSamplesPanel';
  7. jest.mock('sentry/utils/useLocation');
  8. jest.mock('sentry/utils/usePageFilters');
  9. jest.mock('sentry/utils/useOrganization');
  10. describe('messageSpanSamplesPanel', () => {
  11. const organization = OrganizationFixture();
  12. let eventsRequestMock, eventsStatsRequestMock, samplesRequestMock;
  13. jest.mocked(usePageFilters).mockReturnValue({
  14. isReady: true,
  15. desyncedFilters: new Set(),
  16. pinnedFilters: new Set(),
  17. shouldPersist: true,
  18. selection: {
  19. datetime: {
  20. period: '10d',
  21. start: null,
  22. end: null,
  23. utc: false,
  24. },
  25. environments: [],
  26. projects: [],
  27. },
  28. });
  29. jest.mocked(useLocation).mockReturnValue({
  30. pathname: '',
  31. search: '',
  32. query: {transaction: 'sentry.tasks.store.save_event', destination: 'event-queue'},
  33. hash: '',
  34. state: undefined,
  35. action: 'PUSH',
  36. key: '',
  37. });
  38. jest.mocked(useOrganization).mockReturnValue(organization);
  39. beforeEach(() => {
  40. eventsStatsRequestMock = MockApiClient.addMockResponse({
  41. url: `/organizations/${organization.slug}/events-stats/`,
  42. method: 'GET',
  43. body: {
  44. data: [[1699907700, [{count: 7810}]]],
  45. meta: {},
  46. },
  47. });
  48. eventsRequestMock = MockApiClient.addMockResponse({
  49. url: `/organizations/${organization.slug}/events/`,
  50. method: 'GET',
  51. body: {
  52. data: [
  53. {
  54. 'sum(span.duration)': 10.0,
  55. 'trace_status_rate(ok)': 0.8,
  56. 'count_op(queue.publish)': 222,
  57. 'count_op(queue.process)': 333,
  58. 'avg_if(span.duration,span.op,queue.publish)': 3.0,
  59. 'avg_if(span.duration,span.op,queue.process)': 4.0,
  60. 'count()': 555,
  61. 'avg(messaging.message.receive.latency)': 2.0,
  62. 'avg(span.duration)': 3.5,
  63. },
  64. ],
  65. meta: {
  66. fields: {
  67. 'sum(span.duration)': 'duration',
  68. 'trace_status_rate(ok)': 'percentage',
  69. 'count_op(queue.publish)': 'integer',
  70. 'count_op(queue.process)': 'integer',
  71. 'avg_if(span.duration,span.op,queue.publish)': 'duration',
  72. 'avg_if(span.duration,span.op,queue.process)': 'duration',
  73. 'count()': 'integer',
  74. 'avg(messaging.message.receive.latency)': 'number',
  75. 'avg(span.duration)': 'duration',
  76. },
  77. units: {
  78. 'sum(span.duration)': 'millisecond',
  79. 'trace_status_rate(ok)': null,
  80. 'count_op(queue.publish)': null,
  81. 'count_op(queue.process)': null,
  82. 'avg_if(span.duration,span.op,queue.publish)': 'millisecond',
  83. 'avg_if(span.duration,span.op,queue.process)': 'millisecond',
  84. 'count()': null,
  85. 'avg(messaging.message.receive.latency)': null,
  86. 'avg(span.duration)': 'millisecond',
  87. },
  88. },
  89. },
  90. });
  91. samplesRequestMock = MockApiClient.addMockResponse({
  92. url: `/api/0/organizations/${organization.slug}/spans-samples/`,
  93. method: 'GET',
  94. body: {
  95. data: [
  96. {
  97. span_id: '123',
  98. trace: 'abc',
  99. project: 'project',
  100. timestamp: '2024-03-25T20:31:36+00:00',
  101. 'span.duration': 320.300102,
  102. },
  103. ],
  104. },
  105. });
  106. });
  107. afterAll(() => {
  108. jest.resetAllMocks();
  109. });
  110. it('renders consumer panel', async () => {
  111. jest.mocked(useLocation).mockReturnValue({
  112. pathname: '',
  113. search: '',
  114. query: {
  115. transaction: 'sentry.tasks.store.save_event',
  116. destination: 'event-queue',
  117. 'span.op': 'queue.process',
  118. },
  119. hash: '',
  120. state: undefined,
  121. action: 'PUSH',
  122. key: '',
  123. });
  124. render(<MessageSpanSamplesPanel />);
  125. await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
  126. expect(eventsStatsRequestMock).toHaveBeenCalled();
  127. expect(eventsRequestMock).toHaveBeenCalledWith(
  128. `/organizations/${organization.slug}/events/`,
  129. expect.objectContaining({
  130. method: 'GET',
  131. query: expect.objectContaining({
  132. dataset: 'spansMetrics',
  133. environment: [],
  134. field: [
  135. 'count()',
  136. 'count_op(queue.publish)',
  137. 'count_op(queue.process)',
  138. 'sum(span.duration)',
  139. 'avg(span.duration)',
  140. 'avg_if(span.duration,span.op,queue.publish)',
  141. 'avg_if(span.duration,span.op,queue.process)',
  142. 'avg(messaging.message.receive.latency)',
  143. 'trace_status_rate(ok)',
  144. 'time_spent_percentage(app,span.duration)',
  145. ],
  146. per_page: 10,
  147. project: [],
  148. query:
  149. 'span.op:[queue.process,queue.publish] messaging.destination.name:event-queue transaction:sentry.tasks.store.save_event',
  150. statsPeriod: '10d',
  151. }),
  152. })
  153. );
  154. expect(samplesRequestMock).toHaveBeenCalledWith(
  155. `/api/0/organizations/${organization.slug}/spans-samples/`,
  156. expect.objectContaining({
  157. query: expect.objectContaining({
  158. additionalFields: [
  159. 'trace',
  160. 'transaction.id',
  161. 'span.description',
  162. 'measurements.messaging.message.body.size',
  163. 'measurements.messaging.message.receive.latency',
  164. 'measurements.messaging.message.retry.count',
  165. 'messaging.message.id',
  166. 'trace.status',
  167. 'span.duration',
  168. ],
  169. firstBound: 2666.6666666666665,
  170. lowerBound: 0,
  171. project: [],
  172. query:
  173. 'span.op:queue.process transaction:sentry.tasks.store.save_event messaging.destination.name:event-queue',
  174. referrer: undefined,
  175. secondBound: 5333.333333333333,
  176. statsPeriod: '10d',
  177. upperBound: 8000,
  178. }),
  179. })
  180. );
  181. expect(screen.getByRole('table', {name: 'Span Samples'})).toBeInTheDocument();
  182. expect(screen.getByText('Consumer')).toBeInTheDocument();
  183. // Metrics Ribbon
  184. expect(screen.getByText('Processed')).toBeInTheDocument();
  185. expect(screen.getByText('Error Rate')).toBeInTheDocument();
  186. expect(screen.getByText('Avg Time In Queue')).toBeInTheDocument();
  187. expect(screen.getByText('Avg Processing Time')).toBeInTheDocument();
  188. expect(screen.getByText('333')).toBeInTheDocument();
  189. expect(screen.getByText('20%')).toBeInTheDocument();
  190. expect(screen.getByText('2.00ms')).toBeInTheDocument();
  191. expect(screen.getByText('4.00ms')).toBeInTheDocument();
  192. });
  193. it('renders producer panel', async () => {
  194. jest.mocked(useLocation).mockReturnValue({
  195. pathname: '',
  196. search: '',
  197. query: {
  198. transaction: 'sentry.tasks.store.save_event',
  199. destination: 'event-queue',
  200. 'span.op': 'queue.publish',
  201. },
  202. hash: '',
  203. state: undefined,
  204. action: 'PUSH',
  205. key: '',
  206. });
  207. render(<MessageSpanSamplesPanel />);
  208. await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
  209. expect(eventsStatsRequestMock).toHaveBeenCalled();
  210. expect(eventsRequestMock).toHaveBeenCalledWith(
  211. `/organizations/${organization.slug}/events/`,
  212. expect.objectContaining({
  213. method: 'GET',
  214. query: expect.objectContaining({
  215. dataset: 'spansMetrics',
  216. environment: [],
  217. field: [
  218. 'count()',
  219. 'count_op(queue.publish)',
  220. 'count_op(queue.process)',
  221. 'sum(span.duration)',
  222. 'avg(span.duration)',
  223. 'avg_if(span.duration,span.op,queue.publish)',
  224. 'avg_if(span.duration,span.op,queue.process)',
  225. 'avg(messaging.message.receive.latency)',
  226. 'trace_status_rate(ok)',
  227. 'time_spent_percentage(app,span.duration)',
  228. ],
  229. per_page: 10,
  230. project: [],
  231. query:
  232. 'span.op:[queue.process,queue.publish] messaging.destination.name:event-queue transaction:sentry.tasks.store.save_event',
  233. statsPeriod: '10d',
  234. }),
  235. })
  236. );
  237. expect(samplesRequestMock).toHaveBeenCalledWith(
  238. `/api/0/organizations/${organization.slug}/spans-samples/`,
  239. expect.objectContaining({
  240. query: expect.objectContaining({
  241. additionalFields: [
  242. 'trace',
  243. 'transaction.id',
  244. 'span.description',
  245. 'measurements.messaging.message.body.size',
  246. 'measurements.messaging.message.receive.latency',
  247. 'measurements.messaging.message.retry.count',
  248. 'messaging.message.id',
  249. 'trace.status',
  250. 'span.duration',
  251. ],
  252. firstBound: 2666.6666666666665,
  253. lowerBound: 0,
  254. project: [],
  255. query:
  256. 'span.op:queue.publish transaction:sentry.tasks.store.save_event messaging.destination.name:event-queue',
  257. referrer: undefined,
  258. secondBound: 5333.333333333333,
  259. statsPeriod: '10d',
  260. upperBound: 8000,
  261. }),
  262. })
  263. );
  264. expect(screen.getByRole('table', {name: 'Span Samples'})).toBeInTheDocument();
  265. expect(screen.getByText('Producer')).toBeInTheDocument();
  266. // Metrics Ribbon
  267. expect(screen.getByText('Published')).toBeInTheDocument();
  268. expect(screen.getByText('Error Rate')).toBeInTheDocument();
  269. expect(screen.getByText('222')).toBeInTheDocument();
  270. expect(screen.getByText('20%')).toBeInTheDocument();
  271. });
  272. });