messageSpanSamplesPanel.spec.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323
  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: jest.Mock;
  11. let eventsStatsRequestMock: jest.Mock;
  12. let samplesRequestMock: jest.Mock;
  13. let spanFieldTagsMock: jest.Mock;
  14. jest.mocked(usePageFilters).mockReturnValue({
  15. isReady: true,
  16. desyncedFilters: new Set(),
  17. pinnedFilters: new Set(),
  18. shouldPersist: true,
  19. selection: {
  20. datetime: {
  21. period: '10d',
  22. start: null,
  23. end: null,
  24. utc: false,
  25. },
  26. environments: [],
  27. projects: [],
  28. },
  29. });
  30. jest.mocked(useLocation).mockReturnValue({
  31. pathname: '',
  32. search: '',
  33. query: {transaction: 'sentry.tasks.store.save_event', destination: 'event-queue'},
  34. hash: '',
  35. state: undefined,
  36. action: 'PUSH',
  37. key: '',
  38. });
  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. spanFieldTagsMock = MockApiClient.addMockResponse({
  107. url: `/organizations/${organization.slug}/spans/fields/`,
  108. method: 'GET',
  109. body: [
  110. {
  111. key: 'api_key',
  112. name: 'Api Key',
  113. },
  114. {
  115. key: 'bytes.size',
  116. name: 'Bytes.Size',
  117. },
  118. ],
  119. });
  120. });
  121. afterAll(() => {
  122. jest.resetAllMocks();
  123. });
  124. it('renders consumer panel', async () => {
  125. jest.mocked(useLocation).mockReturnValue({
  126. pathname: '',
  127. search: '',
  128. query: {
  129. transaction: 'sentry.tasks.store.save_event',
  130. destination: 'event-queue',
  131. 'span.op': 'queue.process',
  132. },
  133. hash: '',
  134. state: undefined,
  135. action: 'PUSH',
  136. key: '',
  137. });
  138. render(<MessageSpanSamplesPanel />, {organization});
  139. await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
  140. expect(eventsStatsRequestMock).toHaveBeenCalled();
  141. expect(eventsRequestMock).toHaveBeenCalledWith(
  142. `/organizations/${organization.slug}/events/`,
  143. expect.objectContaining({
  144. method: 'GET',
  145. query: expect.objectContaining({
  146. dataset: 'spansMetrics',
  147. environment: [],
  148. field: [
  149. 'count()',
  150. 'count_op(queue.publish)',
  151. 'count_op(queue.process)',
  152. 'sum(span.duration)',
  153. 'avg(span.duration)',
  154. 'avg_if(span.duration,span.op,queue.publish)',
  155. 'avg_if(span.duration,span.op,queue.process)',
  156. 'avg(messaging.message.receive.latency)',
  157. 'trace_status_rate(ok)',
  158. 'time_spent_percentage(app,span.duration)',
  159. ],
  160. per_page: 10,
  161. project: [],
  162. query:
  163. 'span.op:[queue.process,queue.publish] messaging.destination.name:event-queue transaction:sentry.tasks.store.save_event',
  164. statsPeriod: '10d',
  165. }),
  166. })
  167. );
  168. expect(samplesRequestMock).toHaveBeenCalledWith(
  169. `/api/0/organizations/${organization.slug}/spans-samples/`,
  170. expect.objectContaining({
  171. query: expect.objectContaining({
  172. additionalFields: [
  173. 'trace',
  174. 'transaction.id',
  175. 'span.description',
  176. 'measurements.messaging.message.body.size',
  177. 'measurements.messaging.message.receive.latency',
  178. 'measurements.messaging.message.retry.count',
  179. 'messaging.message.id',
  180. 'trace.status',
  181. 'span.duration',
  182. ],
  183. firstBound: 2666.6666666666665,
  184. lowerBound: 0,
  185. project: [],
  186. query:
  187. 'span.op:queue.process transaction:sentry.tasks.store.save_event messaging.destination.name:event-queue',
  188. referrer: undefined,
  189. secondBound: 5333.333333333333,
  190. statsPeriod: '10d',
  191. upperBound: 8000,
  192. }),
  193. })
  194. );
  195. expect(spanFieldTagsMock).toHaveBeenNthCalledWith(
  196. 1,
  197. `/organizations/${organization.slug}/spans/fields/`,
  198. expect.objectContaining({
  199. method: 'GET',
  200. query: {
  201. project: [],
  202. environment: [],
  203. statsPeriod: '1h',
  204. },
  205. })
  206. );
  207. expect(screen.getByRole('table', {name: 'Span Samples'})).toBeInTheDocument();
  208. expect(screen.getByText('Consumer')).toBeInTheDocument();
  209. // Metrics Ribbon
  210. expect(screen.getByText('Processed')).toBeInTheDocument();
  211. expect(screen.getByText('Error Rate')).toBeInTheDocument();
  212. expect(screen.getByText('Avg Time In Queue')).toBeInTheDocument();
  213. expect(screen.getByText('Avg Processing Time')).toBeInTheDocument();
  214. expect(screen.getByText('333')).toBeInTheDocument();
  215. expect(screen.getByText('20%')).toBeInTheDocument();
  216. expect(screen.getByText('2.00ms')).toBeInTheDocument();
  217. expect(screen.getByText('4.00ms')).toBeInTheDocument();
  218. });
  219. it('renders producer panel', async () => {
  220. jest.mocked(useLocation).mockReturnValue({
  221. pathname: '',
  222. search: '',
  223. query: {
  224. transaction: 'sentry.tasks.store.save_event',
  225. destination: 'event-queue',
  226. 'span.op': 'queue.publish',
  227. },
  228. hash: '',
  229. state: undefined,
  230. action: 'PUSH',
  231. key: '',
  232. });
  233. render(<MessageSpanSamplesPanel />, {organization});
  234. await waitForElementToBeRemoved(() => screen.queryAllByTestId('loading-indicator'));
  235. expect(eventsStatsRequestMock).toHaveBeenCalled();
  236. expect(eventsRequestMock).toHaveBeenCalledWith(
  237. `/organizations/${organization.slug}/events/`,
  238. expect.objectContaining({
  239. method: 'GET',
  240. query: expect.objectContaining({
  241. dataset: 'spansMetrics',
  242. environment: [],
  243. field: [
  244. 'count()',
  245. 'count_op(queue.publish)',
  246. 'count_op(queue.process)',
  247. 'sum(span.duration)',
  248. 'avg(span.duration)',
  249. 'avg_if(span.duration,span.op,queue.publish)',
  250. 'avg_if(span.duration,span.op,queue.process)',
  251. 'avg(messaging.message.receive.latency)',
  252. 'trace_status_rate(ok)',
  253. 'time_spent_percentage(app,span.duration)',
  254. ],
  255. per_page: 10,
  256. project: [],
  257. query:
  258. 'span.op:[queue.process,queue.publish] messaging.destination.name:event-queue transaction:sentry.tasks.store.save_event',
  259. statsPeriod: '10d',
  260. }),
  261. })
  262. );
  263. expect(samplesRequestMock).toHaveBeenCalledWith(
  264. `/api/0/organizations/${organization.slug}/spans-samples/`,
  265. expect.objectContaining({
  266. query: expect.objectContaining({
  267. additionalFields: [
  268. 'trace',
  269. 'transaction.id',
  270. 'span.description',
  271. 'measurements.messaging.message.body.size',
  272. 'measurements.messaging.message.receive.latency',
  273. 'measurements.messaging.message.retry.count',
  274. 'messaging.message.id',
  275. 'trace.status',
  276. 'span.duration',
  277. ],
  278. firstBound: 2666.6666666666665,
  279. lowerBound: 0,
  280. project: [],
  281. query:
  282. 'span.op:queue.publish transaction:sentry.tasks.store.save_event messaging.destination.name:event-queue',
  283. referrer: undefined,
  284. secondBound: 5333.333333333333,
  285. statsPeriod: '10d',
  286. upperBound: 8000,
  287. }),
  288. })
  289. );
  290. expect(spanFieldTagsMock).toHaveBeenCalledWith(
  291. `/organizations/${organization.slug}/spans/fields/`,
  292. expect.objectContaining({
  293. method: 'GET',
  294. query: {
  295. project: [],
  296. environment: [],
  297. statsPeriod: '1h',
  298. },
  299. })
  300. );
  301. expect(screen.getByRole('table', {name: 'Span Samples'})).toBeInTheDocument();
  302. expect(screen.getByText('Producer')).toBeInTheDocument();
  303. // Metrics Ribbon
  304. expect(screen.getByText('Published')).toBeInTheDocument();
  305. expect(screen.getByText('Error Rate')).toBeInTheDocument();
  306. expect(screen.getByText('222')).toBeInTheDocument();
  307. expect(screen.getByText('20%')).toBeInTheDocument();
  308. });
  309. });