Beispiel #1
0
describe('timeSrv', function() {
  var ctx = new helpers.ServiceTestContext();
  var _dashboard: any = {
    time: {from: 'now-6h', to: 'now'},
  };

  beforeEach(angularMocks.module('grafana.core'));
  beforeEach(angularMocks.module('grafana.services'));
  beforeEach(ctx.createService('timeSrv'));

  beforeEach(function() {
    ctx.service.init(_dashboard);
  });

  describe('timeRange', function() {
    it('should return unparsed when parse is false', function() {
      ctx.service.setTime({from: 'now', to: 'now-1h' });
      var time = ctx.service.timeRange();
      expect(time.raw.from).to.be('now');
      expect(time.raw.to).to.be('now-1h');
    });

    it('should return parsed when parse is true', function() {
      ctx.service.setTime({from: 'now', to: 'now-1h' });
      var time = ctx.service.timeRange();
      expect(moment.isMoment(time.from)).to.be(true);
      expect(moment.isMoment(time.to)).to.be(true);
    });
  });

  describe('init time from url', function() {
    it('should handle relative times', function() {
      ctx.$location.search({from: 'now-2d', to: 'now'});
      ctx.service.init(_dashboard);
      var time = ctx.service.timeRange();
      expect(time.raw.from).to.be('now-2d');
      expect(time.raw.to).to.be('now');
    });

    it('should handle formated dates', function() {
      ctx.$location.search({from: '20140410T052010', to: '20140520T031022'});
      ctx.service.init(_dashboard);
      var time = ctx.service.timeRange(true);
      expect(time.from.valueOf()).to.equal(new Date("2014-04-10T05:20:10Z").getTime());
      expect(time.to.valueOf()).to.equal(new Date("2014-05-20T03:10:22Z").getTime());
    });

    it('should handle formated dates without time', function() {
      ctx.$location.search({from: '20140410', to: '20140520'});
      ctx.service.init(_dashboard);
      var time = ctx.service.timeRange(true);
      expect(time.from.valueOf()).to.equal(new Date("2014-04-10T00:00:00Z").getTime());
      expect(time.to.valueOf()).to.equal(new Date("2014-05-20T00:00:00Z").getTime());
    });

    it('should handle epochs', function() {
      ctx.$location.search({from: '1410337646373', to: '1410337665699'});
      ctx.service.init(_dashboard);
      var time = ctx.service.timeRange(true);
      expect(time.from.valueOf()).to.equal(1410337646373);
      expect(time.to.valueOf()).to.equal(1410337665699);
    });

    it('should handle bad dates', function() {
      ctx.$location.search({from: '20151126T00010%3C%2Fp%3E%3Cspan%20class', to: 'now'});
      _dashboard.time.from = 'now-6h';
      ctx.service.init(_dashboard);
      expect(ctx.service.time.from).to.equal('now-6h');
      expect(ctx.service.time.to).to.equal('now');
    });
  });

  describe('setTime', function() {
    it('should return disable refresh if refresh is disabled for any range', function() {
      _dashboard.refresh = false;

      ctx.service.setTime({from: '2011-01-01', to: '2015-01-01' });
      expect(_dashboard.refresh).to.be(false);
    });

    it('should restore refresh for absolute time range', function() {
      _dashboard.refresh = '30s';

      ctx.service.setTime({from: '2011-01-01', to: '2015-01-01' });
      expect(_dashboard.refresh).to.be('30s');
    });

    it('should restore refresh after relative time range is set', function() {
      _dashboard.refresh = '10s';
      ctx.service.setTime({from: moment([2011,1,1]), to: moment([2015,1,1])});
      expect(_dashboard.refresh).to.be(false);
      ctx.service.setTime({from: '2011-01-01', to: 'now' });
      expect(_dashboard.refresh).to.be('10s');
    });

    it('should keep refresh after relative time range is changed and now delay exists', function() {
      _dashboard.refresh = '10s';
      ctx.service.setTime({from: 'now-1h', to: 'now-10s' });
      expect(_dashboard.refresh).to.be('10s');
    });
  });

});
  describe('single group by with alias pattern', function() {
    var result;

    beforeEach(function() {
      targets = [{
        refId: 'A',
        metrics: [{type: 'count', id: '1'}],
        alias: '{{term @host}} {{metric}} and {{not_exist}} {{@host}}',
        bucketAggs: [
        {type: 'terms', field: '@host', id: '2'},
        {type: 'date_histogram', field: '@timestamp', id: '3'}
        ],
      }];
      response =  {
        responses: [{
          aggregations: {
            "2": {
              buckets: [
              {
                "3": {
                  buckets: [
                  {doc_count: 1, key: 1000},
                  {doc_count: 3, key: 2000}
                  ]
                },
                doc_count: 4,
                key: 'server1',
              },
              {
                "3": {
                  buckets: [
                  {doc_count: 2, key: 1000},
                  {doc_count: 8, key: 2000}
                  ]
                },
                doc_count: 10,
                key: 'server2',
              },
              {
                "3": {
                  buckets: [
                  {doc_count: 2, key: 1000},
                  {doc_count: 8, key: 2000}
                  ]
                },
                doc_count: 10,
                key: 0,
              },
              ]
            }
          }
        }]
      };

      result = new ElasticResponse(targets, response).getTimeSeries();
    });

    it('should return 2 series', function() {
      expect(result.data.length).to.be(3);
      expect(result.data[0].datapoints.length).to.be(2);
      expect(result.data[0].target).to.be('server1 Count and {{not_exist}} server1');
      expect(result.data[1].target).to.be('server2 Count and {{not_exist}} server2');
      expect(result.data[2].target).to.be('0 Count and {{not_exist}} 0');
    });
  });
