123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230 |
- import { lastValueFrom } from 'rxjs';
- import { AlertState, getDefaultTimeRange, TimeRange } from '@grafana/data';
- import { backendSrv } from 'app/core/services/backend_srv';
- import { disableRBAC, enableRBAC, grantUserPermissions } from 'app/features/alerting/unified/mocks';
- import { Annotation } from 'app/features/alerting/unified/utils/constants';
- import { AccessControlAction } from 'app/types/accessControl';
- import { PromAlertingRuleState, PromRuleDTO, PromRulesResponse, PromRuleType } from 'app/types/unified-alerting-dto';
- import { silenceConsoleOutput } from '../../../../../test/core/utils/silenceConsoleOutput';
- import * as store from '../../../../store/store';
- import { UnifiedAlertStatesWorker } from './UnifiedAlertStatesWorker';
- import { DashboardQueryRunnerOptions } from './types';
- jest.mock('@grafana/runtime', () => ({
- ...(jest.requireActual('@grafana/runtime') as unknown as object),
- getBackendSrv: () => backendSrv,
- }));
- function getDefaultOptions(): DashboardQueryRunnerOptions {
- const dashboard: any = { id: 'an id', uid: 'a uid' };
- const range = getDefaultTimeRange();
- return { dashboard, range };
- }
- function getTestContext() {
- jest.clearAllMocks();
- const dispatchMock = jest.spyOn(store, 'dispatch');
- const options = getDefaultOptions();
- const getMock = jest.spyOn(backendSrv, 'get');
- return { getMock, options, dispatchMock };
- }
- describe('UnifiedAlertStatesWorker', () => {
- const worker = new UnifiedAlertStatesWorker();
- beforeAll(() => {
- disableRBAC();
- });
- describe('when canWork is called with correct props', () => {
- it('then it should return true', () => {
- const options = getDefaultOptions();
- expect(worker.canWork(options)).toBe(true);
- });
- });
- describe('when canWork is called with no dashboard id', () => {
- it('then it should return false', () => {
- const dashboard: any = {};
- const options = { ...getDefaultOptions(), dashboard };
- expect(worker.canWork(options)).toBe(false);
- });
- });
- describe('when canWork is called with wrong range', () => {
- it('then it should return false', () => {
- const defaultRange = getDefaultTimeRange();
- const range: TimeRange = { ...defaultRange, raw: { ...defaultRange.raw, to: 'now-6h' } };
- const options = { ...getDefaultOptions(), range };
- expect(worker.canWork(options)).toBe(false);
- });
- });
- describe('when run is called with incorrect props', () => {
- it('then it should return the correct results', async () => {
- const { getMock, options } = getTestContext();
- const dashboard: any = {};
- await expect(worker.work({ ...options, dashboard })).toEmitValuesWith((received) => {
- expect(received).toHaveLength(1);
- const results = received[0];
- expect(results).toEqual({ alertStates: [], annotations: [] });
- expect(getMock).not.toHaveBeenCalled();
- });
- });
- });
- describe('when run repeatedly for the same dashboard and no alert rules are found', () => {
- it('then canWork should start returning false', async () => {
- const worker = new UnifiedAlertStatesWorker();
- const getResults: PromRulesResponse = {
- status: 'success',
- data: {
- groups: [],
- },
- };
- const { getMock, options } = getTestContext();
- getMock.mockResolvedValue(getResults);
- expect(worker.canWork(options)).toBe(true);
- await lastValueFrom(worker.work(options));
- expect(worker.canWork(options)).toBe(false);
- });
- });
- describe('when run is called with correct props and request is successful', () => {
- function mockPromRuleDTO(overrides: Partial<PromRuleDTO>): PromRuleDTO {
- return {
- alerts: [],
- health: 'ok',
- name: 'foo',
- query: 'foo',
- type: PromRuleType.Alerting,
- state: PromAlertingRuleState.Firing,
- labels: {},
- annotations: {},
- ...overrides,
- };
- }
- it('then it should return the correct results', async () => {
- const getResults: PromRulesResponse = {
- status: 'success',
- data: {
- groups: [
- {
- name: 'group',
- file: '',
- interval: 1,
- rules: [
- mockPromRuleDTO({
- state: PromAlertingRuleState.Firing,
- annotations: {
- [Annotation.dashboardUID]: 'a uid',
- [Annotation.panelID]: '1',
- },
- }),
- mockPromRuleDTO({
- state: PromAlertingRuleState.Inactive,
- annotations: {
- [Annotation.dashboardUID]: 'a uid',
- [Annotation.panelID]: '2',
- },
- }),
- mockPromRuleDTO({
- state: PromAlertingRuleState.Pending,
- annotations: {
- [Annotation.dashboardUID]: 'a uid',
- [Annotation.panelID]: '2',
- },
- }),
- ],
- },
- ],
- },
- };
- const { getMock, options } = getTestContext();
- getMock.mockResolvedValue(getResults);
- await expect(worker.work(options)).toEmitValuesWith((received) => {
- expect(received).toHaveLength(1);
- const results = received[0];
- expect(results).toEqual({
- alertStates: [
- { id: 0, state: AlertState.Alerting, dashboardId: 'an id', panelId: 1 },
- { id: 1, state: AlertState.Pending, dashboardId: 'an id', panelId: 2 },
- ],
- annotations: [],
- });
- });
- expect(getMock).toHaveBeenCalledTimes(1);
- expect(getMock).toHaveBeenCalledWith(
- '/api/prometheus/grafana/api/v1/rules',
- { dashboard_uid: 'a uid' },
- 'dashboard-query-runner-unified-alert-states-an id'
- );
- });
- });
- describe('when run is called with correct props and request fails', () => {
- silenceConsoleOutput();
- it('then it should return the correct results', async () => {
- const { getMock, options, dispatchMock } = getTestContext();
- getMock.mockRejectedValue({ message: 'An error' });
- await expect(worker.work(options)).toEmitValuesWith((received) => {
- expect(received).toHaveLength(1);
- const results = received[0];
- expect(results).toEqual({ alertStates: [], annotations: [] });
- expect(getMock).toHaveBeenCalledTimes(1);
- expect(dispatchMock).toHaveBeenCalledTimes(1);
- });
- });
- });
- describe('when run is called with correct props and request is cancelled', () => {
- silenceConsoleOutput();
- it('then it should return the correct results', async () => {
- const { getMock, options, dispatchMock } = getTestContext();
- getMock.mockRejectedValue({ cancelled: true });
- await expect(worker.work(options)).toEmitValuesWith((received) => {
- expect(received).toHaveLength(1);
- const results = received[0];
- expect(results).toEqual({ alertStates: [], annotations: [] });
- expect(getMock).toHaveBeenCalledTimes(1);
- expect(dispatchMock).not.toHaveBeenCalled();
- });
- });
- });
- });
- describe('UnifiedAlertStateWorker with RBAC', () => {
- beforeAll(() => {
- enableRBAC();
- grantUserPermissions([]);
- });
- it('should not do work with insufficient permissions', () => {
- const worker = new UnifiedAlertStatesWorker();
- const options = getDefaultOptions();
- expect(worker.canWork(options)).toBe(false);
- });
- it('should do work with correct permissions', () => {
- grantUserPermissions([AccessControlAction.AlertingRuleRead, AccessControlAction.AlertingRuleExternalRead]);
- const workerWithPermissions = new UnifiedAlertStatesWorker();
- const options = getDefaultOptions();
- expect(workerWithPermissions.canWork(options)).toBe(true);
- });
- });
|