Files
Ivana Huckova caa82a124d Loki,Prometheus: Fix of showing error message for empty query (#47379)
* Loki,Prometheus: Dont show error on empty query

* Add tests
2022-04-06 11:17:49 +02:00

590 lines
14 KiB
TypeScript

import { buildVisualQueryFromString } from './parsing';
import { PromVisualQuery } from './types';
describe('buildVisualQueryFromString', () => {
it('creates no errors for empty query', () => {
expect(buildVisualQueryFromString('')).toEqual(
noErrors({
labels: [],
operations: [],
metric: '',
})
);
});
it('parses simple query', () => {
expect(buildVisualQueryFromString('counters_logins{app="frontend"}')).toEqual(
noErrors({
metric: 'counters_logins',
labels: [
{
op: '=',
value: 'frontend',
label: 'app',
},
],
operations: [],
})
);
});
it('parses query with rate and interval', () => {
expect(buildVisualQueryFromString('rate(counters_logins{app="frontend"}[5m])')).toEqual(
noErrors({
metric: 'counters_logins',
labels: [
{
op: '=',
value: 'frontend',
label: 'app',
},
],
operations: [
{
id: 'rate',
params: ['5m'],
},
],
})
);
});
it('parses query with nested query and interval variable', () => {
expect(
buildVisualQueryFromString(
'avg(rate(access_evaluation_duration_count{instance="host.docker.internal:3000"}[$__rate_interval]))'
)
).toEqual(
noErrors({
metric: 'access_evaluation_duration_count',
labels: [
{
op: '=',
value: 'host.docker.internal:3000',
label: 'instance',
},
],
operations: [
{
id: 'rate',
params: ['$__rate_interval'],
},
{
id: 'avg',
params: [],
},
],
})
);
});
it('parses query with aggregation by labels', () => {
const visQuery = {
metric: 'metric_name',
labels: [
{
label: 'instance',
op: '=',
value: 'internal:3000',
},
],
operations: [
{
id: '__sum_by',
params: ['app', 'version'],
},
],
};
expect(buildVisualQueryFromString('sum(metric_name{instance="internal:3000"}) by (app, version)')).toEqual(
noErrors(visQuery)
);
expect(buildVisualQueryFromString('sum by (app, version)(metric_name{instance="internal:3000"})')).toEqual(
noErrors(visQuery)
);
});
it('parses query with aggregation without labels', () => {
const visQuery = {
metric: 'metric_name',
labels: [
{
label: 'instance',
op: '=',
value: 'internal:3000',
},
],
operations: [
{
id: '__sum_without',
params: ['app', 'version'],
},
],
};
expect(buildVisualQueryFromString('sum(metric_name{instance="internal:3000"}) without (app, version)')).toEqual(
noErrors(visQuery)
);
expect(buildVisualQueryFromString('sum without (app, version)(metric_name{instance="internal:3000"})')).toEqual(
noErrors(visQuery)
);
});
it('parses aggregation with params', () => {
expect(buildVisualQueryFromString('topk(5, http_requests_total)')).toEqual(
noErrors({
metric: 'http_requests_total',
labels: [],
operations: [
{
id: 'topk',
params: [5],
},
],
})
);
});
it('parses aggregation with params and labels', () => {
expect(buildVisualQueryFromString('topk by(instance, job) (5, http_requests_total)')).toEqual(
noErrors({
metric: 'http_requests_total',
labels: [],
operations: [
{
id: '__topk_by',
params: [5, 'instance', 'job'],
},
],
})
);
});
it('parses function with argument', () => {
expect(
buildVisualQueryFromString('histogram_quantile(0.99, rate(counters_logins{app="backend"}[$__rate_interval]))')
).toEqual(
noErrors({
metric: 'counters_logins',
labels: [{ label: 'app', op: '=', value: 'backend' }],
operations: [
{
id: 'rate',
params: ['$__rate_interval'],
},
{
id: 'histogram_quantile',
params: [0.99],
},
],
})
);
});
it('parses function with multiple arguments', () => {
expect(
buildVisualQueryFromString(
'label_replace(avg_over_time(http_requests_total{instance="foo"}[$__interval]), "instance", "$1", "", "(.*)")'
)
).toEqual(
noErrors({
metric: 'http_requests_total',
labels: [{ label: 'instance', op: '=', value: 'foo' }],
operations: [
{
id: 'avg_over_time',
params: ['$__interval'],
},
{
id: 'label_replace',
params: ['instance', '$1', '', '(.*)'],
},
],
})
);
});
it('parses binary operation with scalar', () => {
expect(buildVisualQueryFromString('avg_over_time(http_requests_total{instance="foo"}[$__interval]) / 2')).toEqual(
noErrors({
metric: 'http_requests_total',
labels: [{ label: 'instance', op: '=', value: 'foo' }],
operations: [
{
id: 'avg_over_time',
params: ['$__interval'],
},
{
id: '__divide_by',
params: [2],
},
],
})
);
});
it('parses binary operation with 2 queries', () => {
expect(
buildVisualQueryFromString('avg_over_time(http_requests_total{instance="foo"}[$__interval]) / sum(logins_count)')
).toEqual(
noErrors({
metric: 'http_requests_total',
labels: [{ label: 'instance', op: '=', value: 'foo' }],
operations: [{ id: 'avg_over_time', params: ['$__interval'] }],
binaryQueries: [
{
operator: '/',
query: {
metric: 'logins_count',
labels: [],
operations: [{ id: 'sum', params: [] }],
},
},
],
})
);
});
it('parses template variables in strings', () => {
expect(buildVisualQueryFromString('http_requests_total{instance="$label_variable"}')).toEqual(
noErrors({
metric: 'http_requests_total',
labels: [{ label: 'instance', op: '=', value: '$label_variable' }],
operations: [],
})
);
});
it('parses template variables for metric', () => {
expect(buildVisualQueryFromString('$metric_variable{instance="foo"}')).toEqual(
noErrors({
metric: '$metric_variable',
labels: [{ label: 'instance', op: '=', value: 'foo' }],
operations: [],
})
);
expect(buildVisualQueryFromString('${metric_variable:fmt}{instance="foo"}')).toEqual(
noErrors({
metric: '${metric_variable:fmt}',
labels: [{ label: 'instance', op: '=', value: 'foo' }],
operations: [],
})
);
expect(buildVisualQueryFromString('[[metric_variable:fmt]]{instance="foo"}')).toEqual(
noErrors({
metric: '[[metric_variable:fmt]]',
labels: [{ label: 'instance', op: '=', value: 'foo' }],
operations: [],
})
);
});
it('parses template variables in label name', () => {
expect(buildVisualQueryFromString('metric{${variable_label}="foo"}')).toEqual(
noErrors({
metric: 'metric',
labels: [{ label: '${variable_label}', op: '=', value: 'foo' }],
operations: [],
})
);
});
it('fails to parse variable for function', () => {
expect(buildVisualQueryFromString('${func_var}(metric{bar="foo"})')).toEqual({
errors: [
{
text: '(',
from: 20,
to: 21,
parentType: 'VectorSelector',
},
{
text: 'metric',
from: 21,
to: 27,
parentType: 'VectorSelector',
},
],
query: {
metric: '${func_var}',
labels: [{ label: 'bar', op: '=', value: 'foo' }],
operations: [],
},
});
});
it('fails to parse malformed query', () => {
expect(buildVisualQueryFromString('asdf-metric{bar="})')).toEqual({
errors: [
{
text: '',
from: 19,
to: 19,
parentType: 'LabelMatchers',
},
],
query: {
metric: 'asdf',
labels: [],
operations: [],
binaryQueries: [
{
operator: '-',
query: {
metric: 'metric',
labels: [{ label: 'bar', op: '=', value: '})' }],
operations: [],
},
},
],
},
});
});
it('fails to parse malformed query 2', () => {
expect(buildVisualQueryFromString('ewafweaf{afea=afe}')).toEqual({
errors: [
{
text: 'afe}',
from: 14,
to: 18,
parentType: 'LabelMatcher',
},
],
query: {
metric: 'ewafweaf',
labels: [{ label: 'afea', op: '=', value: '' }],
operations: [],
},
});
});
it('parses query without metric', () => {
expect(buildVisualQueryFromString('label_replace(rate([$__rate_interval]), "", "$1", "", "(.*)")')).toEqual({
errors: [],
query: {
metric: '',
labels: [],
operations: [
{ id: 'rate', params: ['$__rate_interval'] },
{
id: 'label_replace',
params: ['', '$1', '', '(.*)'],
},
],
},
});
});
it('lone aggregation without params', () => {
expect(buildVisualQueryFromString('sum()')).toEqual({
errors: [],
query: {
metric: '',
labels: [],
operations: [{ id: 'sum', params: [] }],
},
});
});
it('handles multiple binary scalar operations', () => {
expect(buildVisualQueryFromString('cluster_namespace_slug_dialer_name + 1 - 1 / 1 * 1 % 1 ^ 1')).toEqual({
errors: [],
query: {
metric: 'cluster_namespace_slug_dialer_name',
labels: [],
operations: [
{
id: '__addition',
params: [1],
},
{
id: '__subtraction',
params: [1],
},
{
id: '__divide_by',
params: [1],
},
{
id: '__multiply_by',
params: [1],
},
{
id: '__modulo',
params: [1],
},
{
id: '__exponent',
params: [1],
},
],
},
});
});
it('handles scalar comparison operators', () => {
expect(buildVisualQueryFromString('cluster_namespace_slug_dialer_name <= 2.5')).toEqual({
errors: [],
query: {
metric: 'cluster_namespace_slug_dialer_name',
labels: [],
operations: [
{
id: '__less_or_equal',
params: [2.5, false],
},
],
},
});
});
it('handles bool with comparison operator', () => {
expect(buildVisualQueryFromString('cluster_namespace_slug_dialer_name <= bool 2')).toEqual({
errors: [],
query: {
metric: 'cluster_namespace_slug_dialer_name',
labels: [],
operations: [
{
id: '__less_or_equal',
params: [2, true],
},
],
},
});
});
it('handles multiple binary operations', () => {
expect(buildVisualQueryFromString('foo{x="yy"} * metric{y="zz",a="bb"} * metric2')).toEqual({
errors: [],
query: {
metric: 'foo',
labels: [{ label: 'x', op: '=', value: 'yy' }],
operations: [],
binaryQueries: [
{
operator: '*',
query: {
metric: 'metric',
labels: [
{ label: 'y', op: '=', value: 'zz' },
{ label: 'a', op: '=', value: 'bb' },
],
operations: [],
},
},
{
operator: '*',
query: {
metric: 'metric2',
labels: [],
operations: [],
},
},
],
},
});
});
it('handles multiple binary operations and scalar', () => {
expect(buildVisualQueryFromString('foo{x="yy"} * metric{y="zz",a="bb"} * 2')).toEqual({
errors: [],
query: {
metric: 'foo',
labels: [{ label: 'x', op: '=', value: 'yy' }],
operations: [
{
id: '__multiply_by',
params: [2],
},
],
binaryQueries: [
{
operator: '*',
query: {
metric: 'metric',
labels: [
{ label: 'y', op: '=', value: 'zz' },
{ label: 'a', op: '=', value: 'bb' },
],
operations: [],
},
},
],
},
});
});
it('handles binary operation with vector matchers', () => {
expect(buildVisualQueryFromString('foo * on(foo, bar) metric')).toEqual({
errors: [],
query: {
metric: 'foo',
labels: [],
operations: [],
binaryQueries: [
{
operator: '*',
vectorMatches: 'foo, bar',
vectorMatchesType: 'on',
query: { metric: 'metric', labels: [], operations: [] },
},
],
},
});
expect(buildVisualQueryFromString('foo * ignoring(foo) metric')).toEqual({
errors: [],
query: {
metric: 'foo',
labels: [],
operations: [],
binaryQueries: [
{
operator: '*',
vectorMatches: 'foo',
vectorMatchesType: 'ignoring',
query: { metric: 'metric', labels: [], operations: [] },
},
],
},
});
});
it('reports error on parenthesis', () => {
expect(buildVisualQueryFromString('foo / (bar + baz)')).toEqual({
errors: [
{
from: 6,
parentType: 'Expr',
text: '(bar + baz)',
to: 17,
},
],
query: {
metric: 'foo',
labels: [],
operations: [],
binaryQueries: [
{
operator: '/',
query: {
binaryQueries: [{ operator: '+', query: { labels: [], metric: 'baz', operations: [] } }],
metric: 'bar',
labels: [],
operations: [],
},
},
],
},
});
});
});
function noErrors(query: PromVisualQuery) {
return {
errors: [],
query,
};
}