describe('unsavedChangesSrv', function() {
  var _dashboardSrv;
  var _contextSrvStub = { isEditor: true };
  var _rootScope;
  var _location;
  var _timeout;
  var _window;
  var tracker;
  var dash;
  var scope;

  beforeEach(angularMocks.module('grafana.core'));
  beforeEach(angularMocks.module('grafana.services'));
  beforeEach(
    angularMocks.module(function($provide) {
      $provide.value('contextSrv', _contextSrvStub);
      $provide.value('$window', {});
    })
  );

  beforeEach(
    angularMocks.inject(function($location, $rootScope, dashboardSrv, $timeout, $window) {
      _dashboardSrv = dashboardSrv;
      _rootScope = $rootScope;
      _location = $location;
      _timeout = $timeout;
      _window = $window;
    })
  );

  beforeEach(function() {
    dash = _dashboardSrv.create({
      refresh: false,
      panels: [{ test: 'asd', legend: {} }],
      rows: [
        {
          panels: [{ test: 'asd', legend: {} }],
        },
      ],
    });
    scope = _rootScope.$new();
    scope.appEvent = sinon.spy();
    scope.onAppEvent = sinon.spy();

    tracker = new Tracker(dash, scope, undefined, _location, _window, _timeout, contextSrv, _rootScope);
  });

  it('No changes should not have changes', function() {
    expect(tracker.hasChanges()).to.be(false);
  });

  it('Simple change should be registered', function() {
    dash.property = 'google';
    expect(tracker.hasChanges()).to.be(true);
  });

  it('Should ignore a lot of changes', function() {
    dash.time = { from: '1h' };
    dash.refresh = true;
    dash.schemaVersion = 10;
    expect(tracker.hasChanges()).to.be(false);
  });

  it.skip('Should ignore row collapse change', function() {
    dash.rows[0].collapse = true;
    expect(tracker.hasChanges()).to.be(false);
  });

  it('Should ignore panel legend changes', function() {
    dash.panels[0].legend.sortDesc = true;
    dash.panels[0].legend.sort = 'avg';
    expect(tracker.hasChanges()).to.be(false);
  });

  it.skip('Should ignore panel repeats', function() {
    dash.rows[0].panels.push({ repeatPanelId: 10 });
    expect(tracker.hasChanges()).to.be(false);
  });

  it.skip('Should ignore row repeats', function() {
    dash.addEmptyRow();
    dash.rows[1].repeatRowId = 10;
    expect(tracker.hasChanges()).to.be(false);
  });
});
Beispiel #4
0
describe('ElasticQueryBuilder', function() {
  var builder;

  beforeEach(function() {
    builder = new ElasticQueryBuilder({timeField: '@timestamp'});
  });

  it('with defaults', function() {
    var query = builder.build({
      metrics: [{type: 'Count', id: '0'}],
      timeField: '@timestamp',
      bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '1'}],
    });

    expect(query.query.filtered.filter.bool.must[0].range["@timestamp"].gte).to.be("$timeFrom");
    expect(query.aggs["1"].date_histogram.extended_bounds.min).to.be("$timeFrom");
  });

  it('with raw query', function() {
    var query = builder.build({
      rawQuery: '{"query": "$lucene_query"}',
    });

    expect(query.query).to.be("$lucene_query");
  });

  it('with multiple bucket aggs', function() {
    var query = builder.build({
      metrics: [{type: 'count', id: '1'}],
      timeField: '@timestamp',
      bucketAggs: [
        {type: 'terms', field: '@host', id: '2'},
        {type: 'date_histogram', field: '@timestamp', id: '3'}
      ],
    });

    expect(query.aggs["2"].terms.field).to.be("@host");
    expect(query.aggs["2"].aggs["3"].date_histogram.field).to.be("@timestamp");
  });

  it('with select field', function() {
    var query = builder.build({
      metrics: [{type: 'avg', field: '@value', id: '1'}],
      bucketAggs: [{type: 'date_histogram', field: '@timestamp', id: '2'}],
    }, 100, 1000);

    var aggs = query.aggs["2"].aggs;
    expect(aggs["1"].avg.field).to.be("@value");
  });

  it('with term agg and order by metric agg', function() {
    var query = builder.build({
      metrics: [
        {type: 'count', id: '1'},
        {type: 'avg', field: '@value', id: '5'}
      ],
      bucketAggs: [
        {type: 'terms', field: '@host', settings: {size: 5, order: 'asc', orderBy: '5'}, id: '2' },
        {type: 'date_histogram', field: '@timestamp', id: '3'}
      ],
    }, 100, 1000);

    var firstLevel = query.aggs["2"];
    var secondLevel = firstLevel.aggs["3"];

    expect(firstLevel.aggs["5"].avg.field).to.be("@value");
    expect(secondLevel.aggs["5"].avg.field).to.be("@value");
  });

  it('with metric percentiles', function() {
    var query = builder.build({
      metrics: [
        {
          id: '1',
          type: 'percentiles',
          field: '@load_time',
          settings: {
            percents: [1,2,3,4]
          }
        }
      ],
      bucketAggs: [
        {type: 'date_histogram', field: '@timestamp', id: '3'}
      ],
    }, 100, 1000);

    var firstLevel = query.aggs["3"];

    expect(firstLevel.aggs["1"].percentiles.field).to.be("@load_time");
    expect(firstLevel.aggs["1"].percentiles.percents).to.eql([1,2,3,4]);
  });

  it('with filters aggs', function() {
    var query = builder.build({
      metrics: [{type: 'count', id: '1'}],
      timeField: '@timestamp',
      bucketAggs: [
        {
          id: '2',
          type: 'filters',
          settings: {
            filters: [
              {query: '@metric:cpu' },
              {query: '@metric:logins.count' },
            ]
          }
        },
        {type: 'date_histogram', field: '@timestamp', id: '4'}
      ],
    });

    expect(query.aggs["2"].filters.filters["@metric:cpu"].query.query_string.query).to.be("@metric:cpu");
    expect(query.aggs["2"].filters.filters["@metric:logins.count"].query.query_string.query).to.be("@metric:logins.count");
    expect(query.aggs["2"].aggs["4"].date_histogram.field).to.be("@timestamp");
  });

});
Beispiel #5
0
  describe('When performing CloudWatch query', function() {
    var requestParams;

    var query = {
      range: { from: 'now-1h', to: 'now' },
      targets: [
        {
          region: 'us-east-1',
          namespace: 'AWS/EC2',
          metricName: 'CPUUtilization',
          dimensions: {
            InstanceId: 'i-12345678'
          },
          statistics: ['Average'],
          period: 300
        }
      ]
    };

    var response = {
      Datapoints: [
        {
          Average: 1,
          Timestamp: 'Wed Dec 31 1969 16:00:00 GMT-0800 (PST)'
        },
        {
          Average: 2,
          Timestamp: 'Wed Dec 31 1969 16:05:00 GMT-0800 (PST)'
        },
        {
          Average: 5,
          Timestamp: 'Wed Dec 31 1969 16:15:00 GMT-0800 (PST)'
        }
      ],
      Label: 'CPUUtilization'
    };

    beforeEach(function() {
      ctx.backendSrv.datasourceRequest = function(params) {
        requestParams = params;
        return ctx.$q.when({data: response});
      };
    });

    it('should generate the correct query', function(done) {
      ctx.ds.query(query).then(function() {
        var params = requestParams.data.parameters;
        expect(params.namespace).to.be(query.targets[0].namespace);
        expect(params.metricName).to.be(query.targets[0].metricName);
        expect(params.dimensions[0].Name).to.be(Object.keys(query.targets[0].dimensions)[0]);
        expect(params.dimensions[0].Value).to.be(query.targets[0].dimensions[Object.keys(query.targets[0].dimensions)[0]]);
        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' },
        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.data.parameters;
        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('CPUUtilization_Average');
        expect(result.data[0].datapoints[0][0]).to.be(response.Datapoints[0]['Average']);
        done();
      });
      ctx.$rootScope.$apply();
    });

    it('should return null for missing data point', function(done) {
      ctx.ds.query(query).then(function(result) {
        expect(result.data[0].datapoints[2][0]).to.be(null);
        done();
      });
      ctx.$rootScope.$apply();
    });

    it('should generate the correct targets by expanding template variables', function() {
      var templateSrv = {
        variables: [
          {
            name: 'instance_id',
            options: [
              { value: 'i-23456789', selected: false },
              { value: 'i-34567890', selected: true }
            ]
          }
        ],
        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('DashImportCtrl', function() {
  var ctx: any = {};
  var backendSrv = {
    search: sinon.stub().returns(Promise.resolve([])),
    get: sinon.stub()
  };

  beforeEach(angularMocks.module('grafana.core'));

  beforeEach(angularMocks.inject(($rootScope, $controller, $q) => {
    ctx.$q = $q;
    ctx.scope = $rootScope.$new();
    ctx.ctrl = $controller(DashImportCtrl, {
      $scope: ctx.scope,
      backendSrv: backendSrv,
    });
  }));

  describe('when uploading json', function() {
    beforeEach(function() {
      config.datasources = {
        ds: {
          type: 'test-db',
        }
      };

      ctx.ctrl.onUpload({
        '__inputs': [
          {name: 'ds', pluginId: 'test-db', type: 'datasource', pluginName: 'Test DB'}
        ]
      });
    });

    it('should build input model', function() {
      expect(ctx.ctrl.inputs.length).to.eql(1);
      expect(ctx.ctrl.inputs[0].name).to.eql('ds');
      expect(ctx.ctrl.inputs[0].info).to.eql('Select a Test DB data source');
    });

    it('should set inputValid to false', function() {
      expect(ctx.ctrl.inputsValid).to.eql(false);
    });
  });

  describe('when specifing grafana.com url', function() {
    beforeEach(function() {
      ctx.ctrl.gnetUrl = 'http://grafana.com/dashboards/123';
      // setup api mock
      backendSrv.get = sinon.spy(() => {
        return Promise.resolve({
          json: {}
        });
      });
      return ctx.ctrl.checkGnetDashboard();
    });

    it('should call gnet api with correct dashboard id', function() {
      expect(backendSrv.get.getCall(0).args[0]).to.eql('api/gnet/dashboards/123');
    });
  });

  describe('when specifing dashbord id', function() {
    beforeEach(function() {
      ctx.ctrl.gnetUrl = '2342';
      // setup api mock
      backendSrv.get = sinon.spy(() => {
        return Promise.resolve({
          json: {}
        });
      });
      return ctx.ctrl.checkGnetDashboard();
    });

    it('should call gnet api with correct dashboard id', function() {
      expect(backendSrv.get.getCall(0).args[0]).to.eql('api/gnet/dashboards/2342');
    });
  });

});
describe('ElasticDatasource', function() {
  var ctx = new helpers.ServiceTestContext();
  var instanceSettings: any = {jsonData: {}};

  beforeEach(angularMocks.module('grafana.core'));
  beforeEach(angularMocks.module('grafana.services'));
  beforeEach(ctx.providePhase(['templateSrv', 'backendSrv', 'timeSrv']));

  beforeEach(angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
    ctx.$q = $q;
    ctx.$httpBackend =  $httpBackend;
    ctx.$rootScope = $rootScope;
    ctx.$injector = $injector;
    $httpBackend.when('GET', /\.html$/).respond('');
  }));

  function createDatasource(instanceSettings) {
    instanceSettings.jsonData = instanceSettings.jsonData || {};
    ctx.ds = ctx.$injector.instantiate(ElasticDatasource, {instanceSettings: instanceSettings});
  }

  describe('When testing datasource with index pattern', function() {
    beforeEach(function() {
      createDatasource({url: 'http://es.com', index: '[asd-]YYYY.MM.DD', jsonData: {interval: 'Daily', esVersion: '2'}});
    });

    it('should translate index pattern to current day', function() {
      var requestOptions;
      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({data: {}});
      };

      ctx.ds.testDatasource();
      ctx.$rootScope.$apply();

      var today = moment.utc().format("YYYY.MM.DD");
      expect(requestOptions.url).to.be("http://es.com/asd-" + today + '/_mapping');
    });
  });

  describe('When issueing metric query with interval pattern', function() {
    var requestOptions, parts, header;

    beforeEach(function() {
      createDatasource({url: 'http://es.com', index: '[asd-]YYYY.MM.DD', jsonData: {interval: 'Daily', esVersion: '2'}});

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({data: {responses: []}});
      };

      ctx.ds.query({
        range: {
          from: moment.utc([2015, 4, 30, 10]),
          to: moment.utc([2015, 5, 1, 10])
        },
        targets: [{ bucketAggs: [], metrics: [], query: 'escape\\:test' }]
      });

      ctx.$rootScope.$apply();

      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should translate index pattern to current day', function() {
      expect(header.index).to.eql(['asd-2015.05.30', 'asd-2015.05.31', 'asd-2015.06.01']);
    });

    it('should json escape lucene query', function() {
      var body = angular.fromJson(parts[1]);
      expect(body.query.bool.filter[1].query_string.query).to.be('escape\\:test');
    });
  });

  describe('When issueing document query', function() {
    var requestOptions, parts, header;

    beforeEach(function() {
      createDatasource({url: 'http://es.com', index: 'test', jsonData: {esVersion: '2'}});

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({data: {responses: []}});
      };

      ctx.ds.query({
        range: { from: moment([2015, 4, 30, 10]), to: moment([2015, 5, 1, 10]) },
        targets: [{ bucketAggs: [], metrics: [{type: 'raw_document'}], query: 'test' }]
      });

      ctx.$rootScope.$apply();
      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should set search type to query_then_fetch', function() {
      expect(header.search_type).to.eql('query_then_fetch');
    });

    it('should set size', function() {
      var body = angular.fromJson(parts[1]);
      expect(body.size).to.be(500);
    });
  });

  describe('When getting fields', function() {
    var requestOptions, parts, header;

    beforeEach(function() {
      createDatasource({url: 'http://es.com', index: 'metricbeat'});

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({data: {
          metricbeat: {
            mappings: {
              metricsets: {
                _all: {},
                properties: {
                  '@timestamp': {type: 'date'},
                  beat: {
                    properties: {
                      name: {type: 'string'},
                      hostname: {type: 'string'},
                    }
                  },
                  system: {
                    properties: {
                      cpu: {
                        properties: {
                          system: {type: 'float'},
                          user: {type: 'float'},
                        }
                      },
                      process: {
                        properties: {
                          cpu: {
                            properties: {
                              total: {type: 'float'}
                            }
                          },
                          name: {type: 'string'},
                        }
                      },
                    }
                  }
                }
              }
            }
          }
        }});
      };
    });

    it('should return nested fields', function() {
      ctx.ds.getFields({
        find: 'fields',
        query: '*'
      }).then((fieldObjects) => {
        var fields = _.map(fieldObjects, 'text');
        expect(fields).to.eql([
          '@timestamp',
          'beat.name',
          'beat.hostname',
          'system.cpu.system',
          'system.cpu.user',
          'system.process.cpu.total',
          'system.process.name'
        ]);
      });
      ctx.$rootScope.$apply();
    });

    it('should return fields related to query type', function() {
      ctx.ds.getFields({
        find: 'fields',
        query: '*',
        type: 'number'
      }).then((fieldObjects) => {
        var fields = _.map(fieldObjects, 'text');
        expect(fields).to.eql([
          'system.cpu.system',
          'system.cpu.user',
          'system.process.cpu.total'
        ]);
      });

      ctx.ds.getFields({
        find: 'fields',
        query: '*',
        type: 'date'
      }).then((fieldObjects) => {
        var fields = _.map(fieldObjects, 'text');
        expect(fields).to.eql([
          '@timestamp'
        ]);
      });

      ctx.$rootScope.$apply();
    });
  });

  describe('When issuing aggregation query on es5.x', function() {
    var requestOptions, parts, header;

    beforeEach(function() {
      createDatasource({url: 'http://es.com', index: 'test', jsonData: {esVersion: '5'}});

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({data: {responses: []}});
      };

      ctx.ds.query({
        range: { from: moment([2015, 4, 30, 10]), to: moment([2015, 5, 1, 10]) },
        targets: [{
            bucketAggs: [
                {type: 'date_histogram', field: '@timestamp', id: '2'}
            ],
            metrics: [
                {type: 'count'}], query: 'test' }
            ]
      });

      ctx.$rootScope.$apply();
      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
    });

    it('should not set search type to count', function() {
      expect(header.search_type).to.not.eql('count');
    });

    it('should set size to 0', function() {
      var body = angular.fromJson(parts[1]);
      expect(body.size).to.be(0);
    });

  });

  describe('When issuing metricFind query on es5.x', function() {
    var requestOptions, parts, header, body;

    beforeEach(function() {
      createDatasource({url: 'http://es.com', index: 'test', jsonData: {esVersion: '5'}});

      ctx.backendSrv.datasourceRequest = function(options) {
        requestOptions = options;
        return ctx.$q.when({
            data: {
                responses: [{aggregations: {"1": [{buckets: {text: 'test', value: '1'}}]}}]
            }
        });
      };

      ctx.ds.metricFindQuery('{"find": "terms", "field": "test"}');
      ctx.$rootScope.$apply();

      parts = requestOptions.data.split('\n');
      header = angular.fromJson(parts[0]);
      body = angular.fromJson(parts[1]);
    });

    it('should not set search type to count', function() {
      expect(header.search_type).to.not.eql('count');
    });

    it('should set size to 0', function() {
      expect(body.size).to.be(0);
    });

    it('should not set terms aggregation size to 0', function() {
      expect(body['aggs']['1']['terms'].size).to.not.be(0);
    });
  });

});
Beispiel #8
0
describe('GraphiteQueryCtrl', function() {
  var ctx = new helpers.ControllerTestContext();

  beforeEach(angularMocks.module('grafana.core'));
  beforeEach(angularMocks.module('grafana.controllers'));
  beforeEach(angularMocks.module('grafana.services'));

  beforeEach(ctx.providePhase());
  beforeEach(ctx.createControllerPhase('GraphiteQueryCtrl'));

  beforeEach(function() {
    ctx.scope.target = {target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)'};

    ctx.scope.datasource = ctx.datasource;
    ctx.scope.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([]));
  });

  describe('init', function() {
    beforeEach(function() {
      ctx.scope.init();
      ctx.scope.$digest();
    });

    it('should validate metric key exists', function() {
      expect(ctx.scope.datasource.metricFindQuery.getCall(0).args[0]).to.be('test.prod.*');
    });

    it('should delete last segment if no metrics are found', function() {
      expect(ctx.scope.segments[2].value).to.be('select metric');
    });

    it('should parse expression and build function model', function() {
      expect(ctx.scope.functions.length).to.be(2);
    });
  });

  describe('when adding function', function() {
    beforeEach(function() {
      ctx.scope.target.target = 'test.prod.*.count';
      ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: false}]));
      ctx.scope.init();
      ctx.scope.$digest();

      ctx.scope.$parent = { get_data: sinon.spy() };
      ctx.scope.addFunction(gfunc.getFuncDef('aliasByNode'));
    });

    it('should add function with correct node number', function() {
      expect(ctx.scope.functions[0].params[0]).to.be(2);
    });

    it('should update target', function() {
      expect(ctx.scope.target.target).to.be('aliasByNode(test.prod.*.count, 2)');
    });

    it('should call get_data', function() {
      expect(ctx.scope.$parent.get_data.called).to.be(true);
    });
  });

  describe('when adding function before any metric segment', function() {
    beforeEach(function() {
      ctx.scope.target.target = '';
      ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: true}]));
      ctx.scope.init();
      ctx.scope.$digest();

      ctx.scope.$parent = { get_data: sinon.spy() };
      ctx.scope.addFunction(gfunc.getFuncDef('asPercent'));
    });

    it('should add function and remove select metric link', function() {
      expect(ctx.scope.segments.length).to.be(0);
    });
  });

  describe('when initalizing target without metric expression and only function', function() {
    beforeEach(function() {
      ctx.scope.target.target = 'asPercent(#A, #B)';
      ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
      ctx.scope.init();
      ctx.scope.$digest();
      ctx.scope.$parent = { get_data: sinon.spy() };
    });

    it('should not add select metric segment', function() {
      expect(ctx.scope.segments.length).to.be(0);
    });

    it('should add both series refs as params', function() {
      expect(ctx.scope.functions[0].params.length).to.be(2);
    });

  });

  describe('when initializing a target with single param func using variable', function() {
    beforeEach(function() {
      ctx.scope.target.target = 'movingAverage(prod.count, $var)';
      ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
      ctx.scope.init();
      ctx.scope.$digest();
      ctx.scope.$parent = { get_data: sinon.spy() };
    });

    it('should add 2 segments', function() {
      expect(ctx.scope.segments.length).to.be(2);
    });

    it('should add function param', function() {
      expect(ctx.scope.functions[0].params.length).to.be(1);
    });

  });

  describe('when initalizing target without metric expression and function with series-ref', function() {
    beforeEach(function() {
      ctx.scope.target.target = 'asPercent(metric.node.count, #A)';
      ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
      ctx.scope.init();
      ctx.scope.$digest();
      ctx.scope.$parent = { get_data: sinon.spy() };
    });

    it('should add segments', function() {
      expect(ctx.scope.segments.length).to.be(3);
    });

    it('should have correct func params', function() {
      expect(ctx.scope.functions[0].params.length).to.be(1);
    });
  });

  describe('when getting altSegments and metricFindQuery retuns empty array', function() {
    beforeEach(function() {
      ctx.scope.target.target = 'test.count';
      ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([]));
      ctx.scope.init();
      ctx.scope.getAltSegments(1).then(function(results) {
        ctx.altSegments = results;
      });
      ctx.scope.$digest();
      ctx.scope.$parent = { get_data: sinon.spy() };
    });

    it('should have no segments', function() {
      expect(ctx.altSegments.length).to.be(0);
    });

  });

  describe('targetChanged', function() {
    beforeEach(function() {
      ctx.scope.datasource.metricFindQuery.returns(ctx.$q.when([{expandable: false}]));
      ctx.scope.init();
      ctx.scope.$digest();

      ctx.scope.$parent = { get_data: sinon.spy() };
      ctx.scope.target.target = '';
      ctx.scope.targetChanged();
    });

    it('should rebuld target after expression model', function() {
      expect(ctx.scope.target.target).to.be('aliasByNode(scaleToSeconds(test.prod.*, 1), 2)');
    });

    it('should call get_data', function() {
      expect(ctx.scope.$parent.get_data.called).to.be(true);
    });
  });
});
describe('PrometheusDatasource', function() {
  var ctx = new helpers.ServiceTestContext();
  var instanceSettings = {
    url: 'proxied',
    directUrl: 'direct',
    user: '******',
    password: '******',
    jsonData: { httpMethod: 'GET' },
  };

  beforeEach(angularMocks.module('grafana.core'));
  beforeEach(angularMocks.module('grafana.services'));
  beforeEach(ctx.providePhase(['timeSrv']));

  beforeEach(
    angularMocks.inject(function($q, $rootScope, $httpBackend, $injector) {
      ctx.$q = $q;
      ctx.$httpBackend = $httpBackend;
      ctx.$rootScope = $rootScope;
      ctx.ds = $injector.instantiate(PrometheusDatasource, {
        instanceSettings: instanceSettings,
      });
      $httpBackend.when('GET', /\.html$/).respond('');
    })
  );
  describe('When querying prometheus with one target using query editor target spec', function() {
    var results;
    var query = {
      range: { from: time({ seconds: 63 }), to: time({ seconds: 183 }) },
      targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
      interval: '60s',
    };
    // Interval alignment with step
    var urlExpected =
      'proxied/api/v1/query_range?query=' + encodeURIComponent('test{job="testjob"}') + '&start=60&end=240&step=60';
    var response = {
      status: 'success',
      data: {
        resultType: 'matrix',
        result: [
          {
            metric: { __name__: 'test', job: 'testjob' },
            values: [[60, '3846']],
          },
        ],
      },
    };
    beforeEach(function() {
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query).then(function(data) {
        results = data;
      });
      ctx.$httpBackend.flush();
    });
    it('should generate the correct query', function() {
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
    it('should return series list', function() {
      expect(results.data.length).to.be(1);
      expect(results.data[0].target).to.be('test{job="testjob"}');
    });
  });
  describe('When querying prometheus with one target which return multiple series', function() {
    var results;
    var start = 60;
    var end = 360;
    var step = 60;
    var urlExpected =
      'proxied/api/v1/query_range?query=' +
      encodeURIComponent('test{job="testjob"}') +
      '&start=' +
      start +
      '&end=' +
      end +
      '&step=' +
      step;
    var query = {
      range: { from: time({ seconds: start }), to: time({ seconds: end }) },
      targets: [{ expr: 'test{job="testjob"}', format: 'time_series' }],
      interval: '60s',
    };
    var response = {
      status: 'success',
      data: {
        resultType: 'matrix',
        result: [
          {
            metric: { __name__: 'test', job: 'testjob', series: 'series 1' },
            values: [[start + step * 1, '3846'], [start + step * 3, '3847'], [end - step * 1, '3848']],
          },
          {
            metric: { __name__: 'test', job: 'testjob', series: 'series 2' },
            values: [[start + step * 2, '4846']],
          },
        ],
      },
    };
    beforeEach(function() {
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query).then(function(data) {
        results = data;
      });
      ctx.$httpBackend.flush();
    });
    it('should be same length', function() {
      expect(results.data.length).to.be(2);
      expect(results.data[0].datapoints.length).to.be((end - start) / step + 1);
      expect(results.data[1].datapoints.length).to.be((end - start) / step + 1);
    });
    it('should fill null until first datapoint in response', function() {
      expect(results.data[0].datapoints[0][1]).to.be(start * 1000);
      expect(results.data[0].datapoints[0][0]).to.be(null);
      expect(results.data[0].datapoints[1][1]).to.be((start + step * 1) * 1000);
      expect(results.data[0].datapoints[1][0]).to.be(3846);
    });
    it('should fill null after last datapoint in response', function() {
      var length = (end - start) / step + 1;
      expect(results.data[0].datapoints[length - 2][1]).to.be((end - step * 1) * 1000);
      expect(results.data[0].datapoints[length - 2][0]).to.be(3848);
      expect(results.data[0].datapoints[length - 1][1]).to.be(end * 1000);
      expect(results.data[0].datapoints[length - 1][0]).to.be(null);
    });
    it('should fill null at gap between series', function() {
      expect(results.data[0].datapoints[2][1]).to.be((start + step * 2) * 1000);
      expect(results.data[0].datapoints[2][0]).to.be(null);
      expect(results.data[1].datapoints[1][1]).to.be((start + step * 1) * 1000);
      expect(results.data[1].datapoints[1][0]).to.be(null);
      expect(results.data[1].datapoints[3][1]).to.be((start + step * 3) * 1000);
      expect(results.data[1].datapoints[3][0]).to.be(null);
    });
  });
  describe('When querying prometheus with one target and instant = true', function() {
    var results;
    var urlExpected = 'proxied/api/v1/query?query=' + encodeURIComponent('test{job="testjob"}') + '&time=123';
    var query = {
      range: { from: time({ seconds: 63 }), to: time({ seconds: 123 }) },
      targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
      interval: '60s',
    };
    var response = {
      status: 'success',
      data: {
        resultType: 'vector',
        result: [
          {
            metric: { __name__: 'test', job: 'testjob' },
            value: [123, '3846'],
          },
        ],
      },
    };
    beforeEach(function() {
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query).then(function(data) {
        results = data;
      });
      ctx.$httpBackend.flush();
    });
    it('should generate the correct query', function() {
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
    it('should return series list', function() {
      expect(results.data.length).to.be(1);
      expect(results.data[0].target).to.be('test{job="testjob"}');
    });
  });
  describe('When performing annotationQuery', function() {
    var results;
    var urlExpected =
      'proxied/api/v1/query_range?query=' +
      encodeURIComponent('ALERTS{alertstate="firing"}') +
      '&start=60&end=180&step=60';
    var options = {
      annotation: {
        expr: 'ALERTS{alertstate="firing"}',
        tagKeys: 'job',
        titleFormat: '{{alertname}}',
        textFormat: '{{instance}}',
      },
      range: {
        from: time({ seconds: 63 }),
        to: time({ seconds: 123 }),
      },
    };
    var response = {
      status: 'success',
      data: {
        resultType: 'matrix',
        result: [
          {
            metric: {
              __name__: 'ALERTS',
              alertname: 'InstanceDown',
              alertstate: 'firing',
              instance: 'testinstance',
              job: 'testjob',
            },
            values: [[123, '1']],
          },
        ],
      },
    };
    beforeEach(function() {
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.annotationQuery(options).then(function(data) {
        results = data;
      });
      ctx.$httpBackend.flush();
    });
    it('should return annotation list', function() {
      ctx.$rootScope.$apply();
      expect(results.length).to.be(1);
      expect(results[0].tags).to.contain('testjob');
      expect(results[0].title).to.be('InstanceDown');
      expect(results[0].text).to.be('testinstance');
      expect(results[0].time).to.be(123 * 1000);
    });
  });

  describe('When resultFormat is table and instant = true', function() {
    var results;
    var urlExpected = 'proxied/api/v1/query?query=' + encodeURIComponent('test{job="testjob"}') + '&time=123';
    var query = {
      range: { from: time({ seconds: 63 }), to: time({ seconds: 123 }) },
      targets: [{ expr: 'test{job="testjob"}', format: 'time_series', instant: true }],
      interval: '60s',
    };
    var response = {
      status: 'success',
      data: {
        resultType: 'vector',
        result: [
          {
            metric: { __name__: 'test', job: 'testjob' },
            value: [123, '3846'],
          },
        ],
      },
    };

    beforeEach(function() {
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query).then(function(data) {
        results = data;
      });
      ctx.$httpBackend.flush();
    });

    it('should return result', () => {
      expect(results).not.to.be(null);
    });
  });

  describe('The "step" query parameter', function() {
    var response = {
      status: 'success',
      data: {
        resultType: 'matrix',
        result: [],
      },
    };

    it('should be min interval when greater than auto interval', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '10s',
          },
        ],
        interval: '5s',
      };
      var urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });

    it('step should never go below 1', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [{ expr: 'test' }],
        interval: '100ms',
      };
      var urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=1';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });

    it('should be auto interval when greater than min interval', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '5s',
          },
        ],
        interval: '10s',
      };
      var urlExpected = 'proxied/api/v1/query_range?query=test&start=60&end=420&step=10';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
    it('should result in querying fewer than 11000 data points', function() {
      var query = {
        // 6 hour range
        range: { from: time({ hours: 1 }), to: time({ hours: 7 }) },
        targets: [{ expr: 'test' }],
        interval: '1s',
      };
      var end = 7 * 60 * 60;
      var start = 60 * 60;
      var urlExpected = 'proxied/api/v1/query_range?query=test&start=' + start + '&end=' + end + '&step=2';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
    it('should not apply min interval when interval * intervalFactor greater', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '10s',
            intervalFactor: 10,
          },
        ],
        interval: '5s',
      };
      // times get rounded up to interval
      var urlExpected = 'proxied/api/v1/query_range?query=test&start=50&end=450&step=50';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
    it('should apply min interval when interval * intervalFactor smaller', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '15s',
            intervalFactor: 2,
          },
        ],
        interval: '5s',
      };
      var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=60&end=420&step=15';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
    it('should apply intervalFactor to auto interval when greater', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'test',
            interval: '5s',
            intervalFactor: 10,
          },
        ],
        interval: '10s',
      };
      // times get aligned to interval
      var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=0&end=500&step=100';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
    it('should not not be affected by the 11000 data points limit when large enough', function() {
      var query = {
        // 1 week range
        range: { from: time({}), to: time({ hours: 7 * 24 }) },
        targets: [
          {
            expr: 'test',
            intervalFactor: 10,
          },
        ],
        interval: '10s',
      };
      var end = 7 * 24 * 60 * 60;
      var start = 0;
      var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=100';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
    it('should be determined by the 11000 data points limit when too small', function() {
      var query = {
        // 1 week range
        range: { from: time({}), to: time({ hours: 7 * 24 }) },
        targets: [
          {
            expr: 'test',
            intervalFactor: 10,
          },
        ],
        interval: '5s',
      };
      var end = 7 * 24 * 60 * 60;
      var start = 0;
      var urlExpected = 'proxied/api/v1/query_range?query=test' + '&start=' + start + '&end=' + end + '&step=60';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();
    });
  });

  describe('The __interval and __interval_ms template variables', function() {
    var response = {
      status: 'success',
      data: {
        resultType: 'matrix',
        result: [],
      },
    };

    it('should be unchanged when auto interval is greater than min interval', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '5s',
          },
        ],
        interval: '10s',
        scopedVars: {
          __interval: { text: '10s', value: '10s' },
          __interval_ms: { text: 10 * 1000, value: 10 * 1000 },
        },
      };
      var urlExpected =
        'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[10s])') + '&start=60&end=420&step=10';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();

      expect(query.scopedVars.__interval.text).to.be('10s');
      expect(query.scopedVars.__interval.value).to.be('10s');
      expect(query.scopedVars.__interval_ms.text).to.be(10 * 1000);
      expect(query.scopedVars.__interval_ms.value).to.be(10 * 1000);
    });
    it('should be min interval when it is greater than auto interval', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '10s',
          },
        ],
        interval: '5s',
        scopedVars: {
          __interval: { text: '5s', value: '5s' },
          __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
        },
      };
      var urlExpected =
        'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[10s])') + '&start=60&end=420&step=10';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();

      expect(query.scopedVars.__interval.text).to.be('5s');
      expect(query.scopedVars.__interval.value).to.be('5s');
      expect(query.scopedVars.__interval_ms.text).to.be(5 * 1000);
      expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000);
    });
    it('should account for intervalFactor', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '5s',
            intervalFactor: 10,
          },
        ],
        interval: '10s',
        scopedVars: {
          __interval: { text: '10s', value: '10s' },
          __interval_ms: { text: 10 * 1000, value: 10 * 1000 },
        },
      };
      var urlExpected =
        'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[100s])') + '&start=0&end=500&step=100';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();

      expect(query.scopedVars.__interval.text).to.be('10s');
      expect(query.scopedVars.__interval.value).to.be('10s');
      expect(query.scopedVars.__interval_ms.text).to.be(10 * 1000);
      expect(query.scopedVars.__interval_ms.value).to.be(10 * 1000);
    });
    it('should be interval * intervalFactor when greater than min interval', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '10s',
            intervalFactor: 10,
          },
        ],
        interval: '5s',
        scopedVars: {
          __interval: { text: '5s', value: '5s' },
          __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
        },
      };
      var urlExpected =
        'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[50s])') + '&start=50&end=450&step=50';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();

      expect(query.scopedVars.__interval.text).to.be('5s');
      expect(query.scopedVars.__interval.value).to.be('5s');
      expect(query.scopedVars.__interval_ms.text).to.be(5 * 1000);
      expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000);
    });
    it('should be min interval when greater than interval * intervalFactor', function() {
      var query = {
        // 6 minute range
        range: { from: time({ minutes: 1 }), to: time({ minutes: 7 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            interval: '15s',
            intervalFactor: 2,
          },
        ],
        interval: '5s',
        scopedVars: {
          __interval: { text: '5s', value: '5s' },
          __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
        },
      };
      var urlExpected =
        'proxied/api/v1/query_range?query=' + encodeURIComponent('rate(test[15s])') + '&start=60&end=420&step=15';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();

      expect(query.scopedVars.__interval.text).to.be('5s');
      expect(query.scopedVars.__interval.value).to.be('5s');
      expect(query.scopedVars.__interval_ms.text).to.be(5 * 1000);
      expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000);
    });
    it('should be determined by the 11000 data points limit, accounting for intervalFactor', function() {
      var query = {
        // 1 week range
        range: { from: time({}), to: time({ hours: 7 * 24 }) },
        targets: [
          {
            expr: 'rate(test[$__interval])',
            intervalFactor: 10,
          },
        ],
        interval: '5s',
        scopedVars: {
          __interval: { text: '5s', value: '5s' },
          __interval_ms: { text: 5 * 1000, value: 5 * 1000 },
        },
      };
      var end = 7 * 24 * 60 * 60;
      var start = 0;
      var urlExpected =
        'proxied/api/v1/query_range?query=' +
        encodeURIComponent('rate(test[60s])') +
        '&start=' +
        start +
        '&end=' +
        end +
        '&step=60';
      ctx.$httpBackend.expect('GET', urlExpected).respond(response);
      ctx.ds.query(query);
      ctx.$httpBackend.verifyNoOutstandingExpectation();

      expect(query.scopedVars.__interval.text).to.be('5s');
      expect(query.scopedVars.__interval.value).to.be('5s');
      expect(query.scopedVars.__interval_ms.text).to.be(5 * 1000);
      expect(query.scopedVars.__interval_ms.value).to.be(5 * 1000);
    });
  });
});
Beispiel #10
0
describe('VariableSrv', function() {
  var ctx = new helpers.ControllerTestContext();

  beforeEach(angularMocks.module('grafana.core'));
  beforeEach(angularMocks.module('grafana.controllers'));
  beforeEach(angularMocks.module('grafana.services'));

  beforeEach(ctx.providePhase(['datasourceSrv', 'timeSrv', 'templateSrv', '$location']));
  beforeEach(angularMocks.inject(($rootScope, $q, $location, $injector) => {
    ctx.$q = $q;
    ctx.$rootScope = $rootScope;
    ctx.$location = $location;
    ctx.variableSrv = $injector.get('variableSrv');
    ctx.variableSrv.init({
      templating: {list: []},
      events: new Emitter(),
    });
    ctx.$rootScope.$digest();
  }));

  function describeUpdateVariable(desc, fn) {
    describe(desc, function() {
      var scenario: any = {};
      scenario.setup = function(setupFn) {
        scenario.setupFn = setupFn;
      };

      beforeEach(function() {
        scenario.setupFn();
        var ds: any = {};
        ds.metricFindQuery = sinon.stub().returns(ctx.$q.when(scenario.queryResult));
        ctx.datasourceSrv.get = sinon.stub().returns(ctx.$q.when(ds));
        ctx.datasourceSrv.getMetricSources = sinon.stub().returns(scenario.metricSources);


        scenario.variable = ctx.variableSrv.addVariable(scenario.variableModel);
        ctx.variableSrv.updateOptions(scenario.variable);
        ctx.$rootScope.$digest();
      });

      fn(scenario);
    });
  }

  describeUpdateVariable('interval variable without auto', scenario => {
    scenario.setup(() => {
      scenario.variableModel = {type: 'interval', query: '1s,2h,5h,1d', name: 'test'};
    });

    it('should update options array', () => {
      expect(scenario.variable.options.length).to.be(4);
      expect(scenario.variable.options[0].text).to.be('1s');
      expect(scenario.variable.options[0].value).to.be('1s');
    });
  });

  //
  // Interval variable update
  //
  describeUpdateVariable('interval variable with auto', scenario => {
    scenario.setup(() => {
      scenario.variableModel = {type: 'interval', query: '1s,2h,5h,1d', name: 'test', auto: true, auto_count: 10 };

      var range = {
        from: moment(new Date()).subtract(7, 'days').toDate(),
        to: new Date()
      };

      ctx.timeSrv.timeRange = sinon.stub().returns(range);
      ctx.templateSrv.setGrafanaVariable = sinon.spy();
    });

    it('should update options array', function() {
      expect(scenario.variable.options.length).to.be(5);
      expect(scenario.variable.options[0].text).to.be('auto');
      expect(scenario.variable.options[0].value).to.be('$__auto_interval');
    });

    it('should set $__auto_interval', function() {
      var call = ctx.templateSrv.setGrafanaVariable.getCall(0);
      expect(call.args[0]).to.be('$__auto_interval');
      expect(call.args[1]).to.be('12h');
    });
  });

  //
  // Query variable update
  //
  describeUpdateVariable('query variable with empty current object and refresh', function(scenario) {
    scenario.setup(function() {
      scenario.variableModel = {type: 'query', query: '', name: 'test', current: {}};
      scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}];
    });

    it('should set current value to first option', function() {
      expect(scenario.variable.options.length).to.be(2);
      expect(scenario.variable.current.value).to.be('backend1');
    });
  });

  describeUpdateVariable('query variable with multi select and new options does not contain some selected values', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {
          type: 'query',
          query: '',
          name: 'test',
          current: {
            value: ['val1', 'val2', 'val3'],
            text: 'val1 + val2 + val3'
          }
        };
        scenario.queryResult = [{text: 'val2'}, {text: 'val3'}];
      });

      it('should update current value', function() {
        expect(scenario.variable.current.value).to.eql(['val2', 'val3']);
        expect(scenario.variable.current.text).to.eql('val2 + val3');
      });
    });

    describeUpdateVariable('query variable with multi select and new options does not contain any selected values', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {
          type: 'query',
          query: '',
          name: 'test',
          current: {
            value: ['val1', 'val2', 'val3'],
            text: 'val1 + val2 + val3'
          }
        };
        scenario.queryResult = [{text: 'val5'}, {text: 'val6'}];
      });

      it('should update current value with first one', function() {
        expect(scenario.variable.current.value).to.eql('val5');
        expect(scenario.variable.current.text).to.eql('val5');
      });
    });

    describeUpdateVariable('query variable with multi select and $__all selected', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {
          type: 'query',
          query: '',
          name: 'test',
          includeAll: true,
          current: {
            value: ['$__all'],
            text: 'All'
          }
        };
        scenario.queryResult = [{text: 'val5'}, {text: 'val6'}];
      });

      it('should keep current All value', function() {
        expect(scenario.variable.current.value).to.eql(['$__all']);
        expect(scenario.variable.current.text).to.eql('All');
      });
    });

    describeUpdateVariable('query variable with numeric results', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = { type: 'query', query: '', name: 'test', current: {} };
        scenario.queryResult = [{text: 12, value: 12}];
      });

      it('should set current value to first option', function() {
        expect(scenario.variable.current.value).to.be('12');
        expect(scenario.variable.options[0].value).to.be('12');
        expect(scenario.variable.options[0].text).to.be('12');
      });
    });

    describeUpdateVariable('basic query variable', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = { type: 'query', query: 'apps.*', name: 'test' };
        scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}];
      });

      it('should update options array', function() {
        expect(scenario.variable.options.length).to.be(2);
        expect(scenario.variable.options[0].text).to.be('backend1');
        expect(scenario.variable.options[0].value).to.be('backend1');
        expect(scenario.variable.options[1].value).to.be('backend2');
      });

      it('should select first option as value', function() {
        expect(scenario.variable.current.value).to.be('backend1');
      });
    });

    describeUpdateVariable('and existing value still exists in options', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test'};
        scenario.variableModel.current = { value: 'backend2', text: 'backend2'};
        scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}];
      });

      it('should keep variable value', function() {
        expect(scenario.variable.current.text).to.be('backend2');
      });
    });

    describeUpdateVariable('and regex pattern exists', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test'};
        scenario.variableModel.regex = '/apps.*(backend_[0-9]+)/';
        scenario.queryResult = [{text: 'apps.backend.backend_01.counters.req'}, {text: 'apps.backend.backend_02.counters.req'}];
      });

      it('should extract and use match group', function() {
        expect(scenario.variable.options[0].value).to.be('backend_01');
      });
    });

    describeUpdateVariable('and regex pattern exists and no match', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test'};
        scenario.variableModel.regex = '/apps.*(backendasd[0-9]+)/';
        scenario.queryResult = [{text: 'apps.backend.backend_01.counters.req'}, {text: 'apps.backend.backend_02.counters.req'}];
      });

      it('should not add non matching items, None option should be added instead', function() {
        expect(scenario.variable.options.length).to.be(1);
        expect(scenario.variable.options[0].isNone).to.be(true);
      });
    });

    describeUpdateVariable('regex pattern without slashes', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test'};
        scenario.variableModel.regex = 'backend_01';
        scenario.queryResult = [{text: 'apps.backend.backend_01.counters.req'}, {text: 'apps.backend.backend_02.counters.req'}];
      });

      it('should return matches options', function() {
        expect(scenario.variable.options.length).to.be(1);
      });
    });

    describeUpdateVariable('regex pattern remove duplicates', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test'};
        scenario.variableModel.regex = '/backend_01/';
        scenario.queryResult = [{text: 'apps.backend.backend_01.counters.req'}, {text: 'apps.backend.backend_01.counters.req'}];
      });

      it('should return matches options', function() {
        expect(scenario.variable.options.length).to.be(1);
      });
    });

    describeUpdateVariable('with include All', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test', includeAll: true};
        scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
      });

      it('should add All option', function() {
        expect(scenario.variable.options[0].text).to.be('All');
        expect(scenario.variable.options[0].value).to.be('$__all');
      });
    });

    describeUpdateVariable('with include all and custom value', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test', includeAll: true, allValue: '*'};
        scenario.queryResult = [{text: 'backend1'}, {text: 'backend2'}, { text: 'backend3'}];
      });

      it('should add All option with custom value', function() {
        expect(scenario.variable.options[0].value).to.be('$__all');
      });
    });

    describeUpdateVariable('without sort', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test', sort: 0};
        scenario.queryResult = [{text: 'bbb2'}, {text: 'aaa10'}, { text: 'ccc3'}];
      });

      it('should return options without sort', function() {
        expect(scenario.variable.options[0].text).to.be('bbb2');
        expect(scenario.variable.options[1].text).to.be('aaa10');
        expect(scenario.variable.options[2].text).to.be('ccc3');
      });
    });

    describeUpdateVariable('with alphabetical sort (asc)', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test', sort: 1};
        scenario.queryResult = [{text: 'bbb2'}, {text: 'aaa10'}, { text: 'ccc3'}];
      });

      it('should return options with alphabetical sort', function() {
        expect(scenario.variable.options[0].text).to.be('aaa10');
        expect(scenario.variable.options[1].text).to.be('bbb2');
        expect(scenario.variable.options[2].text).to.be('ccc3');
      });
    });

    describeUpdateVariable('with alphabetical sort (desc)', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test', sort: 2};
        scenario.queryResult = [{text: 'bbb2'}, {text: 'aaa10'}, { text: 'ccc3'}];
      });

      it('should return options with alphabetical sort', function() {
        expect(scenario.variable.options[0].text).to.be('ccc3');
        expect(scenario.variable.options[1].text).to.be('bbb2');
        expect(scenario.variable.options[2].text).to.be('aaa10');
      });
    });

    describeUpdateVariable('with numerical sort (asc)', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test', sort: 3};
        scenario.queryResult = [{text: 'bbb2'}, {text: 'aaa10'}, { text: 'ccc3'}];
      });

      it('should return options with numerical sort', function() {
        expect(scenario.variable.options[0].text).to.be('bbb2');
        expect(scenario.variable.options[1].text).to.be('ccc3');
        expect(scenario.variable.options[2].text).to.be('aaa10');
      });
    });

    describeUpdateVariable('with numerical sort (desc)', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'query', query: 'apps.*', name: 'test', sort: 4};
        scenario.queryResult = [{text: 'bbb2'}, {text: 'aaa10'}, { text: 'ccc3'}];
      });

      it('should return options with numerical sort', function() {
        expect(scenario.variable.options[0].text).to.be('aaa10');
        expect(scenario.variable.options[1].text).to.be('ccc3');
        expect(scenario.variable.options[2].text).to.be('bbb2');
      });
    });

    //
    // datasource variable update
    //
    describeUpdateVariable('datasource variable with regex filter', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {
          type: 'datasource',
          query: 'graphite',
          name: 'test',
          current: {value: 'backend4_pee', text: 'backend4_pee'},
          regex: '/pee$/'
        };
        scenario.metricSources = [
          {name: 'backend1', meta: {id: 'influx'}},
          {name: 'backend2_pee', meta: {id: 'graphite'}},
          {name: 'backend3', meta: {id: 'graphite'}},
          {name: 'backend4_pee', meta: {id: 'graphite'}},
        ];
      });

      it('should set only contain graphite ds and filtered using regex', function() {
        expect(scenario.variable.options.length).to.be(2);
        expect(scenario.variable.options[0].value).to.be('backend2_pee');
        expect(scenario.variable.options[1].value).to.be('backend4_pee');
      });

      it('should keep current value if available', function() {
        expect(scenario.variable.current.value).to.be('backend4_pee');
      });
    });

    //
    // Custom variable update
    //
    describeUpdateVariable('update custom variable', function(scenario) {
      scenario.setup(function() {
        scenario.variableModel = {type: 'custom', query: 'hej, hop, asd', name: 'test'};
      });

      it('should update options array', function() {
        expect(scenario.variable.options.length).to.be(3);
        expect(scenario.variable.options[0].text).to.be('hej');
        expect(scenario.variable.options[1].value).to.be('hop');
      });
    });
});