]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/shared/services/prometheus-notification.service.spec.ts
import 14.2.4 nautilus point release
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / shared / services / prometheus-notification.service.spec.ts
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { fakeAsync, TestBed, tick } from '@angular/core/testing';
3
4 import { ToastrModule, ToastrService } from 'ngx-toastr';
5 import { of, throwError } from 'rxjs';
6
7 import {
8 configureTestBed,
9 i18nProviders,
10 PrometheusHelper
11 } from '../../../testing/unit-test-helper';
12 import { PrometheusService } from '../api/prometheus.service';
13 import { NotificationType } from '../enum/notification-type.enum';
14 import { CdNotificationConfig } from '../models/cd-notification';
15 import { AlertmanagerNotification } from '../models/prometheus-alerts';
16 import { SharedModule } from '../shared.module';
17 import { NotificationService } from './notification.service';
18 import { PrometheusAlertFormatter } from './prometheus-alert-formatter';
19 import { PrometheusNotificationService } from './prometheus-notification.service';
20
21 describe('PrometheusNotificationService', () => {
22 let service: PrometheusNotificationService;
23 let notificationService: NotificationService;
24 let notifications: AlertmanagerNotification[];
25 let prometheusService: PrometheusService;
26 let prometheus: PrometheusHelper;
27 let shown: CdNotificationConfig[];
28 let getNotificationSinceMock: Function;
29
30 const toastFakeService = {
31 error: () => true,
32 info: () => true,
33 success: () => true
34 };
35
36 configureTestBed({
37 imports: [ToastrModule.forRoot(), SharedModule, HttpClientTestingModule],
38 providers: [
39 PrometheusNotificationService,
40 PrometheusAlertFormatter,
41 i18nProviders,
42 { provide: ToastrService, useValue: toastFakeService }
43 ]
44 });
45
46 beforeEach(() => {
47 prometheus = new PrometheusHelper();
48
49 service = TestBed.get(PrometheusNotificationService);
50 service['notifications'] = [];
51
52 notificationService = TestBed.get(NotificationService);
53 shown = [];
54 spyOn(notificationService, 'show').and.callThrough();
55 spyOn(notificationService, 'save').and.callFake((n) => shown.push(n));
56
57 spyOn(window, 'setTimeout').and.callFake((fn: Function) => fn());
58
59 prometheusService = TestBed.get(PrometheusService);
60 getNotificationSinceMock = () => of(notifications);
61 spyOn(prometheusService, 'getNotifications').and.callFake(() => getNotificationSinceMock());
62
63 notifications = [prometheus.createNotification()];
64 });
65
66 it('should create', () => {
67 expect(service).toBeTruthy();
68 });
69
70 describe('getLastNotification', () => {
71 it('returns an empty object on the first call', () => {
72 service.refresh();
73 expect(prometheusService.getNotifications).toHaveBeenCalledWith(undefined);
74 expect(service['notifications'].length).toBe(1);
75 });
76
77 it('returns last notification on any other call', () => {
78 service.refresh();
79 notifications = [prometheus.createNotification(1, 'resolved')];
80 service.refresh();
81 expect(prometheusService.getNotifications).toHaveBeenCalledWith(service['notifications'][0]);
82 expect(service['notifications'].length).toBe(2);
83
84 notifications = [prometheus.createNotification(2)];
85 service.refresh();
86 notifications = [prometheus.createNotification(3, 'resolved')];
87 service.refresh();
88 expect(prometheusService.getNotifications).toHaveBeenCalledWith(service['notifications'][2]);
89 expect(service['notifications'].length).toBe(4);
90 });
91 });
92
93 it('notifies not on the first call', () => {
94 service.refresh();
95 expect(notificationService.save).not.toHaveBeenCalled();
96 });
97
98 it('notifies should not call the api again if it failed once', () => {
99 getNotificationSinceMock = () => throwError(new Error('Test error'));
100 service.refresh();
101 expect(prometheusService.getNotifications).toHaveBeenCalledTimes(1);
102 expect(service['backendFailure']).toBe(true);
103 service.refresh();
104 expect(prometheusService.getNotifications).toHaveBeenCalledTimes(1);
105 service['backendFailure'] = false;
106 });
107
108 describe('looks of fired notifications', () => {
109 const asyncRefresh = () => {
110 service.refresh();
111 tick(20);
112 };
113
114 const expectShown = (expected: {}[]) => {
115 tick(500);
116 expect(shown.length).toBe(expected.length);
117 expected.forEach((e, i) =>
118 Object.keys(e).forEach((key) => expect(shown[i][key]).toEqual(expected[i][key]))
119 );
120 };
121
122 beforeEach(() => {
123 service.refresh();
124 });
125
126 it('notifies on the second call', () => {
127 service.refresh();
128 expect(notificationService.show).toHaveBeenCalledTimes(1);
129 });
130
131 it('notify looks on single notification with single alert like', fakeAsync(() => {
132 asyncRefresh();
133 expectShown([
134 new CdNotificationConfig(
135 NotificationType.error,
136 'alert0 (active)',
137 'alert0 is firing ' + prometheus.createLink('http://alert0'),
138 undefined,
139 'Prometheus'
140 )
141 ]);
142 }));
143
144 it('raises multiple pop overs for a single notification with multiple alerts', fakeAsync(() => {
145 asyncRefresh();
146 notifications[0].alerts.push(prometheus.createNotificationAlert('alert1', 'resolved'));
147 asyncRefresh();
148 expectShown([
149 new CdNotificationConfig(
150 NotificationType.error,
151 'alert0 (active)',
152 'alert0 is firing ' + prometheus.createLink('http://alert0'),
153 undefined,
154 'Prometheus'
155 ),
156 new CdNotificationConfig(
157 NotificationType.success,
158 'alert1 (resolved)',
159 'alert1 is resolved ' + prometheus.createLink('http://alert1'),
160 undefined,
161 'Prometheus'
162 )
163 ]);
164 }));
165
166 it('should raise multiple notifications if they do not look like each other', fakeAsync(() => {
167 notifications[0].alerts.push(prometheus.createNotificationAlert('alert1'));
168 notifications.push(prometheus.createNotification());
169 notifications[1].alerts.push(prometheus.createNotificationAlert('alert2'));
170 asyncRefresh();
171 expectShown([
172 new CdNotificationConfig(
173 NotificationType.error,
174 'alert0 (active)',
175 'alert0 is firing ' + prometheus.createLink('http://alert0'),
176 undefined,
177 'Prometheus'
178 ),
179 new CdNotificationConfig(
180 NotificationType.error,
181 'alert1 (active)',
182 'alert1 is firing ' + prometheus.createLink('http://alert1'),
183 undefined,
184 'Prometheus'
185 ),
186 new CdNotificationConfig(
187 NotificationType.error,
188 'alert2 (active)',
189 'alert2 is firing ' + prometheus.createLink('http://alert2'),
190 undefined,
191 'Prometheus'
192 )
193 ]);
194 }));
195
196 it('only shows toasties if it got new data', () => {
197 service.refresh();
198 expect(notificationService.save).toHaveBeenCalledTimes(1);
199 notifications = [];
200 service.refresh();
201 service.refresh();
202 expect(notificationService.save).toHaveBeenCalledTimes(1);
203 notifications = [prometheus.createNotification()];
204 service.refresh();
205 expect(notificationService.save).toHaveBeenCalledTimes(2);
206 service.refresh();
207 expect(notificationService.save).toHaveBeenCalledTimes(3);
208 });
209
210 it('filters out duplicated and non user visible changes in notifications', fakeAsync(() => {
211 asyncRefresh();
212 // Return 2 notifications with 3 duplicated alerts and 1 non visible changed alert
213 const secondAlert = prometheus.createNotificationAlert('alert0');
214 secondAlert.endsAt = new Date().toString(); // Should be ignored as it's not visible
215 notifications[0].alerts.push(secondAlert);
216 notifications.push(prometheus.createNotification());
217 notifications[1].alerts.push(prometheus.createNotificationAlert('alert0'));
218 notifications[1].notified = 'by somebody else';
219 asyncRefresh();
220
221 expectShown([
222 new CdNotificationConfig(
223 NotificationType.error,
224 'alert0 (active)',
225 'alert0 is firing ' + prometheus.createLink('http://alert0'),
226 undefined,
227 'Prometheus'
228 )
229 ]);
230 }));
231 });
232 });