import { addLabelFormatToQuery, addLabelToQuery, addNoPipelineErrorToQuery, addParserToQuery, removeCommentsFromQuery, } from './modifyQuery'; describe('addLabelToQuery()', () => { it('should add label to simple query', () => { expect(() => { addLabelToQuery('foo', '', '=', ''); }).toThrow(); expect(addLabelToQuery('{}', 'bar', '=', 'baz')).toBe('{bar="baz"}'); expect(addLabelToQuery('{x="yy"}', 'bar', '=', 'baz')).toBe('{x="yy", bar="baz"}'); }); it('should add custom operator', () => { expect(addLabelToQuery('{}', 'bar', '!=', 'baz')).toBe('{bar!="baz"}'); expect(addLabelToQuery('{x="yy"}', 'bar', '!=', 'baz')).toBe('{x="yy", bar!="baz"}'); }); it('should not modify ranges', () => { expect(addLabelToQuery('rate({}[1m])', 'foo', '=', 'bar')).toBe('rate({foo="bar"}[1m])'); }); it('should detect in-order function use', () => { expect(addLabelToQuery('sum by (host) (rate({} [1m]))', 'bar', '=', 'baz')).toBe( 'sum by (host) (rate({bar="baz"}[1m]))' ); }); it('should handle selectors with punctuation', () => { expect(addLabelToQuery('{instance="my-host.com:9100"}', 'bar', '=', 'baz')).toBe( '{instance="my-host.com:9100", bar="baz"}' ); expect(addLabelToQuery('{list="a,b,c"}', 'bar', '=', 'baz')).toBe('{list="a,b,c", bar="baz"}'); }); it('should work on arithmetical expressions', () => { expect(addLabelToQuery('{} + {}', 'bar', '=', 'baz')).toBe('{bar="baz"}+ {bar="baz"}'); expect(addLabelToQuery('avg(rate({x="y"} [$__interval]))+ sum(rate({}[5m]))', 'bar', '=', 'baz')).toBe( 'avg(rate({x="y", bar="baz"} [$__interval]))+ sum(rate({bar="baz"}[5m]))' ); expect(addLabelToQuery('{x="yy"} * {y="zz",a="bb"} * {}', 'bar', '=', 'baz')).toBe( '{x="yy", bar="baz"} * {y="zz", a="bb", bar="baz"} * {bar="baz"}' ); }); it('should not add duplicate labels to a query', () => { expect(addLabelToQuery(addLabelToQuery('{x="yy"}', 'bar', '!=', 'baz'), 'bar', '!=', 'baz')).toBe( '{x="yy", bar!="baz"}' ); expect(addLabelToQuery(addLabelToQuery('rate({}[1m])', 'foo', '=', 'bar'), 'foo', '=', 'bar')).toBe( 'rate({foo="bar"}[1m])' ); expect(addLabelToQuery(addLabelToQuery('{list="a,b,c"}', 'bar', '=', 'baz'), 'bar', '=', 'baz')).toBe( '{list="a,b,c", bar="baz"}' ); expect( addLabelToQuery( addLabelToQuery('avg(rate({bar="baz"} [$__interval]))+ sum(rate({bar="baz"}[5m]))', 'bar', '=', 'baz'), 'bar', '=', 'baz' ) ).toBe('avg(rate({bar="baz"} [$__interval]))+ sum(rate({bar="baz"}[5m]))'); }); it('should not remove filters', () => { expect(addLabelToQuery('{x="y"} |="yy"', 'bar', '=', 'baz')).toBe('{x="y", bar="baz"} |="yy"'); expect(addLabelToQuery('{x="y"} |="yy" !~"xx"', 'bar', '=', 'baz')).toBe('{x="y", bar="baz"} |="yy" !~"xx"'); }); it('should add label to query properly with Loki datasource', () => { expect(addLabelToQuery('{job="grafana"} |= "foo-bar"', 'filename', '=', 'test.txt')).toBe( '{job="grafana", filename="test.txt"} |= "foo-bar"' ); }); it('should add labels to metrics with logical operators', () => { expect(addLabelToQuery('{x="y"} or {}', 'bar', '=', 'baz')).toBe('{x="y", bar="baz"} or {bar="baz"}'); expect(addLabelToQuery('{x="y"} and {}', 'bar', '=', 'baz')).toBe('{x="y", bar="baz"} and {bar="baz"}'); }); it('should not add ad-hoc filter to template variables', () => { expect(addLabelToQuery('sum(rate({job="foo"}[2m])) by (value $variable)', 'bar', '=', 'baz')).toBe( 'sum(rate({job="foo", bar="baz"}[2m])) by (value $variable)' ); }); it('should not add ad-hoc filter to range', () => { expect(addLabelToQuery('avg(rate(({job="foo"} > 0)[3h:])) by (label)', 'bar', '=', 'baz')).toBe( 'avg(rate(({job="foo", bar="baz"} > 0)[3h:])) by (label)' ); }); it('should not add ad-hoc filter to labels in label list provided with the group modifier', () => { expect( addLabelToQuery( 'max by (id, name, type) ({type=~"foo|bar|baz-test"}) * on(id) group_right(id, type, name) sum by (id) (rate({} [5m])) * 1000', 'bar', '=', 'baz' ) ).toBe( 'max by (id, name, type) ({type=~"foo|bar|baz-test", bar="baz"}) * on(id) group_right(id, type, name) sum by (id) (rate({bar="baz"}[5m])) * 1000' ); }); it('should not add ad-hoc filter to labels in label list provided with the group modifier', () => { expect(addLabelToQuery('rate({x="y"}[${__range_s}s])', 'bar', '=', 'baz')).toBe( 'rate({x="y", bar="baz"}[${__range_s}s])' ); }); it('should not add ad-hoc filter to labels to math operations', () => { expect(addLabelToQuery('count({job!="foo"} < (5*1024*1024*1024) or vector(0)) - 1', 'bar', '=', 'baz')).toBe( 'count({job!="foo", bar="baz"} < (5*1024*1024*1024) or vector(0)) - 1' ); }); describe('should add label as label filter is query with parser', () => { it('should add label filter after parser', () => { expect(addLabelToQuery('{foo="bar"} | logfmt', 'bar', '=', 'baz')).toBe('{foo="bar"} | logfmt | bar=`baz`'); }); it('should add label filter after last parser when multiple parsers', () => { expect(addLabelToQuery('{foo="bar"} | logfmt | json', 'bar', '=', 'baz')).toBe( '{foo="bar"} | logfmt | json | bar=`baz`' ); }); it('should add label filter after last label filter when multiple label filters', () => { expect(addLabelToQuery('{foo="bar"} | logfmt | x="y"', 'bar', '=', 'baz')).toBe( '{foo="bar"} | logfmt | x="y" | bar=`baz`' ); }); it('should add label filter in metric query', () => { expect(addLabelToQuery('rate({foo="bar"} | logfmt [5m])', 'bar', '=', 'baz')).toBe( 'rate({foo="bar"} | logfmt | bar=`baz` [5m])' ); }); it('should add label filter in complex metric query', () => { expect( addLabelToQuery( 'sum by(host) (rate({foo="bar"} | logfmt | x="y" | line_format "{{.status}}" [5m]))', 'bar', '=', 'baz' ) ).toBe('sum by(host) (rate({foo="bar"} | logfmt | x="y" | bar=`baz` | line_format "{{.status}}" [5m]))'); }); it('should not add adhoc filter to line_format expressions', () => { expect(addLabelToQuery('{foo="bar"} | logfmt | line_format "{{.status}}"', 'bar', '=', 'baz')).toBe( '{foo="bar"} | logfmt | bar=`baz` | line_format "{{.status}}"' ); }); it('should not add adhoc filter to line_format expressions', () => { expect(addLabelToQuery('{foo="bar"} | logfmt | line_format "{{status}}"', 'bar', '=', 'baz')).toBe( '{foo="bar"} | logfmt | bar=`baz` | line_format "{{status}}"' ); }); }); }); describe('addParserToQuery', () => { describe('when query had line filter', () => { it('should add parser after line filter', () => { expect(addParserToQuery('{job="grafana"} |= "error"', 'logfmt')).toBe('{job="grafana"} |= "error" | logfmt'); }); it('should add parser after multiple line filters', () => { expect(addParserToQuery('{job="grafana"} |= "error" |= "info" |= "debug"', 'logfmt')).toBe( '{job="grafana"} |= "error" |= "info" |= "debug" | logfmt' ); }); }); describe('when query has no line filters', () => { it('should add parser after log stream selector in logs query', () => { expect(addParserToQuery('{job="grafana"}', 'logfmt')).toBe('{job="grafana"} | logfmt'); }); it('should add parser after log stream selector in metric query', () => { expect(addParserToQuery('rate({job="grafana"} [5m])', 'logfmt')).toBe('rate({job="grafana"} | logfmt [5m])'); }); }); }); describe('addNoPipelineErrorToQuery', () => { it('should add error filtering after logfmt parser', () => { expect(addNoPipelineErrorToQuery('{job="grafana"} | logfmt')).toBe('{job="grafana"} | logfmt | __error__=``'); }); it('should add error filtering after json parser with expressions', () => { expect(addNoPipelineErrorToQuery('{job="grafana"} | json foo="bar", bar="baz"')).toBe( '{job="grafana"} | json foo="bar", bar="baz" | __error__=``' ); }); it('should not add error filtering if no parser', () => { expect(addNoPipelineErrorToQuery('{job="grafana"} |="no parser"')).toBe('{job="grafana"} |="no parser"'); }); }); describe('addLabelFormatToQuery', () => { it('should add label format at the end of log query when parser', () => { expect(addLabelFormatToQuery('{job="grafana"} | logfmt', { originalLabel: 'lvl', renameTo: 'level' })).toBe( '{job="grafana"} | logfmt | label_format level=lvl' ); }); it('should add label format at the end of log query when no parser', () => { expect(addLabelFormatToQuery('{job="grafana"}', { originalLabel: 'lvl', renameTo: 'level' })).toBe( '{job="grafana"} | label_format level=lvl' ); }); it('should add label format at the end of log query when more label parser', () => { expect( addLabelFormatToQuery('{job="grafana"} | logfmt | label_format a=b', { originalLabel: 'lvl', renameTo: 'level' }) ).toBe('{job="grafana"} | logfmt | label_format a=b | label_format level=lvl'); }); it('should add label format at the end of log query part of metrics query', () => { expect( addLabelFormatToQuery('rate({job="grafana"} | logfmt | label_format a=b [5m])', { originalLabel: 'lvl', renameTo: 'level', }) ).toBe('rate({job="grafana"} | logfmt | label_format a=b | label_format level=lvl [5m])'); }); it('should add label format at the end of multiple log query part of metrics query', () => { expect( addLabelFormatToQuery( 'rate({job="grafana"} | logfmt | label_format a=b [5m]) + rate({job="grafana"} | logfmt | label_format a=b [5m])', { originalLabel: 'lvl', renameTo: 'level' } ) ).toBe( 'rate({job="grafana"} | logfmt | label_format a=b | label_format level=lvl [5m]) + rate({job="grafana"} | logfmt | label_format a=b | label_format level=lvl [5m])' ); }); }); describe('removeCommentsFromQuery', () => { it.each` query | expectedResult ${'{job="grafana"}#hello'} | ${'{job="grafana"}'} ${'{job="grafana"} | logfmt #hello'} | ${'{job="grafana"} | logfmt '} ${'{job="grafana", bar="baz"} |="test" | logfmt | label_format level=lvl #hello'} | ${'{job="grafana", bar="baz"} |="test" | logfmt | label_format level=lvl '} `('strips comments in log query: {$query}', ({ query, expectedResult }) => { expect(removeCommentsFromQuery(query)).toBe(expectedResult); }); it.each` query | expectedResult ${'{job="grafana"}'} | ${'{job="grafana"}'} ${'{job="grafana"} | logfmt'} | ${'{job="grafana"} | logfmt'} ${'{job="grafana", bar="baz"} |="test" | logfmt | label_format level=lvl'} | ${'{job="grafana", bar="baz"} |="test" | logfmt | label_format level=lvl'} `('returns original query if no comments in log query: {$query}', ({ query, expectedResult }) => { expect(removeCommentsFromQuery(query)).toBe(expectedResult); }); it.each` query | expectedResult ${'count_over_time({job="grafana"}[10m])#hello'} | ${'count_over_time({job="grafana"}[10m])'} ${'count_over_time({job="grafana"} | logfmt[10m])#hello'} | ${'count_over_time({job="grafana"} | logfmt[10m])'} ${'rate({job="grafana"} | logfmt | foo="bar" [10m])#hello'} | ${'rate({job="grafana"} | logfmt | foo="bar" [10m])'} `('strips comments in metrics query: {$query}', ({ query, expectedResult }) => { expect(removeCommentsFromQuery(query)).toBe(expectedResult); }); it.each` query | expectedResult ${'count_over_time({job="grafana"}[10m])#hello'} | ${'count_over_time({job="grafana"}[10m])'} ${'count_over_time({job="grafana"} | logfmt[10m])#hello'} | ${'count_over_time({job="grafana"} | logfmt[10m])'} ${'rate({job="grafana"} | logfmt | foo="bar" [10m])'} | ${'rate({job="grafana"} | logfmt | foo="bar" [10m])'} `('returns original query if no comments in metrics query: {$query}', ({ query, expectedResult }) => { expect(removeCommentsFromQuery(query)).toBe(expectedResult); }); });