utils.spec.tsx 8.3 KB

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