useIsRuleEditable.test.tsx 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. import { renderHook } from '@testing-library/react-hooks';
  2. import React from 'react';
  3. import { Provider } from 'react-redux';
  4. import { contextSrv } from 'app/core/services/context_srv';
  5. import { configureStore } from 'app/store/configureStore';
  6. import { AccessControlAction, FolderDTO, StoreState } from 'app/types';
  7. import { disableRBAC, enableRBAC, mockFolder, mockRulerAlertingRule, mockRulerGrafanaRule } from '../mocks';
  8. import { useFolder } from './useFolder';
  9. import { useIsRuleEditable } from './useIsRuleEditable';
  10. import { useUnifiedAlertingSelector } from './useUnifiedAlertingSelector';
  11. jest.mock('./useFolder');
  12. const mocks = {
  13. useFolder: jest.mocked(useFolder),
  14. useUnifiedAlertingSelector: jest.mocked(useUnifiedAlertingSelector),
  15. };
  16. describe('useIsRuleEditable', () => {
  17. describe('RBAC enabled', () => {
  18. beforeEach(enableRBAC);
  19. describe('Grafana rules', () => {
  20. // When RBAC is enabled we require only folder:read permission and apriopriate alerting permissions
  21. beforeEach(() => {
  22. mockUseFolder({ canSave: false });
  23. });
  24. it('Should allow editing when the user has the alert rule update permission', () => {
  25. mockPermissions([AccessControlAction.AlertingRuleUpdate]);
  26. const wrapper = getProviderWrapper();
  27. const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
  28. expect(result.current.loading).toBe(false);
  29. expect(result.current.isEditable).toBe(true);
  30. });
  31. it('Should allow deleting when the user has the alert rule delete permission', () => {
  32. mockPermissions([AccessControlAction.AlertingRuleDelete]);
  33. const wrapper = getProviderWrapper();
  34. const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
  35. expect(result.current.loading).toBe(false);
  36. expect(result.current.isRemovable).toBe(true);
  37. });
  38. it('Should forbid editing when the user has no alert rule update permission', () => {
  39. mockPermissions([]);
  40. const wrapper = getProviderWrapper();
  41. const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
  42. expect(result.current.loading).toBe(false);
  43. expect(result.current.isEditable).toBe(false);
  44. });
  45. it('Should forbid deleting when the user has no alert rule delete permission', () => {
  46. mockPermissions([]);
  47. const wrapper = getProviderWrapper();
  48. const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
  49. expect(result.current.loading).toBe(false);
  50. expect(result.current.isRemovable).toBe(false);
  51. });
  52. it('Should allow editing and deleting when the user has aler rule permissions but does not have folder canSave permission', () => {
  53. mockPermissions([AccessControlAction.AlertingRuleUpdate, AccessControlAction.AlertingRuleDelete]);
  54. mockUseFolder({ canSave: false });
  55. const wrapper = getProviderWrapper();
  56. const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
  57. expect(result.current.loading).toBe(false);
  58. expect(result.current.isEditable).toBe(true);
  59. expect(result.current.isRemovable).toBe(true);
  60. });
  61. });
  62. describe('Cloud rules', () => {
  63. beforeEach(() => {
  64. contextSrv.isEditor = true;
  65. });
  66. it('Should allow editing and deleting when the user has alert rule external write permission', () => {
  67. mockPermissions([AccessControlAction.AlertingRuleExternalWrite]);
  68. const wrapper = getProviderWrapper();
  69. const { result } = renderHook(() => useIsRuleEditable('cortex', mockRulerAlertingRule()), { wrapper });
  70. expect(result.current.loading).toBe(false);
  71. expect(result.current.isEditable).toBe(true);
  72. expect(result.current.isRemovable).toBe(true);
  73. });
  74. it('Should forbid editing and deleting when the user has no alert rule external write permission', () => {
  75. mockPermissions([]);
  76. const wrapper = getProviderWrapper();
  77. const { result } = renderHook(() => useIsRuleEditable('cortex', mockRulerAlertingRule()), { wrapper });
  78. expect(result.current.loading).toBe(false);
  79. expect(result.current.isEditable).toBe(false);
  80. expect(result.current.isRemovable).toBe(false);
  81. });
  82. });
  83. });
  84. describe('RBAC disabled', () => {
  85. beforeEach(disableRBAC);
  86. describe('Grafana rules', () => {
  87. it('Should allow editing and deleting when the user has folder canSave permission', () => {
  88. mockUseFolder({ canSave: true });
  89. const wrapper = getProviderWrapper();
  90. const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
  91. expect(result.current.loading).toBe(false);
  92. expect(result.current.isEditable).toBe(true);
  93. expect(result.current.isRemovable).toBe(true);
  94. });
  95. it('Should forbid editing and deleting when the user has no folder canSave permission', () => {
  96. mockUseFolder({ canSave: false });
  97. const wrapper = getProviderWrapper();
  98. const { result } = renderHook(() => useIsRuleEditable('grafana', mockRulerGrafanaRule()), { wrapper });
  99. expect(result.current.loading).toBe(false);
  100. expect(result.current.isEditable).toBe(false);
  101. expect(result.current.isRemovable).toBe(false);
  102. });
  103. });
  104. });
  105. });
  106. function mockUseFolder(partial?: Partial<FolderDTO>) {
  107. mocks.useFolder.mockReturnValue({ loading: false, folder: mockFolder(partial) });
  108. }
  109. function mockPermissions(grantedPermissions: AccessControlAction[]) {
  110. jest
  111. .spyOn(contextSrv, 'hasPermission')
  112. .mockImplementation((action) => grantedPermissions.includes(action as AccessControlAction));
  113. }
  114. function getProviderWrapper() {
  115. const dataSources = getMockedDataSources();
  116. const store = mockStore({ dataSources });
  117. const wrapper: React.FC = ({ children }) => <Provider store={store}>{children}</Provider>;
  118. return wrapper;
  119. }
  120. function getMockedDataSources(): StoreState['unifiedAlerting']['dataSources'] {
  121. return {
  122. grafana: {
  123. loading: false,
  124. dispatched: false,
  125. result: {
  126. id: 'grafana',
  127. name: 'grafana',
  128. rulerConfig: { dataSourceName: 'grafana', apiVersion: 'legacy' },
  129. },
  130. },
  131. cortex: {
  132. loading: false,
  133. dispatched: false,
  134. result: {
  135. id: 'cortex',
  136. name: 'Cortex',
  137. rulerConfig: { dataSourceName: 'cortex', apiVersion: 'legacy' },
  138. },
  139. },
  140. };
  141. }
  142. function mockStore(unifiedAlerting?: Partial<StoreState['unifiedAlerting']>) {
  143. const defaultState = configureStore().getState();
  144. return configureStore({
  145. ...defaultState,
  146. unifiedAlerting: {
  147. ...defaultState.unifiedAlerting,
  148. ...unifiedAlerting,
  149. },
  150. });
  151. }