_datasource.jest.ts 28 KB

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