PromQueryModeller.test.ts 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. import { PromQueryModeller } from './PromQueryModeller';
  2. import { PromOperationId } from './types';
  3. describe('PromQueryModeller', () => {
  4. const modeller = new PromQueryModeller();
  5. it('Can render query with metric only', () => {
  6. expect(
  7. modeller.renderQuery({
  8. metric: 'my_totals',
  9. labels: [],
  10. operations: [],
  11. })
  12. ).toBe('my_totals');
  13. });
  14. it('Can render query with label filters', () => {
  15. expect(
  16. modeller.renderQuery({
  17. metric: 'my_totals',
  18. labels: [
  19. { label: 'cluster', op: '=', value: 'us-east' },
  20. { label: 'job', op: '=~', value: 'abc' },
  21. ],
  22. operations: [],
  23. })
  24. ).toBe('my_totals{cluster="us-east", job=~"abc"}');
  25. });
  26. it('Can render query with function', () => {
  27. expect(
  28. modeller.renderQuery({
  29. metric: 'my_totals',
  30. labels: [],
  31. operations: [{ id: 'sum', params: [] }],
  32. })
  33. ).toBe('sum(my_totals)');
  34. });
  35. it('Can render query with function with parameter to left of inner expression', () => {
  36. expect(
  37. modeller.renderQuery({
  38. metric: 'metric',
  39. labels: [],
  40. operations: [{ id: PromOperationId.HistogramQuantile, params: [0.86] }],
  41. })
  42. ).toBe('histogram_quantile(0.86, metric)');
  43. });
  44. it('Can render query with function with function parameters to the right of inner expression', () => {
  45. expect(
  46. modeller.renderQuery({
  47. metric: 'metric',
  48. labels: [],
  49. operations: [{ id: PromOperationId.LabelReplace, params: ['server', '$1', 'instance', 'as(.*)d'] }],
  50. })
  51. ).toBe('label_replace(metric, "server", "$1", "instance", "as(.*)d")');
  52. });
  53. it('Can group by expressions', () => {
  54. expect(
  55. modeller.renderQuery({
  56. metric: 'metric',
  57. labels: [],
  58. operations: [{ id: '__sum_by', params: ['server', 'job'] }],
  59. })
  60. ).toBe('sum by(server, job) (metric)');
  61. });
  62. it('Can render avg around a group by', () => {
  63. expect(
  64. modeller.renderQuery({
  65. metric: 'metric',
  66. labels: [],
  67. operations: [
  68. { id: '__sum_by', params: ['server', 'job'] },
  69. { id: 'avg', params: [] },
  70. ],
  71. })
  72. ).toBe('avg(sum by(server, job) (metric))');
  73. });
  74. it('Can use aggregation without label', () => {
  75. expect(
  76. modeller.renderQuery({
  77. metric: 'metric',
  78. labels: [],
  79. operations: [{ id: '__sum_without', params: ['server', 'job'] }],
  80. })
  81. ).toBe('sum without(server, job) (metric)');
  82. });
  83. it('Can render aggregations with parameters', () => {
  84. expect(
  85. modeller.renderQuery({
  86. metric: 'metric',
  87. labels: [],
  88. operations: [{ id: 'topk', params: [5] }],
  89. })
  90. ).toBe('topk(5, metric)');
  91. });
  92. it('Can render rate', () => {
  93. expect(
  94. modeller.renderQuery({
  95. metric: 'metric',
  96. labels: [{ label: 'pod', op: '=', value: 'A' }],
  97. operations: [{ id: PromOperationId.Rate, params: ['$__rate_interval'] }],
  98. })
  99. ).toBe('rate(metric{pod="A"}[$__rate_interval])');
  100. });
  101. it('Can render increase', () => {
  102. expect(
  103. modeller.renderQuery({
  104. metric: 'metric',
  105. labels: [{ label: 'pod', op: '=', value: 'A' }],
  106. operations: [{ id: PromOperationId.Increase, params: ['$__interval'] }],
  107. })
  108. ).toBe('increase(metric{pod="A"}[$__interval])');
  109. });
  110. it('Can render rate with custom range-vector', () => {
  111. expect(
  112. modeller.renderQuery({
  113. metric: 'metric',
  114. labels: [{ label: 'pod', op: '=', value: 'A' }],
  115. operations: [{ id: PromOperationId.Rate, params: ['10m'] }],
  116. })
  117. ).toBe('rate(metric{pod="A"}[10m])');
  118. });
  119. it('Can render multiply operation', () => {
  120. expect(
  121. modeller.renderQuery({
  122. metric: 'metric',
  123. labels: [],
  124. operations: [{ id: PromOperationId.MultiplyBy, params: [1000] }],
  125. })
  126. ).toBe('metric * 1000');
  127. });
  128. it('Can render query with simple binary query', () => {
  129. expect(
  130. modeller.renderQuery({
  131. metric: 'metric_a',
  132. labels: [],
  133. operations: [],
  134. binaryQueries: [
  135. {
  136. operator: '/',
  137. query: {
  138. metric: 'metric_b',
  139. labels: [],
  140. operations: [],
  141. },
  142. },
  143. ],
  144. })
  145. ).toBe('metric_a / metric_b');
  146. });
  147. it('Can render query with multiple binary queries and nesting', () => {
  148. expect(
  149. modeller.renderQuery({
  150. metric: 'metric_a',
  151. labels: [],
  152. operations: [],
  153. binaryQueries: [
  154. {
  155. operator: '+',
  156. query: {
  157. metric: 'metric_b',
  158. labels: [],
  159. operations: [],
  160. },
  161. },
  162. {
  163. operator: '+',
  164. query: {
  165. metric: 'metric_c',
  166. labels: [],
  167. operations: [],
  168. },
  169. },
  170. ],
  171. })
  172. ).toBe('metric_a + metric_b + metric_c');
  173. });
  174. it('Can render query with nested query with binary op', () => {
  175. expect(
  176. modeller.renderQuery({
  177. metric: 'metric_a',
  178. labels: [],
  179. operations: [],
  180. binaryQueries: [
  181. {
  182. operator: '/',
  183. query: {
  184. metric: 'metric_b',
  185. labels: [],
  186. operations: [{ id: PromOperationId.MultiplyBy, params: [1000] }],
  187. },
  188. },
  189. ],
  190. })
  191. ).toBe('metric_a / (metric_b * 1000)');
  192. });
  193. it('Can render query with nested binary query with parentheses', () => {
  194. expect(
  195. modeller.renderQuery({
  196. metric: 'metric_a',
  197. labels: [],
  198. operations: [],
  199. binaryQueries: [
  200. {
  201. operator: '/',
  202. query: {
  203. metric: 'metric_b',
  204. labels: [],
  205. operations: [],
  206. binaryQueries: [
  207. {
  208. operator: '*',
  209. query: {
  210. metric: 'metric_c',
  211. labels: [],
  212. operations: [],
  213. },
  214. },
  215. ],
  216. },
  217. },
  218. ],
  219. })
  220. ).toBe('metric_a / (metric_b * metric_c)');
  221. });
  222. it('Should add parantheis around first query if it has binary op', () => {
  223. expect(
  224. modeller.renderQuery({
  225. metric: 'metric_a',
  226. labels: [],
  227. operations: [{ id: PromOperationId.MultiplyBy, params: [1000] }],
  228. binaryQueries: [
  229. {
  230. operator: '/',
  231. query: {
  232. metric: 'metric_b',
  233. labels: [],
  234. operations: [],
  235. },
  236. },
  237. ],
  238. })
  239. ).toBe('(metric_a * 1000) / metric_b');
  240. });
  241. it('Can render functions that require a range as a parameter', () => {
  242. expect(
  243. modeller.renderQuery({
  244. metric: 'metric_a',
  245. labels: [],
  246. operations: [{ id: 'holt_winters', params: ['5m', 0.5, 0.5] }],
  247. })
  248. ).toBe('holt_winters(metric_a[5m], 0.5, 0.5)');
  249. });
  250. it('Can render functions that require parameters left of a range', () => {
  251. expect(
  252. modeller.renderQuery({
  253. metric: 'metric_a',
  254. labels: [],
  255. operations: [{ id: 'quantile_over_time', params: ['5m', 1] }],
  256. })
  257. ).toBe('quantile_over_time(1, metric_a[5m])');
  258. });
  259. it('Can render the label_join function', () => {
  260. expect(
  261. modeller.renderQuery({
  262. metric: 'metric_a',
  263. labels: [],
  264. operations: [{ id: 'label_join', params: ['label_1', ',', 'label_2'] }],
  265. })
  266. ).toBe('label_join(metric_a, "label_1", ",", "label_2")');
  267. });
  268. it('Can render label_join with extra parameters', () => {
  269. expect(
  270. modeller.renderQuery({
  271. metric: 'metric_a',
  272. labels: [],
  273. operations: [{ id: 'label_join', params: ['label_1', ', ', 'label_2', 'label_3', 'label_4', 'label_5'] }],
  274. })
  275. ).toBe('label_join(metric_a, "label_1", ", ", "label_2", "label_3", "label_4", "label_5")');
  276. });
  277. it('can render vector matchers', () => {
  278. expect(
  279. modeller.renderQuery({
  280. metric: 'metric_a',
  281. labels: [],
  282. operations: [],
  283. binaryQueries: [
  284. {
  285. operator: '/',
  286. vectorMatches: 'le, foo',
  287. vectorMatchesType: 'on',
  288. query: {
  289. metric: 'metric_b',
  290. labels: [],
  291. operations: [],
  292. },
  293. },
  294. ],
  295. })
  296. ).toBe('metric_a / on(le, foo) metric_b');
  297. });
  298. it('can render bool in binary ops', () => {
  299. expect(
  300. modeller.renderQuery({
  301. metric: 'cluster_namespace_slug_dialer_name',
  302. labels: [],
  303. operations: [
  304. {
  305. id: '__less_or_equal',
  306. params: [2, true],
  307. },
  308. ],
  309. })
  310. ).toBe('cluster_namespace_slug_dialer_name <= bool 2');
  311. });
  312. });