diff --git a/package.json b/package.json index d433b0e..67de01a 100644 --- a/package.json +++ b/package.json @@ -32,14 +32,16 @@ "jsdom": "~3.1.2", "q": "~1.4.1", "chai": "~3.5.0", + "sinon-chai": "~2.8.0", "moment": "~2.14.1" }, "dependencies": { "babel-plugin-transform-es2015-modules-systemjs": "^6.5.0", "babel-plugin-transform-es2015-for-of": "^6.6.0", "babel-preset-es2015": "^6.5.0", - "lodash": "~4.0.0", - "mocha": "^2.4.5" + "lodash": "^2.4.1", + "mocha": "^2.4.5", + "sinon": "~1.16.1" }, "homepage": "http://grafana-zabbix.org" } diff --git a/src/datasource-zabbix/specs/datasource_specs.js b/src/datasource-zabbix/specs/datasource_specs.js index 9f8faff..4b292d8 100644 --- a/src/datasource-zabbix/specs/datasource_specs.js +++ b/src/datasource-zabbix/specs/datasource_specs.js @@ -1,15 +1,19 @@ import {Datasource} from "../module"; import Q from "q"; +import sinon from 'sinon'; +import _ from 'lodash'; describe('ZabbixDatasource', function() { var ctx = {}; + var defined = sinon.match.defined; beforeEach(function() { ctx.instanceSettings = { jsonData: { username: 'zabbix', password: 'zabbix', - trends: false + trends: true, + trendsFrom: '7d' } }; ctx.$q = Q; @@ -18,16 +22,32 @@ describe('ZabbixDatasource', function() { ctx.zabbixAPIService = function() {}; ctx.ZabbixCachingProxy = function() {}; ctx.QueryProcessor = function() {}; + ctx.ds = new Datasource(ctx.instanceSettings, ctx.$q, ctx.templateSrv, ctx.alertSrv, ctx.zabbixAPIService, ctx.ZabbixCachingProxy, ctx.QueryProcessor); + + ctx.ds.replaceTemplateVars = function(str) { + return str; + }; }); describe('When querying data', function() { + ctx.options = { + targets: [ + { + group: {filter: ""}, + host: {filter: ""}, + application: {filter: ""}, + item: {filter: ""} + } + ], + range: {from: 'now-7d', to: 'now'} + }; it('should return an empty array when no targets are set', function(done) { var options = { targets: [], - range: {from: null, to: null} + range: {from: 'now-6h', to: 'now'} }; ctx.ds.query(options).then(function(result) { expect(result.data).to.have.length(0); @@ -35,6 +55,38 @@ describe('ZabbixDatasource', function() { }); }); + it('should use trends if it enabled and time more than trendsFrom', function(done) { + var ranges = ['now-7d', 'now-168h', 'now-1M', 'now-1y']; + + _.forEach(ranges, range => { + ctx.options.range.from = range; + ctx.ds.queryNumericData = sinon.spy(); + ctx.ds.query(ctx.options); + + // Check that useTrends options is true + expect(ctx.ds.queryNumericData) + .to.have.been.calledWith(defined, defined, defined, true); + }); + + done(); + }); + + it('shouldnt use trends if it enabled and time less than trendsFrom', function(done) { + var ranges = ['now-6d', 'now-167h', 'now-1h', 'now-30m', 'now-30s']; + + _.forEach(ranges, range => { + ctx.options.range.from = range; + ctx.ds.queryNumericData = sinon.spy(); + ctx.ds.query(ctx.options); + + // Check that useTrends options is false + expect(ctx.ds.queryNumericData) + .to.have.been.calledWith(defined, defined, defined, false); + }); + + done(); + }); + }); }); diff --git a/src/datasource-zabbix/specs/modules/datemath.js b/src/datasource-zabbix/specs/modules/datemath.js new file mode 100644 index 0000000..fd97ab8 --- /dev/null +++ b/src/datasource-zabbix/specs/modules/datemath.js @@ -0,0 +1,111 @@ +import _ from 'lodash'; +import moment from 'moment'; + +var units = ['y', 'M', 'w', 'd', 'h', 'm', 's']; + +export function parse(text, roundUp) { + if (!text) { return undefined; } + if (moment.isMoment(text)) { return text; } + if (_.isDate(text)) { return moment(text); } + + var time; + var mathString = ''; + var index; + var parseString; + + if (text.substring(0, 3) === 'now') { + time = moment(); + mathString = text.substring('now'.length); + } else { + index = text.indexOf('||'); + if (index === -1) { + parseString = text; + mathString = ''; // nothing else + } else { + parseString = text.substring(0, index); + mathString = text.substring(index + 2); + } + // We're going to just require ISO8601 timestamps, k? + time = moment(parseString, moment.ISO_8601); + } + + if (!mathString.length) { + return time; + } + + return parseDateMath(mathString, time, roundUp); +} + +export function isValid(text) { + var date = parse(text); + if (!date) { + return false; + } + + if (moment.isMoment(date)) { + return date.isValid(); + } + + return false; +} + +export function parseDateMath(mathString, time, roundUp) { + var dateTime = time; + var i = 0; + var len = mathString.length; + + while (i < len) { + var c = mathString.charAt(i++); + var type; + var num; + var unit; + + if (c === '/') { + type = 0; + } else if (c === '+') { + type = 1; + } else if (c === '-') { + type = 2; + } else { + return undefined; + } + + if (isNaN(mathString.charAt(i))) { + num = 1; + } else if (mathString.length === 2) { + num = mathString.charAt(i); + } else { + var numFrom = i; + while (!isNaN(mathString.charAt(i))) { + i++; + if (i > 10) { return undefined; } + } + num = parseInt(mathString.substring(numFrom, i), 10); + } + + if (type === 0) { + // rounding is only allowed on whole, single, units (eg M or 1M, not 0.5M or 2M) + if (num !== 1) { + return undefined; + } + } + unit = mathString.charAt(i++); + + if (!_.contains(units, unit)) { + return undefined; + } else { + if (type === 0) { + if (roundUp) { + dateTime.endOf(unit); + } else { + dateTime.startOf(unit); + } + } else if (type === 1) { + dateTime.add(num, unit); + } else if (type === 2) { + dateTime.subtract(num, unit); + } + } + } + return dateTime; +} diff --git a/src/datasource-zabbix/specs/test-main.js b/src/datasource-zabbix/specs/test-main.js index cc846f1..23fa7c1 100644 --- a/src/datasource-zabbix/specs/test-main.js +++ b/src/datasource-zabbix/specs/test-main.js @@ -4,6 +4,9 @@ import prunk from 'prunk'; import {jsdom} from 'jsdom'; import chai from 'chai'; +import sinon from 'sinon'; +import sinonChai from 'sinon-chai'; +import * as dateMath from './modules/datemath'; // Mock angular module var angularMocks = { @@ -17,7 +20,9 @@ var angularMocks = { }; var datemathMock = { - parse: function() {} + parse: dateMath.parse, + parseDateMath: dateMath.parseDateMath, + isValid: dateMath.isValid }; // Mock Grafana modules that are not available outside of the core project @@ -39,5 +44,6 @@ global.Node = window.Node; // Setup Chai chai.should(); +chai.use(sinonChai); global.assert = chai.assert; global.expect = chai.expect;