]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard-v3/dashboard/dashboard-v3.component.spec.ts
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / dashboard-v3 / dashboard / dashboard-v3.component.spec.ts
CommitLineData
1e59de90
TL
1import { HttpClientTestingModule } from '@angular/common/http/testing';
2import { NO_ERRORS_SCHEMA } from '@angular/core';
3import { ComponentFixture, TestBed } from '@angular/core/testing';
4import { By } from '@angular/platform-browser';
5import { RouterTestingModule } from '@angular/router/testing';
6
7import _ from 'lodash';
8import { ToastrModule } from 'ngx-toastr';
9import { BehaviorSubject, of } from 'rxjs';
10
11import { HealthService } from '~/app/shared/api/health.service';
12import { PrometheusService } from '~/app/shared/api/prometheus.service';
13import { CssHelper } from '~/app/shared/classes/css-helper';
14import { AlertmanagerAlert } from '~/app/shared/models/prometheus-alerts';
15import { FeatureTogglesService } from '~/app/shared/services/feature-toggles.service';
16import { PrometheusAlertService } from '~/app/shared/services/prometheus-alert.service';
17import { SummaryService } from '~/app/shared/services/summary.service';
18import { SharedModule } from '~/app/shared/shared.module';
19import { configureTestBed } from '~/testing/unit-test-helper';
20import { PgCategoryService } from '../../shared/pg-category.service';
21import { CardRowComponent } from '../card-row/card-row.component';
22import { CardComponent } from '../card/card.component';
23import { DashboardPieComponent } from '../dashboard-pie/dashboard-pie.component';
24import { PgSummaryPipe } from '../pg-summary.pipe';
25import { DashboardV3Component } from './dashboard-v3.component';
26import { OrchestratorService } from '~/app/shared/api/orchestrator.service';
27
28export class SummaryServiceMock {
29 summaryDataSource = new BehaviorSubject({
30 version:
31 'ceph version 17.0.0-12222-gcd0cd7cb ' +
32 '(b8193bb4cda16ccc5b028c3e1df62bc72350a15d) quincy (dev)'
33 });
34 summaryData$ = this.summaryDataSource.asObservable();
35
36 subscribe(call: any) {
37 return this.summaryData$.subscribe(call);
38 }
39}
40
41describe('Dashbord Component', () => {
42 let component: DashboardV3Component;
43 let fixture: ComponentFixture<DashboardV3Component>;
44 let healthService: HealthService;
45 let orchestratorService: OrchestratorService;
46 let getHealthSpy: jasmine.Spy;
47 let getAlertsSpy: jasmine.Spy;
48 let fakeFeatureTogglesService: jasmine.Spy;
49
50 const healthPayload: Record<string, any> = {
51 health: { status: 'HEALTH_OK' },
52 mon_status: { monmap: { mons: [] }, quorum: [] },
53 osd_map: { osds: [] },
54 mgr_map: { standbys: [] },
55 hosts: 0,
56 rgw: 0,
57 fs_map: { filesystems: [], standbys: [] },
58 iscsi_daemons: 1,
59 client_perf: {},
60 scrub_status: 'Inactive',
61 pools: [],
62 df: { stats: {} },
63 pg_info: { object_stats: { num_objects: 1 } }
64 };
65
66 const alertsPayload: AlertmanagerAlert[] = [
67 {
68 labels: {
69 alertname: 'CephMgrPrometheusModuleInactive',
70 instance: 'ceph2:9283',
71 job: 'ceph',
72 severity: 'critical'
73 },
74 annotations: {
75 description: 'The mgr/prometheus module at ceph2:9283 is unreachable.',
76 summary: 'The mgr/prometheus module is not available'
77 },
78 startsAt: '2022-09-28T08:23:41.152Z',
79 endsAt: '2022-09-28T15:28:01.152Z',
80 generatorURL: 'http://prometheus:9090/testUrl',
81 status: {
82 state: 'active',
83 silencedBy: null,
84 inhibitedBy: null
85 },
86 receivers: ['ceph2'],
87 fingerprint: 'fingerprint'
88 },
89 {
90 labels: {
91 alertname: 'CephOSDDownHigh',
92 instance: 'ceph:9283',
93 job: 'ceph',
94 severity: 'critical'
95 },
96 annotations: {
97 description: '66.67% or 2 of 3 OSDs are down (>= 10%).',
98 summary: 'More than 10% of OSDs are down'
99 },
100 startsAt: '2022-09-28T14:17:22.665Z',
101 endsAt: '2022-09-28T15:28:32.665Z',
102 generatorURL: 'http://prometheus:9090/testUrl',
103 status: {
104 state: 'active',
105 silencedBy: null,
106 inhibitedBy: null
107 },
108 receivers: ['default'],
109 fingerprint: 'fingerprint'
110 },
111 {
112 labels: {
113 alertname: 'CephHealthWarning',
114 instance: 'ceph:9283',
115 job: 'ceph',
116 severity: 'warning'
117 },
118 annotations: {
119 description: 'The cluster state has been HEALTH_WARN for more than 15 minutes.',
120 summary: 'Ceph is in the WARNING state'
121 },
122 startsAt: '2022-09-28T08:41:38.454Z',
123 endsAt: '2022-09-28T15:28:38.454Z',
124 generatorURL: 'http://prometheus:9090/testUrl',
125 status: {
126 state: 'active',
127 silencedBy: null,
128 inhibitedBy: null
129 },
130 receivers: ['ceph'],
131 fingerprint: 'fingerprint'
132 }
133 ];
134
135 const configValueData: any = 'e90a0d58-658e-4148-8f61-e896c86f0696';
136
137 const orchName: any = 'Cephadm';
138
139 configureTestBed({
140 imports: [RouterTestingModule, HttpClientTestingModule, ToastrModule.forRoot(), SharedModule],
141 declarations: [
142 DashboardV3Component,
143 CardComponent,
144 DashboardPieComponent,
145 CardRowComponent,
146 PgSummaryPipe
147 ],
148 schemas: [NO_ERRORS_SCHEMA],
149 providers: [
150 { provide: SummaryService, useClass: SummaryServiceMock },
151 {
152 provide: PrometheusAlertService,
153 useValue: {
154 activeCriticalAlerts: 2,
155 activeWarningAlerts: 1
156 }
157 },
158 CssHelper,
159 PgCategoryService
160 ]
161 });
162
163 beforeEach(() => {
164 fakeFeatureTogglesService = spyOn(TestBed.inject(FeatureTogglesService), 'get').and.returnValue(
165 of({
166 rbd: true,
167 mirroring: true,
168 iscsi: true,
169 cephfs: true,
170 rgw: true
171 })
172 );
173 fixture = TestBed.createComponent(DashboardV3Component);
174 component = fixture.componentInstance;
175 healthService = TestBed.inject(HealthService);
176 orchestratorService = TestBed.inject(OrchestratorService);
177 getHealthSpy = spyOn(TestBed.inject(HealthService), 'getMinimalHealth');
178 getHealthSpy.and.returnValue(of(healthPayload));
179 spyOn(TestBed.inject(PrometheusService), 'ifAlertmanagerConfigured').and.callFake((fn) => fn());
180 getAlertsSpy = spyOn(TestBed.inject(PrometheusService), 'getAlerts');
181 getAlertsSpy.and.returnValue(of(alertsPayload));
182 });
183
184 it('should create', () => {
185 expect(component).toBeTruthy();
186 });
187
188 it('should render all cards', () => {
189 fixture.detectChanges();
190 const dashboardCards = fixture.debugElement.nativeElement.querySelectorAll('cd-card');
191 expect(dashboardCards.length).toBe(5);
192 });
193
194 it('should get corresponding data into detailsCardData', () => {
195 spyOn(healthService, 'getClusterFsid').and.returnValue(of(configValueData));
196 spyOn(orchestratorService, 'getName').and.returnValue(of(orchName));
197 component.ngOnInit();
198 expect(component.detailsCardData.fsid).toBe('e90a0d58-658e-4148-8f61-e896c86f0696');
199 expect(component.detailsCardData.orchestrator).toBe('Cephadm');
200 expect(component.detailsCardData.cephVersion).toBe('17.0.0-12222-gcd0cd7cb quincy (dev)');
201 });
202
203 it('should check if the respective icon is shown for each status', () => {
204 const payload = _.cloneDeep(healthPayload);
205
206 // HEALTH_WARN
207 payload.health['status'] = 'HEALTH_WARN';
208 payload.health['checks'] = [
209 { severity: 'HEALTH_WARN', type: 'WRN', summary: { message: 'fake warning' } }
210 ];
211
212 getHealthSpy.and.returnValue(of(payload));
213 fixture.detectChanges();
214 const clusterStatusCard = fixture.debugElement.query(By.css('cd-card[cardTitle="Status"] i'));
215 expect(clusterStatusCard.nativeElement.title).toEqual(`${payload.health.status}`);
216
217 // HEALTH_ERR
218 payload.health['status'] = 'HEALTH_ERR';
219 payload.health['checks'] = [
220 { severity: 'HEALTH_ERR', type: 'ERR', summary: { message: 'fake error' } }
221 ];
222
223 getHealthSpy.and.returnValue(of(payload));
224 fixture.detectChanges();
225 expect(clusterStatusCard.nativeElement.title).toEqual(`${payload.health.status}`);
226
227 // HEALTH_OK
228 payload.health['status'] = 'HEALTH_OK';
229 payload.health['checks'] = [
230 { severity: 'HEALTH_OK', type: 'OK', summary: { message: 'fake success' } }
231 ];
232
233 getHealthSpy.and.returnValue(of(payload));
234 fixture.detectChanges();
235 expect(clusterStatusCard.nativeElement.title).toEqual(`${payload.health.status}`);
236 });
237
238 it('should show the actual alert count on each alerts pill', () => {
239 fixture.detectChanges();
240
241 const warningAlerts = fixture.debugElement.query(By.css('button[id=warningAlerts] span'));
242
243 const dangerAlerts = fixture.debugElement.query(By.css('button[id=dangerAlerts] span'));
244
245 expect(warningAlerts.nativeElement.textContent).toBe('1');
246 expect(dangerAlerts.nativeElement.textContent).toBe('2');
247 });
248
249 it('should show the critical alerts window and its content', () => {
250 const payload = _.cloneDeep(alertsPayload[0]);
251 component.toggleAlertsWindow('danger');
252 fixture.detectChanges();
253
254 const cardTitle = fixture.debugElement.query(By.css('.tc_alerts h6.card-title'));
255
256 expect(cardTitle.nativeElement.textContent).toBe(payload.labels.alertname);
257 expect(component.alertType).not.toBe('warning');
258 });
259
260 it('should show the warning alerts window and its content', () => {
261 const payload = _.cloneDeep(alertsPayload[2]);
262 component.toggleAlertsWindow('warning');
263 fixture.detectChanges();
264
265 const cardTitle = fixture.debugElement.query(By.css('.tc_alerts h6.card-title'));
266
267 expect(cardTitle.nativeElement.textContent).toBe(payload.labels.alertname);
268 expect(component.alertType).not.toBe('critical');
269 });
270
271 it('should only show the pills when the alerts are not empty', () => {
272 spyOn(TestBed.inject(PrometheusAlertService), 'activeCriticalAlerts').and.returnValue(0);
273 spyOn(TestBed.inject(PrometheusAlertService), 'activeWarningAlerts').and.returnValue(0);
274 fixture.detectChanges();
275
276 const warningAlerts = fixture.debugElement.query(By.css('button[id=warningAlerts]'));
277
278 const dangerAlerts = fixture.debugElement.query(By.css('button[id=dangerAlerts]'));
279
280 expect(warningAlerts).toBe(null);
281 expect(dangerAlerts).toBe(null);
282 });
283
284 it('should render "Status" card text that is not clickable', () => {
285 fixture.detectChanges();
286
287 const clusterStatusCard = fixture.debugElement.query(By.css('cd-card[cardTitle="Status"]'));
288 const clickableContent = clusterStatusCard.query(By.css('.lead.text-primary'));
289 expect(clickableContent).toBeNull();
290 });
291
292 it('should render "Status" card text that is clickable (popover)', () => {
293 const payload = _.cloneDeep(healthPayload);
294 payload.health['status'] = 'HEALTH_WARN';
295 payload.health['checks'] = [
296 { severity: 'HEALTH_WARN', type: 'WRN', summary: { message: 'fake warning' } }
297 ];
298
299 getHealthSpy.and.returnValue(of(payload));
300 fixture.detectChanges();
301
302 const clusterStatusCard = fixture.debugElement.query(By.css('cd-card[cardTitle="Status"]'));
303 const clickableContent = clusterStatusCard.query(By.css('.lead.text-primary'));
304 expect(clickableContent).not.toBeNull();
305 });
306
307 describe('features disabled', () => {
308 beforeEach(() => {
309 fakeFeatureTogglesService.and.returnValue(
310 of({
311 rbd: false,
312 mirroring: false,
313 iscsi: false,
314 cephfs: false,
315 rgw: false
316 })
317 );
318 fixture = TestBed.createComponent(DashboardV3Component);
319 component = fixture.componentInstance;
320 });
321
322 it('should not render items related to disabled features', () => {
323 fixture.detectChanges();
324
325 const iscsiCard = fixture.debugElement.query(By.css('li[id=iscsi-item]'));
326 const rgwCard = fixture.debugElement.query(By.css('li[id=rgw-item]'));
327 const mds = fixture.debugElement.query(By.css('li[id=mds-item]'));
328
329 expect(iscsiCard).toBeFalsy();
330 expect(rgwCard).toBeFalsy();
331 expect(mds).toBeFalsy();
332 });
333 });
334});