diff --git a/public/app/plugins/datasource/influxdb/datasource.js b/public/app/plugins/datasource/influxdb/datasource.js index b9ae9069323..d6a5ccc37ad 100644 --- a/public/app/plugins/datasource/influxdb/datasource.js +++ b/public/app/plugins/datasource/influxdb/datasource.js @@ -2,10 +2,10 @@ define([ 'angular', 'lodash', 'app/core/utils/datemath', - './influxSeries', - './queryBuilder', + './influx_series', + './query_builder', './directives', - './queryCtrl', + './query_ctrl', ], function (angular, _, dateMath, InfluxSeries, InfluxQueryBuilder) { 'use strict'; diff --git a/public/app/plugins/datasource/influxdb/influxSeries.js b/public/app/plugins/datasource/influxdb/influx_series.js similarity index 100% rename from public/app/plugins/datasource/influxdb/influxSeries.js rename to public/app/plugins/datasource/influxdb/influx_series.js diff --git a/public/app/plugins/datasource/influxdb/queryBuilder.js b/public/app/plugins/datasource/influxdb/query_builder.js similarity index 100% rename from public/app/plugins/datasource/influxdb/queryBuilder.js rename to public/app/plugins/datasource/influxdb/query_builder.js diff --git a/public/app/plugins/datasource/influxdb/queryCtrl.js b/public/app/plugins/datasource/influxdb/query_ctrl.js similarity index 99% rename from public/app/plugins/datasource/influxdb/queryCtrl.js rename to public/app/plugins/datasource/influxdb/query_ctrl.js index 5df07d678b4..75dd972fbaa 100644 --- a/public/app/plugins/datasource/influxdb/queryCtrl.js +++ b/public/app/plugins/datasource/influxdb/query_ctrl.js @@ -1,7 +1,7 @@ define([ 'angular', 'lodash', - './queryBuilder', + './query_builder', ], function (angular, _, InfluxQueryBuilder) { 'use strict'; diff --git a/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts b/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts new file mode 100644 index 00000000000..c8c127ed759 --- /dev/null +++ b/public/app/plugins/datasource/influxdb/specs/influx_series_specs.ts @@ -0,0 +1,190 @@ +/// + +import {describe, beforeEach, it, sinon, expect} from 'test/lib/common'; + +declare var InfluxSeries: any; + +describe('when generating timeseries from influxdb response', function() { + + describe('given multiple fields for series', function() { + var options = { + alias: '', + series: [ + { + name: 'cpu', + tags: {app: 'test', server: 'server1'}, + columns: ['time', 'mean', 'max', 'min'], + values: [[1431946625000, 10, 11, 9], [1431946626000, 20, 21, 19]] + } + ] + }; + describe('and no alias', function() { + it('should generate multiple datapoints for each column', function() { + var series = new InfluxSeries(options); + var result = series.getTimeSeries(); + + expect(result.length).to.be(3); + expect(result[0].target).to.be('cpu.mean {app: test, server: server1}'); + expect(result[0].datapoints[0][0]).to.be(10); + expect(result[0].datapoints[0][1]).to.be(1431946625000); + expect(result[0].datapoints[1][0]).to.be(20); + expect(result[0].datapoints[1][1]).to.be(1431946626000); + + expect(result[1].target).to.be('cpu.max {app: test, server: server1}'); + expect(result[1].datapoints[0][0]).to.be(11); + expect(result[1].datapoints[0][1]).to.be(1431946625000); + expect(result[1].datapoints[1][0]).to.be(21); + expect(result[1].datapoints[1][1]).to.be(1431946626000); + + expect(result[2].target).to.be('cpu.min {app: test, server: server1}'); + expect(result[2].datapoints[0][0]).to.be(9); + expect(result[2].datapoints[0][1]).to.be(1431946625000); + expect(result[2].datapoints[1][0]).to.be(19); + expect(result[2].datapoints[1][1]).to.be(1431946626000); + + }); + }); + + describe('and simple alias', function() { + it('should use alias', function() { + options.alias = 'new series'; + var series = new InfluxSeries(options); + var result = series.getTimeSeries(); + + expect(result[0].target).to.be('new series'); + expect(result[1].target).to.be('new series'); + expect(result[2].target).to.be('new series'); + }); + + }); + + describe('and alias patterns', function() { + it('should replace patterns', function() { + options.alias = 'alias: $m -> $tag_server ([[measurement]])'; + var series = new InfluxSeries(options); + var result = series.getTimeSeries(); + + expect(result[0].target).to.be('alias: cpu -> server1 (cpu)'); + expect(result[1].target).to.be('alias: cpu -> server1 (cpu)'); + expect(result[2].target).to.be('alias: cpu -> server1 (cpu)'); + }); + + }); + }); + describe('given measurement with default fieldname', function() { + var options = { series: [ + { + name: 'cpu', + tags: {app: 'test', server: 'server1'}, + columns: ['time', 'value'], + values: [["2015-05-18T10:57:05Z", 10], ["2015-05-18T10:57:06Z", 12]] + }, + { + name: 'cpu', + tags: {app: 'test2', server: 'server2'}, + columns: ['time', 'value'], + values: [["2015-05-18T10:57:05Z", 15], ["2015-05-18T10:57:06Z", 16]] + } + ]}; + + describe('and no alias', function() { + + it('should generate label with no field', function() { + var series = new InfluxSeries(options); + var result = series.getTimeSeries(); + + expect(result[0].target).to.be('cpu {app: test, server: server1}'); + expect(result[1].target).to.be('cpu {app: test2, server: server2}'); + }); + }); + + }); + describe('given two series', function() { + var options = { + alias: '', + series: [ + { + name: 'cpu', + tags: {app: 'test', server: 'server1'}, + columns: ['time', 'mean'], + values: [[1431946625000, 10], [1431946626000, 12]] + }, + { + name: 'cpu', + tags: {app: 'test2', server: 'server2'}, + columns: ['time', 'mean'], + values: [[1431946625000, 15], [1431946626000, 16]] + } + ] + }; + + describe('and no alias', function() { + + it('should generate two time series', function() { + var series = new InfluxSeries(options); + var result = series.getTimeSeries(); + + expect(result.length).to.be(2); + expect(result[0].target).to.be('cpu.mean {app: test, server: server1}'); + expect(result[0].datapoints[0][0]).to.be(10); + expect(result[0].datapoints[0][1]).to.be(1431946625000); + expect(result[0].datapoints[1][0]).to.be(12); + expect(result[0].datapoints[1][1]).to.be(1431946626000); + + expect(result[1].target).to.be('cpu.mean {app: test2, server: server2}'); + expect(result[1].datapoints[0][0]).to.be(15); + expect(result[1].datapoints[0][1]).to.be(1431946625000); + expect(result[1].datapoints[1][0]).to.be(16); + expect(result[1].datapoints[1][1]).to.be(1431946626000); + }); + }); + + describe('and simple alias', function() { + it('should use alias', function() { + options.alias = 'new series'; + var series = new InfluxSeries(options); + var result = series.getTimeSeries(); + + expect(result[0].target).to.be('new series'); + }); + + }); + + describe('and alias patterns', function() { + it('should replace patterns', function() { + options.alias = 'alias: $m -> $tag_server ([[measurement]])'; + var series = new InfluxSeries(options); + var result = series.getTimeSeries(); + + expect(result[0].target).to.be('alias: cpu -> server1 (cpu)'); + expect(result[1].target).to.be('alias: cpu -> server2 (cpu)'); + }); + + }); + + }); + + describe('given measurement with dots', function() { + var options = { + alias: '', + series: [ + { + name: 'app.prod.server1.count', + tags: {}, + columns: ['time', 'mean'], + values: [[1431946625000, 10], [1431946626000, 12]] + } + ] + }; + + it('should replace patterns', function() { + options.alias = 'alias: $1 -> [[3]]'; + var series = new InfluxSeries(options); + var result = series.getTimeSeries(); + + expect(result[0].target).to.be('alias: prod -> count'); + }); + }); + +}); + diff --git a/public/app/plugins/datasource/influxdb/specs/query_builder_specs.ts b/public/app/plugins/datasource/influxdb/specs/query_builder_specs.ts new file mode 100644 index 00000000000..024712341cc --- /dev/null +++ b/public/app/plugins/datasource/influxdb/specs/query_builder_specs.ts @@ -0,0 +1,186 @@ +/// +// +import {describe, beforeEach, it, sinon, expect} from 'test/lib/common'; + +declare var InfluxQueryBuilder: any; + +describe('InfluxQueryBuilder', function() { + + describe('series with mesurement only', function() { + it('should generate correct query', function() { + var builder = new InfluxQueryBuilder({ + measurement: 'cpu', + groupBy: [{type: 'time', interval: 'auto'}] + }); + + var query = builder.build(); + + expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE $timeFilter GROUP BY time($interval)'); + }); + }); + + describe('series with math expr and as expr', function() { + it('should generate correct query', function() { + var builder = new InfluxQueryBuilder({ + measurement: 'cpu', + fields: [{name: 'test', func: 'max', mathExpr: '*2', asExpr: 'new_name'}], + groupBy: [{type: 'time', interval: 'auto'}] + }); + + var query = builder.build(); + + expect(query).to.be('SELECT max("test")*2 AS "new_name" FROM "cpu" WHERE $timeFilter GROUP BY time($interval)'); + }); + }); + + describe('series with single tag only', function() { + it('should generate correct query', function() { + var builder = new InfluxQueryBuilder({ + measurement: 'cpu', + groupBy: [{type: 'time', interval: 'auto'}], + tags: [{key: 'hostname', value: 'server1'}] + }); + + var query = builder.build(); + + expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE "hostname" = \'server1\' AND $timeFilter' + + ' GROUP BY time($interval)'); + }); + + it('should switch regex operator with tag value is regex', function() { + var builder = new InfluxQueryBuilder({ + measurement: 'cpu', + groupBy: [{type: 'time', interval: 'auto'}], + tags: [{key: 'app', value: '/e.*/'}] + }); + + var query = builder.build(); + expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE "app" =~ /e.*/ AND $timeFilter GROUP BY time($interval)'); + }); + }); + + describe('series with multiple fields', function() { + it('should generate correct query', function() { + var builder = new InfluxQueryBuilder({ + measurement: 'cpu', + tags: [], + groupBy: [{type: 'time', interval: 'auto'}], + fields: [{ name: 'tx_in', func: 'sum' }, { name: 'tx_out', func: 'mean' }] + }); + + var query = builder.build(); + expect(query).to.be('SELECT sum("tx_in") AS "tx_in", mean("tx_out") AS "tx_out" ' + + 'FROM "cpu" WHERE $timeFilter GROUP BY time($interval)'); + }); + }); + + describe('series with multiple tags only', function() { + it('should generate correct query', function() { + var builder = new InfluxQueryBuilder({ + measurement: 'cpu', + groupBy: [{type: 'time', interval: 'auto'}], + tags: [{key: 'hostname', value: 'server1'}, {key: 'app', value: 'email', condition: "AND"}] + }); + + var query = builder.build(); + expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE "hostname" = \'server1\' AND "app" = \'email\' AND ' + + '$timeFilter GROUP BY time($interval)'); + }); + }); + + describe('series with tags OR condition', function() { + it('should generate correct query', function() { + var builder = new InfluxQueryBuilder({ + measurement: 'cpu', + groupBy: [{type: 'time', interval: 'auto'}], + tags: [{key: 'hostname', value: 'server1'}, {key: 'hostname', value: 'server2', condition: "OR"}] + }); + + var query = builder.build(); + expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE "hostname" = \'server1\' OR "hostname" = \'server2\' AND ' + + '$timeFilter GROUP BY time($interval)'); + }); + }); + + describe('series with groupByTag', function() { + it('should generate correct query', function() { + var builder = new InfluxQueryBuilder({ + measurement: 'cpu', + tags: [], + groupBy: [{type: 'time', interval: 'auto'}, {type: 'tag', key: 'host'}], + }); + + var query = builder.build(); + expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE $timeFilter ' + + 'GROUP BY time($interval), "host"'); + }); + }); + + describe('when building explore queries', function() { + + it('should only have measurement condition in tag keys query given query with measurement', function() { + var builder = new InfluxQueryBuilder({ measurement: 'cpu', tags: [] }); + var query = builder.buildExploreQuery('TAG_KEYS'); + expect(query).to.be('SHOW TAG KEYS FROM "cpu"'); + }); + + it('should handle regex measurement in tag keys query', function() { + var builder = new InfluxQueryBuilder({ + measurement: '/.*/', + tags: [] + }); + var query = builder.buildExploreQuery('TAG_KEYS'); + expect(query).to.be('SHOW TAG KEYS FROM /.*/'); + }); + + it('should have no conditions in tags keys query given query with no measurement or tag', function() { + var builder = new InfluxQueryBuilder({ measurement: '', tags: [] }); + var query = builder.buildExploreQuery('TAG_KEYS'); + expect(query).to.be('SHOW TAG KEYS'); + }); + + it('should have where condition in tag keys query with tags', function() { + var builder = new InfluxQueryBuilder({ measurement: '', tags: [{key: 'host', value: 'se1'}] }); + var query = builder.buildExploreQuery('TAG_KEYS'); + expect(query).to.be("SHOW TAG KEYS WHERE \"host\" = 'se1'"); + }); + + it('should have no conditions in measurement query for query with no tags', function() { + var builder = new InfluxQueryBuilder({ measurement: '', tags: [] }); + var query = builder.buildExploreQuery('MEASUREMENTS'); + expect(query).to.be('SHOW MEASUREMENTS'); + }); + + it('should have where condition in measurement query for query with tags', function() { + var builder = new InfluxQueryBuilder({measurement: '', tags: [{key: 'app', value: 'email'}]}); + var query = builder.buildExploreQuery('MEASUREMENTS'); + expect(query).to.be("SHOW MEASUREMENTS WHERE \"app\" = 'email'"); + }); + + it('should have where tag name IN filter in tag values query for query with one tag', function() { + var builder = new InfluxQueryBuilder({measurement: '', tags: [{key: 'app', value: 'asdsadsad'}]}); + var query = builder.buildExploreQuery('TAG_VALUES', 'app'); + expect(query).to.be('SHOW TAG VALUES WITH KEY = "app"'); + }); + + it('should have measurement tag condition and tag name IN filter in tag values query', function() { + var builder = new InfluxQueryBuilder({measurement: 'cpu', tags: [{key: 'app', value: 'email'}, {key: 'host', value: 'server1'}]}); + var query = builder.buildExploreQuery('TAG_VALUES', 'app'); + expect(query).to.be('SHOW TAG VALUES FROM "cpu" WITH KEY = "app" WHERE "host" = \'server1\''); + }); + + it('should switch to regex operator in tag condition', function() { + var builder = new InfluxQueryBuilder({measurement: 'cpu', tags: [{key: 'host', value: '/server.*/'}]}); + var query = builder.buildExploreQuery('TAG_VALUES', 'app'); + expect(query).to.be('SHOW TAG VALUES FROM "cpu" WITH KEY = "app" WHERE "host" =~ /server.*/'); + }); + + it('should build show field query', function() { + var builder = new InfluxQueryBuilder({measurement: 'cpu', tags: [{key: 'app', value: 'email'}]}); + var query = builder.buildExploreQuery('FIELDS'); + expect(query).to.be('SHOW FIELD KEYS FROM "cpu"'); + }); + + }); + +}); diff --git a/public/app/plugins/datasource/influxdb/specs/query_ctrl_specs.ts b/public/app/plugins/datasource/influxdb/specs/query_ctrl_specs.ts new file mode 100644 index 00000000000..ee02338c3da --- /dev/null +++ b/public/app/plugins/datasource/influxdb/specs/query_ctrl_specs.ts @@ -0,0 +1,188 @@ +/// +/// +/// + +import {describe, beforeEach, it, sinon, expect, angularMocks} from 'test/lib/common'; + +declare var helpers: any; + +describe('InfluxDBQueryCtrl', function() { + var ctx = new helpers.ControllerTestContext(); + + beforeEach(angularMocks.module('grafana.controllers')); + beforeEach(angularMocks.module('grafana.services')); + beforeEach(ctx.providePhase()); + beforeEach(ctx.createControllerPhase('InfluxQueryCtrl')); + + beforeEach(function() { + ctx.scope.target = {}; + ctx.scope.$parent = { get_data: sinon.spy() }; + + ctx.scope.datasource = ctx.datasource; + ctx.scope.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([])); + }); + + describe('init', function() { + beforeEach(function() { + ctx.scope.init(); + }); + + it('should init tagSegments', function() { + expect(ctx.scope.tagSegments.length).to.be(1); + }); + + it('should init measurementSegment', function() { + expect(ctx.scope.measurementSegment.value).to.be('select measurement'); + }); + }); + + describe('when first tag segment is updated', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button'}, 0); + }); + + it('should update tag key', function() { + expect(ctx.scope.target.tags[0].key).to.be('asd'); + expect(ctx.scope.tagSegments[0].type).to.be('key'); + }); + + it('should add tagSegments', function() { + expect(ctx.scope.tagSegments.length).to.be(3); + }); + }); + + describe('when last tag value segment is updated', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button'}, 0); + ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); + }); + + it('should update tag value', function() { + expect(ctx.scope.target.tags[0].value).to.be('server1'); + }); + + it('should set tag operator', function() { + expect(ctx.scope.target.tags[0].operator).to.be('='); + }); + + it('should add plus button for another filter', function() { + expect(ctx.scope.tagSegments[3].fake).to.be(true); + }); + }); + + describe('when last tag value segment is updated to regex', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button'}, 0); + ctx.scope.tagSegmentUpdated({value: '/server.*/', type: 'value'}, 2); + }); + + it('should update operator', function() { + expect(ctx.scope.tagSegments[1].value).to.be('=~'); + expect(ctx.scope.target.tags[0].operator).to.be('=~'); + }); + }); + + describe('when second tag key is added', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); + ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); + ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); + }); + + it('should update tag key', function() { + expect(ctx.scope.target.tags[1].key).to.be('key2'); + }); + + it('should add AND segment', function() { + expect(ctx.scope.tagSegments[3].value).to.be('AND'); + }); + }); + + describe('when condition is changed', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); + ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); + ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); + ctx.scope.tagSegmentUpdated({value: 'OR', type: 'condition'}, 3); + }); + + it('should update tag condition', function() { + expect(ctx.scope.target.tags[1].condition).to.be('OR'); + }); + + it('should update AND segment', function() { + expect(ctx.scope.tagSegments[3].value).to.be('OR'); + expect(ctx.scope.tagSegments.length).to.be(7); + }); + }); + + describe('when deleting first tag filter after value is selected', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); + ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); + ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 0); + }); + + it('should remove tags', function() { + expect(ctx.scope.target.tags.length).to.be(0); + }); + + it('should remove all segment after 2 and replace with plus button', function() { + expect(ctx.scope.tagSegments.length).to.be(1); + expect(ctx.scope.tagSegments[0].type).to.be('plus-button'); + }); + }); + + describe('when deleting second tag value before second tag value is complete', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); + ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); + ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); + ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4); + }); + + it('should remove all segment after 2 and replace with plus button', function() { + expect(ctx.scope.tagSegments.length).to.be(4); + expect(ctx.scope.tagSegments[3].type).to.be('plus-button'); + }); + }); + + describe('when deleting second tag value before second tag value is complete', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); + ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); + ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); + ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4); + }); + + it('should remove all segment after 2 and replace with plus button', function() { + expect(ctx.scope.tagSegments.length).to.be(4); + expect(ctx.scope.tagSegments[3].type).to.be('plus-button'); + }); + }); + + describe('when deleting second tag value after second tag filter is complete', function() { + beforeEach(function() { + ctx.scope.init(); + ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); + ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); + ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); + ctx.scope.tagSegmentUpdated({value: 'value', type: 'value'}, 6); + ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4); + }); + + it('should remove all segment after 2 and replace with plus button', function() { + expect(ctx.scope.tagSegments.length).to.be(4); + expect(ctx.scope.tagSegments[3].type).to.be('plus-button'); + }); + }); + +}); diff --git a/public/test/specs/influx09-querybuilder-specs.js b/public/test/specs/influx09-querybuilder-specs.js deleted file mode 100644 index 7dd4f4b4ad9..00000000000 --- a/public/test/specs/influx09-querybuilder-specs.js +++ /dev/null @@ -1,187 +0,0 @@ -define([ - 'app/plugins/datasource/influxdb/queryBuilder' -], function(InfluxQueryBuilder) { - 'use strict'; - - describe('InfluxQueryBuilder', function() { - - describe('series with mesurement only', function() { - it('should generate correct query', function() { - var builder = new InfluxQueryBuilder({ - measurement: 'cpu', - groupBy: [{type: 'time', interval: 'auto'}] - }); - - var query = builder.build(); - - expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE $timeFilter GROUP BY time($interval)'); - }); - }); - - describe('series with math expr and as expr', function() { - it('should generate correct query', function() { - var builder = new InfluxQueryBuilder({ - measurement: 'cpu', - fields: [{name: 'test', func: 'max', mathExpr: '*2', asExpr: 'new_name'}], - groupBy: [{type: 'time', interval: 'auto'}] - }); - - var query = builder.build(); - - expect(query).to.be('SELECT max("test")*2 AS "new_name" FROM "cpu" WHERE $timeFilter GROUP BY time($interval)'); - }); - }); - - describe('series with single tag only', function() { - it('should generate correct query', function() { - var builder = new InfluxQueryBuilder({ - measurement: 'cpu', - groupBy: [{type: 'time', interval: 'auto'}], - tags: [{key: 'hostname', value: 'server1'}] - }); - - var query = builder.build(); - - expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE "hostname" = \'server1\' AND $timeFilter' - + ' GROUP BY time($interval)'); - }); - - it('should switch regex operator with tag value is regex', function() { - var builder = new InfluxQueryBuilder({ - measurement: 'cpu', - groupBy: [{type: 'time', interval: 'auto'}], - tags: [{key: 'app', value: '/e.*/'}] - }); - - var query = builder.build(); - expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE "app" =~ /e.*/ AND $timeFilter GROUP BY time($interval)'); - }); - }); - - describe('series with multiple fields', function() { - it('should generate correct query', function() { - var builder = new InfluxQueryBuilder({ - measurement: 'cpu', - tags: [], - groupBy: [{type: 'time', interval: 'auto'}], - fields: [{ name: 'tx_in', func: 'sum' }, { name: 'tx_out', func: 'mean' }] - }); - - var query = builder.build(); - expect(query).to.be('SELECT sum("tx_in") AS "tx_in", mean("tx_out") AS "tx_out" ' + - 'FROM "cpu" WHERE $timeFilter GROUP BY time($interval)'); - }); - }); - - describe('series with multiple tags only', function() { - it('should generate correct query', function() { - var builder = new InfluxQueryBuilder({ - measurement: 'cpu', - groupBy: [{type: 'time', interval: 'auto'}], - tags: [{key: 'hostname', value: 'server1'}, {key: 'app', value: 'email', condition: "AND"}] - }); - - var query = builder.build(); - expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE "hostname" = \'server1\' AND "app" = \'email\' AND ' + - '$timeFilter GROUP BY time($interval)'); - }); - }); - - describe('series with tags OR condition', function() { - it('should generate correct query', function() { - var builder = new InfluxQueryBuilder({ - measurement: 'cpu', - groupBy: [{type: 'time', interval: 'auto'}], - tags: [{key: 'hostname', value: 'server1'}, {key: 'hostname', value: 'server2', condition: "OR"}] - }); - - var query = builder.build(); - expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE "hostname" = \'server1\' OR "hostname" = \'server2\' AND ' + - '$timeFilter GROUP BY time($interval)'); - }); - }); - - describe('series with groupByTag', function() { - it('should generate correct query', function() { - var builder = new InfluxQueryBuilder({ - measurement: 'cpu', - tags: [], - groupBy: [{type: 'time', interval: 'auto'}, {type: 'tag', key: 'host'}], - }); - - var query = builder.build(); - expect(query).to.be('SELECT mean("value") AS "value" FROM "cpu" WHERE $timeFilter ' + - 'GROUP BY time($interval), "host"'); - }); - }); - - describe('when building explore queries', function() { - - it('should only have measurement condition in tag keys query given query with measurement', function() { - var builder = new InfluxQueryBuilder({ measurement: 'cpu', tags: [] }); - var query = builder.buildExploreQuery('TAG_KEYS'); - expect(query).to.be('SHOW TAG KEYS FROM "cpu"'); - }); - - it('should handle regex measurement in tag keys query', function() { - var builder = new InfluxQueryBuilder({ - measurement: '/.*/', - tags: [] - }); - var query = builder.buildExploreQuery('TAG_KEYS'); - expect(query).to.be('SHOW TAG KEYS FROM /.*/'); - }); - - it('should have no conditions in tags keys query given query with no measurement or tag', function() { - var builder = new InfluxQueryBuilder({ measurement: '', tags: [] }); - var query = builder.buildExploreQuery('TAG_KEYS'); - expect(query).to.be('SHOW TAG KEYS'); - }); - - it('should have where condition in tag keys query with tags', function() { - var builder = new InfluxQueryBuilder({ measurement: '', tags: [{key: 'host', value: 'se1'}] }); - var query = builder.buildExploreQuery('TAG_KEYS'); - expect(query).to.be("SHOW TAG KEYS WHERE \"host\" = 'se1'"); - }); - - it('should have no conditions in measurement query for query with no tags', function() { - var builder = new InfluxQueryBuilder({ measurement: '', tags: [] }); - var query = builder.buildExploreQuery('MEASUREMENTS'); - expect(query).to.be('SHOW MEASUREMENTS'); - }); - - it('should have where condition in measurement query for query with tags', function() { - var builder = new InfluxQueryBuilder({measurement: '', tags: [{key: 'app', value: 'email'}]}); - var query = builder.buildExploreQuery('MEASUREMENTS'); - expect(query).to.be("SHOW MEASUREMENTS WHERE \"app\" = 'email'"); - }); - - it('should have where tag name IN filter in tag values query for query with one tag', function() { - var builder = new InfluxQueryBuilder({measurement: '', tags: [{key: 'app', value: 'asdsadsad'}]}); - var query = builder.buildExploreQuery('TAG_VALUES', 'app'); - expect(query).to.be('SHOW TAG VALUES WITH KEY = "app"'); - }); - - it('should have measurement tag condition and tag name IN filter in tag values query', function() { - var builder = new InfluxQueryBuilder({measurement: 'cpu', tags: [{key: 'app', value: 'email'}, {key: 'host', value: 'server1'}]}); - var query = builder.buildExploreQuery('TAG_VALUES', 'app'); - expect(query).to.be('SHOW TAG VALUES FROM "cpu" WITH KEY = "app" WHERE "host" = \'server1\''); - }); - - it('should switch to regex operator in tag condition', function() { - var builder = new InfluxQueryBuilder({measurement: 'cpu', tags: [{key: 'host', value: '/server.*/'}]}); - var query = builder.buildExploreQuery('TAG_VALUES', 'app'); - expect(query).to.be('SHOW TAG VALUES FROM "cpu" WITH KEY = "app" WHERE "host" =~ /server.*/'); - }); - - it('should build show field query', function() { - var builder = new InfluxQueryBuilder({measurement: 'cpu', tags: [{key: 'app', value: 'email'}]}); - var query = builder.buildExploreQuery('FIELDS'); - expect(query).to.be('SHOW FIELD KEYS FROM "cpu"'); - }); - - }); - - }); - -}); diff --git a/public/test/specs/influxSeries-specs.js b/public/test/specs/influxSeries-specs.js deleted file mode 100644 index 0c6d786392d..00000000000 --- a/public/test/specs/influxSeries-specs.js +++ /dev/null @@ -1,180 +0,0 @@ -define([ - 'app/plugins/datasource/influxdb/influxSeries' -], function(InfluxSeries) { - 'use strict'; - - describe('when generating timeseries from influxdb response', function() { - - describe('given multiple fields for series', function() { - var options = { series: [ - { - name: 'cpu', - tags: {app: 'test', server: 'server1'}, - columns: ['time', 'mean', 'max', 'min'], - values: [[1431946625000, 10, 11, 9], [1431946626000, 20, 21, 19]] - } - ]}; - describe('and no alias', function() { - it('should generate multiple datapoints for each column', function() { - var series = new InfluxSeries(options); - var result = series.getTimeSeries(); - - expect(result.length).to.be(3); - expect(result[0].target).to.be('cpu.mean {app: test, server: server1}'); - expect(result[0].datapoints[0][0]).to.be(10); - expect(result[0].datapoints[0][1]).to.be(1431946625000); - expect(result[0].datapoints[1][0]).to.be(20); - expect(result[0].datapoints[1][1]).to.be(1431946626000); - - expect(result[1].target).to.be('cpu.max {app: test, server: server1}'); - expect(result[1].datapoints[0][0]).to.be(11); - expect(result[1].datapoints[0][1]).to.be(1431946625000); - expect(result[1].datapoints[1][0]).to.be(21); - expect(result[1].datapoints[1][1]).to.be(1431946626000); - - expect(result[2].target).to.be('cpu.min {app: test, server: server1}'); - expect(result[2].datapoints[0][0]).to.be(9); - expect(result[2].datapoints[0][1]).to.be(1431946625000); - expect(result[2].datapoints[1][0]).to.be(19); - expect(result[2].datapoints[1][1]).to.be(1431946626000); - - }); - }); - - describe('and simple alias', function() { - it('should use alias', function() { - options.alias = 'new series'; - var series = new InfluxSeries(options); - var result = series.getTimeSeries(); - - expect(result[0].target).to.be('new series'); - expect(result[1].target).to.be('new series'); - expect(result[2].target).to.be('new series'); - }); - - }); - - describe('and alias patterns', function() { - it('should replace patterns', function() { - options.alias = 'alias: $m -> $tag_server ([[measurement]])'; - var series = new InfluxSeries(options); - var result = series.getTimeSeries(); - - expect(result[0].target).to.be('alias: cpu -> server1 (cpu)'); - expect(result[1].target).to.be('alias: cpu -> server1 (cpu)'); - expect(result[2].target).to.be('alias: cpu -> server1 (cpu)'); - }); - - }); - }); - describe('given measurement with default fieldname', function() { - var options = { series: [ - { - name: 'cpu', - tags: {app: 'test', server: 'server1'}, - columns: ['time', 'value'], - values: [["2015-05-18T10:57:05Z", 10], ["2015-05-18T10:57:06Z", 12]] - }, - { - name: 'cpu', - tags: {app: 'test2', server: 'server2'}, - columns: ['time', 'value'], - values: [["2015-05-18T10:57:05Z", 15], ["2015-05-18T10:57:06Z", 16]] - } - ]}; - - describe('and no alias', function() { - - it('should generate label with no field', function() { - var series = new InfluxSeries(options); - var result = series.getTimeSeries(); - - expect(result[0].target).to.be('cpu {app: test, server: server1}'); - expect(result[1].target).to.be('cpu {app: test2, server: server2}'); - }); - }); - - }); - describe('given two series', function() { - var options = { series: [ - { - name: 'cpu', - tags: {app: 'test', server: 'server1'}, - columns: ['time', 'mean'], - values: [[1431946625000, 10], [1431946626000, 12]] - }, - { - name: 'cpu', - tags: {app: 'test2', server: 'server2'}, - columns: ['time', 'mean'], - values: [[1431946625000, 15], [1431946626000, 16]] - } - ]}; - - describe('and no alias', function() { - - it('should generate two time series', function() { - var series = new InfluxSeries(options); - var result = series.getTimeSeries(); - - expect(result.length).to.be(2); - expect(result[0].target).to.be('cpu.mean {app: test, server: server1}'); - expect(result[0].datapoints[0][0]).to.be(10); - expect(result[0].datapoints[0][1]).to.be(1431946625000); - expect(result[0].datapoints[1][0]).to.be(12); - expect(result[0].datapoints[1][1]).to.be(1431946626000); - - expect(result[1].target).to.be('cpu.mean {app: test2, server: server2}'); - expect(result[1].datapoints[0][0]).to.be(15); - expect(result[1].datapoints[0][1]).to.be(1431946625000); - expect(result[1].datapoints[1][0]).to.be(16); - expect(result[1].datapoints[1][1]).to.be(1431946626000); - }); - }); - - describe('and simple alias', function() { - it('should use alias', function() { - options.alias = 'new series'; - var series = new InfluxSeries(options); - var result = series.getTimeSeries(); - - expect(result[0].target).to.be('new series'); - }); - - }); - - describe('and alias patterns', function() { - it('should replace patterns', function() { - options.alias = 'alias: $m -> $tag_server ([[measurement]])'; - var series = new InfluxSeries(options); - var result = series.getTimeSeries(); - - expect(result[0].target).to.be('alias: cpu -> server1 (cpu)'); - expect(result[1].target).to.be('alias: cpu -> server2 (cpu)'); - }); - - }); - - }); - - describe('given measurement with dots', function() { - var options = { series: [ - { - name: 'app.prod.server1.count', - tags: {}, - columns: ['time', 'mean'], - values: [[1431946625000, 10], [1431946626000, 12]] - } - ]}; - - it('should replace patterns', function() { - options.alias = 'alias: $1 -> [[3]]'; - var series = new InfluxSeries(options); - var result = series.getTimeSeries(); - - expect(result[0].target).to.be('alias: prod -> count'); - }); - }); - }); - -}); diff --git a/public/test/specs/influxdbQueryCtrl-specs.js b/public/test/specs/influxdbQueryCtrl-specs.js deleted file mode 100644 index 2d05a032fd6..00000000000 --- a/public/test/specs/influxdbQueryCtrl-specs.js +++ /dev/null @@ -1,188 +0,0 @@ -define([ - './helpers', - 'app/plugins/datasource/influxdb/queryCtrl', - 'app/services/uiSegmentSrv' -], function(helpers) { - 'use strict'; - - describe('InfluxDBQueryCtrl', function() { - var ctx = new helpers.ControllerTestContext(); - - beforeEach(module('grafana.controllers')); - beforeEach(module('grafana.services')); - beforeEach(ctx.providePhase()); - beforeEach(ctx.createControllerPhase('InfluxQueryCtrl')); - - beforeEach(function() { - ctx.scope.target = {}; - ctx.scope.$parent = { get_data: sinon.spy() }; - - ctx.scope.datasource = ctx.datasource; - ctx.scope.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([])); - }); - - describe('init', function() { - beforeEach(function() { - ctx.scope.init(); - }); - - it('should init tagSegments', function() { - expect(ctx.scope.tagSegments.length).to.be(1); - }); - - it('should init measurementSegment', function() { - expect(ctx.scope.measurementSegment.value).to.be('select measurement'); - }); - }); - - describe('when first tag segment is updated', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button'}, 0); - }); - - it('should update tag key', function() { - expect(ctx.scope.target.tags[0].key).to.be('asd'); - expect(ctx.scope.tagSegments[0].type).to.be('key'); - }); - - it('should add tagSegments', function() { - expect(ctx.scope.tagSegments.length).to.be(3); - }); - }); - - describe('when last tag value segment is updated', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button'}, 0); - ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); - }); - - it('should update tag value', function() { - expect(ctx.scope.target.tags[0].value).to.be('server1'); - }); - - it('should set tag operator', function() { - expect(ctx.scope.target.tags[0].operator).to.be('='); - }); - - it('should add plus button for another filter', function() { - expect(ctx.scope.tagSegments[3].fake).to.be(true); - }); - }); - - describe('when last tag value segment is updated to regex', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button'}, 0); - ctx.scope.tagSegmentUpdated({value: '/server.*/', type: 'value'}, 2); - }); - - it('should update operator', function() { - expect(ctx.scope.tagSegments[1].value).to.be('=~'); - expect(ctx.scope.target.tags[0].operator).to.be('=~'); - }); - }); - - describe('when second tag key is added', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); - ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); - ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); - }); - - it('should update tag key', function() { - expect(ctx.scope.target.tags[1].key).to.be('key2'); - }); - - it('should add AND segment', function() { - expect(ctx.scope.tagSegments[3].value).to.be('AND'); - }); - }); - - describe('when condition is changed', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); - ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); - ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); - ctx.scope.tagSegmentUpdated({value: 'OR', type: 'condition'}, 3); - }); - - it('should update tag condition', function() { - expect(ctx.scope.target.tags[1].condition).to.be('OR'); - }); - - it('should update AND segment', function() { - expect(ctx.scope.tagSegments[3].value).to.be('OR'); - expect(ctx.scope.tagSegments.length).to.be(7); - }); - }); - - describe('when deleting first tag filter after value is selected', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); - ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); - ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 0); - }); - - it('should remove tags', function() { - expect(ctx.scope.target.tags.length).to.be(0); - }); - - it('should remove all segment after 2 and replace with plus button', function() { - expect(ctx.scope.tagSegments.length).to.be(1); - expect(ctx.scope.tagSegments[0].type).to.be('plus-button'); - }); - }); - - describe('when deleting second tag value before second tag value is complete', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); - ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); - ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); - ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4); - }); - - it('should remove all segment after 2 and replace with plus button', function() { - expect(ctx.scope.tagSegments.length).to.be(4); - expect(ctx.scope.tagSegments[3].type).to.be('plus-button'); - }); - }); - - describe('when deleting second tag value before second tag value is complete', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); - ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); - ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); - ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4); - }); - - it('should remove all segment after 2 and replace with plus button', function() { - expect(ctx.scope.tagSegments.length).to.be(4); - expect(ctx.scope.tagSegments[3].type).to.be('plus-button'); - }); - }); - - describe('when deleting second tag value after second tag filter is complete', function() { - beforeEach(function() { - ctx.scope.init(); - ctx.scope.tagSegmentUpdated({value: 'asd', type: 'plus-button' }, 0); - ctx.scope.tagSegmentUpdated({value: 'server1', type: 'value'}, 2); - ctx.scope.tagSegmentUpdated({value: 'key2', type: 'plus-button'}, 3); - ctx.scope.tagSegmentUpdated({value: 'value', type: 'value'}, 6); - ctx.scope.tagSegmentUpdated(ctx.scope.removeTagFilterSegment, 4); - }); - - it('should remove all segment after 2 and replace with plus button', function() { - expect(ctx.scope.tagSegments.length).to.be(4); - expect(ctx.scope.tagSegments[3].type).to.be('plus-button'); - }); - }); - - }); -});