diff options
Diffstat (limited to 'src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.spec.ts')
-rw-r--r-- | src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.spec.ts | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.spec.ts b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.spec.ts new file mode 100644 index 000000000..aa3160b30 --- /dev/null +++ b/src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-alert.service.spec.ts @@ -0,0 +1,214 @@ +import { HttpClientTestingModule } from '@angular/common/http/testing'; +import { TestBed } from '@angular/core/testing'; + +import { ToastrModule } from 'ngx-toastr'; +import { Observable, of } from 'rxjs'; + +import { configureTestBed, PrometheusHelper } from '~/testing/unit-test-helper'; +import { PrometheusService } from '../api/prometheus.service'; +import { NotificationType } from '../enum/notification-type.enum'; +import { CdNotificationConfig } from '../models/cd-notification'; +import { AlertmanagerAlert } from '../models/prometheus-alerts'; +import { SharedModule } from '../shared.module'; +import { NotificationService } from './notification.service'; +import { PrometheusAlertFormatter } from './prometheus-alert-formatter'; +import { PrometheusAlertService } from './prometheus-alert.service'; + +describe('PrometheusAlertService', () => { + let service: PrometheusAlertService; + let notificationService: NotificationService; + let alerts: AlertmanagerAlert[]; + let prometheusService: PrometheusService; + let prometheus: PrometheusHelper; + + configureTestBed({ + imports: [ToastrModule.forRoot(), SharedModule, HttpClientTestingModule], + providers: [PrometheusAlertService, PrometheusAlertFormatter] + }); + + beforeEach(() => { + prometheus = new PrometheusHelper(); + }); + + it('should create', () => { + expect(TestBed.inject(PrometheusAlertService)).toBeTruthy(); + }); + + describe('test failing status codes and verify disabling of the alertmanager', () => { + const isDisabledByStatusCode = (statusCode: number, expectedStatus: boolean, done: any) => { + service = TestBed.inject(PrometheusAlertService); + prometheusService = TestBed.inject(PrometheusService); + spyOn(prometheusService, 'ifAlertmanagerConfigured').and.callFake((fn) => fn()); + spyOn(prometheusService, 'getAlerts').and.returnValue( + new Observable((observer: any) => observer.error({ status: statusCode, error: {} })) + ); + const disableFn = spyOn(prometheusService, 'disableAlertmanagerConfig').and.callFake(() => { + expect(expectedStatus).toBe(true); + done(); + }); + + if (!expectedStatus) { + expect(disableFn).not.toHaveBeenCalled(); + done(); + } + + service.getAlerts(); + }; + + it('disables on 504 error which is thrown if the mgr failed', (done) => { + isDisabledByStatusCode(504, true, done); + }); + + it('disables on 404 error which is thrown if the external api cannot be reached', (done) => { + isDisabledByStatusCode(404, true, done); + }); + + it('does not disable on 400 error which is thrown if the external api receives unexpected data', (done) => { + isDisabledByStatusCode(400, false, done); + }); + }); + + it('should flatten the response of getRules()', () => { + service = TestBed.inject(PrometheusAlertService); + prometheusService = TestBed.inject(PrometheusService); + + spyOn(service['prometheusService'], 'ifPrometheusConfigured').and.callFake((fn) => fn()); + spyOn(prometheusService, 'getRules').and.returnValue( + of({ + groups: [ + { + name: 'group1', + rules: [{ name: 'nearly_full', type: 'alerting' }] + }, + { + name: 'test', + rules: [ + { name: 'load_0', type: 'alerting' }, + { name: 'load_1', type: 'alerting' }, + { name: 'load_2', type: 'alerting' } + ] + } + ] + }) + ); + + service.getRules(); + + expect(service.rules as any).toEqual([ + { name: 'nearly_full', type: 'alerting', group: 'group1' }, + { name: 'load_0', type: 'alerting', group: 'test' }, + { name: 'load_1', type: 'alerting', group: 'test' }, + { name: 'load_2', type: 'alerting', group: 'test' } + ]); + }); + + describe('refresh', () => { + beforeEach(() => { + service = TestBed.inject(PrometheusAlertService); + service['alerts'] = []; + service['canAlertsBeNotified'] = false; + + spyOn(window, 'setTimeout').and.callFake((fn: Function) => fn()); + + notificationService = TestBed.inject(NotificationService); + spyOn(notificationService, 'show').and.stub(); + + prometheusService = TestBed.inject(PrometheusService); + spyOn(prometheusService, 'ifAlertmanagerConfigured').and.callFake((fn) => fn()); + spyOn(prometheusService, 'getAlerts').and.callFake(() => of(alerts)); + + alerts = [prometheus.createAlert('alert0')]; + service.refresh(); + }); + + it('should not notify on first call', () => { + expect(notificationService.show).not.toHaveBeenCalled(); + }); + + it('should not notify with no change', () => { + service.refresh(); + expect(notificationService.show).not.toHaveBeenCalled(); + }); + + it('should notify on alert change', () => { + alerts = [prometheus.createAlert('alert0', 'resolved')]; + service.refresh(); + expect(notificationService.show).toHaveBeenCalledWith( + new CdNotificationConfig( + NotificationType.success, + 'alert0 (resolved)', + 'alert0 is resolved ' + prometheus.createLink('http://alert0'), + undefined, + 'Prometheus' + ) + ); + }); + + it('should not notify on change to suppressed', () => { + alerts = [prometheus.createAlert('alert0', 'suppressed')]; + service.refresh(); + expect(notificationService.show).not.toHaveBeenCalled(); + }); + + it('should notify on a new alert', () => { + alerts = [prometheus.createAlert('alert1'), prometheus.createAlert('alert0')]; + service.refresh(); + expect(notificationService.show).toHaveBeenCalledTimes(1); + expect(notificationService.show).toHaveBeenCalledWith( + new CdNotificationConfig( + NotificationType.error, + 'alert1 (active)', + 'alert1 is active ' + prometheus.createLink('http://alert1'), + undefined, + 'Prometheus' + ) + ); + }); + + it('should notify a resolved alert if it is not there anymore', () => { + alerts = []; + service.refresh(); + expect(notificationService.show).toHaveBeenCalledTimes(1); + expect(notificationService.show).toHaveBeenCalledWith( + new CdNotificationConfig( + NotificationType.success, + 'alert0 (resolved)', + 'alert0 is active ' + prometheus.createLink('http://alert0'), + undefined, + 'Prometheus' + ) + ); + }); + + it('should call multiple times for multiple changes', () => { + const alert1 = prometheus.createAlert('alert1'); + alerts.push(alert1); + service.refresh(); + alerts = [alert1, prometheus.createAlert('alert2')]; + service.refresh(); + expect(notificationService.show).toHaveBeenCalledTimes(2); + }); + }); + + describe('alert badge', () => { + beforeEach(() => { + service = TestBed.inject(PrometheusAlertService); + + prometheusService = TestBed.inject(PrometheusService); + spyOn(prometheusService, 'ifAlertmanagerConfigured').and.callFake((fn) => fn()); + spyOn(prometheusService, 'getAlerts').and.callFake(() => of(alerts)); + + alerts = [ + prometheus.createAlert('alert0', 'active'), + prometheus.createAlert('alert1', 'suppressed'), + prometheus.createAlert('alert2', 'suppressed') + ]; + service.refresh(); + }); + + it('should count active alerts', () => { + service.refresh(); + expect(service.activeAlerts).toBe(1); + }); + }); +}); |