datasource.jest.ts 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560
  1. import '../datasource';
  2. //import { describe, beforeEach, it, expect, angularMocks } from 'test/lib/common';
  3. import { TemplateSrvStub, jestTimeSrvStub } from 'test/specs/helpers';
  4. import CloudWatchDatasource from '../datasource';
  5. import 'app/features/dashboard/time_srv';
  6. describe('CloudWatchDatasource', function() {
  7. let instanceSettings = {
  8. jsonData: { defaultRegion: 'us-east-1', access: 'proxy' },
  9. };
  10. let templateSrv = new TemplateSrvStub();
  11. let timeSrv = new jestTimeSrvStub();
  12. let backendSrv = {};
  13. let ctx = <any>{
  14. backendSrv,
  15. templateSrv
  16. };
  17. beforeEach(() => {
  18. ctx.ds = new CloudWatchDatasource(instanceSettings, {}, backendSrv, templateSrv, timeSrv);
  19. });
  20. // beforeEach(angularMocks.module('grafana.core'));
  21. // beforeEach(angularMocks.module('grafana.services'));
  22. // beforeEach(angularMocks.module('grafana.controllers'));
  23. // beforeEach(ctx.providePhase(['templateSrv', 'backendSrv']));
  24. // beforeEach(ctx.createService('timeSrv'));
  25. // beforeEach(
  26. // angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
  27. // ctx.$q = $q;
  28. // ctx.$httpBackend = $httpBackend;
  29. // ctx.$rootScope = $rootScope;
  30. // ctx.ds = $injector.instantiate(CloudWatchDatasource, {
  31. // instanceSettings: instanceSettings,
  32. // });
  33. // $httpBackend.when('GET', /\.html$/).respond('');
  34. // })
  35. // );
  36. describe('When performing CloudWatch query', function() {
  37. var requestParams;
  38. var query = {
  39. range: { from: 'now-1h', to: 'now' },
  40. rangeRaw: { from: 1483228800, to: 1483232400 },
  41. targets: [
  42. {
  43. region: 'us-east-1',
  44. namespace: 'AWS/EC2',
  45. metricName: 'CPUUtilization',
  46. dimensions: {
  47. InstanceId: 'i-12345678',
  48. },
  49. statistics: ['Average'],
  50. period: '300',
  51. },
  52. ],
  53. };
  54. var response = {
  55. timings: [null],
  56. results: {
  57. A: {
  58. error: '',
  59. refId: 'A',
  60. series: [
  61. {
  62. name: 'CPUUtilization_Average',
  63. points: [[1, 1483228800000], [2, 1483229100000], [5, 1483229700000]],
  64. tags: {
  65. InstanceId: 'i-12345678',
  66. },
  67. },
  68. ],
  69. },
  70. },
  71. };
  72. beforeEach(async () => {
  73. ctx.backendSrv.datasourceRequest = await jest.fn((params) => {
  74. requestParams = params.data;
  75. return Promise.resolve({ data: response });
  76. });
  77. });
  78. it('should generate the correct query', function(done) {
  79. ctx.ds.query(query).then(function() {
  80. var params = requestParams.queries[0];
  81. expect(params.namespace).toBe(query.targets[0].namespace);
  82. expect(params.metricName).toBe(query.targets[0].metricName);
  83. expect(params.dimensions['InstanceId']).toBe('i-12345678');
  84. expect(params.statistics).toEqual(query.targets[0].statistics);
  85. expect(params.period).toBe(query.targets[0].period);
  86. done();
  87. });
  88. //ctx.$rootScope.$apply();
  89. });
  90. it('should generate the correct query with interval variable', function(done) {
  91. ctx.templateSrv.data = {
  92. period: '10m',
  93. };
  94. var query = {
  95. range: { from: 'now-1h', to: 'now' },
  96. rangeRaw: { from: 1483228800, to: 1483232400 },
  97. targets: [
  98. {
  99. region: 'us-east-1',
  100. namespace: 'AWS/EC2',
  101. metricName: 'CPUUtilization',
  102. dimensions: {
  103. InstanceId: 'i-12345678',
  104. },
  105. statistics: ['Average'],
  106. period: '[[period]]',
  107. },
  108. ],
  109. };
  110. ctx.ds.query(query).then(function() {
  111. var params = requestParams.queries[0];
  112. expect(params.period).toBe('600');
  113. done();
  114. });
  115. //ctx.$rootScope.$apply();
  116. });
  117. it('should return series list', function(done) {
  118. ctx.ds.query(query).then(function(result) {
  119. expect(result.data[0].target).toBe(response.results.A.series[0].name);
  120. expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]);
  121. done();
  122. });
  123. //ctx.$rootScope.$apply();
  124. });
  125. it('should generate the correct targets by expanding template variables', function() {
  126. var templateSrv = {
  127. variables: [
  128. {
  129. name: 'instance_id',
  130. options: [
  131. { text: 'i-23456789', value: 'i-23456789', selected: false },
  132. { text: 'i-34567890', value: 'i-34567890', selected: true },
  133. ],
  134. current: {
  135. text: 'i-34567890',
  136. value: 'i-34567890',
  137. },
  138. },
  139. ],
  140. replace: function(target, scopedVars) {
  141. if (target === '$instance_id' && scopedVars['instance_id']['text'] === 'i-34567890') {
  142. return 'i-34567890';
  143. } else {
  144. return '';
  145. }
  146. },
  147. getVariableName: function(e) {
  148. return 'instance_id';
  149. },
  150. variableExists: function(e) {
  151. return true;
  152. },
  153. containsVariable: function(str, variableName) {
  154. return str.indexOf('$' + variableName) !== -1;
  155. },
  156. };
  157. var targets = [
  158. {
  159. region: 'us-east-1',
  160. namespace: 'AWS/EC2',
  161. metricName: 'CPUUtilization',
  162. dimensions: {
  163. InstanceId: '$instance_id',
  164. },
  165. statistics: ['Average'],
  166. period: 300,
  167. },
  168. ];
  169. var result = ctx.ds.expandTemplateVariable(targets, {}, templateSrv);
  170. expect(result[0].dimensions.InstanceId).toBe('i-34567890');
  171. });
  172. it('should generate the correct targets by expanding template variables from url', function() {
  173. var templateSrv = {
  174. variables: [
  175. {
  176. name: 'instance_id',
  177. options: [
  178. { text: 'i-23456789', value: 'i-23456789', selected: false },
  179. { text: 'i-34567890', value: 'i-34567890', selected: false },
  180. ],
  181. current: 'i-45678901',
  182. },
  183. ],
  184. replace: function(target, scopedVars) {
  185. if (target === '$instance_id') {
  186. return 'i-45678901';
  187. } else {
  188. return '';
  189. }
  190. },
  191. getVariableName: function(e) {
  192. return 'instance_id';
  193. },
  194. variableExists: function(e) {
  195. return true;
  196. },
  197. containsVariable: function(str, variableName) {
  198. return str.indexOf('$' + variableName) !== -1;
  199. },
  200. };
  201. var targets = [
  202. {
  203. region: 'us-east-1',
  204. namespace: 'AWS/EC2',
  205. metricName: 'CPUUtilization',
  206. dimensions: {
  207. InstanceId: '$instance_id',
  208. },
  209. statistics: ['Average'],
  210. period: 300,
  211. },
  212. ];
  213. var result = ctx.ds.expandTemplateVariable(targets, {}, templateSrv);
  214. expect(result[0].dimensions.InstanceId).toBe('i-45678901');
  215. });
  216. });
  217. describe('When query region is "default"', function() {
  218. it('should return the datasource region if empty or "default"', function() {
  219. var defaultRegion = instanceSettings.jsonData.defaultRegion;
  220. expect(ctx.ds.getActualRegion()).toBe(defaultRegion);
  221. expect(ctx.ds.getActualRegion('')).toBe(defaultRegion);
  222. expect(ctx.ds.getActualRegion('default')).toBe(defaultRegion);
  223. });
  224. it('should return the specified region if specified', function() {
  225. expect(ctx.ds.getActualRegion('some-fake-region-1')).toBe('some-fake-region-1');
  226. });
  227. var requestParams;
  228. beforeEach(function() {
  229. ctx.ds.performTimeSeriesQuery = jest.fn((request) => {
  230. requestParams = request;
  231. return Promise.resolve({ data: {} });
  232. });
  233. });
  234. it('should query for the datasource region if empty or "default"', function(done) {
  235. var query = {
  236. range: { from: 'now-1h', to: 'now' },
  237. rangeRaw: { from: 1483228800, to: 1483232400 },
  238. targets: [
  239. {
  240. region: 'default',
  241. namespace: 'AWS/EC2',
  242. metricName: 'CPUUtilization',
  243. dimensions: {
  244. InstanceId: 'i-12345678',
  245. },
  246. statistics: ['Average'],
  247. period: 300,
  248. },
  249. ],
  250. };
  251. ctx.ds.query(query).then(function(result) {
  252. expect(requestParams.queries[0].region).toBe(instanceSettings.jsonData.defaultRegion);
  253. done();
  254. });
  255. //ctx.$rootScope.$apply();
  256. });
  257. });
  258. describe('When performing CloudWatch query for extended statistics', function() {
  259. var query = {
  260. range: { from: 'now-1h', to: 'now' },
  261. rangeRaw: { from: 1483228800, to: 1483232400 },
  262. targets: [
  263. {
  264. region: 'us-east-1',
  265. namespace: 'AWS/ApplicationELB',
  266. metricName: 'TargetResponseTime',
  267. dimensions: {
  268. LoadBalancer: 'lb',
  269. TargetGroup: 'tg',
  270. },
  271. statistics: ['p90.00'],
  272. period: 300,
  273. },
  274. ],
  275. };
  276. var response = {
  277. timings: [null],
  278. results: {
  279. A: {
  280. error: '',
  281. refId: 'A',
  282. series: [
  283. {
  284. name: 'TargetResponseTime_p90.00',
  285. points: [[1, 1483228800000], [2, 1483229100000], [5, 1483229700000]],
  286. tags: {
  287. LoadBalancer: 'lb',
  288. TargetGroup: 'tg',
  289. },
  290. },
  291. ],
  292. },
  293. },
  294. };
  295. beforeEach(function() {
  296. ctx.backendSrv.datasourceRequest = jest.fn((params) => {
  297. return Promise.resolve({ data: response });
  298. });
  299. });
  300. it('should return series list', function(done) {
  301. ctx.ds.query(query).then(function(result) {
  302. expect(result.data[0].target).toBe(response.results.A.series[0].name);
  303. expect(result.data[0].datapoints[0][0]).toBe(response.results.A.series[0].points[0][0]);
  304. done();
  305. });
  306. //ctx.$rootScope.$apply();
  307. });
  308. });
  309. function describeMetricFindQuery(query, func) {
  310. describe('metricFindQuery ' + query, () => {
  311. let scenario: any = {};
  312. scenario.setup = setupCallback => {
  313. beforeEach(() => {
  314. setupCallback();
  315. ctx.backendSrv.datasourceRequest = jest.fn((args) => {
  316. scenario.request = args.data;
  317. return Promise.resolve({ data: scenario.requestResponse });
  318. });
  319. ctx.ds.metricFindQuery(query).then(args => {
  320. scenario.result = args;
  321. });
  322. //ctx.$rootScope.$apply();
  323. });
  324. };
  325. func(scenario);
  326. });
  327. }
  328. describeMetricFindQuery('regions()', scenario => {
  329. scenario.setup(() => {
  330. scenario.requestResponse = {
  331. results: {
  332. metricFindQuery: {
  333. tables: [{ rows: [['us-east-1', 'us-east-1']] }],
  334. },
  335. },
  336. };
  337. });
  338. it('should call __GetRegions and return result', () => {
  339. expect(scenario.result[0].text).toContain('us-east-1');
  340. expect(scenario.request.queries[0].type).toBe('metricFindQuery');
  341. expect(scenario.request.queries[0].subtype).toBe('regions');
  342. });
  343. });
  344. describeMetricFindQuery('namespaces()', scenario => {
  345. scenario.setup(() => {
  346. scenario.requestResponse = {
  347. results: {
  348. metricFindQuery: {
  349. tables: [{ rows: [['AWS/EC2', 'AWS/EC2']] }],
  350. },
  351. },
  352. };
  353. });
  354. it('should call __GetNamespaces and return result', () => {
  355. expect(scenario.result[0].text).toContain('AWS/EC2');
  356. expect(scenario.request.queries[0].type).toBe('metricFindQuery');
  357. expect(scenario.request.queries[0].subtype).toBe('namespaces');
  358. });
  359. });
  360. describeMetricFindQuery('metrics(AWS/EC2)', scenario => {
  361. scenario.setup(() => {
  362. scenario.requestResponse = {
  363. results: {
  364. metricFindQuery: {
  365. tables: [{ rows: [['CPUUtilization', 'CPUUtilization']] }],
  366. },
  367. },
  368. };
  369. });
  370. it('should call __GetMetrics and return result', () => {
  371. expect(scenario.result[0].text).toBe('CPUUtilization');
  372. expect(scenario.request.queries[0].type).toBe('metricFindQuery');
  373. expect(scenario.request.queries[0].subtype).toBe('metrics');
  374. });
  375. });
  376. describeMetricFindQuery('dimension_keys(AWS/EC2)', scenario => {
  377. scenario.setup(() => {
  378. scenario.requestResponse = {
  379. results: {
  380. metricFindQuery: {
  381. tables: [{ rows: [['InstanceId', 'InstanceId']] }],
  382. },
  383. },
  384. };
  385. });
  386. it('should call __GetDimensions and return result', () => {
  387. expect(scenario.result[0].text).toBe('InstanceId');
  388. expect(scenario.request.queries[0].type).toBe('metricFindQuery');
  389. expect(scenario.request.queries[0].subtype).toBe('dimension_keys');
  390. });
  391. });
  392. describeMetricFindQuery('dimension_values(us-east-1,AWS/EC2,CPUUtilization,InstanceId)', scenario => {
  393. scenario.setup(() => {
  394. scenario.requestResponse = {
  395. results: {
  396. metricFindQuery: {
  397. tables: [{ rows: [['i-12345678', 'i-12345678']] }],
  398. },
  399. },
  400. };
  401. });
  402. it('should call __ListMetrics and return result', () => {
  403. expect(scenario.result[0].text).toContain('i-12345678');
  404. expect(scenario.request.queries[0].type).toBe('metricFindQuery');
  405. expect(scenario.request.queries[0].subtype).toBe('dimension_values');
  406. });
  407. });
  408. describeMetricFindQuery('dimension_values(default,AWS/EC2,CPUUtilization,InstanceId)', scenario => {
  409. scenario.setup(() => {
  410. scenario.requestResponse = {
  411. results: {
  412. metricFindQuery: {
  413. tables: [{ rows: [['i-12345678', 'i-12345678']] }],
  414. },
  415. },
  416. };
  417. });
  418. it('should call __ListMetrics and return result', () => {
  419. expect(scenario.result[0].text).toContain('i-12345678');
  420. expect(scenario.request.queries[0].type).toBe('metricFindQuery');
  421. expect(scenario.request.queries[0].subtype).toBe('dimension_values');
  422. });
  423. });
  424. it('should caclculate the correct period', function() {
  425. var hourSec = 60 * 60;
  426. var daySec = hourSec * 24;
  427. var start = 1483196400 * 1000;
  428. var testData: any[] = [
  429. [
  430. { period: 60, namespace: 'AWS/EC2' },
  431. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  432. hourSec * 3,
  433. 60,
  434. ],
  435. [
  436. { period: null, namespace: 'AWS/EC2' },
  437. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  438. hourSec * 3,
  439. 300,
  440. ],
  441. [
  442. { period: 60, namespace: 'AWS/ELB' },
  443. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  444. hourSec * 3,
  445. 60,
  446. ],
  447. [
  448. { period: null, namespace: 'AWS/ELB' },
  449. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  450. hourSec * 3,
  451. 60,
  452. ],
  453. [
  454. { period: 1, namespace: 'CustomMetricsNamespace' },
  455. {
  456. range: {
  457. from: new Date(start),
  458. to: new Date(start + (1440 - 1) * 1000),
  459. },
  460. },
  461. hourSec * 3 - 1,
  462. 1,
  463. ],
  464. [
  465. { period: 1, namespace: 'CustomMetricsNamespace' },
  466. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  467. hourSec * 3 - 1,
  468. 60,
  469. ],
  470. [
  471. { period: 60, namespace: 'CustomMetricsNamespace' },
  472. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  473. hourSec * 3,
  474. 60,
  475. ],
  476. [
  477. { period: null, namespace: 'CustomMetricsNamespace' },
  478. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  479. hourSec * 3 - 1,
  480. 60,
  481. ],
  482. [
  483. { period: null, namespace: 'CustomMetricsNamespace' },
  484. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  485. hourSec * 3,
  486. 60,
  487. ],
  488. [
  489. { period: null, namespace: 'CustomMetricsNamespace' },
  490. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  491. daySec * 15,
  492. 60,
  493. ],
  494. [
  495. { period: null, namespace: 'CustomMetricsNamespace' },
  496. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  497. daySec * 63,
  498. 300,
  499. ],
  500. [
  501. { period: null, namespace: 'CustomMetricsNamespace' },
  502. { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } },
  503. daySec * 455,
  504. 3600,
  505. ],
  506. ];
  507. for (let t of testData) {
  508. let target = t[0];
  509. let options = t[1];
  510. let now = new Date(options.range.from.valueOf() + t[2] * 1000);
  511. let expected = t[3];
  512. let actual = ctx.ds.getPeriod(target, options, now);
  513. expect(actual).toBe(expected);
  514. }
  515. });
  516. });