_datasource.jest.ts 28 KB

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