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';
6 import * as _ from 'lodash';
7 import { PopoverModule } from 'ngx-bootstrap/popover';
8 import { of } from 'rxjs';
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';
25 describe('HealthComponent', () => {
26 let component: HealthComponent;
27 let fixture: ComponentFixture<HealthComponent>;
28 let getHealthSpy: jasmine.Spy;
29 const healthPayload: Record<string, any> = {
30 health: { status: 'HEALTH_OK' },
31 mon_status: { monmap: { mons: [] }, quorum: [] },
32 osd_map: { osds: [] },
33 mgr_map: { standbys: [] },
36 fs_map: { filesystems: [], standbys: [] },
39 scrub_status: 'Inactive',
42 pg_info: { object_stats: { num_objects: 0 } }
44 const fakeAuthStorageService = {
45 getPermissions: () => {
46 return new Permissions({ log: ['read'] });
49 let fakeFeatureTogglesService: jasmine.Spy;
52 imports: [SharedModule, HttpClientTestingModule, PopoverModule.forRoot()],
61 schemas: [NO_ERRORS_SCHEMA],
64 { provide: AuthStorageService, useValue: fakeAuthStorageService },
66 RefreshIntervalService
71 fakeFeatureTogglesService = spyOn(TestBed.get(FeatureTogglesService), 'get').and.returnValue(
80 fixture = TestBed.createComponent(HealthComponent);
81 component = fixture.componentInstance;
82 getHealthSpy = spyOn(TestBed.get(HealthService), 'getMinimalHealth');
83 getHealthSpy.and.returnValue(of(healthPayload));
86 it('should create', () => {
87 expect(component).toBeTruthy();
90 it('should render all info groups and all info cards', () => {
91 fixture.detectChanges();
93 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
94 expect(infoGroups.length).toBe(3);
96 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
97 expect(infoCards.length).toBe(18);
100 describe('features disabled', () => {
102 fakeFeatureTogglesService.and.returnValue(
111 fixture = TestBed.createComponent(HealthComponent);
112 component = fixture.componentInstance;
115 it('should not render cards related to disabled features', () => {
116 fixture.detectChanges();
118 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
119 expect(infoGroups.length).toBe(3);
121 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
122 expect(infoCards.length).toBe(15);
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;
134 payload.fs_map = null;
135 payload.iscsi_daemons = null;
137 getHealthSpy.and.returnValue(of(payload));
138 fixture.detectChanges();
140 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
141 expect(infoGroups.length).toBe(2);
143 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
144 expect(infoCards.length).toBe(10);
147 it('should render all except "Performance" group and cards', () => {
148 const payload = _.cloneDeep(healthPayload);
149 payload.scrub_status = '';
150 payload.client_perf = null;
152 getHealthSpy.and.returnValue(of(payload));
153 fixture.detectChanges();
155 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
156 expect(infoGroups.length).toBe(2);
158 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
159 expect(infoCards.length).toBe(13);
162 it('should render all except "Capacity" group and cards', () => {
163 const payload = _.cloneDeep(healthPayload);
164 payload.pools = null;
166 payload.pg_info = null;
168 getHealthSpy.and.returnValue(of(payload));
169 fixture.detectChanges();
171 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
172 expect(infoGroups.length).toBe(2);
174 const infoCards = fixture.debugElement.nativeElement.querySelectorAll('cd-info-card');
175 expect(infoCards.length).toBe(13);
178 it('should render all groups and 1 card per group', () => {
179 const payload: Record<string, any> = { hosts: 0, scrub_status: 'Inactive', pools: [] };
181 getHealthSpy.and.returnValue(of(payload));
182 fixture.detectChanges();
184 const infoGroups = fixture.debugElement.nativeElement.querySelectorAll('cd-info-group');
185 expect(infoGroups.length).toBe(3);
187 _.each(infoGroups, (infoGroup) => {
188 expect(infoGroup.querySelectorAll('cd-info-card').length).toBe(1);
192 it('should render "Cluster Status" card text that is not clickable', () => {
193 fixture.detectChanges();
195 const clusterStatusCard = fixture.debugElement.query(
196 By.css('cd-info-card[cardTitle="Cluster Status"]')
198 const clickableContent = clusterStatusCard.query(By.css('.info-card-content-clickable'));
199 expect(clickableContent).toBeNull();
200 expect(clusterStatusCard.nativeElement.textContent).toEqual(` ${healthPayload.health.status} `);
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' } }
210 getHealthSpy.and.returnValue(of(payload));
211 fixture.detectChanges();
213 expect(component.permissions.log.read).toBeTruthy();
215 const clusterStatusCard = fixture.debugElement.query(
216 By.css('cd-info-card[cardTitle="Cluster Status"]')
218 const clickableContent = clusterStatusCard.query(By.css('.info-card-content-clickable'));
219 expect(clickableContent.nativeElement.textContent).toEqual(` ${payload.health.status} `);
222 it('event binding "prepareReadWriteRatio" is called', () => {
223 const prepareReadWriteRatio = spyOn(component, 'prepareReadWriteRatio');
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();
231 expect(prepareReadWriteRatio).toHaveBeenCalled();
234 it('event binding "prepareRawUsage" is called', () => {
235 const prepareRawUsage = spyOn(component, 'prepareRawUsage');
237 fixture.detectChanges();
239 expect(prepareRawUsage).toHaveBeenCalled();
242 it('event binding "preparePgStatus" is called', () => {
243 const preparePgStatus = spyOn(component, 'preparePgStatus');
245 fixture.detectChanges();
247 expect(preparePgStatus).toHaveBeenCalled();
250 it('event binding "prepareObjects" is called', () => {
251 const prepareObjects = spyOn(component, 'prepareObjects');
253 fixture.detectChanges();
255 expect(prepareObjects).toHaveBeenCalled();
258 describe('preparePgStatus', () => {
259 const calcPercentage = (data: number) => Math.round((data / 10) * 100) || 0;
261 const expectedChart = (data: number[]) => ({
263 `Clean (${calcPercentage(data[0])}%)`,
264 `Working (${calcPercentage(data[1])}%)`,
265 `Warning (${calcPercentage(data[2])}%)`,
266 `Unknown (${calcPercentage(data[3])}%)`
269 dataset: [{ data: data }]
272 it('gets no data', () => {
273 const chart = { dataset: [{}], options: {} };
274 component.preparePgStatus(chart, {
277 expect(chart).toEqual(expectedChart([undefined, undefined, undefined, undefined]));
280 it('gets data from all categories', () => {
281 const chart = { dataset: [{}], options: {} };
282 component.preparePgStatus(chart, {
285 'clean+active+scrubbing+nonMappedState': 4,
286 'clean+active+scrubbing': 2,
288 'clean+active+scrubbing+down': 3
292 expect(chart).toEqual(expectedChart([1, 2, 3, 4]));
296 describe('isClientReadWriteChartShowable', () => {
298 component.healthData = healthPayload;
301 it('returns false', () => {
302 component.healthData['client_perf'] = {};
304 expect(component.isClientReadWriteChartShowable()).toBeFalsy();
307 it('returns false', () => {
308 component.healthData['client_perf'] = { read_op_per_sec: undefined, write_op_per_sec: 0 };
310 expect(component.isClientReadWriteChartShowable()).toBeFalsy();
313 it('returns true', () => {
314 component.healthData['client_perf'] = { read_op_per_sec: 1, write_op_per_sec: undefined };
316 expect(component.isClientReadWriteChartShowable()).toBeTruthy();
319 it('returns true', () => {
320 component.healthData['client_perf'] = { read_op_per_sec: 2, write_op_per_sec: 3 };
322 expect(component.isClientReadWriteChartShowable()).toBeTruthy();
326 describe('calcPercentage', () => {
327 it('returns correct value', () => {
328 expect(component['calcPercentage'](1, undefined)).toEqual(0);
329 expect(component['calcPercentage'](1, null)).toEqual(0);
330 expect(component['calcPercentage'](1, 0)).toEqual(0);
331 expect(component['calcPercentage'](undefined, 1)).toEqual(0);
332 expect(component['calcPercentage'](null, 1)).toEqual(0);
333 expect(component['calcPercentage'](0, 1)).toEqual(0);
334 expect(component['calcPercentage'](2.346, 10)).toEqual(23);
335 expect(component['calcPercentage'](2.35, 10)).toEqual(24);