UnifiedAlertStatesWorker.test.ts 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import { lastValueFrom } from 'rxjs';
  2. import { AlertState, getDefaultTimeRange, TimeRange } from '@grafana/data';
  3. import { backendSrv } from 'app/core/services/backend_srv';
  4. import { disableRBAC, enableRBAC, grantUserPermissions } from 'app/features/alerting/unified/mocks';
  5. import { Annotation } from 'app/features/alerting/unified/utils/constants';
  6. import { AccessControlAction } from 'app/types/accessControl';
  7. import { PromAlertingRuleState, PromRuleDTO, PromRulesResponse, PromRuleType } from 'app/types/unified-alerting-dto';
  8. import { silenceConsoleOutput } from '../../../../../test/core/utils/silenceConsoleOutput';
  9. import * as store from '../../../../store/store';
  10. import { UnifiedAlertStatesWorker } from './UnifiedAlertStatesWorker';
  11. import { DashboardQueryRunnerOptions } from './types';
  12. jest.mock('@grafana/runtime', () => ({
  13. ...(jest.requireActual('@grafana/runtime') as unknown as object),
  14. getBackendSrv: () => backendSrv,
  15. }));
  16. function getDefaultOptions(): DashboardQueryRunnerOptions {
  17. const dashboard: any = { id: 'an id', uid: 'a uid' };
  18. const range = getDefaultTimeRange();
  19. return { dashboard, range };
  20. }
  21. function getTestContext() {
  22. jest.clearAllMocks();
  23. const dispatchMock = jest.spyOn(store, 'dispatch');
  24. const options = getDefaultOptions();
  25. const getMock = jest.spyOn(backendSrv, 'get');
  26. return { getMock, options, dispatchMock };
  27. }
  28. describe('UnifiedAlertStatesWorker', () => {
  29. const worker = new UnifiedAlertStatesWorker();
  30. beforeAll(() => {
  31. disableRBAC();
  32. });
  33. describe('when canWork is called with correct props', () => {
  34. it('then it should return true', () => {
  35. const options = getDefaultOptions();
  36. expect(worker.canWork(options)).toBe(true);
  37. });
  38. });
  39. describe('when canWork is called with no dashboard id', () => {
  40. it('then it should return false', () => {
  41. const dashboard: any = {};
  42. const options = { ...getDefaultOptions(), dashboard };
  43. expect(worker.canWork(options)).toBe(false);
  44. });
  45. });
  46. describe('when canWork is called with wrong range', () => {
  47. it('then it should return false', () => {
  48. const defaultRange = getDefaultTimeRange();
  49. const range: TimeRange = { ...defaultRange, raw: { ...defaultRange.raw, to: 'now-6h' } };
  50. const options = { ...getDefaultOptions(), range };
  51. expect(worker.canWork(options)).toBe(false);
  52. });
  53. });
  54. describe('when run is called with incorrect props', () => {
  55. it('then it should return the correct results', async () => {
  56. const { getMock, options } = getTestContext();
  57. const dashboard: any = {};
  58. await expect(worker.work({ ...options, dashboard })).toEmitValuesWith((received) => {
  59. expect(received).toHaveLength(1);
  60. const results = received[0];
  61. expect(results).toEqual({ alertStates: [], annotations: [] });
  62. expect(getMock).not.toHaveBeenCalled();
  63. });
  64. });
  65. });
  66. describe('when run repeatedly for the same dashboard and no alert rules are found', () => {
  67. it('then canWork should start returning false', async () => {
  68. const worker = new UnifiedAlertStatesWorker();
  69. const getResults: PromRulesResponse = {
  70. status: 'success',
  71. data: {
  72. groups: [],
  73. },
  74. };
  75. const { getMock, options } = getTestContext();
  76. getMock.mockResolvedValue(getResults);
  77. expect(worker.canWork(options)).toBe(true);
  78. await lastValueFrom(worker.work(options));
  79. expect(worker.canWork(options)).toBe(false);
  80. });
  81. });
  82. describe('when run is called with correct props and request is successful', () => {
  83. function mockPromRuleDTO(overrides: Partial<PromRuleDTO>): PromRuleDTO {
  84. return {
  85. alerts: [],
  86. health: 'ok',
  87. name: 'foo',
  88. query: 'foo',
  89. type: PromRuleType.Alerting,
  90. state: PromAlertingRuleState.Firing,
  91. labels: {},
  92. annotations: {},
  93. ...overrides,
  94. };
  95. }
  96. it('then it should return the correct results', async () => {
  97. const getResults: PromRulesResponse = {
  98. status: 'success',
  99. data: {
  100. groups: [
  101. {
  102. name: 'group',
  103. file: '',
  104. interval: 1,
  105. rules: [
  106. mockPromRuleDTO({
  107. state: PromAlertingRuleState.Firing,
  108. annotations: {
  109. [Annotation.dashboardUID]: 'a uid',
  110. [Annotation.panelID]: '1',
  111. },
  112. }),
  113. mockPromRuleDTO({
  114. state: PromAlertingRuleState.Inactive,
  115. annotations: {
  116. [Annotation.dashboardUID]: 'a uid',
  117. [Annotation.panelID]: '2',
  118. },
  119. }),
  120. mockPromRuleDTO({
  121. state: PromAlertingRuleState.Pending,
  122. annotations: {
  123. [Annotation.dashboardUID]: 'a uid',
  124. [Annotation.panelID]: '2',
  125. },
  126. }),
  127. ],
  128. },
  129. ],
  130. },
  131. };
  132. const { getMock, options } = getTestContext();
  133. getMock.mockResolvedValue(getResults);
  134. await expect(worker.work(options)).toEmitValuesWith((received) => {
  135. expect(received).toHaveLength(1);
  136. const results = received[0];
  137. expect(results).toEqual({
  138. alertStates: [
  139. { id: 0, state: AlertState.Alerting, dashboardId: 'an id', panelId: 1 },
  140. { id: 1, state: AlertState.Pending, dashboardId: 'an id', panelId: 2 },
  141. ],
  142. annotations: [],
  143. });
  144. });
  145. expect(getMock).toHaveBeenCalledTimes(1);
  146. expect(getMock).toHaveBeenCalledWith(
  147. '/api/prometheus/grafana/api/v1/rules',
  148. { dashboard_uid: 'a uid' },
  149. 'dashboard-query-runner-unified-alert-states-an id'
  150. );
  151. });
  152. });
  153. describe('when run is called with correct props and request fails', () => {
  154. silenceConsoleOutput();
  155. it('then it should return the correct results', async () => {
  156. const { getMock, options, dispatchMock } = getTestContext();
  157. getMock.mockRejectedValue({ message: 'An error' });
  158. await expect(worker.work(options)).toEmitValuesWith((received) => {
  159. expect(received).toHaveLength(1);
  160. const results = received[0];
  161. expect(results).toEqual({ alertStates: [], annotations: [] });
  162. expect(getMock).toHaveBeenCalledTimes(1);
  163. expect(dispatchMock).toHaveBeenCalledTimes(1);
  164. });
  165. });
  166. });
  167. describe('when run is called with correct props and request is cancelled', () => {
  168. silenceConsoleOutput();
  169. it('then it should return the correct results', async () => {
  170. const { getMock, options, dispatchMock } = getTestContext();
  171. getMock.mockRejectedValue({ cancelled: true });
  172. await expect(worker.work(options)).toEmitValuesWith((received) => {
  173. expect(received).toHaveLength(1);
  174. const results = received[0];
  175. expect(results).toEqual({ alertStates: [], annotations: [] });
  176. expect(getMock).toHaveBeenCalledTimes(1);
  177. expect(dispatchMock).not.toHaveBeenCalled();
  178. });
  179. });
  180. });
  181. });
  182. describe('UnifiedAlertStateWorker with RBAC', () => {
  183. beforeAll(() => {
  184. enableRBAC();
  185. grantUserPermissions([]);
  186. });
  187. it('should not do work with insufficient permissions', () => {
  188. const worker = new UnifiedAlertStatesWorker();
  189. const options = getDefaultOptions();
  190. expect(worker.canWork(options)).toBe(false);
  191. });
  192. it('should do work with correct permissions', () => {
  193. grantUserPermissions([AccessControlAction.AlertingRuleRead, AccessControlAction.AlertingRuleExternalRead]);
  194. const workerWithPermissions = new UnifiedAlertStatesWorker();
  195. const options = getDefaultOptions();
  196. expect(workerWithPermissions.canWork(options)).toBe(true);
  197. });
  198. });