utils.spec.tsx 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300
  1. import type {MRI} from 'sentry/types/metrics';
  2. import {MetricDisplayType, MetricExpressionType} from 'sentry/utils/metrics/types';
  3. import type {DashboardMetricsExpression} from 'sentry/views/dashboards/metrics/types';
  4. import type {Widget} from 'sentry/views/dashboards/types';
  5. import {DisplayType, WidgetType} from 'sentry/views/dashboards/types';
  6. import {expressionsToWidget, getMetricExpressions, toMetricDisplayType} from './utils';
  7. const mockGetVirtualMRIQuery = jest.fn((mri: MRI) => {
  8. if (mri === 'g:custom/span_attribute_123@milisecond') {
  9. return {
  10. mri: 'v:custom/span.duration@milisecond' as const,
  11. conditionId: 1,
  12. aggregation: 'sum' as const,
  13. };
  14. }
  15. return null;
  16. });
  17. describe('getMetricExpressions function', () => {
  18. it('should return a query', () => {
  19. const widget = {
  20. queries: [
  21. {
  22. aggregates: ['avg(d:transactions/duration@milisecond)'],
  23. conditions: 'foo:bar',
  24. columns: ['release'],
  25. name: 'query_1',
  26. orderby: 'asc',
  27. },
  28. ],
  29. } as Widget;
  30. const metricQueries = getMetricExpressions(widget, undefined, mockGetVirtualMRIQuery);
  31. expect(metricQueries).toEqual([
  32. {
  33. groupBy: ['release'],
  34. id: 0,
  35. mri: 'd:transactions/duration@milisecond',
  36. aggregation: 'avg',
  37. query: 'foo:bar',
  38. type: MetricExpressionType.QUERY,
  39. orderBy: 'asc',
  40. isHidden: false,
  41. } satisfies DashboardMetricsExpression,
  42. ]);
  43. expect(mockGetVirtualMRIQuery).toHaveBeenCalledTimes(1);
  44. });
  45. it('should return an equation', () => {
  46. const widget = {
  47. queries: [
  48. {
  49. aggregates: ['equation|$a + $b'],
  50. conditions: 'foo:bar',
  51. columns: ['release'],
  52. name: 'query_1',
  53. },
  54. ],
  55. } as Widget;
  56. const metricQueries = getMetricExpressions(widget, undefined, mockGetVirtualMRIQuery);
  57. expect(metricQueries).toEqual([
  58. {
  59. id: 0,
  60. formula: '$a + $b',
  61. type: MetricExpressionType.EQUATION,
  62. isHidden: false,
  63. } satisfies DashboardMetricsExpression,
  64. ]);
  65. });
  66. it('should return metricQueries with correct parameters with dashboardFilters', () => {
  67. const widget = {
  68. queries: [
  69. {
  70. aggregates: ['avg(d:transactions/duration@milisecond)'],
  71. conditions: 'foo:bar',
  72. columns: ['release'],
  73. name: '0',
  74. orderby: 'desc',
  75. },
  76. {
  77. aggregates: ['avg(d:transactions/duration@milisecond)'],
  78. conditions: 'foo:baz',
  79. columns: [],
  80. name: '1',
  81. orderby: '',
  82. },
  83. ],
  84. } as Widget;
  85. const metricQueries = getMetricExpressions(
  86. widget,
  87. {release: ['1.0']},
  88. mockGetVirtualMRIQuery
  89. );
  90. expect(metricQueries).toEqual([
  91. {
  92. groupBy: ['release'],
  93. id: 0,
  94. mri: 'd:transactions/duration@milisecond',
  95. aggregation: 'avg',
  96. query: 'foo:bar release:1.0',
  97. type: MetricExpressionType.QUERY,
  98. orderBy: 'desc',
  99. isHidden: false,
  100. } satisfies DashboardMetricsExpression,
  101. {
  102. groupBy: [],
  103. id: 1,
  104. mri: 'd:transactions/duration@milisecond',
  105. aggregation: 'avg',
  106. query: 'foo:baz release:1.0',
  107. type: MetricExpressionType.QUERY,
  108. orderBy: undefined,
  109. isHidden: false,
  110. } satisfies DashboardMetricsExpression,
  111. ]);
  112. });
  113. it('should return metricQueries with correct parameters with multiple dashboardFilters', () => {
  114. const widget = {
  115. queries: [
  116. {
  117. aggregates: ['avg(d:transactions/duration@milisecond)'],
  118. conditions: '',
  119. columns: ['release'],
  120. name: '1',
  121. },
  122. ],
  123. } as Widget;
  124. const metricQueries = getMetricExpressions(
  125. widget,
  126. {release: ['1.0', '2.0']},
  127. mockGetVirtualMRIQuery
  128. );
  129. expect(metricQueries).toEqual([
  130. {
  131. groupBy: ['release'],
  132. id: 1,
  133. mri: 'd:transactions/duration@milisecond',
  134. aggregation: 'avg',
  135. query: 'release:[1.0,2.0]',
  136. type: MetricExpressionType.QUERY,
  137. orderBy: undefined,
  138. isHidden: false,
  139. } satisfies DashboardMetricsExpression,
  140. ]);
  141. });
  142. it('should map span extracted metrics to virtual metrics', () => {
  143. const widget = {
  144. queries: [
  145. {
  146. aggregates: ['sum(g:custom/span_attribute_123@milisecond)'],
  147. conditions: 'foo:bar',
  148. columns: ['release'],
  149. name: '0',
  150. orderby: 'desc',
  151. },
  152. ],
  153. } as Widget;
  154. const metricQueries = getMetricExpressions(widget, undefined, mockGetVirtualMRIQuery);
  155. expect(metricQueries).toEqual([
  156. {
  157. groupBy: ['release'],
  158. id: 0,
  159. mri: 'v:custom/span.duration@milisecond',
  160. aggregation: 'sum',
  161. condition: 1,
  162. query: 'foo:bar',
  163. type: MetricExpressionType.QUERY,
  164. orderBy: 'desc',
  165. isHidden: false,
  166. } satisfies DashboardMetricsExpression,
  167. ]);
  168. });
  169. });
  170. describe('toMetricDisplayType', () => {
  171. it('should return the displayType if it is a valid MetricDisplayType', () => {
  172. expect(MetricDisplayType.BAR).toEqual(toMetricDisplayType(DisplayType.BAR));
  173. expect(MetricDisplayType.LINE).toEqual(toMetricDisplayType(DisplayType.LINE));
  174. expect(MetricDisplayType.AREA).toEqual(toMetricDisplayType(DisplayType.AREA));
  175. });
  176. it('should return MetricDisplayType.LINE if the displayType is invalid or unsupported', () => {
  177. expect(MetricDisplayType.LINE).toEqual(toMetricDisplayType(DisplayType.BIG_NUMBER));
  178. expect(MetricDisplayType.LINE).toEqual(toMetricDisplayType(DisplayType.TABLE));
  179. expect(MetricDisplayType.LINE).toEqual(toMetricDisplayType(DisplayType.TOP_N));
  180. expect(MetricDisplayType.LINE).toEqual(toMetricDisplayType(undefined));
  181. expect(MetricDisplayType.LINE).toEqual(toMetricDisplayType(''));
  182. });
  183. });
  184. describe('expressionsToWidget', () => {
  185. it('should return a widget with queries', () => {
  186. const metricExpressions = [
  187. {
  188. groupBy: ['release'],
  189. id: 0,
  190. mri: 'd:transactions/duration@milisecond',
  191. aggregation: 'avg',
  192. query: 'foo:bar',
  193. type: MetricExpressionType.QUERY,
  194. orderBy: 'asc',
  195. isHidden: true,
  196. } satisfies DashboardMetricsExpression,
  197. ];
  198. const widget = expressionsToWidget(metricExpressions, 'title', DisplayType.LINE);
  199. expect(widget).toEqual({
  200. title: 'title',
  201. displayType: DisplayType.LINE,
  202. interval: '5m',
  203. limit: 10,
  204. widgetType: WidgetType.METRICS,
  205. queries: [
  206. {
  207. aggregates: ['avg(d:transactions/duration@milisecond)'],
  208. fields: ['avg(d:transactions/duration@milisecond)'],
  209. conditions: 'foo:bar',
  210. columns: ['release'],
  211. name: '0',
  212. orderby: 'asc',
  213. isHidden: true,
  214. fieldAliases: [],
  215. },
  216. ],
  217. } satisfies Widget);
  218. });
  219. it('should return a widget with equations', () => {
  220. const metricExpressions = [
  221. {
  222. id: 1,
  223. formula: '$a + $b',
  224. type: MetricExpressionType.EQUATION,
  225. isHidden: false,
  226. } satisfies DashboardMetricsExpression,
  227. ];
  228. const widget = expressionsToWidget(metricExpressions, 'title', DisplayType.LINE);
  229. expect(widget).toEqual({
  230. title: 'title',
  231. displayType: DisplayType.LINE,
  232. interval: '5m',
  233. limit: 10,
  234. widgetType: WidgetType.METRICS,
  235. queries: [
  236. {
  237. aggregates: ['equation|$a + $b'],
  238. fields: ['equation|$a + $b'],
  239. conditions: '',
  240. columns: [],
  241. name: '1',
  242. orderby: '',
  243. isHidden: false,
  244. fieldAliases: [],
  245. },
  246. ],
  247. } satisfies Widget);
  248. });
  249. it('should should be reversible by getMetricExpressions', () => {
  250. const metricExpressions = [
  251. {
  252. groupBy: ['release'],
  253. id: 0,
  254. mri: 'd:transactions/duration@milisecond',
  255. aggregation: 'avg',
  256. query: 'foo:bar',
  257. type: MetricExpressionType.QUERY,
  258. orderBy: 'asc',
  259. isHidden: true,
  260. } satisfies DashboardMetricsExpression,
  261. {
  262. id: 1,
  263. formula: '$a + $b',
  264. type: MetricExpressionType.EQUATION,
  265. isHidden: false,
  266. } satisfies DashboardMetricsExpression,
  267. ];
  268. const widget = expressionsToWidget(metricExpressions, 'title', DisplayType.LINE);
  269. expect(getMetricExpressions(widget, undefined, mockGetVirtualMRIQuery)).toEqual(
  270. metricExpressions
  271. );
  272. });
  273. });