From b74465e4280e4552def390ec34a1a17ed259a5ce Mon Sep 17 00:00:00 2001 From: Sonia Aguilar <33540275+soniaAguilarPeiron@users.noreply.github.com> Date: Wed, 3 Dec 2025 12:33:48 +0100 Subject: [PATCH] Alerting: Fix 'Rule group does not exist' error toast (#101949) (#114766) Alerting: Fix "Rule group does not exist" error toast (#101949) --- .../unified/hooks/useCombinedRule.test.tsx | 92 +++++++++++++++++++ .../alerting/unified/hooks/useCombinedRule.ts | 6 ++ 2 files changed, 98 insertions(+) create mode 100644 public/app/features/alerting/unified/hooks/useCombinedRule.test.tsx diff --git a/public/app/features/alerting/unified/hooks/useCombinedRule.test.tsx b/public/app/features/alerting/unified/hooks/useCombinedRule.test.tsx new file mode 100644 index 00000000000..9c1cf313d7c --- /dev/null +++ b/public/app/features/alerting/unified/hooks/useCombinedRule.test.tsx @@ -0,0 +1,92 @@ +import { HttpResponse, http } from 'msw'; +import { render, screen, waitFor } from 'test/test-utils'; + +import { AppNotificationList } from 'app/core/components/AppNotifications/AppNotificationList'; +import { AccessControlAction } from 'app/types/accessControl'; +import { GrafanaRuleIdentifier } from 'app/types/unified-alerting'; + +import { setupMswServer } from '../mockApi'; +import { grantUserPermissions } from '../mocks'; +import { grafanaRulerNamespace, grafanaRulerRule } from '../mocks/grafanaRulerApi'; +import { GRAFANA_RULES_SOURCE_NAME } from '../utils/datasource'; + +import { useCombinedRule } from './useCombinedRule'; + +const server = setupMswServer(); + +beforeAll(() => { + grantUserPermissions([AccessControlAction.AlertingRuleExternalRead, AccessControlAction.AlertingRuleRead]); +}); + +// Test component that uses useCombinedRule hook +const UseCombinedRuleTestComponent = ({ ruleIdentifier }: { ruleIdentifier: GrafanaRuleIdentifier }) => { + const { loading, error, result } = useCombinedRule({ ruleIdentifier }); + + return ( + <> + +
{loading ? 'loading' : 'not-loading'}
+
{error ? 'has-error' : 'no-error'}
+
{result ? 'has-result' : 'no-result'}
+ + ); +}; + +describe('useCombinedRule', () => { + describe('when rule group returns 404', () => { + it('should not show error notification when rule group does not exist', async () => { + // Mock the getAlertRule endpoint to return a valid rule + server.use( + http.get('/api/ruler/grafana/api/v1/rule/:uid', () => { + return HttpResponse.json(grafanaRulerRule); + }) + ); + + // Mock the ruler group endpoint to return 404 (simulating a new or deleted group) + server.use( + http.get('/api/ruler/grafana/api/v1/rules/:namespace/:group', () => { + return HttpResponse.json({ error: 'rule group does not exist' }, { status: 404 }); + }) + ); + + // Mock the prometheus rules endpoint + server.use( + http.get('/api/prometheus/grafana/api/v1/rules', () => { + return HttpResponse.json({ + status: 'success', + data: { groups: [] }, + }); + }) + ); + + // Mock the folder endpoint + server.use( + http.get('/api/folders/:uid', () => { + return HttpResponse.json({ + uid: grafanaRulerNamespace.uid, + title: grafanaRulerNamespace.name, + }); + }) + ); + + const ruleIdentifier: GrafanaRuleIdentifier = { + ruleSourceName: GRAFANA_RULES_SOURCE_NAME, + uid: grafanaRulerRule.grafana_alert.uid, + }; + + render(); + + // Wait for the hook to finish loading + await waitFor(() => { + expect(screen.getByTestId('loading')).toHaveTextContent('not-loading'); + }); + + // The hook should have an error (404) + expect(screen.getByTestId('error')).toHaveTextContent('has-error'); + + // Verify that no error notification was shown + // If showErrorAlert was true, we would see an alert with role="status" + expect(screen.queryByRole('status')).not.toBeInTheDocument(); + }); + }); +}); diff --git a/public/app/features/alerting/unified/hooks/useCombinedRule.ts b/public/app/features/alerting/unified/hooks/useCombinedRule.ts index 4bb06a6c962..3fd0faeb783 100644 --- a/public/app/features/alerting/unified/hooks/useCombinedRule.ts +++ b/public/app/features/alerting/unified/hooks/useCombinedRule.ts @@ -68,6 +68,9 @@ export function useCloudCombinedRulesMatching( rulerConfig: rulerConfig, namespace: nsGroup.namespace.name, group: nsGroup.group.name, + // Suppress error notifications for 404s - the group may not exist yet (new group) + // or may have been deleted (last rule removed) + notificationOptions: { showErrorAlert: false }, }).unwrap(); rulerGroups.push(rulerGroup); }) @@ -145,6 +148,9 @@ export function useCombinedRule({ ruleIdentifier, limitAlerts }: Props): Request rulerConfig: dsFeatures.rulerConfig, namespace: ruleLocation.namespace, group: ruleLocation.group, + // Suppress error notifications for 404s - the group may not exist yet (new group) + // or may have been deleted (last rule removed) + notificationOptions: { showErrorAlert: false }, }); }, [dsFeatures, fetchRulerRuleGroup, ruleLocation]);