]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | import { HttpClientTestingModule } from '@angular/common/http/testing'; |
2 | import { ComponentFixture, TestBed } from '@angular/core/testing'; | |
3 | import { RouterTestingModule } from '@angular/router/testing'; | |
4 | ||
494da23a | 5 | import * as _ from 'lodash'; |
11fdf7f2 TL |
6 | import { BsModalService } from 'ngx-bootstrap/modal'; |
7 | import { TabsModule } from 'ngx-bootstrap/tabs'; | |
494da23a | 8 | import { ToastrModule } from 'ngx-toastr'; |
11fdf7f2 TL |
9 | import { of } from 'rxjs'; |
10 | ||
eafe8130 TL |
11 | import { |
12 | configureTestBed, | |
13 | expectItemTasks, | |
14 | i18nProviders | |
15 | } from '../../../../testing/unit-test-helper'; | |
494da23a | 16 | import { ConfigurationService } from '../../../shared/api/configuration.service'; |
11fdf7f2 TL |
17 | import { PoolService } from '../../../shared/api/pool.service'; |
18 | import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component'; | |
19 | import { ExecutingTask } from '../../../shared/models/executing-task'; | |
92f5a8d4 | 20 | import { AuthStorageService } from '../../../shared/services/auth-storage.service'; |
11fdf7f2 TL |
21 | import { SummaryService } from '../../../shared/services/summary.service'; |
22 | import { TaskWrapperService } from '../../../shared/services/task-wrapper.service'; | |
23 | import { SharedModule } from '../../../shared/shared.module'; | |
24 | import { RbdConfigurationListComponent } from '../../block/rbd-configuration-list/rbd-configuration-list.component'; | |
25 | import { PgCategoryService } from '../../shared/pg-category.service'; | |
26 | import { Pool } from '../pool'; | |
27 | import { PoolDetailsComponent } from '../pool-details/pool-details.component'; | |
28 | import { PoolListComponent } from './pool-list.component'; | |
29 | ||
30 | describe('PoolListComponent', () => { | |
31 | let component: PoolListComponent; | |
32 | let fixture: ComponentFixture<PoolListComponent>; | |
33 | let poolService: PoolService; | |
34 | ||
eafe8130 TL |
35 | const createPool = (name, id): Pool => { |
36 | return _.merge(new Pool(name), { | |
37 | pool: id, | |
38 | pg_num: 256, | |
39 | pg_placement_num: 256, | |
40 | pg_num_target: 256, | |
41 | pg_placement_num_target: 256 | |
42 | }); | |
11fdf7f2 TL |
43 | }; |
44 | ||
eafe8130 TL |
45 | const getPoolList = (): Pool[] => { |
46 | return [createPool('a', 0), createPool('b', 1), createPool('c', 2)]; | |
11fdf7f2 TL |
47 | }; |
48 | ||
49 | configureTestBed({ | |
50 | declarations: [PoolListComponent, PoolDetailsComponent, RbdConfigurationListComponent], | |
51 | imports: [ | |
52 | SharedModule, | |
494da23a | 53 | ToastrModule.forRoot(), |
11fdf7f2 TL |
54 | RouterTestingModule, |
55 | TabsModule.forRoot(), | |
56 | HttpClientTestingModule | |
57 | ], | |
58 | providers: [i18nProviders, PgCategoryService] | |
59 | }); | |
60 | ||
61 | beforeEach(() => { | |
62 | fixture = TestBed.createComponent(PoolListComponent); | |
63 | component = fixture.componentInstance; | |
64 | component.permissions.pool.read = true; | |
65 | poolService = TestBed.get(PoolService); | |
eafe8130 | 66 | spyOn(poolService, 'getList').and.callFake(() => of(getPoolList())); |
11fdf7f2 TL |
67 | fixture.detectChanges(); |
68 | }); | |
69 | ||
70 | it('should create', () => { | |
71 | expect(component).toBeTruthy(); | |
72 | }); | |
73 | ||
81eedcae TL |
74 | it('should have columns that are sortable', () => { |
75 | expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy(); | |
76 | }); | |
77 | ||
494da23a | 78 | describe('monAllowPoolDelete', () => { |
92f5a8d4 | 79 | let configOptRead: boolean; |
494da23a TL |
80 | let configurationService: ConfigurationService; |
81 | ||
82 | beforeEach(() => { | |
92f5a8d4 TL |
83 | configOptRead = true; |
84 | spyOn(TestBed.get(AuthStorageService), 'getPermissions').and.callFake(() => ({ | |
85 | configOpt: { read: configOptRead } | |
86 | })); | |
494da23a TL |
87 | configurationService = TestBed.get(ConfigurationService); |
88 | }); | |
89 | ||
90 | it('should set value correctly if mon_allow_pool_delete flag is set to true', () => { | |
91 | const configOption = { | |
92 | name: 'mon_allow_pool_delete', | |
93 | value: [ | |
94 | { | |
95 | section: 'mon', | |
96 | value: 'true' | |
97 | } | |
98 | ] | |
99 | }; | |
100 | spyOn(configurationService, 'get').and.returnValue(of(configOption)); | |
101 | fixture = TestBed.createComponent(PoolListComponent); | |
102 | component = fixture.componentInstance; | |
103 | expect(component.monAllowPoolDelete).toBe(true); | |
104 | }); | |
105 | ||
106 | it('should set value correctly if mon_allow_pool_delete flag is set to false', () => { | |
107 | const configOption = { | |
108 | name: 'mon_allow_pool_delete', | |
109 | value: [ | |
110 | { | |
111 | section: 'mon', | |
112 | value: 'false' | |
113 | } | |
114 | ] | |
115 | }; | |
116 | spyOn(configurationService, 'get').and.returnValue(of(configOption)); | |
117 | fixture = TestBed.createComponent(PoolListComponent); | |
118 | component = fixture.componentInstance; | |
119 | expect(component.monAllowPoolDelete).toBe(false); | |
120 | }); | |
121 | ||
122 | it('should set value correctly if mon_allow_pool_delete flag is not set', () => { | |
123 | const configOption = { | |
124 | name: 'mon_allow_pool_delete' | |
125 | }; | |
126 | spyOn(configurationService, 'get').and.returnValue(of(configOption)); | |
127 | fixture = TestBed.createComponent(PoolListComponent); | |
128 | component = fixture.componentInstance; | |
129 | expect(component.monAllowPoolDelete).toBe(false); | |
130 | }); | |
92f5a8d4 TL |
131 | |
132 | it('should set value correctly w/o config-opt read privileges', () => { | |
133 | configOptRead = false; | |
134 | fixture = TestBed.createComponent(PoolListComponent); | |
135 | component = fixture.componentInstance; | |
136 | expect(component.monAllowPoolDelete).toBe(false); | |
137 | }); | |
494da23a TL |
138 | }); |
139 | ||
11fdf7f2 TL |
140 | describe('pool deletion', () => { |
141 | let taskWrapper: TaskWrapperService; | |
142 | ||
143 | const setSelectedPool = (poolName: string) => { | |
144 | component.selection.selected = [{ pool_name: poolName }]; | |
145 | component.selection.update(); | |
146 | }; | |
147 | ||
148 | const callDeletion = () => { | |
149 | component.deletePoolModal(); | |
150 | const deletion: CriticalConfirmationModalComponent = component.modalRef.content; | |
151 | deletion.submitActionObservable(); | |
152 | }; | |
153 | ||
154 | const testPoolDeletion = (poolName) => { | |
155 | setSelectedPool(poolName); | |
156 | callDeletion(); | |
157 | expect(poolService.delete).toHaveBeenCalledWith(poolName); | |
158 | expect(taskWrapper.wrapTaskAroundCall).toHaveBeenCalledWith({ | |
159 | task: { | |
160 | name: 'pool/delete', | |
161 | metadata: { | |
162 | pool_name: poolName | |
163 | } | |
164 | }, | |
165 | call: undefined // because of stub | |
166 | }); | |
167 | }; | |
168 | ||
169 | beforeEach(() => { | |
170 | spyOn(TestBed.get(BsModalService), 'show').and.callFake((deletionClass, config) => { | |
171 | return { | |
172 | content: Object.assign(new deletionClass(), config.initialState) | |
173 | }; | |
174 | }); | |
175 | spyOn(poolService, 'delete').and.stub(); | |
176 | taskWrapper = TestBed.get(TaskWrapperService); | |
177 | spyOn(taskWrapper, 'wrapTaskAroundCall').and.callThrough(); | |
178 | }); | |
179 | ||
180 | it('should pool deletion with two different pools', () => { | |
181 | testPoolDeletion('somePoolName'); | |
182 | testPoolDeletion('aDifferentPoolName'); | |
183 | }); | |
184 | }); | |
185 | ||
186 | describe('handling of executing tasks', () => { | |
11fdf7f2 TL |
187 | let summaryService: SummaryService; |
188 | ||
189 | const addTask = (name: string, pool: string) => { | |
190 | const task = new ExecutingTask(); | |
191 | task.name = name; | |
192 | task.metadata = { pool_name: pool }; | |
193 | summaryService.addRunningTask(task); | |
194 | }; | |
195 | ||
196 | beforeEach(() => { | |
197 | summaryService = TestBed.get(SummaryService); | |
198 | summaryService['summaryDataSource'].next({ executing_tasks: [], finished_tasks: [] }); | |
11fdf7f2 TL |
199 | }); |
200 | ||
201 | it('gets all pools without executing pools', () => { | |
202 | expect(component.pools.length).toBe(3); | |
203 | expect(component.pools.every((pool) => !pool.executingTasks)).toBeTruthy(); | |
204 | }); | |
205 | ||
206 | it('gets a pool from a task during creation', () => { | |
207 | addTask('pool/create', 'd'); | |
208 | expect(component.pools.length).toBe(4); | |
eafe8130 | 209 | expectItemTasks(component.pools[3], 'Creating'); |
11fdf7f2 TL |
210 | }); |
211 | ||
212 | it('gets all pools with one executing pools', () => { | |
213 | addTask('pool/create', 'a'); | |
214 | expect(component.pools.length).toBe(3); | |
eafe8130 | 215 | expectItemTasks(component.pools[0], 'Creating'); |
11fdf7f2 TL |
216 | expect(component.pools[1].cdExecuting).toBeFalsy(); |
217 | expect(component.pools[2].cdExecuting).toBeFalsy(); | |
218 | }); | |
219 | ||
220 | it('gets all pools with multiple executing pools', () => { | |
221 | addTask('pool/create', 'a'); | |
222 | addTask('pool/edit', 'a'); | |
223 | addTask('pool/delete', 'a'); | |
224 | addTask('pool/edit', 'b'); | |
225 | addTask('pool/delete', 'b'); | |
226 | addTask('pool/delete', 'c'); | |
227 | expect(component.pools.length).toBe(3); | |
eafe8130 TL |
228 | expectItemTasks(component.pools[0], 'Creating..., Updating..., Deleting'); |
229 | expectItemTasks(component.pools[1], 'Updating..., Deleting'); | |
230 | expectItemTasks(component.pools[2], 'Deleting'); | |
11fdf7f2 TL |
231 | }); |
232 | ||
233 | it('gets all pools with multiple executing tasks (not only pool tasks)', () => { | |
234 | addTask('rbd/create', 'a'); | |
235 | addTask('rbd/edit', 'a'); | |
236 | addTask('pool/delete', 'a'); | |
237 | addTask('pool/edit', 'b'); | |
238 | addTask('rbd/delete', 'b'); | |
239 | addTask('rbd/delete', 'c'); | |
240 | expect(component.pools.length).toBe(3); | |
eafe8130 TL |
241 | expectItemTasks(component.pools[0], 'Deleting'); |
242 | expectItemTasks(component.pools[1], 'Updating'); | |
11fdf7f2 TL |
243 | expect(component.pools[2].cdExecuting).toBeFalsy(); |
244 | }); | |
245 | }); | |
246 | ||
247 | describe('getPgStatusCellClass', () => { | |
248 | const testMethod = (value, expected) => | |
249 | expect(component.getPgStatusCellClass('', '', value)).toEqual({ | |
250 | 'text-right': true, | |
251 | [expected]: true | |
252 | }); | |
253 | ||
254 | it('pg-clean', () => { | |
255 | testMethod('8 active+clean', 'pg-clean'); | |
256 | }); | |
257 | ||
258 | it('pg-working', () => { | |
259 | testMethod(' 8 active+clean+scrubbing+deep, 255 active+clean ', 'pg-working'); | |
260 | }); | |
261 | ||
262 | it('pg-warning', () => { | |
263 | testMethod('8 active+clean+scrubbing+down', 'pg-warning'); | |
264 | testMethod('8 active+clean+scrubbing+down+nonMappedState', 'pg-warning'); | |
265 | }); | |
266 | ||
267 | it('pg-unknown', () => { | |
268 | testMethod('8 active+clean+scrubbing+nonMappedState', 'pg-unknown'); | |
269 | testMethod('8 ', 'pg-unknown'); | |
270 | testMethod('', 'pg-unknown'); | |
271 | }); | |
272 | }); | |
273 | ||
494da23a TL |
274 | describe('custom row comparators', () => { |
275 | const expectCorrectComparator = (statsAttribute: string) => { | |
276 | const mockPool = (v) => ({ stats: { [statsAttribute]: { latest: v } } }); | |
277 | const columnDefinition = _.find( | |
278 | component.columns, | |
279 | (column) => column.prop === `stats.${statsAttribute}.rates` | |
280 | ); | |
281 | expect(columnDefinition.comparator(undefined, undefined, mockPool(2), mockPool(1))).toBe(1); | |
282 | expect(columnDefinition.comparator(undefined, undefined, mockPool(1), mockPool(2))).toBe(-1); | |
283 | }; | |
284 | ||
285 | it('compares read bytes correctly', () => { | |
286 | expectCorrectComparator('rd_bytes'); | |
287 | }); | |
288 | ||
289 | it('compares write bytes correctly', () => { | |
290 | expectCorrectComparator('wr_bytes'); | |
291 | }); | |
292 | }); | |
293 | ||
11fdf7f2 | 294 | describe('transformPoolsData', () => { |
eafe8130 | 295 | let pool: Pool; |
11fdf7f2 | 296 | |
eafe8130 TL |
297 | const getPoolData = (o) => [ |
298 | _.merge( | |
299 | _.merge(createPool('a', 0), { | |
81eedcae TL |
300 | cdIsBinary: true, |
301 | pg_status: '', | |
302 | stats: { | |
494da23a TL |
303 | bytes_used: { latest: 0, rate: 0, rates: [] }, |
304 | max_avail: { latest: 0, rate: 0, rates: [] }, | |
305 | rd: { latest: 0, rate: 0, rates: [] }, | |
306 | rd_bytes: { latest: 0, rate: 0, rates: [] }, | |
307 | wr: { latest: 0, rate: 0, rates: [] }, | |
308 | wr_bytes: { latest: 0, rate: 0, rates: [] } | |
81eedcae TL |
309 | }, |
310 | usage: 0 | |
eafe8130 TL |
311 | }), |
312 | o | |
313 | ) | |
314 | ]; | |
315 | ||
316 | beforeEach(() => { | |
317 | pool = createPool('a', 0); | |
318 | }); | |
319 | ||
320 | it('transforms pools data correctly', () => { | |
321 | pool = _.merge(pool, { | |
322 | stats: { | |
323 | bytes_used: { latest: 5, rate: 0, rates: [] }, | |
324 | max_avail: { latest: 15, rate: 0, rates: [] }, | |
325 | rd_bytes: { latest: 6, rate: 4, rates: [[0, 2], [1, 6]] } | |
326 | }, | |
327 | pg_status: { 'active+clean': 8, down: 2 } | |
328 | }); | |
329 | expect(component.transformPoolsData([pool])).toEqual( | |
330 | getPoolData({ | |
331 | pg_status: '8 active+clean, 2 down', | |
332 | stats: { | |
333 | bytes_used: { latest: 5, rate: 0, rates: [] }, | |
334 | max_avail: { latest: 15, rate: 0, rates: [] }, | |
335 | rd_bytes: { latest: 6, rate: 4, rates: [2, 6] } | |
336 | }, | |
337 | usage: 0.25 | |
338 | }) | |
339 | ); | |
340 | }); | |
341 | ||
342 | it('transforms pools data correctly if stats are missing', () => { | |
343 | expect(component.transformPoolsData([pool])).toEqual(getPoolData({})); | |
11fdf7f2 TL |
344 | }); |
345 | ||
346 | it('transforms empty pools data correctly', () => { | |
eafe8130 TL |
347 | expect(component.transformPoolsData(undefined)).toEqual(undefined); |
348 | expect(component.transformPoolsData([])).toEqual([]); | |
349 | }); | |
350 | ||
351 | it('shows not marked pools in progress if pg_num does not match pg_num_target', () => { | |
352 | const pools = [ | |
353 | _.merge(pool, { | |
354 | pg_num: 32, | |
355 | pg_num_target: 16, | |
356 | pg_placement_num: 32, | |
357 | pg_placement_num_target: 16 | |
358 | }) | |
359 | ]; | |
360 | expect(component.transformPoolsData(pools)).toEqual( | |
361 | getPoolData({ | |
362 | cdExecuting: 'Updating', | |
363 | pg_num: 32, | |
364 | pg_num_target: 16, | |
365 | pg_placement_num: 32, | |
366 | pg_placement_num_target: 16 | |
367 | }) | |
368 | ); | |
369 | }); | |
370 | ||
371 | it('shows marked pools in progress as defined by task', () => { | |
372 | const pools = [ | |
373 | _.merge(pool, { | |
374 | pg_num: 32, | |
375 | pg_num_target: 16, | |
376 | pg_placement_num: 32, | |
377 | pg_placement_num_target: 16, | |
378 | cdExecuting: 'Updating... 50%' | |
379 | }) | |
380 | ]; | |
381 | expect(component.transformPoolsData(pools)).toEqual( | |
382 | getPoolData({ | |
383 | cdExecuting: 'Updating... 50%', | |
384 | pg_num: 32, | |
385 | pg_num_target: 16, | |
386 | pg_placement_num: 32, | |
387 | pg_placement_num_target: 16 | |
388 | }) | |
389 | ); | |
11fdf7f2 TL |
390 | }); |
391 | }); | |
392 | ||
393 | describe('transformPgStatus', () => { | |
394 | it('returns status groups correctly', () => { | |
395 | const pgStatus = { 'active+clean': 8 }; | |
396 | const expected = '8 active+clean'; | |
397 | ||
398 | expect(component.transformPgStatus(pgStatus)).toEqual(expected); | |
399 | }); | |
400 | ||
401 | it('returns separated status groups', () => { | |
402 | const pgStatus = { 'active+clean': 8, down: 2 }; | |
403 | const expected = '8 active+clean, 2 down'; | |
404 | ||
405 | expect(component.transformPgStatus(pgStatus)).toEqual(expected); | |
406 | }); | |
407 | ||
408 | it('returns separated statuses correctly', () => { | |
409 | const pgStatus = { active: 8, down: 2 }; | |
410 | const expected = '8 active, 2 down'; | |
411 | ||
412 | expect(component.transformPgStatus(pgStatus)).toEqual(expected); | |
413 | }); | |
414 | ||
415 | it('returns empty string', () => { | |
416 | const pgStatus = undefined; | |
417 | const expected = ''; | |
418 | ||
419 | expect(component.transformPgStatus(pgStatus)).toEqual(expected); | |
420 | }); | |
421 | }); | |
422 | ||
11fdf7f2 | 423 | describe('getSelectionTiers', () => { |
11fdf7f2 TL |
424 | const setSelectionTiers = (tiers: number[]) => { |
425 | component.selection.selected = [ | |
426 | { | |
427 | tiers | |
428 | } | |
429 | ]; | |
430 | component.selection.update(); | |
431 | component.getSelectionTiers(); | |
432 | }; | |
433 | ||
434 | beforeEach(() => { | |
eafe8130 | 435 | component.pools = getPoolList(); |
11fdf7f2 TL |
436 | }); |
437 | ||
438 | it('should select multiple existing cache tiers', () => { | |
439 | setSelectionTiers([0, 1, 2]); | |
eafe8130 | 440 | expect(component.selectionCacheTiers).toEqual(getPoolList()); |
11fdf7f2 TL |
441 | }); |
442 | ||
443 | it('should select correct existing cache tier', () => { | |
444 | setSelectionTiers([0]); | |
eafe8130 | 445 | expect(component.selectionCacheTiers).toEqual([createPool('a', 0)]); |
11fdf7f2 TL |
446 | }); |
447 | ||
448 | it('should not select cache tier if id is invalid', () => { | |
449 | setSelectionTiers([-1]); | |
450 | expect(component.selectionCacheTiers).toEqual([]); | |
451 | }); | |
452 | ||
453 | it('should not select cache tier if empty', () => { | |
454 | setSelectionTiers([]); | |
455 | expect(component.selectionCacheTiers).toEqual([]); | |
456 | }); | |
457 | ||
458 | it('should be able to selected one pool with multiple tiers, than with a single tier, than with no tiers', () => { | |
459 | setSelectionTiers([0, 1, 2]); | |
eafe8130 | 460 | expect(component.selectionCacheTiers).toEqual(getPoolList()); |
11fdf7f2 | 461 | setSelectionTiers([0]); |
eafe8130 | 462 | expect(component.selectionCacheTiers).toEqual([createPool('a', 0)]); |
11fdf7f2 TL |
463 | setSelectionTiers([]); |
464 | expect(component.selectionCacheTiers).toEqual([]); | |
465 | }); | |
466 | }); | |
494da23a TL |
467 | |
468 | describe('getDisableDesc', () => { | |
469 | it('should return message if mon_allow_pool_delete flag is set to false', () => { | |
470 | component.monAllowPoolDelete = false; | |
471 | expect(component.getDisableDesc()).toBe( | |
472 | 'Pool deletion is disabled by the mon_allow_pool_delete configuration setting.' | |
473 | ); | |
474 | }); | |
475 | ||
476 | it('should return undefined if mon_allow_pool_delete flag is set to true', () => { | |
477 | component.monAllowPoolDelete = true; | |
478 | expect(component.getDisableDesc()).toBeUndefined(); | |
479 | }); | |
480 | }); | |
11fdf7f2 | 481 | }); |