_datasource.jest.ts 28 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773
  1. import moment from 'moment';
  2. import { PrometheusDatasource } from '../datasource';
  3. import $q from 'q';
  4. const SECOND = 1000;
  5. const MINUTE = 60 * SECOND;
  6. const HOUR = 60 * MINUTE;
  7. const time = ({ hours = 0, seconds = 0, minutes = 0 }) => moment(hours * HOUR + minutes * MINUTE + seconds * SECOND);
  8. let ctx = <any>{};
  9. let instanceSettings = {
  10. url: 'proxied',
  11. directUrl: 'direct',
  12. user: 'test',
  13. password: 'mupp',
  14. jsonData: { httpMethod: 'GET' },
  15. };
  16. let backendSrv = <any>{
  17. datasourceRequest: jest.fn(),
  18. };
  19. let templateSrv = {
  20. replace: jest.fn(str => str),
  21. };
  22. let timeSrv = {
  23. timeRange: () => {
  24. return { to: { diff: () => 2000 }, from: '' };
  25. },
  26. };
  27. describe('PrometheusDatasource', function() {
  28. // beforeEach(angularMocks.module('grafana.core'));
  29. // beforeEach(angularMocks.module('grafana.services'));
  30. // beforeEach(ctx.providePhase(['timeSrv']));
  31. // beforeEach(
  32. // angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
  33. // ctx.$q = $q;
  34. // ctx.$httpBackend = $httpBackend;
  35. // ctx.$rootScope = $rootScope;
  36. // ctx.ds = $injector.instantiate(PrometheusDatasource, {
  37. // instanceSettings: instanceSettings,
  38. // });
  39. // $httpBackend.when('GET', /\.html$/).respond('');
  40. // })
  41. // );
  42. describe('When querying prometheus with one target using query editor target spec', async () => {
  43. var results;
  44. var query = {
  45. range: { from: time({ seconds: 63 }), to: time({ seconds: 183 }) },
  46. targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
  47. interval: '60s',
  48. };
  49. // Interval alignment with step
  50. var urlExpected =
  51. 'proxied/api/v1/query_range?query=' + encodeURIComponent('test{job="testjob"}') + '&start=60&end=240&step=60';
  52. var response = {
  53. data: {
  54. status: 'success',
  55. data: {
  56. resultType: 'matrix',
  57. result: [
  58. {
  59. metric: { __name__: 'test', job: 'testjob' },
  60. values: [[60, '3846']],
  61. },
  62. ],
  63. },
  64. },
  65. };
  66. beforeEach(async () => {
  67. // ctx.$httpBackend.expect('GET', urlExpected).respond(response);
  68. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  69. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  70. await ctx.ds.query(query).then(function(data) {
  71. results = data;
  72. });
  73. // ctx.$httpBackend.flush();
  74. });
  75. it('should generate the correct query', function() {
  76. // ctx.$httpBackend.verifyNoOutstandingExpectation();
  77. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  78. expect(res.method).toBe('GET');
  79. expect(res.url).toBe(urlExpected);
  80. });
  81. it('should return series list', async () => {
  82. expect(results.data.length).toBe(1);
  83. expect(results.data[0].target).toBe('test{job="testjob"}');
  84. });
  85. });
  86. describe('When querying prometheus with one target which return multiple series', function() {
  87. var results;
  88. var start = 60;
  89. var end = 360;
  90. var step = 60;
  91. // var urlExpected =
  92. // 'proxied/api/v1/query_range?query=' +
  93. // encodeURIComponent('test{job="testjob"}') +
  94. // '&start=' +
  95. // start +
  96. // '&end=' +
  97. // end +
  98. // '&step=' +
  99. // step;
  100. var query = {
  101. range: { from: time({ seconds: start }), to: time({ seconds: end }) },
  102. targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
  103. interval: '60s',
  104. };
  105. var response = {
  106. status: 'success',
  107. data: {
  108. data: {
  109. resultType: 'matrix',
  110. result: [
  111. {
  112. metric: { __name__: 'test', job: 'testjob', series: 'series 1' },
  113. values: [[start + step * 1, '3846'], [start + step * 3, '3847'], [end - step * 1, '3848']],
  114. },
  115. {
  116. metric: { __name__: 'test', job: 'testjob', series: 'series 2' },
  117. values: [[start + step * 2, '4846']],
  118. },
  119. ],
  120. },
  121. },
  122. };
  123. beforeEach(async () => {
  124. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  125. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  126. await ctx.ds.query(query).then(function(data) {
  127. results = data;
  128. });
  129. });
  130. it('should be same length', function() {
  131. expect(results.data.length).toBe(2);
  132. expect(results.data[0].datapoints.length).toBe((end - start) / step + 1);
  133. expect(results.data[1].datapoints.length).toBe((end - start) / step + 1);
  134. });
  135. it('should fill null until first datapoint in response', function() {
  136. expect(results.data[0].datapoints[0][1]).toBe(start * 1000);
  137. expect(results.data[0].datapoints[0][0]).toBe(null);
  138. expect(results.data[0].datapoints[1][1]).toBe((start + step * 1) * 1000);
  139. expect(results.data[0].datapoints[1][0]).toBe(3846);
  140. });
  141. it('should fill null after last datapoint in response', function() {
  142. var length = (end - start) / step + 1;
  143. expect(results.data[0].datapoints[length - 2][1]).toBe((end - step * 1) * 1000);
  144. expect(results.data[0].datapoints[length - 2][0]).toBe(3848);
  145. expect(results.data[0].datapoints[length - 1][1]).toBe(end * 1000);
  146. expect(results.data[0].datapoints[length - 1][0]).toBe(null);
  147. });
  148. it('should fill null at gap between series', function() {
  149. expect(results.data[0].datapoints[2][1]).toBe((start + step * 2) * 1000);
  150. expect(results.data[0].datapoints[2][0]).toBe(null);
  151. expect(results.data[1].datapoints[1][1]).toBe((start + step * 1) * 1000);
  152. expect(results.data[1].datapoints[1][0]).toBe(null);
  153. expect(results.data[1].datapoints[3][1]).toBe((start + step * 3) * 1000);
  154. expect(results.data[1].datapoints[3][0]).toBe(null);
  155. });
  156. });
  157. describe('When querying prometheus with one target and instant = true', function() {
  158. var results;
  159. var urlExpected = 'proxied/api/v1/query?query=' + encodeURIComponent('test{job="testjob"}') + '&time=123';
  160. var query = {
  161. range: { from: time({ seconds: 63 }), to: time({ seconds: 123 }) },
  162. targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
  163. interval: '60s',
  164. };
  165. var response = {
  166. status: 'success',
  167. data: {
  168. data: {
  169. resultType: 'vector',
  170. result: [
  171. {
  172. metric: { __name__: 'test', job: 'testjob' },
  173. value: [123, '3846'],
  174. },
  175. ],
  176. },
  177. },
  178. };
  179. beforeEach(async () => {
  180. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  181. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  182. await ctx.ds.query(query).then(function(data) {
  183. results = data;
  184. });
  185. });
  186. it('should generate the correct query', function() {
  187. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  188. expect(res.method).toBe('GET');
  189. expect(res.url).toBe(urlExpected);
  190. });
  191. it('should return series list', function() {
  192. expect(results.data.length).toBe(1);
  193. expect(results.data[0].target).toBe('test{job="testjob"}');
  194. });
  195. });
  196. describe('When performing annotationQuery', function() {
  197. var results;
  198. // var urlExpected =
  199. // 'proxied/api/v1/query_range?query=' +
  200. // encodeURIComponent('ALERTS{alertstate="firing"}') +
  201. // '&start=60&end=180&step=60';
  202. var options = {
  203. annotation: {
  204. expr: 'ALERTS{alertstate="firing"}',
  205. tagKeys: 'job',
  206. titleFormat: '{{alertname}}',
  207. textFormat: '{{instance}}',
  208. },
  209. range: {
  210. from: time({ seconds: 63 }),
  211. to: time({ seconds: 123 }),
  212. },
  213. };
  214. var response = {
  215. status: 'success',
  216. data: {
  217. data: {
  218. resultType: 'matrix',
  219. result: [
  220. {
  221. metric: {
  222. __name__: 'ALERTS',
  223. alertname: 'InstanceDown',
  224. alertstate: 'firing',
  225. instance: 'testinstance',
  226. job: 'testjob',
  227. },
  228. values: [[123, '1']],
  229. },
  230. ],
  231. },
  232. },
  233. };
  234. beforeEach(async () => {
  235. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  236. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  237. await ctx.ds.annotationQuery(options).then(function(data) {
  238. results = data;
  239. });
  240. });
  241. it('should return annotation list', function() {
  242. // ctx.$rootScope.$apply();
  243. expect(results.length).toBe(1);
  244. expect(results[0].tags).toContain('testjob');
  245. expect(results[0].title).toBe('InstanceDown');
  246. expect(results[0].text).toBe('testinstance');
  247. expect(results[0].time).toBe(123 * 1000);
  248. });
  249. });
  250. describe('When resultFormat is table and instant = true', function() {
  251. var results;
  252. var urlExpected = 'proxied/api/v1/query?query=' + encodeURIComponent('test{job="testjob"}') + '&time=123';
  253. var query = {
  254. range: { from: time({ seconds: 63 }), to: time({ seconds: 123 }) },
  255. targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
  256. interval: '60s',
  257. };
  258. var response = {
  259. status: 'success',
  260. data: {
  261. data: {
  262. resultType: 'vector',
  263. result: [
  264. {
  265. metric: { __name__: 'test', job: 'testjob' },
  266. value: [123, '3846'],
  267. },
  268. ],
  269. },
  270. },
  271. };
  272. beforeEach(async () => {
  273. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  274. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  275. await ctx.ds.query(query).then(function(data) {
  276. results = data;
  277. });
  278. });
  279. it('should return result', () => {
  280. expect(results).not.toBe(null);
  281. });
  282. });
  283. describe('The "step" query parameter', function() {
  284. var response = {
  285. status: 'success',
  286. data: {
  287. data: {
  288. resultType: 'matrix',
  289. result: [],
  290. },
  291. },
  292. };
  293. it('should be min interval when greater than auto interval', async () => {
  294. let query = {
  295. // 6 minute range
  296. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  297. targets: [
  298. {
  299. expr: 'test',
  300. interval: '10s',
  301. },
  302. ],
  303. interval: '5s',
  304. };
  305. let urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10';
  306. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  307. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  308. await ctx.ds.query(query);
  309. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  310. expect(res.method).toBe('GET');
  311. expect(res.url).toBe(urlExpected);
  312. });
  313. it('step should never go below 1', async () => {
  314. var query = {
  315. // 6 minute range
  316. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  317. targets: [{ expr: 'test' }],
  318. interval: '100ms',
  319. };
  320. var urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=1';
  321. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  322. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  323. await ctx.ds.query(query);
  324. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  325. expect(res.method).toBe('GET');
  326. expect(res.url).toBe(urlExpected);
  327. });
  328. it('should be auto interval when greater than min interval', async () => {
  329. var query = {
  330. // 6 minute range
  331. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  332. targets: [
  333. {
  334. expr: 'test',
  335. interval: '5s',
  336. },
  337. ],
  338. interval: '10s',
  339. };
  340. var urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10';
  341. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  342. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  343. await ctx.ds.query(query);
  344. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  345. expect(res.method).toBe('GET');
  346. expect(res.url).toBe(urlExpected);
  347. });
  348. it('should result in querying fewer than 11000 data points', async () => {
  349. var query = {
  350. // 6 hour range
  351. range: { from: time({ hours: 1 }), to: time({ hours: 7 }) },
  352. targets: [{ expr: 'test' }],
  353. interval: '1s',
  354. };
  355. var end = 7 * 60 * 60;
  356. var start = 60 * 60;
  357. var urlExpected = 'proxied/api/v1/query_range?query=test&start=' + start + '&end=' + end + '&step=2';
  358. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  359. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  360. await ctx.ds.query(query);
  361. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  362. expect(res.method).toBe('GET');
  363. expect(res.url).toBe(urlExpected);
  364. });
  365. it('should not apply min interval when interval * intervalFactor greater', async () => {
  366. var query = {
  367. // 6 minute range
  368. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  369. targets: [
  370. {
  371. expr: 'test',
  372. interval: '10s',
  373. intervalFactor: 10,
  374. },
  375. ],
  376. interval: '5s',
  377. };
  378. // times get rounded up to interval
  379. var urlExpected = 'proxied/api/v1/query_range?query=test&start=50&end=450&step=50';
  380. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  381. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  382. await ctx.ds.query(query);
  383. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  384. expect(res.method).toBe('GET');
  385. expect(res.url).toBe(urlExpected);
  386. });
  387. it('should apply min interval when interval * intervalFactor smaller', async () => {
  388. var query = {
  389. // 6 minute range
  390. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  391. targets: [
  392. {
  393. expr: 'test',
  394. interval: '15s',
  395. intervalFactor: 2,
  396. },
  397. ],
  398. interval: '5s',
  399. };
  400. var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=60&end=420&step=15';
  401. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  402. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  403. await ctx.ds.query(query);
  404. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  405. expect(res.method).toBe('GET');
  406. expect(res.url).toBe(urlExpected);
  407. });
  408. it('should apply intervalFactor to auto interval when greater', async () => {
  409. var query = {
  410. // 6 minute range
  411. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  412. targets: [
  413. {
  414. expr: 'test',
  415. interval: '5s',
  416. intervalFactor: 10,
  417. },
  418. ],
  419. interval: '10s',
  420. };
  421. // times get aligned to interval
  422. var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=0&end=500&step=100';
  423. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  424. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  425. await ctx.ds.query(query);
  426. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  427. expect(res.method).toBe('GET');
  428. expect(res.url).toBe(urlExpected);
  429. });
  430. it('should not not be affected by the 11000 data points limit when large enough', async () => {
  431. var query = {
  432. // 1 week range
  433. range: { from: time({}), to: time({ hours: 7 * 24 }) },
  434. targets: [
  435. {
  436. expr: 'test',
  437. intervalFactor: 10,
  438. },
  439. ],
  440. interval: '10s',
  441. };
  442. var end = 7 * 24 * 60 * 60;
  443. var start = 0;
  444. var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=100';
  445. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  446. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  447. await ctx.ds.query(query);
  448. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  449. expect(res.method).toBe('GET');
  450. expect(res.url).toBe(urlExpected);
  451. });
  452. it('should be determined by the 11000 data points limit when too small', async () => {
  453. var query = {
  454. // 1 week range
  455. range: { from: time({}), to: time({ hours: 7 * 24 }) },
  456. targets: [
  457. {
  458. expr: 'test',
  459. intervalFactor: 10,
  460. },
  461. ],
  462. interval: '5s',
  463. };
  464. var end = 7 * 24 * 60 * 60;
  465. var start = 0;
  466. var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=60';
  467. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  468. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  469. await ctx.ds.query(query);
  470. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  471. expect(res.method).toBe('GET');
  472. expect(res.url).toBe(urlExpected);
  473. });
  474. });
  475. describe('The __interval and __interval_ms template variables', function() {
  476. var response = {
  477. status: 'success',
  478. data: {
  479. data: {
  480. resultType: 'matrix',
  481. result: [],
  482. },
  483. },
  484. };
  485. it('should be unchanged when auto interval is greater than min interval', async () => {
  486. var query = {
  487. // 6 minute range
  488. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  489. targets: [
  490. {
  491. expr: 'rate(test[$__interval])',
  492. interval: '5s',
  493. },
  494. ],
  495. interval: '10s',
  496. scopedVars: {
  497. __interval: { text: '10s', value: '10s' },
  498. __interval_ms: { text: 10 * 1000, value: 10 * 1000 },
  499. },
  500. };
  501. var urlExpected =
  502. 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[10s])') + '&start=60&end=420&step=10';
  503. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  504. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  505. await ctx.ds.query(query);
  506. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  507. expect(res.method).toBe('GET');
  508. expect(res.url).toBe(urlExpected);
  509. expect(query.scopedVars.__interval.text).toBe('10s');
  510. expect(query.scopedVars.__interval.value).toBe('10s');
  511. expect(query.scopedVars.__interval_ms.text).toBe(10 * 1000);
  512. expect(query.scopedVars.__interval_ms.value).toBe(10 * 1000);
  513. });
  514. it('should be min interval when it is greater than auto interval', async () => {
  515. var query = {
  516. // 6 minute range
  517. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  518. targets: [
  519. {
  520. expr: 'rate(test[$__interval])',
  521. interval: '10s',
  522. },
  523. ],
  524. interval: '5s',
  525. scopedVars: {
  526. __interval: { text: '5s', value: '5s' },
  527. __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
  528. },
  529. };
  530. var urlExpected =
  531. 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[10s])') + '&start=60&end=420&step=10';
  532. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  533. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  534. await ctx.ds.query(query);
  535. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  536. expect(res.method).toBe('GET');
  537. expect(res.url).toBe(urlExpected);
  538. expect(query.scopedVars.__interval.text).toBe('5s');
  539. expect(query.scopedVars.__interval.value).toBe('5s');
  540. expect(query.scopedVars.__interval_ms.text).toBe(5 * 1000);
  541. expect(query.scopedVars.__interval_ms.value).toBe(5 * 1000);
  542. });
  543. it('should account for intervalFactor', async () => {
  544. var query = {
  545. // 6 minute range
  546. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  547. targets: [
  548. {
  549. expr: 'rate(test[$__interval])',
  550. interval: '5s',
  551. intervalFactor: 10,
  552. },
  553. ],
  554. interval: '10s',
  555. scopedVars: {
  556. __interval: { text: '10s', value: '10s' },
  557. __interval_ms: { text: 10 * 1000, value: 10 * 1000 },
  558. },
  559. };
  560. var urlExpected =
  561. 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[100s])') + '&start=0&end=500&step=100';
  562. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  563. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  564. await ctx.ds.query(query);
  565. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  566. expect(res.method).toBe('GET');
  567. expect(res.url).toBe(urlExpected);
  568. expect(query.scopedVars.__interval.text).toBe('10s');
  569. expect(query.scopedVars.__interval.value).toBe('10s');
  570. expect(query.scopedVars.__interval_ms.text).toBe(10 * 1000);
  571. expect(query.scopedVars.__interval_ms.value).toBe(10 * 1000);
  572. });
  573. it('should be interval * intervalFactor when greater than min interval', async () => {
  574. var query = {
  575. // 6 minute range
  576. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  577. targets: [
  578. {
  579. expr: 'rate(test[$__interval])',
  580. interval: '10s',
  581. intervalFactor: 10,
  582. },
  583. ],
  584. interval: '5s',
  585. scopedVars: {
  586. __interval: { text: '5s', value: '5s' },
  587. __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
  588. },
  589. };
  590. var urlExpected =
  591. 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[50s])') + '&start=50&end=450&step=50';
  592. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  593. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  594. await ctx.ds.query(query);
  595. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  596. expect(res.method).toBe('GET');
  597. expect(res.url).toBe(urlExpected);
  598. expect(query.scopedVars.__interval.text).toBe('5s');
  599. expect(query.scopedVars.__interval.value).toBe('5s');
  600. expect(query.scopedVars.__interval_ms.text).toBe(5 * 1000);
  601. expect(query.scopedVars.__interval_ms.value).toBe(5 * 1000);
  602. });
  603. it('should be min interval when greater than interval * intervalFactor', async () => {
  604. var query = {
  605. // 6 minute range
  606. range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
  607. targets: [
  608. {
  609. expr: 'rate(test[$__interval])',
  610. interval: '15s',
  611. intervalFactor: 2,
  612. },
  613. ],
  614. interval: '5s',
  615. scopedVars: {
  616. __interval: { text: '5s', value: '5s' },
  617. __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
  618. },
  619. };
  620. var urlExpected =
  621. 'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[15s])') + '&start=60&end=420&step=15';
  622. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  623. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  624. await ctx.ds.query(query);
  625. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  626. expect(res.method).toBe('GET');
  627. expect(res.url).toBe(urlExpected);
  628. expect(query.scopedVars.__interval.text).toBe('5s');
  629. expect(query.scopedVars.__interval.value).toBe('5s');
  630. expect(query.scopedVars.__interval_ms.text).toBe(5 * 1000);
  631. expect(query.scopedVars.__interval_ms.value).toBe(5 * 1000);
  632. });
  633. it('should be determined by the 11000 data points limit, accounting for intervalFactor', async () => {
  634. var query = {
  635. // 1 week range
  636. range: { from: time({}), to: time({ hours: 7 * 24 }) },
  637. targets: [
  638. {
  639. expr: 'rate(test[$__interval])',
  640. intervalFactor: 10,
  641. },
  642. ],
  643. interval: '5s',
  644. scopedVars: {
  645. __interval: { text: '5s', value: '5s' },
  646. __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
  647. },
  648. };
  649. var end = 7 * 24 * 60 * 60;
  650. var start = 0;
  651. var urlExpected =
  652. 'proxied/api/v1/query_range?query=' +
  653. encodeURIComponent('rate(test[60s])') +
  654. '&start=' +
  655. start +
  656. '&end=' +
  657. end +
  658. '&step=60';
  659. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  660. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  661. await ctx.ds.query(query);
  662. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  663. expect(res.method).toBe('GET');
  664. expect(res.url).toBe(urlExpected);
  665. expect(query.scopedVars.__interval.text).toBe('5s');
  666. expect(query.scopedVars.__interval.value).toBe('5s');
  667. expect(query.scopedVars.__interval_ms.text).toBe(5 * 1000);
  668. expect(query.scopedVars.__interval_ms.value).toBe(5 * 1000);
  669. });
  670. });
  671. });
  672. describe('PrometheusDatasource for POST', function() {
  673. // var ctx = new helpers.ServiceTestContext();
  674. let instanceSettings = {
  675. url: 'proxied',
  676. directUrl: 'direct',
  677. user: 'test',
  678. password: 'mupp',
  679. jsonData: { httpMethod: 'POST' },
  680. };
  681. // beforeEach(angularMocks.module('grafana.core'));
  682. // beforeEach(angularMocks.module('grafana.services'));
  683. // beforeEach(ctx.providePhase(['timeSrv']));
  684. // beforeEach(
  685. // // angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
  686. // // ctx.$q = $q;
  687. // // ctx.$httpBackend = $httpBackend;
  688. // // ctx.$rootScope = $rootScope;
  689. // // ctx.ds = $injector.instantiate(PrometheusDatasource, { instanceSettings: instanceSettings });
  690. // // $httpBackend.when('GET', /\.html$/).respond('');
  691. // // })
  692. // );
  693. describe('When querying prometheus with one target using query editor target spec', function() {
  694. var results;
  695. var urlExpected = 'proxied/api/v1/query_range';
  696. var dataExpected = {
  697. query: 'test{job="testjob"}',
  698. start: 1 * 60,
  699. end: 3 * 60,
  700. step: 60,
  701. };
  702. var query = {
  703. range: { from: time({ minutes: 1, seconds: 3 }), to: time({ minutes: 2, seconds: 3 }) },
  704. targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
  705. interval: '60s',
  706. };
  707. var response = {
  708. status: 'success',
  709. data: {
  710. data: {
  711. resultType: 'matrix',
  712. result: [
  713. {
  714. metric: { __name__: 'test', job: 'testjob' },
  715. values: [[2 * 60, '3846']],
  716. },
  717. ],
  718. },
  719. },
  720. };
  721. beforeEach(async () => {
  722. backendSrv.datasourceRequest = jest.fn(() => Promise.resolve(response));
  723. ctx.ds = new PrometheusDatasource(instanceSettings, $q, <any>backendSrv, templateSrv, timeSrv);
  724. await ctx.ds.query(query).then(function(data) {
  725. results = data;
  726. });
  727. });
  728. it('should generate the correct query', function() {
  729. let res = backendSrv.datasourceRequest.mock.calls[0][0];
  730. expect(res.method).toBe('POST');
  731. expect(res.url).toBe(urlExpected);
  732. expect(res.data).toEqual(dataExpected);
  733. });
  734. it('should return series list', function() {
  735. expect(results.data.length).toBe(1);
  736. expect(results.data[0].target).toBe('test{job="testjob"}');
  737. });
  738. });
  739. });