LokiQueryModeller.test.ts 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. import { LokiQueryModeller } from './LokiQueryModeller';
  2. import { LokiOperationId } from './types';
  3. describe('LokiQueryModeller', () => {
  4. const modeller = new LokiQueryModeller();
  5. it('Can query with labels only', () => {
  6. expect(
  7. modeller.renderQuery({
  8. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  9. operations: [],
  10. })
  11. ).toBe('{app="grafana"}');
  12. });
  13. it('Can query with pipeline operation json', () => {
  14. expect(
  15. modeller.renderQuery({
  16. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  17. operations: [{ id: LokiOperationId.Json, params: [] }],
  18. })
  19. ).toBe('{app="grafana"} | json');
  20. });
  21. it('Can query with pipeline operation logfmt', () => {
  22. expect(
  23. modeller.renderQuery({
  24. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  25. operations: [{ id: LokiOperationId.Logfmt, params: [] }],
  26. })
  27. ).toBe('{app="grafana"} | logfmt');
  28. });
  29. it('Can query with pipeline operation regexp', () => {
  30. expect(
  31. modeller.renderQuery({
  32. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  33. operations: [{ id: LokiOperationId.Regexp, params: ['re'] }],
  34. })
  35. ).toBe('{app="grafana"} | regexp `re`');
  36. });
  37. it('Can query with pipeline operation pattern', () => {
  38. expect(
  39. modeller.renderQuery({
  40. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  41. operations: [{ id: LokiOperationId.Pattern, params: ['<pattern>'] }],
  42. })
  43. ).toBe('{app="grafana"} | pattern `<pattern>`');
  44. });
  45. it('Can query with pipeline operation unpack', () => {
  46. expect(
  47. modeller.renderQuery({
  48. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  49. operations: [{ id: LokiOperationId.Unpack, params: [] }],
  50. })
  51. ).toBe('{app="grafana"} | unpack');
  52. });
  53. it('Can query with line filter contains operation', () => {
  54. expect(
  55. modeller.renderQuery({
  56. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  57. operations: [{ id: LokiOperationId.LineContains, params: ['error'] }],
  58. })
  59. ).toBe('{app="grafana"} |= `error`');
  60. });
  61. it('Can query with line filter contains operation with empty params', () => {
  62. expect(
  63. modeller.renderQuery({
  64. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  65. operations: [{ id: LokiOperationId.LineContains, params: [''] }],
  66. })
  67. ).toBe('{app="grafana"} |= ``');
  68. });
  69. it('Can query with line filter contains not operation', () => {
  70. expect(
  71. modeller.renderQuery({
  72. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  73. operations: [{ id: LokiOperationId.LineContainsNot, params: ['error'] }],
  74. })
  75. ).toBe('{app="grafana"} != `error`');
  76. });
  77. it('Can query with line regex filter', () => {
  78. expect(
  79. modeller.renderQuery({
  80. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  81. operations: [{ id: LokiOperationId.LineMatchesRegex, params: ['error'] }],
  82. })
  83. ).toBe('{app="grafana"} |~ `error`');
  84. });
  85. it('Can query with line not matching regex', () => {
  86. expect(
  87. modeller.renderQuery({
  88. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  89. operations: [{ id: LokiOperationId.LineMatchesRegexNot, params: ['error'] }],
  90. })
  91. ).toBe('{app="grafana"} !~ `error`');
  92. });
  93. it('Can query with label filter expression', () => {
  94. expect(
  95. modeller.renderQuery({
  96. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  97. operations: [{ id: LokiOperationId.LabelFilter, params: ['__error__', '=', 'value'] }],
  98. })
  99. ).toBe('{app="grafana"} | __error__=`value`');
  100. });
  101. it('Can query with label filter expression using greater than operator', () => {
  102. expect(
  103. modeller.renderQuery({
  104. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  105. operations: [{ id: LokiOperationId.LabelFilter, params: ['count', '>', 'value'] }],
  106. })
  107. ).toBe('{app="grafana"} | count > value');
  108. });
  109. it('Can query no formatting errors operation', () => {
  110. expect(
  111. modeller.renderQuery({
  112. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  113. operations: [{ id: LokiOperationId.LabelFilterNoErrors, params: [] }],
  114. })
  115. ).toBe('{app="grafana"} | __error__=``');
  116. });
  117. it('Can query with unwrap operation', () => {
  118. expect(
  119. modeller.renderQuery({
  120. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  121. operations: [{ id: LokiOperationId.Unwrap, params: ['count'] }],
  122. })
  123. ).toBe('{app="grafana"} | unwrap count');
  124. });
  125. it('Can render with line_format operation', () => {
  126. expect(
  127. modeller.renderQuery({
  128. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  129. operations: [{ id: LokiOperationId.LineFormat, params: ['{{.status_code}}'] }],
  130. })
  131. ).toBe('{app="grafana"} | line_format `{{.status_code}}`');
  132. });
  133. it('Can render with label_format operation', () => {
  134. expect(
  135. modeller.renderQuery({
  136. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  137. operations: [{ id: LokiOperationId.LabelFormat, params: ['new', 'old'] }],
  138. })
  139. ).toBe('{app="grafana"} | label_format old=`new`');
  140. });
  141. it('Can render simply binary operation with scalar', () => {
  142. expect(
  143. modeller.renderQuery({
  144. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  145. operations: [{ id: LokiOperationId.MultiplyBy, params: [1000] }],
  146. })
  147. ).toBe('{app="grafana"} * 1000');
  148. });
  149. it('Can render query with simple binary query', () => {
  150. expect(
  151. modeller.renderQuery({
  152. labels: [{ label: 'app', op: '=', value: 'grafana' }],
  153. operations: [{ id: LokiOperationId.Rate, params: ['5m'] }],
  154. binaryQueries: [
  155. {
  156. operator: '/',
  157. query: {
  158. labels: [{ label: 'job', op: '=', value: 'backup' }],
  159. operations: [{ id: LokiOperationId.CountOverTime, params: ['5m'] }],
  160. },
  161. },
  162. ],
  163. })
  164. ).toBe('rate({app="grafana"} [5m]) / count_over_time({job="backup"} [5m])');
  165. });
  166. describe('On add operation handlers', () => {
  167. it('When adding function without range vector param should automatically add rate', () => {
  168. const query = {
  169. labels: [],
  170. operations: [],
  171. };
  172. const def = modeller.getOperationDef('sum')!;
  173. const result = def.addOperationHandler(def, query, modeller);
  174. expect(result.operations[0].id).toBe('rate');
  175. expect(result.operations[1].id).toBe('sum');
  176. });
  177. it('When adding function without range vector param should automatically add rate after existing pipe operation', () => {
  178. const query = {
  179. labels: [],
  180. operations: [{ id: LokiOperationId.Json, params: [] }],
  181. };
  182. const def = modeller.getOperationDef('sum')!;
  183. const result = def.addOperationHandler(def, query, modeller);
  184. expect(result.operations[0].id).toBe(LokiOperationId.Json);
  185. expect(result.operations[1].id).toBe('rate');
  186. expect(result.operations[2].id).toBe('sum');
  187. });
  188. it('When adding a pipe operation after a function operation should add pipe operation first', () => {
  189. const query = {
  190. labels: [],
  191. operations: [{ id: 'rate', params: [] }],
  192. };
  193. const def = modeller.getOperationDef(LokiOperationId.Json)!;
  194. const result = def.addOperationHandler(def, query, modeller);
  195. expect(result.operations[0].id).toBe(LokiOperationId.Json);
  196. expect(result.operations[1].id).toBe('rate');
  197. });
  198. it('When adding a pipe operation after a line filter operation', () => {
  199. const query = {
  200. labels: [],
  201. operations: [{ id: LokiOperationId.LineContains, params: ['error'] }],
  202. };
  203. const def = modeller.getOperationDef(LokiOperationId.Json)!;
  204. const result = def.addOperationHandler(def, query, modeller);
  205. expect(result.operations[0].id).toBe(LokiOperationId.LineContains);
  206. expect(result.operations[1].id).toBe(LokiOperationId.Json);
  207. });
  208. it('When adding a line filter operation after format operation', () => {
  209. const query = {
  210. labels: [],
  211. operations: [{ id: LokiOperationId.Json, params: [] }],
  212. };
  213. const def = modeller.getOperationDef(LokiOperationId.LineContains)!;
  214. const result = def.addOperationHandler(def, query, modeller);
  215. expect(result.operations[0].id).toBe(LokiOperationId.LineContains);
  216. expect(result.operations[1].id).toBe(LokiOperationId.Json);
  217. });
  218. it('When adding a rate it should not add another rate', () => {
  219. const query = {
  220. labels: [],
  221. operations: [{ id: LokiOperationId.Rate, params: [] }],
  222. };
  223. const def = modeller.getOperationDef(LokiOperationId.Rate)!;
  224. const result = def.addOperationHandler(def, query, modeller);
  225. expect(result.operations.length).toBe(1);
  226. });
  227. it('When adding unwrap it should be added after format and error filter', () => {
  228. const query = {
  229. labels: [],
  230. operations: [
  231. { id: LokiOperationId.Json, params: [] },
  232. { id: LokiOperationId.LabelFilterNoErrors, params: [] },
  233. ],
  234. };
  235. const def = modeller.getOperationDef(LokiOperationId.Unwrap)!;
  236. const result = def.addOperationHandler(def, query, modeller);
  237. expect(result.operations[1].id).toBe(LokiOperationId.Unwrap);
  238. });
  239. });
  240. });