fix TriggerPanelCtrl tests

This commit is contained in:
Alexander Zobnin
2020-05-12 19:12:41 +03:00
parent 72b739f4cb
commit f7105d9f06
5 changed files with 294 additions and 278 deletions

View File

@@ -1,8 +1,6 @@
import _ from 'lodash';
import mocks from '../../test-setup/mocks';
import { TriggerPanelCtrl } from '../triggers_panel_ctrl';
import { PANEL_DEFAULTS, DEFAULT_TARGET } from '../triggers_panel_ctrl';
// import { create } from 'domain';
let datasourceSrvMock, zabbixDSMock;
@@ -14,87 +12,39 @@ jest.mock('@grafana/runtime', () => {
describe('TriggerPanelCtrl', () => {
let ctx: any = {};
const timeoutMock = () => {};
let createPanelCtrl;
let createPanelCtrl: () => any;
beforeEach(() => {
ctx = { scope: { panel: PANEL_DEFAULTS } };
ctx.scope.panel.targets = [{
...DEFAULT_TARGET,
datasource: 'zabbix_default',
}];
zabbixDSMock = {
replaceTemplateVars: () => {},
zabbix: {
getTriggers: jest.fn().mockReturnValue([generateTrigger("1"), generateTrigger("1")]),
getExtendedEventData: jest.fn().mockResolvedValue([]),
getEventAlerts: jest.fn().mockResolvedValue([]),
}
};
datasourceSrvMock = {
getMetricSources: () => {
return [
{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix_default' },
{ meta: {id: 'alexanderzobnin-zabbix-datasource'}, value: {}, name: 'zabbix' },
{ meta: {id: 'graphite'}, value: {}, name: 'graphite' },
];
},
get: () => Promise.resolve(zabbixDSMock)
};
const timeoutMock = (fn: () => any) => Promise.resolve(fn());
createPanelCtrl = () => new TriggerPanelCtrl(ctx.scope, {}, timeoutMock);
const getTriggersResp = [
[
createTrigger({
triggerid: "1", lastchange: "1510000010", priority: 5, lastEvent: {eventid: "11"}, hosts: [{maintenance_status: '1'}]
}),
createTrigger({
triggerid: "2", lastchange: "1510000040", priority: 3, lastEvent: {eventid: "12"}
}),
],
[
createTrigger({triggerid: "3", lastchange: "1510000020", priority: 4, lastEvent: {eventid: "13"}}),
createTrigger({triggerid: "4", lastchange: "1510000030", priority: 2, lastEvent: {eventid: "14"}}),
]
];
// Simulate 2 data sources
zabbixDSMock.zabbix.getTriggers = jest.fn()
.mockReturnValueOnce(getTriggersResp[0])
.mockReturnValueOnce(getTriggersResp[1]);
zabbixDSMock.zabbix.getExtendedEventData = jest.fn()
.mockReturnValue(Promise.resolve([defaultEvent]));
ctx.panelCtrl = createPanelCtrl();
});
describe('When adding new panel', () => {
it('should suggest all zabbix data sources', () => {
ctx.scope.panel = {};
const panelCtrl = createPanelCtrl();
expect(panelCtrl.available_datasources).toEqual([
'zabbix_default', 'zabbix'
ctx.dataFramesReceived = generateDataFramesResponse([
{id: "1", lastchange: "1510000010", priority: 5},
{id: "2", lastchange: "1510000040", priority: 3},
{id: "3", lastchange: "1510000020", priority: 4},
{id: "4", lastchange: "1510000030", priority: 2},
]);
});
it('should load first zabbix data source as default', () => {
ctx.scope.panel = {};
const panelCtrl = createPanelCtrl();
expect(panelCtrl.panel.targets[0].datasource).toEqual('zabbix_default');
});
it('should rewrite default empty target', () => {
ctx.scope.panel = {
targets: [{
"target": "",
"refId": "A"
}],
};
const panelCtrl = createPanelCtrl();
expect(panelCtrl.available_datasources).toEqual([
'zabbix_default', 'zabbix'
]);
});
});
describe('When refreshing panel', () => {
beforeEach(() => {
ctx.scope.panel.datasources = ['zabbix_default', 'zabbix'];
@@ -112,8 +62,8 @@ describe('TriggerPanelCtrl', () => {
});
it('should format triggers', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
const formattedTrigger: any = _.find(ctx.panelCtrl.triggerList, {triggerid: "1"});
ctx.panelCtrl.onDataFramesReceived(ctx.dataFramesReceived).then(() => {
const formattedTrigger: any = _.find(ctx.panelCtrl.renderData, {triggerid: "1"});
expect(formattedTrigger.host).toBe('backend01');
expect(formattedTrigger.hostTechName).toBe('backend01_tech');
expect(formattedTrigger.datasource).toBe('zabbix_default');
@@ -124,8 +74,8 @@ describe('TriggerPanelCtrl', () => {
});
it('should sort triggers by time by default', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
const trigger_ids = _.map(ctx.panelCtrl.triggerList, 'triggerid');
ctx.panelCtrl.onDataFramesReceived(ctx.dataFramesReceived).then(() => {
const trigger_ids = _.map(ctx.panelCtrl.renderData, 'triggerid');
expect(trigger_ids).toEqual([
'2', '4', '3', '1'
]);
@@ -135,27 +85,14 @@ describe('TriggerPanelCtrl', () => {
it('should sort triggers by severity', (done) => {
ctx.panelCtrl.panel.sortTriggersBy = { text: 'severity', value: 'priority' };
ctx.panelCtrl.onRefresh().then(() => {
const trigger_ids = _.map(ctx.panelCtrl.triggerList, 'triggerid');
ctx.panelCtrl.onDataFramesReceived(ctx.dataFramesReceived).then(() => {
const trigger_ids = _.map(ctx.panelCtrl.renderData, 'triggerid');
expect(trigger_ids).toEqual([
'1', '3', '2', '4'
]);
done();
});
});
it('should add acknowledges to trigger', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
const trigger = getTriggerById(1, ctx);
expect(trigger.acknowledges).toHaveLength(1);
expect(trigger.acknowledges[0].message).toBe("event ack");
expect(getTriggerById(2, ctx).acknowledges).toBe(undefined);
expect(getTriggerById(3, ctx).acknowledges).toBe(undefined);
expect(getTriggerById(4, ctx).acknowledges).toBe(undefined);
done();
});
});
});
describe('When formatting triggers', () => {
@@ -171,8 +108,8 @@ describe('TriggerPanelCtrl', () => {
});
it('should format host name to display (default)', (done) => {
ctx.panelCtrl.onRefresh().then(() => {
const trigger = getTriggerById(1, ctx);
ctx.panelCtrl.onDataFramesReceived(ctx.dataFramesReceived).then(() => {
const trigger = getProblemById(1, ctx);
const hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01');
done();
@@ -182,8 +119,8 @@ describe('TriggerPanelCtrl', () => {
it('should format host name to display (tech name)', (done) => {
ctx.panelCtrl.panel.hostField = false;
ctx.panelCtrl.panel.hostTechNameField = true;
ctx.panelCtrl.onRefresh().then(() => {
const trigger = getTriggerById(1, ctx);
ctx.panelCtrl.onDataFramesReceived(ctx.dataFramesReceived).then(() => {
const trigger = getProblemById(1, ctx);
const hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01_tech');
done();
@@ -193,8 +130,8 @@ describe('TriggerPanelCtrl', () => {
it('should format host name to display (both tech and visible)', (done) => {
ctx.panelCtrl.panel.hostField = true;
ctx.panelCtrl.panel.hostTechNameField = true;
ctx.panelCtrl.onRefresh().then(() => {
const trigger = getTriggerById(1, ctx);
ctx.panelCtrl.onDataFramesReceived(ctx.dataFramesReceived).then(() => {
const trigger = getProblemById(1, ctx);
const hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe('backend01 (backend01_tech)');
done();
@@ -204,105 +141,117 @@ describe('TriggerPanelCtrl', () => {
it('should hide hostname if both visible and tech name checkboxes unset', (done) => {
ctx.panelCtrl.panel.hostField = false;
ctx.panelCtrl.panel.hostTechNameField = false;
ctx.panelCtrl.onRefresh().then(() => {
const trigger = getTriggerById(1, ctx);
ctx.panelCtrl.onDataFramesReceived(ctx.dataFramesReceived).then(() => {
const trigger = getProblemById(1, ctx);
const hostname = ctx.panelCtrl.formatHostName(trigger);
expect(hostname).toBe("");
done();
});
});
});
describe('When formatting acknowledges', () => {
beforeEach(() => {
ctx.panelCtrl = createPanelCtrl();
});
it('should build proper user name', () => {
const ack = {
alias: 'alias', name: 'name', surname: 'surname'
};
const formatted = ctx.panelCtrl.formatAcknowledge(ack);
expect(formatted.user).toBe('alias (name surname)');
});
it('should return empty name if it is not defined', () => {
const formatted = ctx.panelCtrl.formatAcknowledge({});
expect(formatted.user).toBe('');
});
});
});
const defaultTrigger: any = {
"triggerid": "13565",
"value": "1",
"groups": [{"groupid": "1", "name": "Backend"}] ,
"hosts": [{"host": "backend01_tech", "hostid": "10001","maintenance_status": "0", "name": "backend01"}] ,
"lastEvent": {
"eventid": "11",
"clock": "1507229064",
"ns": "556202037",
"acknowledged": "1",
"value": "1",
"object": "0",
"source": "0",
"objectid": "13565",
},
"tags": [] ,
"lastchange": "1440259530",
"priority": "2",
"description": "Lack of free swap space on server",
const defaultProblem: any = {
"acknowledges": [],
"comments": "It probably means that the systems requires\nmore physical memory.",
"url": "https://host.local/path",
"templateid": "0", "expression": "{13174}<50", "manual_close": "0", "correlation_mode": "0",
"correlation_tag": "", "recovery_mode": "0", "recovery_expression": "", "state": "0", "status": "0",
"flags": "0", "type": "0", "items": [] , "error": ""
};
const defaultEvent: any = {
"eventid": "11",
"acknowledges": [
"correlation_mode": "0",
"correlation_tag": "",
"datasource": "zabbix_default",
"description": "Lack of free swap space on server",
"error": "",
"expression": "{13297}>20",
"flags": "0",
"groups": [
{
"acknowledgeid": "185",
"action": "0",
"alias": "api",
"clock": "1512382246",
"eventid": "11",
"message": "event ack",
"name": "api",
"surname": "user",
"userid": "3"
"groupid": "2",
"name": "Linux servers"
},
{
"groupid": "9",
"name": "Backend"
}
],
"clock": "1507229064",
"ns": "556202037",
"acknowledged": "1",
"value": "1",
"hosts": [
{
"host": "backend01_tech",
"hostid": "10111",
"maintenance_status": "1",
"name": "backend01",
"proxy_hostid": "0"
}
],
"items": [
{
"itemid": "23979",
"key_": "system.cpu.util[,iowait]",
"lastvalue": "25.2091",
"name": "CPU $2 time"
}
],
"lastEvent": {
"acknowledged": "0",
"clock": "1589297010",
"eventid": "4399289",
"name": "Disk I/O is overloaded on backend01",
"ns": "224779201",
"object": "0",
"objectid": "13682",
"severity": "2",
"source": "0",
"objectid": "1",
"value": "1"
},
"lastchange": "1440259530",
"maintenance": true,
"manual_close": "0",
"priority": "2",
"recovery_expression": "",
"recovery_mode": "0",
"showAckButton": true,
"state": "0",
"status": "0",
"tags": [],
"templateid": "13671",
"triggerid": "13682",
"type": "0",
"url": "",
"value": "1"
};
function generateTrigger(id, timestamp?, severity?): any {
const trigger = _.cloneDeep(defaultTrigger);
trigger.triggerid = id.toString();
function generateDataFramesResponse(problemDescs: any[] = [{id: 1}]): any {
const problems = problemDescs.map(problem => generateProblem(problem.id, problem.lastchange, problem.priority));
return [
{
"fields": [
{
"config": {},
"name": "Problems",
"state": {
"scopedVars": {},
"title": null
},
"type": "other",
"values": problems,
}
],
"length": 16,
"name": "problems"
}
];
}
function generateProblem(id, timestamp?, severity?): any {
const problem = _.cloneDeep(defaultProblem);
problem.triggerid = id.toString();
if (severity) {
trigger.priority = severity.toString();
problem.priority = severity.toString();
}
if (timestamp) {
trigger.lastchange = timestamp;
problem.lastchange = timestamp;
}
return trigger;
return problem;
}
function createTrigger(props): any {
let trigger = _.cloneDeep(defaultTrigger);
trigger = _.merge(trigger, props);
trigger.lastEvent.objectid = trigger.triggerid;
return trigger;
}
function getTriggerById(id, ctx): any {
return _.find(ctx.panelCtrl.triggerList, {triggerid: id.toString()});
function getProblemById(id, ctx): any {
return _.find(ctx.panelCtrl.renderData, {triggerid: id.toString()});
}

View File

@@ -118,7 +118,7 @@ export class TriggerPanelCtrl extends MetricsPanelCtrl {
this.addEditorTab('Options', triggerPanelOptionsTab);
}
onDataFramesReceived(data) {
onDataFramesReceived(data: any): Promise<any> {
let problems = [];
if (data && data.length) {
@@ -133,17 +133,18 @@ export class TriggerPanelCtrl extends MetricsPanelCtrl {
}
} catch (error) {
console.log(error);
return Promise.reject(error);
}
}
}
this.loading = false;
problems = _.flatten(problems);
this.renderProblems(problems);
return this.renderProblems(problems);
}
onDataSnapshotLoad(snapshotData) {
this.onDataFramesReceived(snapshotData);
return this.onDataFramesReceived(snapshotData);
}
onRender() {

View File

@@ -2,7 +2,7 @@
/* globals global: false */
import { JSDOM } from 'jsdom';
import { PanelCtrl } from './panelStub';
import { PanelCtrl, MetricsPanelCtrl } from './panelStub';
// Mock Grafana modules that are not available outside of the core project
// Required for loading module.js
@@ -24,12 +24,19 @@ jest.mock('grafana/app/core/core_module', () => {
};
}, {virtual: true});
let mockPanelCtrl = PanelCtrl;
jest.mock('grafana/app/core/core', () => ({
contextSrv: {},
}), {virtual: true});
const mockPanelCtrl = PanelCtrl;
const mockMetricsPanelCtrl = MetricsPanelCtrl;
jest.mock('grafana/app/plugins/sdk', () => {
return {
QueryCtrl: null,
loadPluginCss: () => {},
PanelCtrl: mockPanelCtrl
PanelCtrl: mockPanelCtrl,
MetricsPanelCtrl: mockMetricsPanelCtrl,
};
}, {virtual: true});

View File

@@ -1,99 +0,0 @@
// JSHint options
/* jshint ignore:start */
export class PanelCtrl {
constructor($scope, $injector) {
this.$injector = $injector;
this.$scope = $scope;
this.panel = $scope.panel;
this.timing = {};
this.events = {
on: () => {},
emit: () => {}
};
}
init() {
}
renderingCompleted() {
}
refresh() {
}
publishAppEvent(evtName, evt) {
}
changeView(fullscreen, edit) {
}
viewPanel() {
this.changeView(true, false);
}
editPanel() {
this.changeView(true, true);
}
exitFullscreen() {
this.changeView(false, false);
}
initEditMode() {
}
changeTab(newIndex) {
}
addEditorTab(title, directiveFn, index) {
}
getMenu() {
return [];
}
getExtendedMenu() {
return [];
}
otherPanelInFullscreenMode() {
return false;
}
calculatePanelHeight() {
}
render(payload) {
}
toggleEditorHelp(index) {
}
duplicate() {
}
updateColumnSpan(span) {
}
removePanel() {
}
editPanelJson() {
}
replacePanel(newPanel, oldPanel) {
}
sharePanel() {
}
getInfoMode() {
}
getInfoContent(options) {
}
openInspector() {
}
}

158
src/test-setup/panelStub.ts Normal file
View File

@@ -0,0 +1,158 @@
import { PanelEvents } from '@grafana/data';
export class PanelCtrl {
panel: any;
error: any;
dashboard: any;
pluginName: string;
pluginId: string;
editorTabs: any;
$scope: any;
$injector: any;
$location: any;
$timeout: any;
editModeInitiated: boolean;
height: number;
width: number;
containerHeight: any;
events: any;
loading: boolean;
timing: any;
constructor($scope, $injector) {
this.$injector = $injector;
this.$scope = $scope;
this.panel = $scope.panel;
this.timing = {};
this.events = {
on: () => {},
emit: () => {}
};
}
init() {
}
renderingCompleted() {
}
refresh() {
}
publishAppEvent(evtName, evt) {
}
changeView(fullscreen, edit) {
}
viewPanel() {
this.changeView(true, false);
}
editPanel() {
this.changeView(true, true);
}
exitFullscreen() {
this.changeView(false, false);
}
initEditMode() {
}
changeTab(newIndex) {
}
addEditorTab(title, directiveFn, index) {
}
getMenu() {
return [];
}
getExtendedMenu() {
return [];
}
otherPanelInFullscreenMode() {
return false;
}
calculatePanelHeight() {
}
render(payload) {
}
toggleEditorHelp(index) {
}
duplicate() {
}
updateColumnSpan(span) {
}
removePanel() {
}
editPanelJson() {
}
replacePanel(newPanel, oldPanel) {
}
sharePanel() {
}
getInfoMode() {
}
getInfoContent(options) {
}
openInspector() {
}
}
export class MetricsPanelCtrl extends PanelCtrl {
scope: any;
datasource: any;
$timeout: any;
contextSrv: any;
datasourceSrv: any;
timeSrv: any;
templateSrv: any;
range: any;
interval: any;
intervalMs: any;
resolution: any;
timeInfo?: string;
skipDataOnInit: boolean;
dataList: any[];
querySubscription?: any;
useDataFrames = false;
constructor($scope, $injector) {
super($scope, $injector);
this.events.on(PanelEvents.refresh, this.onMetricsPanelRefresh.bind(this));
}
onInitMetricsPanelEditMode() {}
onMetricsPanelRefresh() {}
setTimeQueryStart() {}
setTimeQueryEnd() {}
updateTimeRange() {}
calculateInterval() {}
applyPanelTimeOverrides() {}
issueQueries(datasource) {}
handleQueryResult(result) {}
handleDataStream(stream) {}
setDatasource(datasource) {}
getAdditionalMenuItems() {}
explore() {}
addQuery(target) {}
removeQuery(target) {}
moveQuery(target, direction) {}
}