Files
grafana-zabbix/src/datasource/specs/utils.spec.ts
Jocelyn Collado-Kuri ce4a8d3e19 Migrate from DatasourceAPI to DatasourceWithBackend (#2123)
This PR migrates the use of `DatasourceApi` to `DatasourceWithBackend`,
with this a couple additional improvements were made:

1. Migrate to use `interpolateVariablesInQuery` everywhere instead of
the custom `replaceTemplateVariables` we were using
2. Moves util functions out of `datasource.ts` and into the existing
`utils.ts`

<img width="1261" height="406" alt="Screenshot 2025-11-20 at 11 37
56 AM"
src="https://github.com/user-attachments/assets/9e396cf2-eab0-49d1-958c-963a2e896eba"
/>

Now we can see the `query` calls being made to the backend:
<img width="367" height="102" alt="Screenshot 2025-11-20 at 11 38 18 AM"
src="https://github.com/user-attachments/assets/a5a9a337-7f19-4f7c-9d04-9d30c0216fb2"
/>

Tested:
- By running queries from Explore and Dashboards (with and without
variables)
- By interacting with all the different Editors to make sure `ComboBox`
was working as expected


Next:
Once this is merged, we will next be able to slowly move away from using
the `ZabbixConnector` to make backend datasource calls.

Fixes:
[#131](https://github.com/orgs/grafana/projects/457/views/40?pane=issue&itemId=139450234&issue=grafana%7Coss-big-tent-squad%7C131)
2025-12-16 09:58:02 -08:00

265 lines
7.7 KiB
TypeScript

import _ from 'lodash';
import * as utils from '../utils';
import { replaceTemplateVars, zabbixTemplateFormat } from '../utils';
describe('Utils', () => {
describe('expandItemName()', () => {
it('should properly expand unquoted params', (done) => {
let test_cases = [
{
name: `CPU $2 time`,
key: `system.cpu.util[,user,avg1]`,
expected: 'CPU user time',
},
{
name: `CPU $2 time - $3`,
key: `system.cpu.util[,system,avg1]`,
expected: 'CPU system time - avg1',
},
{
name: `CPU - $1 - $2 - $3`,
key: `system.cpu.util[,system,avg1]`,
expected: 'CPU - - system - avg1',
},
];
_.each(test_cases, (test_case) => {
let expandedName = utils.expandItemName(test_case.name, test_case.key);
expect(expandedName).toBe(test_case.expected);
});
done();
});
it('should properly expand quoted params with commas', (done) => {
let test_cases = [
{
name: `CPU $2 time`,
key: `system.cpu.util["type=user,value=avg",user]`,
expected: 'CPU user time',
},
{
name: `CPU $1 time`,
key: `system.cpu.util["type=user,value=avg","user"]`,
expected: 'CPU type=user,value=avg time',
},
{
name: `CPU $1 time $3`,
key: `system.cpu.util["type=user,value=avg",,"user"]`,
expected: 'CPU type=user,value=avg time user',
},
{
name: `CPU $1 $2 $3`,
key: `system.cpu.util["type=user,value=avg",time,"user"]`,
expected: 'CPU type=user,value=avg time user',
},
];
_.each(test_cases, (test_case) => {
let expandedName = utils.expandItemName(test_case.name, test_case.key);
expect(expandedName).toBe(test_case.expected);
});
done();
});
it('should properly expand array params', (done) => {
let test_cases = [
{
name: `CPU $2 - $3 time`,
key: `system.cpu.util[,[user,system],avg1]`,
expected: 'CPU user,system - avg1 time',
},
{
name: `CPU $2 - $3 time`,
key: `system.cpu.util[,["user,system",iowait],avg1]`,
expected: `CPU "user,system",iowait - avg1 time`,
},
{
name: `CPU - $2 - $3 - $4`,
key: `system.cpu.util[,[],["user,system",iowait],avg1]`,
expected: `CPU - - "user,system",iowait - avg1`,
},
];
_.each(test_cases, (test_case) => {
let expandedName = utils.expandItemName(test_case.name, test_case.key);
expect(expandedName).toBe(test_case.expected);
});
done();
});
});
describe('splitTemplateQuery()', () => {
// Backward compatibility
it('should properly split query in old format', (done) => {
let test_cases = [
{
query: `/alu/./tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9{2}/`,
expected: ['/alu/', '/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9{2}/'],
},
{
query: `a.b.c.d`,
expected: ['a', 'b', 'c', 'd'],
},
];
_.each(test_cases, (test_case) => {
let splitQuery = utils.splitTemplateQuery(test_case.query);
expect(splitQuery).toEqual(test_case.expected);
});
done();
});
it('should properly split query', (done) => {
let test_cases = [
{
query: `{alu}{/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9]*/}`,
expected: ['alu', '/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9]*/'],
},
{
query: `{alu}{/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9]{2}/}`,
expected: ['alu', '/tw-(nyc|que|brx|dwt|brk)-sta_(\\w|\\d)*-alu-[0-9]{2}/'],
},
{
query: `{a}{b}{c}{d}`,
expected: ['a', 'b', 'c', 'd'],
},
{
query: `{a}{b.c.d}`,
expected: ['a', 'b.c.d'],
},
];
_.each(test_cases, (test_case) => {
let splitQuery = utils.splitTemplateQuery(test_case.query);
expect(splitQuery).toEqual(test_case.expected);
});
done();
});
});
describe('getArrayDepth()', () => {
it('should calculate proper array depth', () => {
const test_cases = [
{
array: [],
depth: 1,
},
{
array: [1, 2, 3],
depth: 1,
},
{
array: [
[1, 2],
[3, 4],
],
depth: 2,
},
{
array: [
[
[1, 2],
[3, 4],
],
[
[1, 2],
[3, 4],
],
],
depth: 3,
},
];
for (const test_case of test_cases) {
expect(utils.getArrayDepth(test_case.array)).toBe(test_case.depth);
}
});
});
describe('replaceTemplateVars()', () => {
function testReplacingVariable(target, varValue, expectedResult, done) {
const templateSrv = {
replace: jest.fn((target) => zabbixTemplateFormat(varValue)),
getVariables: jest.fn(),
containsTemplate: jest.fn(),
updateTimeRange: jest.fn(),
};
let result = replaceTemplateVars(templateSrv, target, {});
expect(result).toBe(expectedResult);
done();
}
/*
* Alphanumerics, spaces, dots, dashes and underscores
* are allowed in Zabbix host name.
* 'AaBbCc0123 .-_'
*/
it('should return properly escaped regex', (done) => {
let target = '$host';
let template_var_value = 'AaBbCc0123 .-_';
let expected_result = '/^AaBbCc0123 \\.-_$/';
testReplacingVariable(target, template_var_value, expected_result, done);
});
/*
* Single-value variable
* $host = backend01
* $host => /^backend01|backend01$/
*/
it('should return proper regex for single value', (done) => {
let target = '$host';
let template_var_value = 'backend01';
let expected_result = '/^backend01$/';
testReplacingVariable(target, template_var_value, expected_result, done);
});
/*
* Multi-value variable
* $host = [backend01, backend02]
* $host => /^(backend01|backend01)$/
*/
it('should return proper regex for multi-value', (done) => {
let target = '$host';
let template_var_value = ['backend01', 'backend02'];
let expected_result = '/^(backend01|backend02)$/';
testReplacingVariable(target, template_var_value, expected_result, done);
});
});
describe('replaceVariablesInFuncParams()', () => {
it('should interpolate numeric and string params with templateSrv', () => {
const replaceMock = jest
.fn()
.mockImplementation((value) => (value === '42' ? '100' : value.replace('$var', 'result')));
const templateSrv = { replace: replaceMock };
const scopedVars = { some: 'var' } as any;
const functions = [
{
def: { name: 'test' },
params: [42, '$var'],
},
];
const [fn] = utils.replaceVariablesInFuncParams(templateSrv as any, functions as any, scopedVars);
expect(replaceMock).toHaveBeenCalledWith('42', scopedVars);
expect(replaceMock).toHaveBeenCalledWith('$var', scopedVars);
expect(fn.params).toEqual([100, 'result']);
});
it('should keep params undefined when function has none', () => {
const templateSrv = { replace: jest.fn() };
const functions = [{ def: { name: 'noop' } }];
const [fn] = utils.replaceVariablesInFuncParams(templateSrv as any, functions as any, {} as any);
expect(fn.params).toBeUndefined();
expect(templateSrv.replace).not.toHaveBeenCalled();
});
});
});