import "../datasource"; import { describe, beforeEach, it, expect, angularMocks } from "test/lib/common"; import helpers from "test/specs/helpers"; import CloudWatchDatasource from "../datasource"; describe("CloudWatchDatasource", function() { var ctx = new helpers.ServiceTestContext(); var instanceSettings = { jsonData: { defaultRegion: "us-east-1", access: "proxy" } }; beforeEach(angularMocks.module("grafana.core")); beforeEach(angularMocks.module("grafana.services")); beforeEach(angularMocks.module("grafana.controllers")); beforeEach(ctx.providePhase(["templateSrv", "backendSrv"])); beforeEach( angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) { ctx.$q = $q; ctx.$httpBackend = $httpBackend; ctx.$rootScope = $rootScope; ctx.ds = $injector.instantiate(CloudWatchDatasource, { instanceSettings: instanceSettings }); $httpBackend.when("GET", /\.html$/).respond(""); }) ); describe("When performing CloudWatch query", function() { var requestParams; var query = { range: { from: "now-1h", to: "now" }, rangeRaw: { from: 1483228800, to: 1483232400 }, targets: [ { region: "us-east-1", namespace: "AWS/EC2", metricName: "CPUUtilization", dimensions: { InstanceId: "i-12345678" }, statistics: ["Average"], period: "300" } ] }; var response = { timings: [null], results: { A: { error: "", refId: "A", series: [ { name: "CPUUtilization_Average", points: [ [1, 1483228800000], [2, 1483229100000], [5, 1483229700000] ], tags: { InstanceId: "i-12345678" } } ] } } }; beforeEach(function() { ctx.backendSrv.datasourceRequest = function(params) { requestParams = params.data; return ctx.$q.when({ data: response }); }; }); it("should generate the correct query", function(done) { ctx.ds.query(query).then(function() { var params = requestParams.queries[0]; expect(params.namespace).to.be(query.targets[0].namespace); expect(params.metricName).to.be(query.targets[0].metricName); expect(params.dimensions["InstanceId"]).to.be("i-12345678"); expect(params.statistics).to.eql(query.targets[0].statistics); expect(params.period).to.be(query.targets[0].period); done(); }); ctx.$rootScope.$apply(); }); it("should generate the correct query with interval variable", function(done) { ctx.templateSrv.data = { period: "10m" }; var query = { range: { from: "now-1h", to: "now" }, rangeRaw: { from: 1483228800, to: 1483232400 }, targets: [ { region: "us-east-1", namespace: "AWS/EC2", metricName: "CPUUtilization", dimensions: { InstanceId: "i-12345678" }, statistics: ["Average"], period: "[[period]]" } ] }; ctx.ds.query(query).then(function() { var params = requestParams.queries[0]; expect(params.period).to.be("600"); done(); }); ctx.$rootScope.$apply(); }); it("should return series list", function(done) { ctx.ds.query(query).then(function(result) { expect(result.data[0].target).to.be(response.results.A.series[0].name); expect(result.data[0].datapoints[0][0]).to.be( response.results.A.series[0].points[0][0] ); done(); }); ctx.$rootScope.$apply(); }); it("should generate the correct targets by expanding template variables", function() { var templateSrv = { variables: [ { name: "instance_id", options: [ { text: "i-23456789", value: "i-23456789", selected: false }, { text: "i-34567890", value: "i-34567890", selected: true } ] } ], replace: function(target, scopedVars) { if ( target === "$instance_id" && scopedVars["instance_id"]["text"] === "i-34567890" ) { return "i-34567890"; } else { return ""; } }, getVariableName: function(e) { return "instance_id"; }, variableExists: function(e) { return true; }, containsVariable: function(str, variableName) { return str.indexOf("$" + variableName) !== -1; } }; var targets = [ { region: "us-east-1", namespace: "AWS/EC2", metricName: "CPUUtilization", dimensions: { InstanceId: "$instance_id" }, statistics: ["Average"], period: 300 } ]; var result = ctx.ds.expandTemplateVariable(targets, {}, templateSrv); expect(result[0].dimensions.InstanceId).to.be("i-34567890"); }); }); describe('When query region is "default"', function() { it('should return the datasource region if empty or "default"', function() { var defaultRegion = instanceSettings.jsonData.defaultRegion; expect(ctx.ds.getActualRegion()).to.be(defaultRegion); expect(ctx.ds.getActualRegion("")).to.be(defaultRegion); expect(ctx.ds.getActualRegion("default")).to.be(defaultRegion); }); it("should return the specified region if specified", function() { expect(ctx.ds.getActualRegion("some-fake-region-1")).to.be( "some-fake-region-1" ); }); var requestParams; beforeEach(function() { ctx.ds.performTimeSeriesQuery = function(request) { requestParams = request; return ctx.$q.when({ data: {} }); }; }); it('should query for the datasource region if empty or "default"', function(done) { var query = { range: { from: "now-1h", to: "now" }, rangeRaw: { from: 1483228800, to: 1483232400 }, targets: [ { region: "default", namespace: "AWS/EC2", metricName: "CPUUtilization", dimensions: { InstanceId: "i-12345678" }, statistics: ["Average"], period: 300 } ] }; ctx.ds.query(query).then(function(result) { expect(requestParams.queries[0].region).to.be( instanceSettings.jsonData.defaultRegion ); done(); }); ctx.$rootScope.$apply(); }); }); describe("When performing CloudWatch query for extended statistics", function() { var query = { range: { from: "now-1h", to: "now" }, rangeRaw: { from: 1483228800, to: 1483232400 }, targets: [ { region: "us-east-1", namespace: "AWS/ApplicationELB", metricName: "TargetResponseTime", dimensions: { LoadBalancer: "lb", TargetGroup: "tg" }, statistics: ["p90.00"], period: 300 } ] }; var response = { timings: [null], results: { A: { error: "", refId: "A", series: [ { name: "TargetResponseTime_p90.00", points: [ [1, 1483228800000], [2, 1483229100000], [5, 1483229700000] ], tags: { LoadBalancer: "lb", TargetGroup: "tg" } } ] } } }; beforeEach(function() { ctx.backendSrv.datasourceRequest = function(params) { return ctx.$q.when({ data: response }); }; }); it("should return series list", function(done) { ctx.ds.query(query).then(function(result) { expect(result.data[0].target).to.be(response.results.A.series[0].name); expect(result.data[0].datapoints[0][0]).to.be( response.results.A.series[0].points[0][0] ); done(); }); ctx.$rootScope.$apply(); }); }); function describeMetricFindQuery(query, func) { describe("metricFindQuery " + query, () => { let scenario: any = {}; scenario.setup = setupCallback => { beforeEach(() => { setupCallback(); ctx.backendSrv.datasourceRequest = args => { scenario.request = args.data; return ctx.$q.when({ data: scenario.requestResponse }); }; ctx.ds.metricFindQuery(query).then(args => { scenario.result = args; }); ctx.$rootScope.$apply(); }); }; func(scenario); }); } describeMetricFindQuery("regions()", scenario => { scenario.setup(() => { scenario.requestResponse = { results: { metricFindQuery: { tables: [{ rows: [["us-east-1", "us-east-1"]] }] } } }; }); it("should call __GetRegions and return result", () => { expect(scenario.result[0].text).to.contain("us-east-1"); expect(scenario.request.queries[0].type).to.be("metricFindQuery"); expect(scenario.request.queries[0].subtype).to.be("regions"); }); }); describeMetricFindQuery("namespaces()", scenario => { scenario.setup(() => { scenario.requestResponse = { results: { metricFindQuery: { tables: [{ rows: [["AWS/EC2", "AWS/EC2"]] }] } } }; }); it("should call __GetNamespaces and return result", () => { expect(scenario.result[0].text).to.contain("AWS/EC2"); expect(scenario.request.queries[0].type).to.be("metricFindQuery"); expect(scenario.request.queries[0].subtype).to.be("namespaces"); }); }); describeMetricFindQuery("metrics(AWS/EC2)", scenario => { scenario.setup(() => { scenario.requestResponse = { results: { metricFindQuery: { tables: [{ rows: [["CPUUtilization", "CPUUtilization"]] }] } } }; }); it("should call __GetMetrics and return result", () => { expect(scenario.result[0].text).to.be("CPUUtilization"); expect(scenario.request.queries[0].type).to.be("metricFindQuery"); expect(scenario.request.queries[0].subtype).to.be("metrics"); }); }); describeMetricFindQuery("dimension_keys(AWS/EC2)", scenario => { scenario.setup(() => { scenario.requestResponse = { results: { metricFindQuery: { tables: [{ rows: [["InstanceId", "InstanceId"]] }] } } }; }); it("should call __GetDimensions and return result", () => { expect(scenario.result[0].text).to.be("InstanceId"); expect(scenario.request.queries[0].type).to.be("metricFindQuery"); expect(scenario.request.queries[0].subtype).to.be("dimension_keys"); }); }); describeMetricFindQuery( "dimension_values(us-east-1,AWS/EC2,CPUUtilization,InstanceId)", scenario => { scenario.setup(() => { scenario.requestResponse = { results: { metricFindQuery: { tables: [{ rows: [["i-12345678", "i-12345678"]] }] } } }; }); it("should call __ListMetrics and return result", () => { expect(scenario.result[0].text).to.contain("i-12345678"); expect(scenario.request.queries[0].type).to.be("metricFindQuery"); expect(scenario.request.queries[0].subtype).to.be("dimension_values"); }); } ); describeMetricFindQuery( "dimension_values(default,AWS/EC2,CPUUtilization,InstanceId)", scenario => { scenario.setup(() => { scenario.requestResponse = { results: { metricFindQuery: { tables: [{ rows: [["i-12345678", "i-12345678"]] }] } } }; }); it("should call __ListMetrics and return result", () => { expect(scenario.result[0].text).to.contain("i-12345678"); expect(scenario.request.queries[0].type).to.be("metricFindQuery"); expect(scenario.request.queries[0].subtype).to.be("dimension_values"); }); } ); it("should caclculate the correct period", function() { var hourSec = 60 * 60; var daySec = hourSec * 24; var start = 1483196400 * 1000; var testData: any[] = [ [ { period: 60, namespace: "AWS/EC2" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, hourSec * 3, 60 ], [ { period: null, namespace: "AWS/EC2" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, hourSec * 3, 300 ], [ { period: 60, namespace: "AWS/ELB" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, hourSec * 3, 60 ], [ { period: null, namespace: "AWS/ELB" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, hourSec * 3, 60 ], [ { period: 1, namespace: "CustomMetricsNamespace" }, { range: { from: new Date(start), to: new Date(start + (1440 - 1) * 1000) } }, hourSec * 3 - 1, 1 ], [ { period: 1, namespace: "CustomMetricsNamespace" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, hourSec * 3 - 1, 60 ], [ { period: 60, namespace: "CustomMetricsNamespace" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, hourSec * 3, 60 ], [ { period: null, namespace: "CustomMetricsNamespace" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, hourSec * 3 - 1, 60 ], [ { period: null, namespace: "CustomMetricsNamespace" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, hourSec * 3, 60 ], [ { period: null, namespace: "CustomMetricsNamespace" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, daySec * 15, 60 ], [ { period: null, namespace: "CustomMetricsNamespace" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, daySec * 63, 300 ], [ { period: null, namespace: "CustomMetricsNamespace" }, { range: { from: new Date(start), to: new Date(start + 3600 * 1000) } }, daySec * 455, 3600 ] ]; for (let t of testData) { let target = t[0]; let options = t[1]; let now = new Date(options.range.from.valueOf() + t[2] * 1000); let expected = t[3]; let actual = ctx.ds.getPeriod(target, options, now); expect(actual).to.be(expected); } }); });