Files
grafana/public/app/plugins/datasource/prometheus/specs/datasource.test.ts

1422 lines
49 KiB
TypeScript
Raw Normal View History

2018-02-21 20:22:46 +03:00
import _ from 'lodash';
// @ts-ignore
2018-02-21 20:22:46 +03:00
import q from 'q';
import {
alignRange,
extractRuleMappingFromGroups,
PrometheusDatasource,
prometheusRegularEscape,
prometheusSpecialRegexEscape,
} from '../datasource';
import { dateTime } from '@grafana/data';
import { DataSourceInstanceSettings, DataQueryResponseData } from '@grafana/ui';
import { PromOptions } from '../types';
import { TemplateSrv } from 'app/features/templating/template_srv';
import { TimeSrv } from 'app/features/dashboard/services/TimeSrv';
import { CustomVariable } from 'app/features/templating/custom_variable';
jest.mock('../metric_find_query');
2018-02-21 20:22:46 +03:00
const DEFAULT_TEMPLATE_SRV_MOCK = {
getAdhocFilters: () => [] as any[],
replace: (a: string) => a,
};
2018-02-21 20:22:46 +03:00
describe('PrometheusDatasource', () => {
const ctx: any = {};
const instanceSettings = ({
2018-02-21 20:22:46 +03:00
url: 'proxied',
directUrl: 'direct',
user: 'test',
password: 'mupp',
2019-04-15 12:11:52 +02:00
jsonData: {} as any,
} as unknown) as DataSourceInstanceSettings<PromOptions>;
2018-02-21 20:22:46 +03:00
ctx.backendSrvMock = {};
ctx.templateSrvMock = DEFAULT_TEMPLATE_SRV_MOCK;
ctx.timeSrvMock = {
timeRange: () => {
return {
from: dateTime(1531468681),
to: dateTime(1531489712),
};
},
};
2018-02-21 20:22:46 +03:00
beforeEach(() => {
ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
});
describe('Datasource metadata requests', () => {
it('should perform a GET request with the default config', () => {
ctx.backendSrvMock.datasourceRequest = jest.fn();
ctx.ds.metadataRequest('/foo');
expect(ctx.backendSrvMock.datasourceRequest.mock.calls.length).toBe(1);
expect(ctx.backendSrvMock.datasourceRequest.mock.calls[0][0].method).toBe('GET');
});
it('should still perform a GET request with the DS HTTP method set to POST', () => {
ctx.backendSrvMock.datasourceRequest = jest.fn();
const postSettings = _.cloneDeep(instanceSettings);
postSettings.jsonData.httpMethod = 'POST';
const ds = new PrometheusDatasource(postSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
ds.metadataRequest('/foo');
expect(ctx.backendSrvMock.datasourceRequest.mock.calls.length).toBe(1);
expect(ctx.backendSrvMock.datasourceRequest.mock.calls[0][0].method).toBe('GET');
});
});
describe('When using adhoc filters', () => {
const DEFAULT_QUERY_EXPRESSION = 'metric{job="foo"} - metric';
const target = { expr: DEFAULT_QUERY_EXPRESSION };
afterEach(() => {
ctx.templateSrvMock.getAdhocFilters = DEFAULT_TEMPLATE_SRV_MOCK.getAdhocFilters;
});
it('should not modify expression with no filters', () => {
const result = ctx.ds.createQuery(target, { interval: '15s' });
expect(result).toMatchObject({ expr: DEFAULT_QUERY_EXPRESSION });
});
it('should add filters to expression', () => {
ctx.templateSrvMock.getAdhocFilters = () => [
{
key: 'k1',
operator: '=',
value: 'v1',
},
{
key: 'k2',
operator: '!=',
value: 'v2',
},
];
const result = ctx.ds.createQuery(target, { interval: '15s' });
expect(result).toMatchObject({ expr: 'metric{job="foo",k1="v1",k2!="v2"} - metric{k1="v1",k2!="v2"}' });
});
it('should add escaping if needed to regex filter expressions', () => {
ctx.templateSrvMock.getAdhocFilters = () => [
{
key: 'k1',
operator: '=~',
value: 'v.*',
},
{
key: 'k2',
operator: '=~',
value: `v'.*`,
},
];
const result = ctx.ds.createQuery(target, { interval: '15s' });
expect(result).toMatchObject({
expr: `metric{job="foo",k1=~"v.*",k2=~"v\\\\'.*"} - metric{k1=~"v.*",k2=~"v\\\\'.*"}`,
});
});
});
describe('When performing performSuggestQuery', () => {
it('should cache response', async () => {
ctx.backendSrvMock.datasourceRequest.mockReturnValue(
Promise.resolve({
status: 'success',
data: { data: ['value1', 'value2', 'value3'] },
})
);
let results = await ctx.ds.performSuggestQuery('value', true);
expect(results).toHaveLength(3);
ctx.backendSrvMock.datasourceRequest.mockReset();
results = await ctx.ds.performSuggestQuery('value', true);
expect(results).toHaveLength(3);
});
});
2018-02-21 20:22:46 +03:00
describe('When converting prometheus histogram to heatmap format', () => {
beforeEach(() => {
ctx.query = {
range: { from: dateTime(1443454528000), to: dateTime(1443454528000) },
2018-02-21 20:22:46 +03:00
targets: [{ expr: 'test{job="testjob"}', format: 'heatmap', legendFormat: '{{le}}' }],
interval: '1s',
2018-02-21 20:22:46 +03:00
};
});
it('should convert cumullative histogram to ordinary', () => {
const resultMock = [
{
metric: { __name__: 'metric', job: 'testjob', le: '10' },
values: [[1443454528.0, '10'], [1443454528.0, '10']],
},
{
metric: { __name__: 'metric', job: 'testjob', le: '20' },
values: [[1443454528.0, '20'], [1443454528.0, '10']],
},
{
metric: { __name__: 'metric', job: 'testjob', le: '30' },
values: [[1443454528.0, '25'], [1443454528.0, '10']],
},
];
const responseMock = { data: { data: { result: resultMock } } };
const expected = [
{
target: '10',
datapoints: [[10, 1443454528000], [10, 1443454528000]],
},
{
target: '20',
datapoints: [[10, 1443454528000], [0, 1443454528000]],
},
{
target: '30',
datapoints: [[5, 1443454528000], [0, 1443454528000]],
},
];
ctx.ds.performTimeSeriesQuery = jest.fn().mockReturnValue(responseMock);
return ctx.ds.query(ctx.query).then((result: any) => {
const results = result.data;
return expect(results).toMatchObject(expected);
2018-02-21 20:22:46 +03:00
});
});
it('should sort series by label value', () => {
const resultMock = [
{
metric: { __name__: 'metric', job: 'testjob', le: '2' },
values: [[1443454528.0, '10'], [1443454528.0, '10']],
},
{
metric: { __name__: 'metric', job: 'testjob', le: '4' },
values: [[1443454528.0, '20'], [1443454528.0, '10']],
},
{
metric: { __name__: 'metric', job: 'testjob', le: '+Inf' },
values: [[1443454528.0, '25'], [1443454528.0, '10']],
},
{
metric: { __name__: 'metric', job: 'testjob', le: '1' },
values: [[1443454528.0, '25'], [1443454528.0, '10']],
},
];
const responseMock = { data: { data: { result: resultMock } } };
const expected = ['1', '2', '4', '+Inf'];
ctx.ds.performTimeSeriesQuery = jest.fn().mockReturnValue(responseMock);
return ctx.ds.query(ctx.query).then((result: any) => {
const seriesLabels = _.map(result.data, 'target');
2018-02-21 20:22:46 +03:00
return expect(seriesLabels).toEqual(expected);
});
});
});
2018-08-01 14:27:45 +02:00
describe('alignRange', () => {
it('does not modify already aligned intervals with perfect step', () => {
const range = alignRange(0, 3, 3, 0);
expect(range.start).toEqual(0);
expect(range.end).toEqual(3);
});
2018-08-01 14:27:45 +02:00
it('does modify end-aligned intervals to reflect number of steps possible', () => {
const range = alignRange(1, 6, 3, 0);
expect(range.start).toEqual(0);
expect(range.end).toEqual(6);
});
2018-08-01 14:27:45 +02:00
it('does align intervals that are a multiple of steps', () => {
const range = alignRange(1, 4, 3, 0);
expect(range.start).toEqual(0);
expect(range.end).toEqual(3);
});
2018-08-01 14:27:45 +02:00
it('does align intervals that are not a multiple of steps', () => {
const range = alignRange(1, 5, 3, 0);
expect(range.start).toEqual(0);
expect(range.end).toEqual(3);
});
it('does align intervals with local midnight -UTC offset', () => {
//week range, location 4+ hours UTC offset, 24h step time
const range = alignRange(4 * 60 * 60, (7 * 24 + 4) * 60 * 60, 24 * 60 * 60, -4 * 60 * 60); //04:00 UTC, 7 day range
expect(range.start).toEqual(4 * 60 * 60);
expect(range.end).toEqual((7 * 24 + 4) * 60 * 60);
});
it('does align intervals with local midnight +UTC offset', () => {
//week range, location 4- hours UTC offset, 24h step time
const range = alignRange(20 * 60 * 60, (8 * 24 - 4) * 60 * 60, 24 * 60 * 60, 4 * 60 * 60); //20:00 UTC on day1, 7 days later is 20:00 on day8
expect(range.start).toEqual(20 * 60 * 60);
expect(range.end).toEqual((8 * 24 - 4) * 60 * 60);
});
});
describe('extractRuleMappingFromGroups()', () => {
it('returns empty mapping for no rule groups', () => {
expect(extractRuleMappingFromGroups([])).toEqual({});
});
it('returns a mapping for recording rules only', () => {
const groups = [
{
rules: [
{
name: 'HighRequestLatency',
query: 'job:request_latency_seconds:mean5m{job="myjob"} > 0.5',
type: 'alerting',
},
{
name: 'job:http_inprogress_requests:sum',
query: 'sum(http_inprogress_requests) by (job)',
type: 'recording',
},
],
file: '/rules.yaml',
interval: 60,
name: 'example',
},
];
const mapping = extractRuleMappingFromGroups(groups);
expect(mapping).toEqual({ 'job:http_inprogress_requests:sum': 'sum(http_inprogress_requests) by (job)' });
});
});
2018-08-01 14:27:45 +02:00
describe('Prometheus regular escaping', () => {
it('should not escape non-string', () => {
expect(prometheusRegularEscape(12)).toEqual(12);
});
2018-08-01 14:27:45 +02:00
it('should not escape simple string', () => {
expect(prometheusRegularEscape('cryptodepression')).toEqual('cryptodepression');
});
2018-08-01 14:27:45 +02:00
it("should escape '", () => {
expect(prometheusRegularEscape("looking'glass")).toEqual("looking\\\\'glass");
});
2018-08-01 14:27:45 +02:00
it('should escape multiple characters', () => {
expect(prometheusRegularEscape("'looking'glass'")).toEqual("\\\\'looking\\\\'glass\\\\'");
});
});
2018-08-01 14:27:45 +02:00
describe('Prometheus regexes escaping', () => {
it('should not escape simple string', () => {
expect(prometheusSpecialRegexEscape('cryptodepression')).toEqual('cryptodepression');
});
it('should escape $^*+?.()|\\', () => {
expect(prometheusSpecialRegexEscape("looking'glass")).toEqual("looking\\\\'glass");
expect(prometheusSpecialRegexEscape('looking{glass')).toEqual('looking\\\\{glass');
expect(prometheusSpecialRegexEscape('looking}glass')).toEqual('looking\\\\}glass');
expect(prometheusSpecialRegexEscape('looking[glass')).toEqual('looking\\\\[glass');
expect(prometheusSpecialRegexEscape('looking]glass')).toEqual('looking\\\\]glass');
expect(prometheusSpecialRegexEscape('looking$glass')).toEqual('looking\\\\$glass');
expect(prometheusSpecialRegexEscape('looking^glass')).toEqual('looking\\\\^glass');
expect(prometheusSpecialRegexEscape('looking*glass')).toEqual('looking\\\\*glass');
expect(prometheusSpecialRegexEscape('looking+glass')).toEqual('looking\\\\+glass');
expect(prometheusSpecialRegexEscape('looking?glass')).toEqual('looking\\\\?glass');
expect(prometheusSpecialRegexEscape('looking.glass')).toEqual('looking\\\\.glass');
expect(prometheusSpecialRegexEscape('looking(glass')).toEqual('looking\\\\(glass');
expect(prometheusSpecialRegexEscape('looking)glass')).toEqual('looking\\\\)glass');
expect(prometheusSpecialRegexEscape('looking\\glass')).toEqual('looking\\\\\\\\glass');
expect(prometheusSpecialRegexEscape('looking|glass')).toEqual('looking\\\\|glass');
});
2018-08-01 14:27:45 +02:00
it('should escape multiple special characters', () => {
expect(prometheusSpecialRegexEscape('+looking$glass?')).toEqual('\\\\+looking\\\\$glass\\\\?');
});
});
describe('When interpolating variables', () => {
beforeEach(() => {
ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
ctx.variable = new CustomVariable({}, {});
});
describe('and value is a string', () => {
it('should only escape single quotes', () => {
expect(ctx.ds.interpolateQueryExpr("abc'$^*{}[]+?.()|", ctx.variable)).toEqual("abc\\\\'$^*{}[]+?.()|");
});
});
describe('and value is a number', () => {
it('should return a number', () => {
expect(ctx.ds.interpolateQueryExpr(1000, ctx.variable)).toEqual(1000);
});
});
describe('and variable allows multi-value', () => {
beforeEach(() => {
ctx.variable.multi = true;
});
it('should regex escape values if the value is a string', () => {
expect(ctx.ds.interpolateQueryExpr('looking*glass', ctx.variable)).toEqual('looking\\\\*glass');
});
it('should return pipe separated values if the value is an array of strings', () => {
expect(ctx.ds.interpolateQueryExpr(['a|bc', 'de|f'], ctx.variable)).toEqual('a\\\\|bc|de\\\\|f');
});
});
describe('and variable allows all', () => {
beforeEach(() => {
ctx.variable.includeAll = true;
});
it('should regex escape values if the array is a string', () => {
expect(ctx.ds.interpolateQueryExpr('looking*glass', ctx.variable)).toEqual('looking\\\\*glass');
});
it('should return pipe separated values if the value is an array of strings', () => {
expect(ctx.ds.interpolateQueryExpr(['a|bc', 'de|f'], ctx.variable)).toEqual('a\\\\|bc|de\\\\|f');
});
});
});
describe('metricFindQuery', () => {
beforeEach(() => {
const query = 'query_result(topk(5,rate(http_request_duration_microseconds_count[$__interval])))';
ctx.templateSrvMock.replace = jest.fn();
ctx.timeSrvMock.timeRange = () => {
return {
from: dateTime(1531468681),
to: dateTime(1531489712),
};
};
ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
ctx.ds.metricFindQuery(query);
});
it('should call templateSrv.replace with scopedVars', () => {
expect(ctx.templateSrvMock.replace.mock.calls[0][1]).toBeDefined();
});
it('should have the correct range and range_ms', () => {
const range = ctx.templateSrvMock.replace.mock.calls[0][1].__range;
const rangeMs = ctx.templateSrvMock.replace.mock.calls[0][1].__range_ms;
const rangeS = ctx.templateSrvMock.replace.mock.calls[0][1].__range_s;
expect(range).toEqual({ text: '21s', value: '21s' });
expect(rangeMs).toEqual({ text: 21031, value: 21031 });
expect(rangeS).toEqual({ text: 21, value: 21 });
});
it('should pass the default interval value', () => {
const interval = ctx.templateSrvMock.replace.mock.calls[0][1].__interval;
const intervalMs = ctx.templateSrvMock.replace.mock.calls[0][1].__interval_ms;
expect(interval).toEqual({ text: '15s', value: '15s' });
expect(intervalMs).toEqual({ text: 15000, value: 15000 });
});
});
2018-02-21 20:22:46 +03:00
});
2018-08-01 14:26:29 +02:00
const SECOND = 1000;
const MINUTE = 60 * SECOND;
const HOUR = 60 * MINUTE;
const time = ({ hours = 0, seconds = 0, minutes = 0 }) => dateTime(hours * HOUR + minutes * MINUTE + seconds * SECOND);
2018-08-01 14:26:29 +02:00
const ctx = {} as any;
const instanceSettings = ({
2018-08-01 14:26:29 +02:00
url: 'proxied',
directUrl: 'direct',
user: 'test',
password: 'mupp',
jsonData: { httpMethod: 'GET' },
} as unknown) as DataSourceInstanceSettings<PromOptions>;
const backendSrv = {
2018-08-01 14:26:29 +02:00
datasourceRequest: jest.fn(),
} as any;
2018-08-01 14:26:29 +02:00
const templateSrv = ({
getAdhocFilters: (): any[] => [],
2018-08-01 14:26:29 +02:00
replace: jest.fn(str => str),
} as unknown) as TemplateSrv;
2018-08-01 14:26:29 +02:00
const timeSrv = ({
2018-08-01 14:26:29 +02:00
timeRange: () => {
return {
from: dateTime(1531468681),
to: dateTime(1531468681 + 2000),
};
2018-08-01 14:26:29 +02:00
},
} as unknown) as TimeSrv;
2018-08-01 14:26:29 +02:00
2018-08-01 14:27:45 +02:00
describe('PrometheusDatasource', () => {
2019-03-13 16:16:28 +01:00
describe('When querying prometheus with one target using query editor target spec', () => {
describe('and query syntax is valid', () => {
let results: any;
const query = {
range: { from: time({ seconds: 63 }), to: time({ seconds: 183 }) },
targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
interval: '60s',
};
// Interval alignment with step
const urlExpected =
'proxied/api/v1/query_range?query=' + encodeURIComponent('test{job="testjob"}') + '&start=60&end=180&step=60';
2018-08-01 14:26:29 +02:00
beforeEach(async () => {
const response = {
2018-08-01 14:26:29 +02:00
data: {
status: 'success',
data: {
resultType: 'matrix',
result: [
{
metric: { __name__: 'test', job: 'testjob' },
values: [[60, '3846']],
},
],
},
2018-08-01 14:26:29 +02:00
},
};
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
await ctx.ds.query(query).then((data: any) => {
results = data;
});
});
it('should generate the correct query', () => {
const res = backendSrv.datasourceRequest.mock.calls[0][0];
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('should return series list', async () => {
expect(results.data.length).toBe(1);
expect(results.data[0].target).toBe('test{job="testjob"}');
});
});
describe('and query syntax is invalid', () => {
let results: string;
const query = {
range: { from: time({ seconds: 63 }), to: time({ seconds: 183 }) },
targets: [{ expr: 'tes;;t{job="testjob"}', format: 'time_series' }],
interval: '60s',
};
const errMessage = 'parse error at char 25: could not parse remaining input';
const response = {
data: {
status: 'error',
errorType: 'bad_data',
error: errMessage,
2018-08-01 14:26:29 +02:00
},
};
beforeEach(async () => {
backendSrv.datasourceRequest = jest.fn(() => Promise.reject(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
await ctx.ds.query(query).catch((e: any) => {
results = e.message;
});
2018-08-01 14:26:29 +02:00
});
it('should generate an error', () => {
expect(results).toBe(`"${errMessage}"`);
});
2018-08-01 14:26:29 +02:00
});
});
describe('When querying prometheus with one target which returns multiple series', () => {
let results: any;
const start = 60;
const end = 360;
const step = 60;
2018-08-01 14:26:29 +02:00
const query = {
2018-08-01 14:26:29 +02:00
range: { from: time({ seconds: start }), to: time({ seconds: end }) },
targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
interval: '60s',
};
beforeEach(async () => {
const response = {
2018-08-01 14:26:29 +02:00
status: 'success',
data: {
data: {
resultType: 'matrix',
result: [
{
metric: { __name__: 'test', job: 'testjob', series: 'series 1' },
values: [[start + step * 1, '3846'], [start + step * 3, '3847'], [end - step * 1, '3848']],
},
{
metric: { __name__: 'test', job: 'testjob', series: 'series 2' },
values: [[start + step * 2, '4846']],
},
],
},
},
};
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query).then((data: any) => {
2018-08-01 14:26:29 +02:00
results = data;
});
});
2018-08-01 14:27:45 +02:00
it('should be same length', () => {
2018-08-01 14:26:29 +02:00
expect(results.data.length).toBe(2);
expect(results.data[0].datapoints.length).toBe((end - start) / step + 1);
expect(results.data[1].datapoints.length).toBe((end - start) / step + 1);
});
2018-08-01 14:27:45 +02:00
it('should fill null until first datapoint in response', () => {
2018-08-01 14:26:29 +02:00
expect(results.data[0].datapoints[0][1]).toBe(start * 1000);
expect(results.data[0].datapoints[0][0]).toBe(null);
expect(results.data[0].datapoints[1][1]).toBe((start + step * 1) * 1000);
expect(results.data[0].datapoints[1][0]).toBe(3846);
});
2018-08-01 14:27:45 +02:00
it('should fill null after last datapoint in response', () => {
const length = (end - start) / step + 1;
2018-08-01 14:26:29 +02:00
expect(results.data[0].datapoints[length - 2][1]).toBe((end - step * 1) * 1000);
expect(results.data[0].datapoints[length - 2][0]).toBe(3848);
expect(results.data[0].datapoints[length - 1][1]).toBe(end * 1000);
expect(results.data[0].datapoints[length - 1][0]).toBe(null);
});
2018-08-01 14:27:45 +02:00
it('should fill null at gap between series', () => {
2018-08-01 14:26:29 +02:00
expect(results.data[0].datapoints[2][1]).toBe((start + step * 2) * 1000);
expect(results.data[0].datapoints[2][0]).toBe(null);
expect(results.data[1].datapoints[1][1]).toBe((start + step * 1) * 1000);
expect(results.data[1].datapoints[1][0]).toBe(null);
expect(results.data[1].datapoints[3][1]).toBe((start + step * 3) * 1000);
expect(results.data[1].datapoints[3][0]).toBe(null);
});
});
2018-08-01 14:27:45 +02:00
describe('When querying prometheus with one target and instant = true', () => {
let results: any;
const urlExpected = 'proxied/api/v1/query?query=' + encodeURIComponent('test{job="testjob"}') + '&time=123';
const query = {
2018-08-01 14:26:29 +02:00
range: { from: time({ seconds: 63 }), to: time({ seconds: 123 }) },
targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
interval: '60s',
};
beforeEach(async () => {
const response = {
2018-08-01 14:26:29 +02:00
status: 'success',
data: {
data: {
resultType: 'vector',
result: [
{
metric: { __name__: 'test', job: 'testjob' },
value: [123, '3846'],
},
],
},
},
};
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query).then((data: any) => {
2018-08-01 14:26:29 +02:00
results = data;
});
});
2018-08-01 14:27:45 +02:00
it('should generate the correct query', () => {
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
2018-08-01 14:27:45 +02:00
it('should return series list', () => {
2018-08-01 14:26:29 +02:00
expect(results.data.length).toBe(1);
expect(results.data[0].target).toBe('test{job="testjob"}');
});
});
2018-08-01 14:27:45 +02:00
describe('When performing annotationQuery', () => {
let results: any;
2018-08-01 14:26:29 +02:00
2018-08-28 01:58:53 +09:00
const options: any = {
2018-08-01 14:26:29 +02:00
annotation: {
expr: 'ALERTS{alertstate="firing"}',
tagKeys: 'job',
titleFormat: '{{alertname}}',
textFormat: '{{instance}}',
},
range: {
from: time({ seconds: 63 }),
to: time({ seconds: 123 }),
},
};
2018-08-28 01:58:53 +09:00
const response = {
status: 'success',
data: {
2018-08-01 14:26:29 +02:00
data: {
2018-08-28 01:58:53 +09:00
resultType: 'matrix',
result: [
{
metric: {
__name__: 'ALERTS',
alertname: 'InstanceDown',
alertstate: 'firing',
instance: 'testinstance',
job: 'testjob',
2018-08-01 14:26:29 +02:00
},
2018-08-28 01:58:53 +09:00
values: [[123, '1']],
},
],
2018-08-01 14:26:29 +02:00
},
2018-08-28 01:58:53 +09:00
},
};
2018-08-01 14:26:29 +02:00
2018-08-28 01:58:53 +09:00
describe('not use useValueForTime', () => {
beforeEach(async () => {
options.annotation.useValueForTime = false;
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.annotationQuery(options).then((data: any) => {
2018-08-28 01:58:53 +09:00
results = data;
});
});
it('should return annotation list', () => {
expect(results.length).toBe(1);
expect(results[0].tags).toContain('testjob');
expect(results[0].title).toBe('InstanceDown');
expect(results[0].text).toBe('testinstance');
expect(results[0].time).toBe(123 * 1000);
2018-08-01 14:26:29 +02:00
});
});
2018-08-28 01:58:53 +09:00
describe('use useValueForTime', () => {
beforeEach(async () => {
options.annotation.useValueForTime = true;
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-28 01:58:53 +09:00
await ctx.ds.annotationQuery(options).then((data: any) => {
2018-08-28 01:58:53 +09:00
results = data;
});
});
it('should return annotation list', () => {
expect(results[0].time).toEqual(1);
});
});
describe('step parameter', () => {
beforeEach(() => {
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
});
it('should use default step for short range if no interval is given', () => {
const query = {
...options,
range: {
from: time({ seconds: 63 }),
to: time({ seconds: 123 }),
},
};
ctx.ds.annotationQuery(query);
const req = backendSrv.datasourceRequest.mock.calls[0][0];
expect(req.url).toContain('step=60');
});
it('should use custom step for short range', () => {
const annotation = {
...options.annotation,
step: '10s',
};
const query = {
...options,
annotation,
range: {
from: time({ seconds: 63 }),
to: time({ seconds: 123 }),
},
};
ctx.ds.annotationQuery(query);
const req = backendSrv.datasourceRequest.mock.calls[0][0];
expect(req.url).toContain('step=10');
});
it('should use custom step for short range', () => {
const annotation = {
...options.annotation,
step: '10s',
};
const query = {
...options,
annotation,
range: {
from: time({ seconds: 63 }),
to: time({ seconds: 123 }),
},
};
ctx.ds.annotationQuery(query);
const req = backendSrv.datasourceRequest.mock.calls[0][0];
expect(req.url).toContain('step=10');
});
it('should use dynamic step on long ranges if no option was given', () => {
const query = {
...options,
range: {
from: time({ seconds: 63 }),
to: time({ hours: 24 * 30, seconds: 63 }),
},
};
ctx.ds.annotationQuery(query);
const req = backendSrv.datasourceRequest.mock.calls[0][0];
// Range in seconds: (to - from) / 1000
2019-01-15 17:14:58 +01:00
// Max_datapoints: 11000
// Step: range / max_datapoints
const step = 236;
expect(req.url).toContain(`step=${step}`);
});
});
2018-08-01 14:26:29 +02:00
});
2018-08-01 14:27:45 +02:00
describe('When resultFormat is table and instant = true', () => {
let results: any;
const query = {
2018-08-01 14:26:29 +02:00
range: { from: time({ seconds: 63 }), to: time({ seconds: 123 }) },
targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
interval: '60s',
};
beforeEach(async () => {
const response = {
2018-08-01 14:26:29 +02:00
status: 'success',
data: {
data: {
resultType: 'vector',
result: [
{
metric: { __name__: 'test', job: 'testjob' },
value: [123, '3846'],
},
],
},
},
};
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
await ctx.ds.query(query).then((data: any) => {
2018-08-01 14:26:29 +02:00
results = data;
});
});
it('should return result', () => {
expect(results).not.toBe(null);
});
});
2018-08-01 14:27:45 +02:00
describe('The "step" query parameter', () => {
const response = {
2018-08-01 14:26:29 +02:00
status: 'success',
data: {
data: {
resultType: 'matrix',
result: [] as DataQueryResponseData[],
2018-08-01 14:26:29 +02:00
},
},
};
it('should be min interval when greater than auto interval', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'test',
interval: '10s',
},
],
interval: '5s',
};
const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('step should never go below 1', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [{ expr: 'test' }],
interval: '100ms',
};
const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=1';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('should be auto interval when greater than min interval', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'test',
interval: '5s',
},
],
interval: '10s',
};
const urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('should result in querying fewer than 11000 data points', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 hour range
range: { from: time({ hours: 1 }), to: time({ hours: 7 }) },
targets: [{ expr: 'test' }],
interval: '1s',
};
const end = 7 * 60 * 60;
const start = 60 * 60;
const urlExpected = 'proxied/api/v1/query_range?query=test&start=' + start + '&end=' + end + '&step=2';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('should not apply min interval when interval * intervalFactor greater', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'test',
interval: '10s',
intervalFactor: 10,
},
],
interval: '5s',
};
// times get rounded up to interval
const urlExpected = 'proxied/api/v1/query_range?query=test&start=50&end=400&step=50';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('should apply min interval when interval * intervalFactor smaller', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'test',
interval: '15s',
intervalFactor: 2,
},
],
interval: '5s',
};
const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=60&end=420&step=15';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('should apply intervalFactor to auto interval when greater', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'test',
interval: '5s',
intervalFactor: 10,
},
],
interval: '10s',
};
// times get aligned to interval
const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=0&end=400&step=100';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('should not not be affected by the 11000 data points limit when large enough', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 1 week range
range: { from: time({}), to: time({ hours: 7 * 24 }) },
targets: [
{
expr: 'test',
intervalFactor: 10,
},
],
interval: '10s',
};
const end = 7 * 24 * 60 * 60;
const start = 0;
const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=100';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
it('should be determined by the 11000 data points limit when too small', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 1 week range
range: { from: time({}), to: time({ hours: 7 * 24 }) },
targets: [
{
expr: 'test',
intervalFactor: 10,
},
],
interval: '5s',
};
const end = 7 * 24 * 60 * 60;
const start = 0;
const urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=60';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
});
});
2018-08-01 14:27:45 +02:00
describe('The __interval and __interval_ms template variables', () => {
const response = {
2018-08-01 14:26:29 +02:00
status: 'success',
data: {
data: {
resultType: 'matrix',
result: [] as DataQueryResponseData[],
2018-08-01 14:26:29 +02:00
},
},
};
it('should be unchanged when auto interval is greater than min interval', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'rate(test[$__interval])',
interval: '5s',
},
],
interval: '10s',
scopedVars: {
__interval: { text: '10s', value: '10s' },
__interval_ms: { text: 10 * 1000, value: 10 * 1000 },
},
};
const urlExpected =
2018-08-01 14:26:29 +02:00
'proxied/api/v1/query_range?query=' +
encodeURIComponent('rate(test[$__interval])') +
'&start=60&end=420&step=10';
templateSrv.replace = jest.fn(str => str);
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
// @ts-ignore
2018-08-01 14:26:29 +02:00
expect(templateSrv.replace.mock.calls[0][1]).toEqual({
__interval: {
text: '10s',
value: '10s',
},
__interval_ms: {
text: 10000,
value: 10000,
},
});
});
it('should be min interval when it is greater than auto interval', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'rate(test[$__interval])',
interval: '10s',
},
],
interval: '5s',
scopedVars: {
__interval: { text: '5s', value: '5s' },
__interval_ms: { text: 5 * 1000, value: 5 * 1000 },
},
};
const urlExpected =
2018-08-01 14:26:29 +02:00
'proxied/api/v1/query_range?query=' +
encodeURIComponent('rate(test[$__interval])') +
'&start=60&end=420&step=10';
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
templateSrv.replace = jest.fn(str => str);
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
// @ts-ignore
2018-08-01 14:26:29 +02:00
expect(templateSrv.replace.mock.calls[0][1]).toEqual({
__interval: {
text: '5s',
value: '5s',
},
__interval_ms: {
text: 5000,
value: 5000,
},
});
});
it('should account for intervalFactor', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'rate(test[$__interval])',
interval: '5s',
intervalFactor: 10,
},
],
interval: '10s',
scopedVars: {
__interval: { text: '10s', value: '10s' },
__interval_ms: { text: 10 * 1000, value: 10 * 1000 },
},
};
const urlExpected =
2018-08-01 14:26:29 +02:00
'proxied/api/v1/query_range?query=' +
encodeURIComponent('rate(test[$__interval])') +
'&start=0&end=400&step=100';
2018-08-01 14:26:29 +02:00
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
templateSrv.replace = jest.fn(str => str);
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
// @ts-ignore
2018-08-01 14:26:29 +02:00
expect(templateSrv.replace.mock.calls[0][1]).toEqual({
__interval: {
text: '10s',
value: '10s',
},
__interval_ms: {
text: 10000,
value: 10000,
},
});
expect(query.scopedVars.__interval.text).toBe('10s');
expect(query.scopedVars.__interval.value).toBe('10s');
expect(query.scopedVars.__interval_ms.text).toBe(10 * 1000);
expect(query.scopedVars.__interval_ms.value).toBe(10 * 1000);
});
it('should be interval * intervalFactor when greater than min interval', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'rate(test[$__interval])',
interval: '10s',
intervalFactor: 10,
},
],
interval: '5s',
scopedVars: {
__interval: { text: '5s', value: '5s' },
__interval_ms: { text: 5 * 1000, value: 5 * 1000 },
},
};
const urlExpected =
2018-08-01 14:26:29 +02:00
'proxied/api/v1/query_range?query=' +
encodeURIComponent('rate(test[$__interval])') +
'&start=50&end=400&step=50';
2018-08-01 14:26:29 +02:00
templateSrv.replace = jest.fn(str => str);
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
// @ts-ignore
2018-08-01 14:26:29 +02:00
expect(templateSrv.replace.mock.calls[0][1]).toEqual({
__interval: {
text: '5s',
value: '5s',
},
__interval_ms: {
text: 5000,
value: 5000,
},
});
});
it('should be min interval when greater than interval * intervalFactor', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 6 minute range
range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
targets: [
{
expr: 'rate(test[$__interval])',
interval: '15s',
intervalFactor: 2,
},
],
interval: '5s',
scopedVars: {
__interval: { text: '5s', value: '5s' },
__interval_ms: { text: 5 * 1000, value: 5 * 1000 },
},
};
const urlExpected =
2018-08-01 14:26:29 +02:00
'proxied/api/v1/query_range?query=' +
encodeURIComponent('rate(test[$__interval])') +
'&start=60&end=420&step=15';
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
// @ts-ignore
2018-08-01 14:26:29 +02:00
expect(templateSrv.replace.mock.calls[0][1]).toEqual({
__interval: {
text: '5s',
value: '5s',
},
__interval_ms: {
text: 5000,
value: 5000,
},
});
});
it('should be determined by the 11000 data points limit, accounting for intervalFactor', async () => {
const query = {
2018-08-01 14:26:29 +02:00
// 1 week range
range: { from: time({}), to: time({ hours: 7 * 24 }) },
targets: [
{
expr: 'rate(test[$__interval])',
intervalFactor: 10,
},
],
interval: '5s',
scopedVars: {
__interval: { text: '5s', value: '5s' },
__interval_ms: { text: 5 * 1000, value: 5 * 1000 },
},
};
const end = 7 * 24 * 60 * 60;
const start = 0;
const urlExpected =
2018-08-01 14:26:29 +02:00
'proxied/api/v1/query_range?query=' +
encodeURIComponent('rate(test[$__interval])') +
'&start=' +
start +
'&end=' +
end +
'&step=60';
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
templateSrv.replace = jest.fn(str => str);
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
2018-08-01 14:26:29 +02:00
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('GET');
expect(res.url).toBe(urlExpected);
// @ts-ignore
2018-08-01 14:26:29 +02:00
expect(templateSrv.replace.mock.calls[0][1]).toEqual({
__interval: {
text: '5s',
value: '5s',
},
__interval_ms: {
text: 5000,
value: 5000,
},
});
});
});
describe('The __range, __range_s and __range_ms variables', () => {
const response = {
status: 'success',
data: {
data: {
resultType: 'matrix',
result: [] as DataQueryResponseData[],
},
},
};
it('should use overridden ranges, not dashboard ranges', async () => {
const expectedRangeSecond = 3600;
const expectedRangeString = '1h';
const query = {
range: {
from: time({}),
to: time({ hours: 1 }),
},
targets: [
{
expr: 'test[${__range_s}s]',
},
],
interval: '60s',
};
const urlExpected = `proxied/api/v1/query_range?query=${encodeURIComponent(
query.targets[0].expr
)}&start=0&end=3600&step=60`;
templateSrv.replace = jest.fn(str => str);
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
await ctx.ds.query(query);
const res = backendSrv.datasourceRequest.mock.calls[0][0];
expect(res.url).toBe(urlExpected);
// @ts-ignore
expect(templateSrv.replace.mock.calls[1][1]).toEqual({
__range_s: {
text: expectedRangeSecond,
value: expectedRangeSecond,
},
__range: {
text: expectedRangeString,
value: expectedRangeString,
},
__range_ms: {
text: expectedRangeSecond * 1000,
value: expectedRangeSecond * 1000,
},
});
});
});
2018-08-01 14:26:29 +02:00
});
2018-08-01 14:27:45 +02:00
describe('PrometheusDatasource for POST', () => {
// const ctx = new helpers.ServiceTestContext();
const instanceSettings = ({
2018-08-01 14:26:29 +02:00
url: 'proxied',
directUrl: 'direct',
user: 'test',
password: 'mupp',
jsonData: { httpMethod: 'POST' },
} as unknown) as DataSourceInstanceSettings<PromOptions>;
2018-08-01 14:26:29 +02:00
2018-08-01 14:27:45 +02:00
describe('When querying prometheus with one target using query editor target spec', () => {
let results: any;
const urlExpected = 'proxied/api/v1/query_range';
const dataExpected = {
2018-08-01 14:26:29 +02:00
query: 'test{job="testjob"}',
start: 1 * 60,
end: 2 * 60,
2018-08-01 14:26:29 +02:00
step: 60,
};
const query = {
2018-08-01 14:26:29 +02:00
range: { from: time({ minutes: 1, seconds: 3 }), to: time({ minutes: 2, seconds: 3 }) },
targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
interval: '60s',
};
beforeEach(async () => {
const response = {
2018-08-01 14:26:29 +02:00
status: 'success',
data: {
data: {
resultType: 'matrix',
result: [
{
metric: { __name__: 'test', job: 'testjob' },
values: [[2 * 60, '3846']],
},
],
},
},
};
backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
await ctx.ds.query(query).then((data: any) => {
2018-08-01 14:26:29 +02:00
results = data;
});
});
2018-08-01 14:27:45 +02:00
it('should generate the correct query', () => {
const res = backendSrv.datasourceRequest.mock.calls[0][0];
2018-08-01 14:26:29 +02:00
expect(res.method).toBe('POST');
expect(res.url).toBe(urlExpected);
expect(res.data).toEqual(dataExpected);
});
2018-08-01 14:27:45 +02:00
it('should return series list', () => {
2018-08-01 14:26:29 +02:00
expect(results.data.length).toBe(1);
expect(results.data[0].target).toBe('test{job="testjob"}');
});
});
describe('When querying prometheus via check headers X-Dashboard-Id and X-Panel-Id', () => {
const options = { dashboardId: 1, panelId: 2 };
const httpOptions = {
headers: {} as { [key: string]: number | undefined },
};
it('with proxy access tracing headers should be added', () => {
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
ctx.ds._addTracingHeaders(httpOptions, options);
expect(httpOptions.headers['X-Dashboard-Id']).toBe(1);
expect(httpOptions.headers['X-Panel-Id']).toBe(2);
});
it('with direct access tracing headers should not be added', () => {
instanceSettings.url = 'http://127.0.0.1:8000';
ctx.ds = new PrometheusDatasource(instanceSettings, q, backendSrv as any, templateSrv as any, timeSrv as any);
ctx.ds._addTracingHeaders(httpOptions, options);
expect(httpOptions.headers['X-Dashboard-Id']).toBe(undefined);
expect(httpOptions.headers['X-Panel-Id']).toBe(undefined);
});
});
2018-08-01 14:26:29 +02:00
});