]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/dashboard/health/health.component.spec.ts
import ceph nautilus 14.2.2
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / dashboard / health / health.component.spec.ts
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { NO_ERRORS_SCHEMA } from '@angular/core';
3 import { ComponentFixture, TestBed } from '@angular/core/testing';
4 import { By } from '@angular/platform-browser';
5
6 import * as _ from 'lodash';
7 import { PopoverModule } from 'ngx-bootstrap/popover';
8 import { of } from 'rxjs';
9
10 import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
11 import { HealthService } from '../../../shared/api/health.service';
12 import { Permissions } from '../../../shared/models/permissions';
13 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
14 import { FeatureTogglesService } from '../../../shared/services/feature-toggles.service';
15 import { RefreshIntervalService } from '../../../shared/services/refresh-interval.service';
16 import { SharedModule } from '../../../shared/shared.module';
17 import { PgCategoryService } from '../../shared/pg-category.service';
18 import { HealthPieComponent } from '../health-pie/health-pie.component';
19 import { MdsSummaryPipe } from '../mds-summary.pipe';
20 import { MgrSummaryPipe } from '../mgr-summary.pipe';
21 import { MonSummaryPipe } from '../mon-summary.pipe';
22 import { OsdSummaryPipe } from '../osd-summary.pipe';
23 import { HealthComponent } from './health.component';
24
25 describe('HealthComponent', () => {
26 let component: HealthComponent;
27 let fixture: ComponentFixture<HealthComponent>;
28 let getHealthSpy;
29 const healthPayload = {
30 health: { status: 'HEALTH_OK' },
31 mon_status: { monmap: { mons: [] }, quorum: [] },
32 osd_map: { osds: [] },
33 mgr_map: { standbys: [] },
34 hosts: 0,
35 rgw: 0,
36 fs_map: { filesystems: [] },
37 iscsi_daemons: 0,
38 client_perf: {},
39 scrub_status: 'Inactive',
40 pools: [],
41 df: { stats: {} },
42 pg_info: { object_stats: { num_objects: 0 } }
43 };
44 const fakeAuthStorageService = {
45 getPermissions: () => {
46 return new Permissions({ log: ['read'] });
47 }
48 };
49 let fakeFeatureTogglesService;
50
51 configureTestBed({
52 imports: [SharedModule, HttpClientTestingModule, PopoverModule.forRoot()],
53 declarations: [
54 HealthComponent,
55 HealthPieComponent,
56 MonSummaryPipe,
57 OsdSummaryPipe,
58 MdsSummaryPipe,
59 MgrSummaryPipe
60 ],
61 schemas: [NO_ERRORS_SCHEMA],
62 providers: [
63 i18nProviders,
64 { provide: AuthStorageService, useValue: fakeAuthStorageService },
65 PgCategoryService,
66 RefreshIntervalService
67 ]
68 });
69
70 beforeEach(() => {
71 fakeFeatureTogglesService = spyOn(TestBed.get(FeatureTogglesService), 'get').and.returnValue(
72 of({
73 rbd: true,
74 mirroring: true,
75 iscsi: true,
76 cephfs: true,
77 rgw: true
78 })
79 );
80 fixture = TestBed.createComponent(HealthComponent);
81 component = fixture.componentInstance;
82 getHealthSpy = spyOn(TestBed.get(HealthService), 'getMinimalHealth');
83 getHealthSpy.and.returnValue(of(healthPayload));
84 });
85
86 it('should create', () => {
87 expect(component).toBeTruthy();
88 });
89
90 it('should render all info groups and all info cards', () => {
91 fixture.detectChanges();
92
93 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
94 expect(infoGroups.length).toBe(3);
95
96 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
97 expect(infoCards.length).toBe(18);
98 });
99
100 describe('features disabled', () => {
101 beforeEach(() => {
102 fakeFeatureTogglesService.and.returnValue(
103 of({
104 rbd: false,
105 mirroring: false,
106 iscsi: false,
107 cephfs: false,
108 rgw: false
109 })
110 );
111 fixture = TestBed.createComponent(HealthComponent);
112 component = fixture.componentInstance;
113 });
114
115 it('should not render cards related to disabled features', () => {
116 fixture.detectChanges();
117
118 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
119 expect(infoGroups.length).toBe(3);
120
121 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
122 expect(infoCards.length).toBe(15);
123 });
124 });
125
126 it('should render all except "Status" group and cards', () => {
127 const payload = _.cloneDeep(healthPayload);
128 payload.health.status = '';
129 payload.mon_status = null;
130 payload.osd_map = null;
131 payload.mgr_map = null;
132 payload.hosts = null;
133 payload.rgw = null;
134 payload.fs_map = null;
135 payload.iscsi_daemons = null;
136
137 getHealthSpy.and.returnValue(of(payload));
138 fixture.detectChanges();
139
140 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
141 expect(infoGroups.length).toBe(2);
142
143 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
144 expect(infoCards.length).toBe(10);
145 });
146
147 it('should render all except "Performance" group and cards', () => {
148 const payload = _.cloneDeep(healthPayload);
149 payload.scrub_status = '';
150 payload.client_perf = null;
151
152 getHealthSpy.and.returnValue(of(payload));
153 fixture.detectChanges();
154
155 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
156 expect(infoGroups.length).toBe(2);
157
158 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
159 expect(infoCards.length).toBe(13);
160 });
161
162 it('should render all except "Capacity" group and cards', () => {
163 const payload = _.cloneDeep(healthPayload);
164 payload.pools = null;
165 payload.df = null;
166 payload.pg_info = null;
167
168 getHealthSpy.and.returnValue(of(payload));
169 fixture.detectChanges();
170
171 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
172 expect(infoGroups.length).toBe(2);
173
174 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
175 expect(infoCards.length).toBe(13);
176 });
177
178 it('should render all groups and 1 card per group', () => {
179 const payload = { hosts: 0, scrub_status: 'Inactive', pools: [] };
180
181 getHealthSpy.and.returnValue(of(payload));
182 fixture.detectChanges();
183
184 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
185 expect(infoGroups.length).toBe(3);
186
187 _.each(infoGroups, (infoGroup) => {
188 expect(infoGroup.querySelectorAll('cd-info-card').length).toBe(1);
189 });
190 });
191
192 it('should render "Cluster Status" card text that is not clickable', () => {
193 fixture.detectChanges();
194
195 const clusterStatusCard = fixture.debugElement.query(
196 By.css('cd-info-card[cardTitle="Cluster Status"]')
197 );
198 const clickableContent = clusterStatusCard.query(By.css('.info-card-content-clickable'));
199 expect(clickableContent).toBeNull();
200 expect(clusterStatusCard.nativeElement.textContent).toEqual(` ${healthPayload.health.status} `);
201 });
202
203 it('should render "Cluster Status" card text that is clickable (popover)', () => {
204 const payload = _.cloneDeep(healthPayload);
205 payload.health['status'] = 'HEALTH_WARN';
206 payload.health['checks'] = [
207 { severity: 'HEALTH_WARN', type: 'WRN', summary: { message: 'fake warning' } }
208 ];
209
210 getHealthSpy.and.returnValue(of(payload));
211 fixture.detectChanges();
212
213 expect(component.permissions.log.read).toBeTruthy();
214
215 const clusterStatusCard = fixture.debugElement.query(
216 By.css('cd-info-card[cardTitle="Cluster Status"]')
217 );
218 const clickableContent = clusterStatusCard.query(By.css('.info-card-content-clickable'));
219 expect(clickableContent.nativeElement.textContent).toEqual(` ${payload.health.status} `);
220 });
221
222 it('event binding "prepareReadWriteRatio" is called', () => {
223 const prepareReadWriteRatio = spyOn(component, 'prepareReadWriteRatio');
224
225 const payload = _.cloneDeep(healthPayload);
226 payload.client_perf['read_op_per_sec'] = 1;
227 payload.client_perf['write_op_per_sec'] = 1;
228 getHealthSpy.and.returnValue(of(payload));
229 fixture.detectChanges();
230
231 expect(prepareReadWriteRatio).toHaveBeenCalled();
232 });
233
234 it('event binding "prepareRawUsage" is called', () => {
235 const prepareRawUsage = spyOn(component, 'prepareRawUsage');
236
237 fixture.detectChanges();
238
239 expect(prepareRawUsage).toHaveBeenCalled();
240 });
241
242 it('event binding "preparePgStatus" is called', () => {
243 const preparePgStatus = spyOn(component, 'preparePgStatus');
244
245 fixture.detectChanges();
246
247 expect(preparePgStatus).toHaveBeenCalled();
248 });
249
250 it('event binding "prepareObjects" is called', () => {
251 const prepareObjects = spyOn(component, 'prepareObjects');
252
253 fixture.detectChanges();
254
255 expect(prepareObjects).toHaveBeenCalled();
256 });
257
258 describe('preparePgStatus', () => {
259 const calcPercentage = (data) => Math.round((data / 10) * 100) || 0;
260
261 const expectedChart = (data: number[]) => ({
262 labels: [
263 `Clean (${calcPercentage(data[0])}%)`,
264 `Working (${calcPercentage(data[1])}%)`,
265 `Warning (${calcPercentage(data[2])}%)`,
266 `Unknown (${calcPercentage(data[3])}%)`
267 ],
268 options: {},
269 dataset: [{ data: data }]
270 });
271
272 it('gets no data', () => {
273 const chart = { dataset: [{}], options: {} };
274 component.preparePgStatus(chart, {
275 pg_info: { pgs_per_osd: 0 }
276 });
277 expect(chart).toEqual(expectedChart([undefined, undefined, undefined, undefined]));
278 });
279
280 it('gets data from all categories', () => {
281 const chart = { dataset: [{}], options: {} };
282 component.preparePgStatus(chart, {
283 pg_info: {
284 pgs_per_osd: 10,
285 statuses: {
286 'clean+active+scrubbing+nonMappedState': 4,
287 'clean+active+scrubbing': 2,
288 'clean+active': 1,
289 'clean+active+scrubbing+down': 3
290 }
291 }
292 });
293 expect(chart).toEqual(expectedChart([1, 2, 3, 4]));
294 });
295 });
296
297 describe('isClientReadWriteChartShowable', () => {
298 beforeEach(() => {
299 component.healthData = healthPayload;
300 });
301
302 it('returns false', () => {
303 component.healthData['client_perf'] = {};
304
305 expect(component.isClientReadWriteChartShowable()).toBeFalsy();
306 });
307
308 it('returns false', () => {
309 component.healthData['client_perf'] = { read_op_per_sec: undefined, write_op_per_sec: 0 };
310
311 expect(component.isClientReadWriteChartShowable()).toBeFalsy();
312 });
313
314 it('returns true', () => {
315 component.healthData['client_perf'] = { read_op_per_sec: 1, write_op_per_sec: undefined };
316
317 expect(component.isClientReadWriteChartShowable()).toBeTruthy();
318 });
319
320 it('returns true', () => {
321 component.healthData['client_perf'] = { read_op_per_sec: 2, write_op_per_sec: 3 };
322
323 expect(component.isClientReadWriteChartShowable()).toBeTruthy();
324 });
325 });
326
327 describe('calcPercentage', () => {
328 it('returns correct value', () => {
329 expect(component['calcPercentage'](1, undefined)).toEqual(0);
330 expect(component['calcPercentage'](1, null)).toEqual(0);
331 expect(component['calcPercentage'](1, 0)).toEqual(0);
332 expect(component['calcPercentage'](undefined, 1)).toEqual(0);
333 expect(component['calcPercentage'](null, 1)).toEqual(0);
334 expect(component['calcPercentage'](0, 1)).toEqual(0);
335 expect(component['calcPercentage'](2.346, 10)).toEqual(23);
336 expect(component['calcPercentage'](2.35, 10)).toEqual(24);
337 });
338 });
339 });