messageSpanSamplesPanel.spec.tsx 10 KB

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