diff --git a/public/app/plugins/datasource/graphite/specs/query_ctrl.jest.ts b/public/app/plugins/datasource/graphite/specs/query_ctrl.jest.ts new file mode 100644 index 00000000000..b38ad56427b --- /dev/null +++ b/public/app/plugins/datasource/graphite/specs/query_ctrl.jest.ts @@ -0,0 +1,324 @@ +import { uiSegmentSrv } from 'app/core/services/segment_srv'; +import gfunc from '../gfunc'; +import { GraphiteQueryCtrl } from '../query_ctrl'; + +describe('GraphiteQueryCtrl', () => { + let ctx = { + datasource: { + metricFindQuery: jest.fn(() => Promise.resolve([])), + getFuncDefs: jest.fn(() => Promise.resolve(gfunc.getFuncDefs('1.0'))), + getFuncDef: gfunc.getFuncDef, + waitForFuncDefsLoaded: jest.fn(() => Promise.resolve(null)), + createFuncInstance: gfunc.createFuncInstance, + }, + target: { target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)' }, + panelCtrl: { + refresh: jest.fn(), + }, + }; + + ctx.panelCtrl.panel = { + targets: [ctx.target], + }; + + beforeEach(() => { + GraphiteQueryCtrl.prototype.target = ctx.target; + GraphiteQueryCtrl.prototype.datasource = ctx.datasource; + + GraphiteQueryCtrl.prototype.panelCtrl = ctx.panelCtrl; + + ctx.ctrl = new GraphiteQueryCtrl( + {}, + {}, + new uiSegmentSrv({ trustAsHtml: html => html }, { highlightVariablesAsHtml: () => {} }), + {}, + {} + ); + }); + + describe('init', () => { + it('should validate metric key exists', () => { + expect(ctx.datasource.metricFindQuery.mock.calls[0][0]).toBe('test.prod.*'); + }); + + it('should delete last segment if no metrics are found', () => { + expect(ctx.ctrl.segments[2].value).toBe('select metric'); + }); + + it('should parse expression and build function model', () => { + expect(ctx.ctrl.queryModel.functions.length).toBe(2); + }); + }); + + describe('when adding function', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'test.prod.*.count'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + ctx.ctrl.addFunction(gfunc.getFuncDef('aliasByNode')); + }); + + it('should add function with correct node number', () => { + expect(ctx.ctrl.queryModel.functions[0].params[0]).toBe(2); + }); + + it('should update target', () => { + expect(ctx.ctrl.target.target).toBe('aliasByNode(test.prod.*.count, 2)'); + }); + + it('should call refresh', () => { + expect(ctx.panelCtrl.refresh).toHaveBeenCalled(); + }); + }); + + describe('when adding function before any metric segment', () => { + beforeEach(() => { + ctx.ctrl.target.target = ''; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: true }]); + ctx.ctrl.parseTarget(); + ctx.ctrl.addFunction(gfunc.getFuncDef('asPercent')); + }); + + it('should add function and remove select metric link', () => { + expect(ctx.ctrl.segments.length).toBe(0); + }); + }); + + describe('when initializing target without metric expression and only function', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'asPercent(#A, #B)'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([]); + ctx.ctrl.parseTarget(); + }); + + it('should not add select metric segment', () => { + expect(ctx.ctrl.segments.length).toBe(1); + }); + + it('should add second series ref as param', () => { + expect(ctx.ctrl.queryModel.functions[0].params.length).toBe(1); + }); + }); + + describe('when initializing a target with single param func using variable', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'movingAverage(prod.count, $var)'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([]); + ctx.ctrl.parseTarget(); + }); + + it('should add 2 segments', () => { + expect(ctx.ctrl.segments.length).toBe(2); + }); + + it('should add function param', () => { + expect(ctx.ctrl.queryModel.functions[0].params.length).toBe(1); + }); + }); + + describe('when initializing target without metric expression and function with series-ref', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'asPercent(metric.node.count, #A)'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([]); + ctx.ctrl.parseTarget(); + }); + + it('should add segments', () => { + expect(ctx.ctrl.segments.length).toBe(3); + }); + + it('should have correct func params', () => { + expect(ctx.ctrl.queryModel.functions[0].params.length).toBe(1); + }); + }); + + describe('when getting altSegments and metricFindQuery returns empty array', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'test.count'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([]); + ctx.ctrl.parseTarget(); + ctx.ctrl.getAltSegments(1).then(function(results) { + ctx.altSegments = results; + }); + }); + + it('should have no segments', () => { + expect(ctx.altSegments.length).toBe(0); + }); + }); + + describe('targetChanged', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'aliasByNode(scaleToSeconds(test.prod.*, 1), 2)'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + ctx.ctrl.target.target = ''; + ctx.ctrl.targetChanged(); + }); + + it('should rebuild target after expression model', () => { + expect(ctx.ctrl.target.target).toBe('aliasByNode(scaleToSeconds(test.prod.*, 1), 2)'); + }); + + it('should call panelCtrl.refresh', () => { + expect(ctx.panelCtrl.refresh).toHaveBeenCalled(); + }); + }); + + describe('when updating targets with nested query', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'scaleToSeconds(#A, 60)'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + }); + + it('should add function params', () => { + expect(ctx.ctrl.queryModel.segments.length).toBe(1); + expect(ctx.ctrl.queryModel.segments[0].value).toBe('#A'); + + expect(ctx.ctrl.queryModel.functions[0].params.length).toBe(1); + expect(ctx.ctrl.queryModel.functions[0].params[0]).toBe(60); + }); + + it('target should remain the same', () => { + expect(ctx.ctrl.target.target).toBe('scaleToSeconds(#A, 60)'); + }); + + it('targetFull should include nested queries', () => { + ctx.ctrl.panelCtrl.panel.targets = [ + { + target: 'nested.query.count', + refId: 'A', + }, + ]; + + ctx.ctrl.updateModelTarget(); + + expect(ctx.ctrl.target.target).toBe('scaleToSeconds(#A, 60)'); + + expect(ctx.ctrl.target.targetFull).toBe('scaleToSeconds(nested.query.count, 60)'); + }); + }); + + describe('when updating target used in other query', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'metrics.a.count'; + ctx.ctrl.target.refId = 'A'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + + ctx.ctrl.panelCtrl.panel.targets = [ctx.ctrl.target, { target: 'sumSeries(#A)', refId: 'B' }]; + + ctx.ctrl.updateModelTarget(); + }); + + it('targetFull of other query should update', () => { + expect(ctx.ctrl.panel.targets[1].targetFull).toBe('sumSeries(metrics.a.count)'); + }); + }); + + describe('when adding seriesByTag function', () => { + beforeEach(() => { + ctx.ctrl.target.target = ''; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + ctx.ctrl.addFunction(gfunc.getFuncDef('seriesByTag')); + }); + + it('should update functions', () => { + expect(ctx.ctrl.queryModel.getSeriesByTagFuncIndex()).toBe(0); + }); + + it('should update seriesByTagUsed flag', () => { + expect(ctx.ctrl.queryModel.seriesByTagUsed).toBe(true); + }); + + it('should update target', () => { + expect(ctx.ctrl.target.target).toBe('seriesByTag()'); + }); + + it('should call refresh', () => { + expect(ctx.panelCtrl.refresh).toHaveBeenCalled(); + }); + }); + + describe('when parsing seriesByTag function', () => { + beforeEach(() => { + ctx.ctrl.target.target = "seriesByTag('tag1=value1', 'tag2!=~value2')"; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + }); + + it('should add tags', () => { + const expected = [ + { key: 'tag1', operator: '=', value: 'value1' }, + { key: 'tag2', operator: '!=~', value: 'value2' }, + ]; + expect(ctx.ctrl.queryModel.tags).toEqual(expected); + }); + + it('should add plus button', () => { + expect(ctx.ctrl.addTagSegments.length).toBe(1); + }); + }); + + describe('when tag added', () => { + beforeEach(() => { + ctx.ctrl.target.target = 'seriesByTag()'; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + ctx.ctrl.addNewTag({ value: 'tag1' }); + }); + + it('should update tags with default value', () => { + const expected = [{ key: 'tag1', operator: '=', value: '' }]; + expect(ctx.ctrl.queryModel.tags).toEqual(expected); + }); + + it('should update target', () => { + const expected = "seriesByTag('tag1=')"; + expect(ctx.ctrl.target.target).toEqual(expected); + }); + }); + + describe('when tag changed', () => { + beforeEach(() => { + ctx.ctrl.target.target = "seriesByTag('tag1=value1', 'tag2!=~value2')"; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + ctx.ctrl.tagChanged({ key: 'tag1', operator: '=', value: 'new_value' }, 0); + }); + + it('should update tags', () => { + const expected = [ + { key: 'tag1', operator: '=', value: 'new_value' }, + { key: 'tag2', operator: '!=~', value: 'value2' }, + ]; + expect(ctx.ctrl.queryModel.tags).toEqual(expected); + }); + + it('should update target', () => { + const expected = "seriesByTag('tag1=new_value', 'tag2!=~value2')"; + expect(ctx.ctrl.target.target).toEqual(expected); + }); + }); + + describe('when tag removed', () => { + beforeEach(() => { + ctx.ctrl.target.target = "seriesByTag('tag1=value1', 'tag2!=~value2')"; + ctx.ctrl.datasource.metricFindQuery = () => Promise.resolve([{ expandable: false }]); + ctx.ctrl.parseTarget(); + ctx.ctrl.removeTag(0); + }); + + it('should update tags', () => { + const expected = [{ key: 'tag2', operator: '!=~', value: 'value2' }]; + expect(ctx.ctrl.queryModel.tags).toEqual(expected); + }); + + it('should update target', () => { + const expected = "seriesByTag('tag2!=~value2')"; + expect(ctx.ctrl.target.target).toEqual(expected); + }); + }); +}); diff --git a/public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts b/public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts deleted file mode 100644 index b4f7718930f..00000000000 --- a/public/app/plugins/datasource/graphite/specs/query_ctrl_specs.ts +++ /dev/null @@ -1,338 +0,0 @@ -import 'app/core/services/segment_srv'; -import { describe, beforeEach, it, sinon, expect, angularMocks } from 'test/lib/common'; - -import gfunc from '../gfunc'; -import helpers from 'test/specs/helpers'; -import { GraphiteQueryCtrl } from '../query_ctrl'; - -describe('GraphiteQueryCtrl', function() { - var ctx = new helpers.ControllerTestContext(); - - beforeEach(angularMocks.module('grafana.core')); - beforeEach(angularMocks.module('grafana.controllers')); - beforeEach(angularMocks.module('grafana.services')); - beforeEach( - angularMocks.module(function($compileProvider) { - $compileProvider.preAssignBindingsEnabled(true); - }) - ); - - beforeEach(ctx.providePhase()); - beforeEach( - angularMocks.inject(($rootScope, $controller, $q) => { - ctx.$q = $q; - ctx.scope = $rootScope.$new(); - ctx.target = { target: 'aliasByNode(scaleToSeconds(test.prod.*,1),2)' }; - ctx.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([])); - ctx.datasource.getFuncDefs = sinon.stub().returns(ctx.$q.when(gfunc.getFuncDefs('1.0'))); - ctx.datasource.getFuncDef = gfunc.getFuncDef; - ctx.datasource.waitForFuncDefsLoaded = sinon.stub().returns(ctx.$q.when(null)); - ctx.datasource.createFuncInstance = gfunc.createFuncInstance; - ctx.panelCtrl = { panel: {} }; - ctx.panelCtrl = { - panel: { - targets: [ctx.target], - }, - }; - ctx.panelCtrl.refresh = sinon.spy(); - - ctx.ctrl = $controller( - GraphiteQueryCtrl, - { $scope: ctx.scope }, - { - panelCtrl: ctx.panelCtrl, - datasource: ctx.datasource, - target: ctx.target, - } - ); - ctx.scope.$digest(); - }) - ); - - describe('init', function() { - it('should validate metric key exists', function() { - expect(ctx.datasource.metricFindQuery.getCall(0).args[0]).to.be('test.prod.*'); - }); - - it('should delete last segment if no metrics are found', function() { - expect(ctx.ctrl.segments[2].value).to.be('select metric'); - }); - - it('should parse expression and build function model', function() { - expect(ctx.ctrl.queryModel.functions.length).to.be(2); - }); - }); - - describe('when adding function', function() { - beforeEach(function() { - ctx.ctrl.target.target = 'test.prod.*.count'; - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - ctx.ctrl.addFunction(gfunc.getFuncDef('aliasByNode')); - }); - - it('should add function with correct node number', function() { - expect(ctx.ctrl.queryModel.functions[0].params[0]).to.be(2); - }); - - it('should update target', function() { - expect(ctx.ctrl.target.target).to.be('aliasByNode(test.prod.*.count, 2)'); - }); - - it('should call refresh', function() { - expect(ctx.panelCtrl.refresh.called).to.be(true); - }); - }); - - describe('when adding function before any metric segment', function() { - beforeEach(function() { - ctx.ctrl.target.target = ''; - ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([{ expandable: true }])); - ctx.ctrl.parseTarget(); - ctx.ctrl.addFunction(gfunc.getFuncDef('asPercent')); - }); - - it('should add function and remove select metric link', function() { - expect(ctx.ctrl.segments.length).to.be(0); - }); - }); - - describe('when initializing target without metric expression and only function', function() { - beforeEach(function() { - ctx.ctrl.target.target = 'asPercent(#A, #B)'; - ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([])); - ctx.ctrl.parseTarget(); - ctx.scope.$digest(); - }); - - it('should not add select metric segment', function() { - expect(ctx.ctrl.segments.length).to.be(1); - }); - - it('should add second series ref as param', function() { - expect(ctx.ctrl.queryModel.functions[0].params.length).to.be(1); - }); - }); - - describe('when initializing a target with single param func using variable', function() { - beforeEach(function() { - ctx.ctrl.target.target = 'movingAverage(prod.count, $var)'; - ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([])); - ctx.ctrl.parseTarget(); - }); - - it('should add 2 segments', function() { - expect(ctx.ctrl.segments.length).to.be(2); - }); - - it('should add function param', function() { - expect(ctx.ctrl.queryModel.functions[0].params.length).to.be(1); - }); - }); - - describe('when initializing target without metric expression and function with series-ref', function() { - beforeEach(function() { - ctx.ctrl.target.target = 'asPercent(metric.node.count, #A)'; - ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([])); - ctx.ctrl.parseTarget(); - }); - - it('should add segments', function() { - expect(ctx.ctrl.segments.length).to.be(3); - }); - - it('should have correct func params', function() { - expect(ctx.ctrl.queryModel.functions[0].params.length).to.be(1); - }); - }); - - describe('when getting altSegments and metricFindQuery returns empty array', function() { - beforeEach(function() { - ctx.ctrl.target.target = 'test.count'; - ctx.ctrl.datasource.metricFindQuery.returns(ctx.$q.when([])); - ctx.ctrl.parseTarget(); - ctx.ctrl.getAltSegments(1).then(function(results) { - ctx.altSegments = results; - }); - ctx.scope.$digest(); - }); - - it('should have no segments', function() { - expect(ctx.altSegments.length).to.be(0); - }); - }); - - describe('targetChanged', function() { - beforeEach(function() { - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - ctx.ctrl.target.target = ''; - ctx.ctrl.targetChanged(); - }); - - it('should rebuld target after expression model', function() { - expect(ctx.ctrl.target.target).to.be('aliasByNode(scaleToSeconds(test.prod.*, 1), 2)'); - }); - - it('should call panelCtrl.refresh', function() { - expect(ctx.panelCtrl.refresh.called).to.be(true); - }); - }); - - describe('when updating targets with nested query', function() { - beforeEach(function() { - ctx.ctrl.target.target = 'scaleToSeconds(#A, 60)'; - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - }); - - it('should add function params', function() { - expect(ctx.ctrl.queryModel.segments.length).to.be(1); - expect(ctx.ctrl.queryModel.segments[0].value).to.be('#A'); - - expect(ctx.ctrl.queryModel.functions[0].params.length).to.be(1); - expect(ctx.ctrl.queryModel.functions[0].params[0]).to.be(60); - }); - - it('target should remain the same', function() { - expect(ctx.ctrl.target.target).to.be('scaleToSeconds(#A, 60)'); - }); - - it('targetFull should include nested queries', function() { - ctx.ctrl.panelCtrl.panel.targets = [ - { - target: 'nested.query.count', - refId: 'A', - }, - ]; - - ctx.ctrl.updateModelTarget(); - - expect(ctx.ctrl.target.target).to.be('scaleToSeconds(#A, 60)'); - - expect(ctx.ctrl.target.targetFull).to.be('scaleToSeconds(nested.query.count, 60)'); - }); - }); - - describe('when updating target used in other query', function() { - beforeEach(function() { - ctx.ctrl.target.target = 'metrics.a.count'; - ctx.ctrl.target.refId = 'A'; - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - - ctx.ctrl.panelCtrl.panel.targets = [ctx.ctrl.target, { target: 'sumSeries(#A)', refId: 'B' }]; - - ctx.ctrl.updateModelTarget(); - }); - - it('targetFull of other query should update', function() { - expect(ctx.ctrl.panel.targets[1].targetFull).to.be('sumSeries(metrics.a.count)'); - }); - }); - - describe('when adding seriesByTag function', function() { - beforeEach(function() { - ctx.ctrl.target.target = ''; - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - ctx.ctrl.addFunction(gfunc.getFuncDef('seriesByTag')); - }); - - it('should update functions', function() { - expect(ctx.ctrl.queryModel.getSeriesByTagFuncIndex()).to.be(0); - }); - - it('should update seriesByTagUsed flag', function() { - expect(ctx.ctrl.queryModel.seriesByTagUsed).to.be(true); - }); - - it('should update target', function() { - expect(ctx.ctrl.target.target).to.be('seriesByTag()'); - }); - - it('should call refresh', function() { - expect(ctx.panelCtrl.refresh.called).to.be(true); - }); - }); - - describe('when parsing seriesByTag function', function() { - beforeEach(function() { - ctx.ctrl.target.target = "seriesByTag('tag1=value1', 'tag2!=~value2')"; - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - }); - - it('should add tags', function() { - const expected = [ - { key: 'tag1', operator: '=', value: 'value1' }, - { key: 'tag2', operator: '!=~', value: 'value2' }, - ]; - expect(ctx.ctrl.queryModel.tags).to.eql(expected); - }); - - it('should add plus button', function() { - expect(ctx.ctrl.addTagSegments.length).to.be(1); - }); - }); - - describe('when tag added', function() { - beforeEach(function() { - ctx.ctrl.target.target = 'seriesByTag()'; - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - ctx.ctrl.addNewTag({ value: 'tag1' }); - }); - - it('should update tags with default value', function() { - const expected = [{ key: 'tag1', operator: '=', value: '' }]; - expect(ctx.ctrl.queryModel.tags).to.eql(expected); - }); - - it('should update target', function() { - const expected = "seriesByTag('tag1=')"; - expect(ctx.ctrl.target.target).to.eql(expected); - }); - }); - - describe('when tag changed', function() { - beforeEach(function() { - ctx.ctrl.target.target = "seriesByTag('tag1=value1', 'tag2!=~value2')"; - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - ctx.ctrl.tagChanged({ key: 'tag1', operator: '=', value: 'new_value' }, 0); - }); - - it('should update tags', function() { - const expected = [ - { key: 'tag1', operator: '=', value: 'new_value' }, - { key: 'tag2', operator: '!=~', value: 'value2' }, - ]; - expect(ctx.ctrl.queryModel.tags).to.eql(expected); - }); - - it('should update target', function() { - const expected = "seriesByTag('tag1=new_value', 'tag2!=~value2')"; - expect(ctx.ctrl.target.target).to.eql(expected); - }); - }); - - describe('when tag removed', function() { - beforeEach(function() { - ctx.ctrl.target.target = "seriesByTag('tag1=value1', 'tag2!=~value2')"; - ctx.ctrl.datasource.metricFindQuery = sinon.stub().returns(ctx.$q.when([{ expandable: false }])); - ctx.ctrl.parseTarget(); - ctx.ctrl.removeTag(0); - }); - - it('should update tags', function() { - const expected = [{ key: 'tag2', operator: '!=~', value: 'value2' }]; - expect(ctx.ctrl.queryModel.tags).to.eql(expected); - }); - - it('should update target', function() { - const expected = "seriesByTag('tag2!=~value2')"; - expect(ctx.ctrl.target.target).to.eql(expected); - }); - }); -});