datasource.jest.ts 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  1. import _ from 'lodash';
  2. import moment from 'moment';
  3. import q from 'q';
  4. import { alignRange, PrometheusDatasource, prometheusSpecialRegexEscape, prometheusRegularEscape } from '../datasource';
  5. jest.mock('../metric_find_query');
  6. describe('PrometheusDatasource', () => {
  7. let ctx: any = {};
  8. let instanceSettings = {
  9. url: 'proxied',
  10. directUrl: 'direct',
  11. user: 'test',
  12. password: 'mupp',
  13. jsonData: {},
  14. };
  15. ctx.backendSrvMock = {};
  16. ctx.templateSrvMock = {
  17. replace: a => a,
  18. };
  19. ctx.timeSrvMock = {
  20. timeRange: () => {
  21. return {
  22. from: moment(1531468681),
  23. to: moment(1531489712),
  24. };
  25. },
  26. };
  27. beforeEach(() => {
  28. ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
  29. });
  30. describe('Datasource metadata requests', () => {
  31. it('should perform a GET request with the default config', () => {
  32. ctx.backendSrvMock.datasourceRequest = jest.fn();
  33. ctx.ds.metadataRequest('/foo');
  34. expect(ctx.backendSrvMock.datasourceRequest.mock.calls.length).toBe(1);
  35. expect(ctx.backendSrvMock.datasourceRequest.mock.calls[0][0].method).toBe('GET');
  36. });
  37. it('should still perform a GET request with the DS HTTP method set to POST', () => {
  38. ctx.backendSrvMock.datasourceRequest = jest.fn();
  39. const postSettings = _.cloneDeep(instanceSettings);
  40. postSettings.jsonData.httpMethod = 'POST';
  41. const ds = new PrometheusDatasource(postSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
  42. ds.metadataRequest('/foo');
  43. expect(ctx.backendSrvMock.datasourceRequest.mock.calls.length).toBe(1);
  44. expect(ctx.backendSrvMock.datasourceRequest.mock.calls[0][0].method).toBe('GET');
  45. });
  46. });
  47. describe('When performing performSuggestQuery', () => {
  48. it('should cache response', async () => {
  49. ctx.backendSrvMock.datasourceRequest.mockReturnValue(
  50. Promise.resolve({
  51. status: 'success',
  52. data: { data: ['value1', 'value2', 'value3'] },
  53. })
  54. );
  55. let results = await ctx.ds.performSuggestQuery('value', true);
  56. expect(results).toHaveLength(3);
  57. ctx.backendSrvMock.datasourceRequest.mockReset();
  58. results = await ctx.ds.performSuggestQuery('value', true);
  59. expect(results).toHaveLength(3);
  60. });
  61. });
  62. describe('When converting prometheus histogram to heatmap format', () => {
  63. beforeEach(() => {
  64. ctx.query = {
  65. range: { from: moment(1443454528000), to: moment(1443454528000) },
  66. targets: [{ expr: 'test{job="testjob"}', format: 'heatmap', legendFormat: '{{le}}' }],
  67. interval: '1s',
  68. };
  69. });
  70. it('should convert cumullative histogram to ordinary', () => {
  71. const resultMock = [
  72. {
  73. metric: { __name__: 'metric', job: 'testjob', le: '10' },
  74. values: [[1443454528.0, '10'], [1443454528.0, '10']],
  75. },
  76. {
  77. metric: { __name__: 'metric', job: 'testjob', le: '20' },
  78. values: [[1443454528.0, '20'], [1443454528.0, '10']],
  79. },
  80. {
  81. metric: { __name__: 'metric', job: 'testjob', le: '30' },
  82. values: [[1443454528.0, '25'], [1443454528.0, '10']],
  83. },
  84. ];
  85. const responseMock = { data: { data: { result: resultMock } } };
  86. const expected = [
  87. {
  88. target: '10',
  89. datapoints: [[10, 1443454528000], [10, 1443454528000]],
  90. },
  91. {
  92. target: '20',
  93. datapoints: [[10, 1443454528000], [0, 1443454528000]],
  94. },
  95. {
  96. target: '30',
  97. datapoints: [[5, 1443454528000], [0, 1443454528000]],
  98. },
  99. ];
  100. ctx.ds.performTimeSeriesQuery = jest.fn().mockReturnValue(responseMock);
  101. return ctx.ds.query(ctx.query).then(result => {
  102. let results = result.data;
  103. return expect(results).toEqual(expected);
  104. });
  105. });
  106. it('should sort series by label value', () => {
  107. const resultMock = [
  108. {
  109. metric: { __name__: 'metric', job: 'testjob', le: '2' },
  110. values: [[1443454528.0, '10'], [1443454528.0, '10']],
  111. },
  112. {
  113. metric: { __name__: 'metric', job: 'testjob', le: '4' },
  114. values: [[1443454528.0, '20'], [1443454528.0, '10']],
  115. },
  116. {
  117. metric: { __name__: 'metric', job: 'testjob', le: '+Inf' },
  118. values: [[1443454528.0, '25'], [1443454528.0, '10']],
  119. },
  120. {
  121. metric: { __name__: 'metric', job: 'testjob', le: '1' },
  122. values: [[1443454528.0, '25'], [1443454528.0, '10']],
  123. },
  124. ];
  125. const responseMock = { data: { data: { result: resultMock } } };
  126. const expected = ['1', '2', '4', '+Inf'];
  127. ctx.ds.performTimeSeriesQuery = jest.fn().mockReturnValue(responseMock);
  128. return ctx.ds.query(ctx.query).then(result => {
  129. let seriesLabels = _.map(result.data, 'target');
  130. return expect(seriesLabels).toEqual(expected);
  131. });
  132. });
  133. });
  134. describe('alignRange', function() {
  135. it('does not modify already aligned intervals with perfect step', function() {
  136. const range = alignRange(0, 3, 3);
  137. expect(range.start).toEqual(0);
  138. expect(range.end).toEqual(3);
  139. });
  140. it('does modify end-aligned intervals to reflect number of steps possible', function() {
  141. const range = alignRange(1, 6, 3);
  142. expect(range.start).toEqual(0);
  143. expect(range.end).toEqual(6);
  144. });
  145. it('does align intervals that are a multiple of steps', function() {
  146. const range = alignRange(1, 4, 3);
  147. expect(range.start).toEqual(0);
  148. expect(range.end).toEqual(6);
  149. });
  150. it('does align intervals that are not a multiple of steps', function() {
  151. const range = alignRange(1, 5, 3);
  152. expect(range.start).toEqual(0);
  153. expect(range.end).toEqual(6);
  154. });
  155. });
  156. describe('Prometheus regular escaping', function() {
  157. it('should not escape non-string', function() {
  158. expect(prometheusRegularEscape(12)).toEqual(12);
  159. });
  160. it('should not escape simple string', function() {
  161. expect(prometheusRegularEscape('cryptodepression')).toEqual('cryptodepression');
  162. });
  163. it("should escape '", function() {
  164. expect(prometheusRegularEscape("looking'glass")).toEqual("looking\\\\'glass");
  165. });
  166. it('should escape multiple characters', function() {
  167. expect(prometheusRegularEscape("'looking'glass'")).toEqual("\\\\'looking\\\\'glass\\\\'");
  168. });
  169. });
  170. describe('Prometheus regexes escaping', function() {
  171. it('should not escape simple string', function() {
  172. expect(prometheusSpecialRegexEscape('cryptodepression')).toEqual('cryptodepression');
  173. });
  174. it('should escape $^*+?.()\\', function() {
  175. expect(prometheusSpecialRegexEscape("looking'glass")).toEqual("looking\\\\'glass");
  176. expect(prometheusSpecialRegexEscape('looking{glass')).toEqual('looking\\\\{glass');
  177. expect(prometheusSpecialRegexEscape('looking}glass')).toEqual('looking\\\\}glass');
  178. expect(prometheusSpecialRegexEscape('looking[glass')).toEqual('looking\\\\[glass');
  179. expect(prometheusSpecialRegexEscape('looking]glass')).toEqual('looking\\\\]glass');
  180. expect(prometheusSpecialRegexEscape('looking$glass')).toEqual('looking\\\\$glass');
  181. expect(prometheusSpecialRegexEscape('looking^glass')).toEqual('looking\\\\^glass');
  182. expect(prometheusSpecialRegexEscape('looking*glass')).toEqual('looking\\\\*glass');
  183. expect(prometheusSpecialRegexEscape('looking+glass')).toEqual('looking\\\\+glass');
  184. expect(prometheusSpecialRegexEscape('looking?glass')).toEqual('looking\\\\?glass');
  185. expect(prometheusSpecialRegexEscape('looking.glass')).toEqual('looking\\\\.glass');
  186. expect(prometheusSpecialRegexEscape('looking(glass')).toEqual('looking\\\\(glass');
  187. expect(prometheusSpecialRegexEscape('looking)glass')).toEqual('looking\\\\)glass');
  188. expect(prometheusSpecialRegexEscape('looking\\glass')).toEqual('looking\\\\\\\\glass');
  189. });
  190. it('should escape multiple special characters', function() {
  191. expect(prometheusSpecialRegexEscape('+looking$glass?')).toEqual('\\\\+looking\\\\$glass\\\\?');
  192. });
  193. });
  194. describe('metricFindQuery', () => {
  195. beforeEach(() => {
  196. let query = 'query_result(topk(5,rate(http_request_duration_microseconds_count[$__interval])))';
  197. ctx.templateSrvMock.replace = jest.fn();
  198. ctx.timeSrvMock.timeRange = () => {
  199. return {
  200. from: moment(1531468681),
  201. to: moment(1531489712),
  202. };
  203. };
  204. ctx.ds = new PrometheusDatasource(instanceSettings, q, ctx.backendSrvMock, ctx.templateSrvMock, ctx.timeSrvMock);
  205. ctx.ds.metricFindQuery(query);
  206. });
  207. it('should call templateSrv.replace with scopedVars', () => {
  208. expect(ctx.templateSrvMock.replace.mock.calls[0][1]).toBeDefined();
  209. });
  210. it('should have the correct range and range_ms', () => {
  211. let range = ctx.templateSrvMock.replace.mock.calls[0][1].__range;
  212. let rangeMs = ctx.templateSrvMock.replace.mock.calls[0][1].__range_ms;
  213. expect(range).toEqual({ text: '21s', value: '21s' });
  214. expect(rangeMs).toEqual({ text: 21031, value: 21031 });
  215. });
  216. it('should pass the default interval value', () => {
  217. let interval = ctx.templateSrvMock.replace.mock.calls[0][1].__interval;
  218. let intervalMs = ctx.templateSrvMock.replace.mock.calls[0][1].__interval_ms;
  219. expect(interval).toEqual({ text: '15s', value: '15s' });
  220. expect(intervalMs).toEqual({ text: 15000, value: 15000 });
  221. });
  222. });
  223. });