2018-02-21 20:22:46 +03:00
|
|
|
|
import _ from 'lodash';
|
|
|
|
|
|
import moment from 'moment';
|
|
|
|
|
|
import q from 'q';
|
2018-05-25 16:46:38 +02:00
|
|
|
|
import { alignRange, PrometheusDatasource, prometheusSpecialRegexEscape, prometheusRegularEscape } from '../datasource';
|
2018-02-21 20:22:46 +03:00
|
|
|
|
|
|
|
|
|
|
describe('PrometheusDatasource', () => {
|
|
|
|
|
|
let ctx: any = {};
|
|
|
|
|
|
let instanceSettings = {
|
|
|
|
|
|
url: 'proxied',
|
|
|
|
|
|
directUrl: 'direct',
|
|
|
|
|
|
user: 'test',
|
|
|
|
|
|
password: 'mupp',
|
|
|
|
|
|
jsonData: {},
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
ctx.backendSrvMock = {};
|
2018-04-24 16:26:46 +02:00
|
|
|
|
|
2018-02-21 20:22:46 +03:00
|
|
|
|
ctx.templateSrvMock = {
|
|
|
|
|
|
replace: a => a,
|
|
|
|
|
|
};
|
|
|
|
|
|
ctx.timeSrvMock = {};
|
|
|
|
|
|
|
|
|
|
|
|
beforeEach(() => {
|
|
|
|
|
|
ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2018-04-24 16:26:46 +02:00
|
|
|
|
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');
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2018-04-25 15:36:47 +02:00
|
|
|
|
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: moment(1443454528000), to: moment(1443454528000) },
|
|
|
|
|
|
targets: [{ expr: 'test{job="testjob"}', format: 'heatmap', legendFormat: '{{le}}' }],
|
2018-07-02 20:04:36 +02:00
|
|
|
|
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 => {
|
|
|
|
|
|
let results = result.data;
|
|
|
|
|
|
return expect(results).toEqual(expected);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
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 => {
|
|
|
|
|
|
let seriesLabels = _.map(result.data, 'target');
|
|
|
|
|
|
return expect(seriesLabels).toEqual(expected);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2018-03-27 16:16:00 +02:00
|
|
|
|
|
2018-05-25 16:46:38 +02:00
|
|
|
|
describe('alignRange', function() {
|
|
|
|
|
|
it('does not modify already aligned intervals with perfect step', function() {
|
|
|
|
|
|
const range = alignRange(0, 3, 3);
|
|
|
|
|
|
expect(range.start).toEqual(0);
|
|
|
|
|
|
expect(range.end).toEqual(3);
|
|
|
|
|
|
});
|
|
|
|
|
|
it('does modify end-aligned intervals to reflect number of steps possible', function() {
|
|
|
|
|
|
const range = alignRange(1, 6, 3);
|
|
|
|
|
|
expect(range.start).toEqual(0);
|
|
|
|
|
|
expect(range.end).toEqual(6);
|
|
|
|
|
|
});
|
|
|
|
|
|
it('does align intervals that are a multiple of steps', function() {
|
|
|
|
|
|
const range = alignRange(1, 4, 3);
|
|
|
|
|
|
expect(range.start).toEqual(0);
|
|
|
|
|
|
expect(range.end).toEqual(6);
|
|
|
|
|
|
});
|
|
|
|
|
|
it('does align intervals that are not a multiple of steps', function() {
|
|
|
|
|
|
const range = alignRange(1, 5, 3);
|
|
|
|
|
|
expect(range.start).toEqual(0);
|
|
|
|
|
|
expect(range.end).toEqual(6);
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2018-03-27 16:16:00 +02:00
|
|
|
|
describe('Prometheus regular escaping', function() {
|
2018-07-16 12:40:55 +02:00
|
|
|
|
it('should not escape non-string', function() {
|
|
|
|
|
|
expect(prometheusRegularEscape(12)).toEqual(12);
|
|
|
|
|
|
});
|
2018-03-27 16:16:00 +02:00
|
|
|
|
it('should not escape simple string', function() {
|
|
|
|
|
|
expect(prometheusRegularEscape('cryptodepression')).toEqual('cryptodepression');
|
|
|
|
|
|
});
|
|
|
|
|
|
it("should escape '", function() {
|
|
|
|
|
|
expect(prometheusRegularEscape("looking'glass")).toEqual("looking\\\\'glass");
|
|
|
|
|
|
});
|
|
|
|
|
|
it('should escape multiple characters', function() {
|
|
|
|
|
|
expect(prometheusRegularEscape("'looking'glass'")).toEqual("\\\\'looking\\\\'glass\\\\'");
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
describe('Prometheus regexes escaping', function() {
|
|
|
|
|
|
it('should not escape simple string', function() {
|
|
|
|
|
|
expect(prometheusSpecialRegexEscape('cryptodepression')).toEqual('cryptodepression');
|
|
|
|
|
|
});
|
|
|
|
|
|
it('should escape $^*+?.()\\', function() {
|
|
|
|
|
|
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');
|
|
|
|
|
|
});
|
|
|
|
|
|
it('should escape multiple special characters', function() {
|
|
|
|
|
|
expect(prometheusSpecialRegexEscape('+looking$glass?')).toEqual('\\\\+looking\\\\$glass\\\\?');
|
|
|
|
|
|
});
|
|
|
|
|
|
});
|
2018-02-21 20:22:46 +03:00
|
|
|
|
});
|