]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/pool-list/pool-list.component.spec.ts
import ceph nautilus 14.2.2
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / pool / pool-list / pool-list.component.spec.ts
CommitLineData
11fdf7f2
TL
1import { HttpClientTestingModule } from '@angular/common/http/testing';
2import { ComponentFixture, TestBed } from '@angular/core/testing';
3import { RouterTestingModule } from '@angular/router/testing';
4
5import { ToastModule } from 'ng2-toastr';
6import { BsModalService } from 'ngx-bootstrap/modal';
7import { TabsModule } from 'ngx-bootstrap/tabs';
8import { of } from 'rxjs';
9
10import { configureTestBed, i18nProviders } from '../../../../testing/unit-test-helper';
11import { PoolService } from '../../../shared/api/pool.service';
12import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
13import { ExecutingTask } from '../../../shared/models/executing-task';
14import { SummaryService } from '../../../shared/services/summary.service';
15import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
16import { SharedModule } from '../../../shared/shared.module';
17import { RbdConfigurationListComponent } from '../../block/rbd-configuration-list/rbd-configuration-list.component';
18import { PgCategoryService } from '../../shared/pg-category.service';
19import { Pool } from '../pool';
20import { PoolDetailsComponent } from '../pool-details/pool-details.component';
21import { PoolListComponent } from './pool-list.component';
22
23describe('PoolListComponent', () => {
24 let component: PoolListComponent;
25 let fixture: ComponentFixture<PoolListComponent>;
26 let poolService: PoolService;
27
28 const addPool = (pools, name, id) => {
29 const pool = new Pool(name);
30 pool.pool = id;
31 pool.pg_num = 256;
32 pools.push(pool);
33 };
34
35 const setUpPools = (pools) => {
36 addPool(pools, 'a', 0);
37 addPool(pools, 'b', 1);
38 addPool(pools, 'c', 2);
39 component.pools = pools;
40 };
41
42 configureTestBed({
43 declarations: [PoolListComponent, PoolDetailsComponent, RbdConfigurationListComponent],
44 imports: [
45 SharedModule,
46 ToastModule.forRoot(),
47 RouterTestingModule,
48 TabsModule.forRoot(),
49 HttpClientTestingModule
50 ],
51 providers: [i18nProviders, PgCategoryService]
52 });
53
54 beforeEach(() => {
55 fixture = TestBed.createComponent(PoolListComponent);
56 component = fixture.componentInstance;
57 component.permissions.pool.read = true;
58 poolService = TestBed.get(PoolService);
59 fixture.detectChanges();
60 });
61
62 it('should create', () => {
63 expect(component).toBeTruthy();
64 });
65
81eedcae
TL
66 it('should have columns that are sortable', () => {
67 expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy();
68 });
69
11fdf7f2
TL
70 describe('pool deletion', () => {
71 let taskWrapper: TaskWrapperService;
72
73 const setSelectedPool = (poolName: string) => {
74 component.selection.selected = [{ pool_name: poolName }];
75 component.selection.update();
76 };
77
78 const callDeletion = () => {
79 component.deletePoolModal();
80 const deletion: CriticalConfirmationModalComponent = component.modalRef.content;
81 deletion.submitActionObservable();
82 };
83
84 const testPoolDeletion = (poolName) => {
85 setSelectedPool(poolName);
86 callDeletion();
87 expect(poolService.delete).toHaveBeenCalledWith(poolName);
88 expect(taskWrapper.wrapTaskAroundCall).toHaveBeenCalledWith({
89 task: {
90 name: 'pool/delete',
91 metadata: {
92 pool_name: poolName
93 }
94 },
95 call: undefined // because of stub
96 });
97 };
98
99 beforeEach(() => {
100 spyOn(TestBed.get(BsModalService), 'show').and.callFake((deletionClass, config) => {
101 return {
102 content: Object.assign(new deletionClass(), config.initialState)
103 };
104 });
105 spyOn(poolService, 'delete').and.stub();
106 taskWrapper = TestBed.get(TaskWrapperService);
107 spyOn(taskWrapper, 'wrapTaskAroundCall').and.callThrough();
108 });
109
110 it('should pool deletion with two different pools', () => {
111 testPoolDeletion('somePoolName');
112 testPoolDeletion('aDifferentPoolName');
113 });
114 });
115
116 describe('handling of executing tasks', () => {
117 let pools: Pool[];
118 let summaryService: SummaryService;
119
120 const addTask = (name: string, pool: string) => {
121 const task = new ExecutingTask();
122 task.name = name;
123 task.metadata = { pool_name: pool };
124 summaryService.addRunningTask(task);
125 };
126
127 beforeEach(() => {
128 summaryService = TestBed.get(SummaryService);
129 summaryService['summaryDataSource'].next({ executing_tasks: [], finished_tasks: [] });
130 pools = [];
131 setUpPools(pools);
132 spyOn(poolService, 'getList').and.callFake(() => of(pools));
133 fixture.detectChanges();
134 });
135
136 it('gets all pools without executing pools', () => {
137 expect(component.pools.length).toBe(3);
138 expect(component.pools.every((pool) => !pool.executingTasks)).toBeTruthy();
139 });
140
141 it('gets a pool from a task during creation', () => {
142 addTask('pool/create', 'd');
143 expect(component.pools.length).toBe(4);
144 expect(component.pools[3].cdExecuting).toBe('Creating');
145 });
146
147 it('gets all pools with one executing pools', () => {
148 addTask('pool/create', 'a');
149 expect(component.pools.length).toBe(3);
150 expect(component.pools[0].cdExecuting).toBe('Creating');
151 expect(component.pools[1].cdExecuting).toBeFalsy();
152 expect(component.pools[2].cdExecuting).toBeFalsy();
153 });
154
155 it('gets all pools with multiple executing pools', () => {
156 addTask('pool/create', 'a');
157 addTask('pool/edit', 'a');
158 addTask('pool/delete', 'a');
159 addTask('pool/edit', 'b');
160 addTask('pool/delete', 'b');
161 addTask('pool/delete', 'c');
162 expect(component.pools.length).toBe(3);
163 expect(component.pools[0].cdExecuting).toBe('Creating, Updating, Deleting');
164 expect(component.pools[1].cdExecuting).toBe('Updating, Deleting');
165 expect(component.pools[2].cdExecuting).toBe('Deleting');
166 });
167
168 it('gets all pools with multiple executing tasks (not only pool tasks)', () => {
169 addTask('rbd/create', 'a');
170 addTask('rbd/edit', 'a');
171 addTask('pool/delete', 'a');
172 addTask('pool/edit', 'b');
173 addTask('rbd/delete', 'b');
174 addTask('rbd/delete', 'c');
175 expect(component.pools.length).toBe(3);
176 expect(component.pools[0].cdExecuting).toBe('Deleting');
177 expect(component.pools[1].cdExecuting).toBe('Updating');
178 expect(component.pools[2].cdExecuting).toBeFalsy();
179 });
180 });
181
182 describe('getPgStatusCellClass', () => {
183 const testMethod = (value, expected) =>
184 expect(component.getPgStatusCellClass('', '', value)).toEqual({
185 'text-right': true,
186 [expected]: true
187 });
188
189 it('pg-clean', () => {
190 testMethod('8 active+clean', 'pg-clean');
191 });
192
193 it('pg-working', () => {
194 testMethod(' 8 active+clean+scrubbing+deep, 255 active+clean ', 'pg-working');
195 });
196
197 it('pg-warning', () => {
198 testMethod('8 active+clean+scrubbing+down', 'pg-warning');
199 testMethod('8 active+clean+scrubbing+down+nonMappedState', 'pg-warning');
200 });
201
202 it('pg-unknown', () => {
203 testMethod('8 active+clean+scrubbing+nonMappedState', 'pg-unknown');
204 testMethod('8 ', 'pg-unknown');
205 testMethod('', 'pg-unknown');
206 });
207 });
208
209 describe('transformPoolsData', () => {
210 it('transforms pools data correctly', () => {
211 const pools = [
212 {
81eedcae
TL
213 stats: {
214 bytes_used: { latest: 5, rate: 0, series: [] },
215 max_avail: { latest: 15, rate: 0, series: [] },
216 rd_bytes: { latest: 6, rate: 4, series: [[0, 2], [1, 6]] }
217 },
11fdf7f2
TL
218 pg_status: { 'active+clean': 8, down: 2 }
219 }
220 ];
221 const expected = [
222 {
223 cdIsBinary: true,
224 pg_status: '8 active+clean, 2 down',
225 stats: {
81eedcae
TL
226 bytes_used: { latest: 5, rate: 0, series: [] },
227 max_avail: { latest: 15, rate: 0, series: [] },
11fdf7f2
TL
228 rd: { latest: 0, rate: 0, series: [] },
229 rd_bytes: { latest: 6, rate: 4, series: [2, 6] },
230 wr: { latest: 0, rate: 0, series: [] },
231 wr_bytes: { latest: 0, rate: 0, series: [] }
81eedcae
TL
232 },
233 usage: 0.25
11fdf7f2
TL
234 }
235 ];
81eedcae
TL
236 expect(component.transformPoolsData(pools)).toEqual(expected);
237 });
11fdf7f2 238
81eedcae
TL
239 it('transforms pools data correctly if stats are missing', () => {
240 const pools = [{}];
241 const expected = [
242 {
243 cdIsBinary: true,
244 pg_status: '',
245 stats: {
246 bytes_used: { latest: 0, rate: 0, series: [] },
247 max_avail: { latest: 0, rate: 0, series: [] },
248 rd: { latest: 0, rate: 0, series: [] },
249 rd_bytes: { latest: 0, rate: 0, series: [] },
250 wr: { latest: 0, rate: 0, series: [] },
251 wr_bytes: { latest: 0, rate: 0, series: [] }
252 },
253 usage: 0
254 }
255 ];
11fdf7f2
TL
256 expect(component.transformPoolsData(pools)).toEqual(expected);
257 });
258
259 it('transforms empty pools data correctly', () => {
260 const pools = undefined;
261 const expected = undefined;
11fdf7f2
TL
262 expect(component.transformPoolsData(pools)).toEqual(expected);
263 });
264 });
265
266 describe('transformPgStatus', () => {
267 it('returns status groups correctly', () => {
268 const pgStatus = { 'active+clean': 8 };
269 const expected = '8 active+clean';
270
271 expect(component.transformPgStatus(pgStatus)).toEqual(expected);
272 });
273
274 it('returns separated status groups', () => {
275 const pgStatus = { 'active+clean': 8, down: 2 };
276 const expected = '8 active+clean, 2 down';
277
278 expect(component.transformPgStatus(pgStatus)).toEqual(expected);
279 });
280
281 it('returns separated statuses correctly', () => {
282 const pgStatus = { active: 8, down: 2 };
283 const expected = '8 active, 2 down';
284
285 expect(component.transformPgStatus(pgStatus)).toEqual(expected);
286 });
287
288 it('returns empty string', () => {
289 const pgStatus = undefined;
290 const expected = '';
291
292 expect(component.transformPgStatus(pgStatus)).toEqual(expected);
293 });
294 });
295
296 describe('getPoolDetails', () => {
297 it('returns pool details corretly', () => {
298 const pool = { prop1: 1, cdIsBinary: true, prop2: 2, cdExecuting: true, prop3: 3 };
299 const expected = { prop1: 1, prop2: 2, prop3: 3 };
300
301 expect(component.getPoolDetails(pool)).toEqual(expected);
302 });
303 });
304
305 describe('getSelectionTiers', () => {
306 let pools: Pool[];
307 const setSelectionTiers = (tiers: number[]) => {
308 component.selection.selected = [
309 {
310 tiers
311 }
312 ];
313 component.selection.update();
314 component.getSelectionTiers();
315 };
316
317 beforeEach(() => {
318 pools = [];
319 setUpPools(pools);
320 });
321
322 it('should select multiple existing cache tiers', () => {
323 setSelectionTiers([0, 1, 2]);
324 expect(component.selectionCacheTiers).toEqual(pools);
325 });
326
327 it('should select correct existing cache tier', () => {
328 setSelectionTiers([0]);
329 expect(component.selectionCacheTiers).toEqual([{ pg_num: 256, pool: 0, pool_name: 'a' }]);
330 });
331
332 it('should not select cache tier if id is invalid', () => {
333 setSelectionTiers([-1]);
334 expect(component.selectionCacheTiers).toEqual([]);
335 });
336
337 it('should not select cache tier if empty', () => {
338 setSelectionTiers([]);
339 expect(component.selectionCacheTiers).toEqual([]);
340 });
341
342 it('should be able to selected one pool with multiple tiers, than with a single tier, than with no tiers', () => {
343 setSelectionTiers([0, 1, 2]);
344 expect(component.selectionCacheTiers).toEqual(pools);
345 setSelectionTiers([0]);
346 expect(component.selectionCacheTiers).toEqual([{ pg_num: 256, pool: 0, pool_name: 'a' }]);
347 setSelectionTiers([]);
348 expect(component.selectionCacheTiers).toEqual([]);
349 });
350 });
351});