]> git.proxmox.com Git - ceph.git/blame - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / rbd-list / rbd-list.component.spec.ts
CommitLineData
2a845540 1import { HttpHeaders } from '@angular/common/http';
11fdf7f2
TL
2import { HttpClientTestingModule } from '@angular/common/http/testing';
3import { ComponentFixture, TestBed } from '@angular/core/testing';
e306af50 4import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
11fdf7f2
TL
5import { RouterTestingModule } from '@angular/router/testing';
6
f67539c2 7import { NgbNavModule, NgbTooltipModule } from '@ng-bootstrap/ng-bootstrap';
494da23a 8import { ToastrModule } from 'ngx-toastr';
11fdf7f2
TL
9import { BehaviorSubject, of } from 'rxjs';
10
f67539c2 11import { RbdService } from '~/app/shared/api/rbd.service';
f67539c2 12import { TableActionsComponent } from '~/app/shared/datatable/table-actions/table-actions.component';
f67539c2
TL
13import { ExecutingTask } from '~/app/shared/models/executing-task';
14import { SummaryService } from '~/app/shared/services/summary.service';
15import { TaskListService } from '~/app/shared/services/task-list.service';
16import { SharedModule } from '~/app/shared/shared.module';
17import { configureTestBed, expectItemTasks, PermissionHelper } from '~/testing/unit-test-helper';
11fdf7f2
TL
18import { RbdConfigurationListComponent } from '../rbd-configuration-list/rbd-configuration-list.component';
19import { RbdDetailsComponent } from '../rbd-details/rbd-details.component';
20import { RbdSnapshotListComponent } from '../rbd-snapshot-list/rbd-snapshot-list.component';
9f95a23c 21import { RbdTabsComponent } from '../rbd-tabs/rbd-tabs.component';
11fdf7f2
TL
22import { RbdListComponent } from './rbd-list.component';
23import { RbdModel } from './rbd-model';
24
25describe('RbdListComponent', () => {
26 let fixture: ComponentFixture<RbdListComponent>;
27 let component: RbdListComponent;
28 let summaryService: SummaryService;
29 let rbdService: RbdService;
2a845540 30 let headers: HttpHeaders;
11fdf7f2 31
9f95a23c 32 const refresh = (data: any) => {
11fdf7f2
TL
33 summaryService['summaryDataSource'].next(data);
34 };
35
36 configureTestBed({
37 imports: [
e306af50 38 BrowserAnimationsModule,
11fdf7f2 39 SharedModule,
f67539c2
TL
40 NgbNavModule,
41 NgbTooltipModule,
494da23a 42 ToastrModule.forRoot(),
11fdf7f2
TL
43 RouterTestingModule,
44 HttpClientTestingModule
45 ],
46 declarations: [
47 RbdListComponent,
48 RbdDetailsComponent,
49 RbdSnapshotListComponent,
9f95a23c
TL
50 RbdConfigurationListComponent,
51 RbdTabsComponent
11fdf7f2 52 ],
f67539c2 53 providers: [TaskListService]
11fdf7f2
TL
54 });
55
56 beforeEach(() => {
57 fixture = TestBed.createComponent(RbdListComponent);
58 component = fixture.componentInstance;
f67539c2
TL
59 summaryService = TestBed.inject(SummaryService);
60 rbdService = TestBed.inject(RbdService);
2a845540 61 headers = new HttpHeaders().set('X-Total-Count', '10');
11fdf7f2
TL
62
63 // this is needed because summaryService isn't being reset after each test.
64 summaryService['summaryDataSource'] = new BehaviorSubject(null);
65 summaryService['summaryData$'] = summaryService['summaryDataSource'].asObservable();
66 });
67
68 it('should create', () => {
69 expect(component).toBeTruthy();
70 });
71
72 describe('after ngOnInit', () => {
73 beforeEach(() => {
74 fixture.detectChanges();
75 spyOn(rbdService, 'list').and.callThrough();
76 });
77
78 it('should load images on init', () => {
79 refresh({});
80 expect(rbdService.list).toHaveBeenCalled();
81 });
82
83 it('should not load images on init because no data', () => {
84 refresh(undefined);
85 expect(rbdService.list).not.toHaveBeenCalled();
86 });
87
88 it('should call error function on init when summary service fails', () => {
89 spyOn(component.table, 'reset');
90 summaryService['summaryDataSource'].error(undefined);
91 expect(component.table.reset).toHaveBeenCalled();
11fdf7f2
TL
92 });
93 });
94
9f95a23c
TL
95 describe('handling of deletion', () => {
96 beforeEach(() => {
97 fixture.detectChanges();
98 });
99
100 it('should check if there are no snapshots', () => {
101 component.selection.add({
102 id: '-1',
103 name: 'rbd1',
104 pool_name: 'rbd'
105 });
106 expect(component.hasSnapshots()).toBeFalsy();
107 });
108
109 it('should check if there are snapshots', () => {
110 component.selection.add({
111 id: '-1',
112 name: 'rbd1',
113 pool_name: 'rbd',
114 snapshots: [{}, {}]
115 });
116 expect(component.hasSnapshots()).toBeTruthy();
117 });
118
119 it('should get delete disable description', () => {
120 component.selection.add({
121 id: '-1',
122 name: 'rbd1',
123 pool_name: 'rbd',
124 snapshots: [
125 {
126 children: [{}]
127 }
128 ]
129 });
f91f0fd5 130 expect(component.getDeleteDisableDesc(component.selection)).toBe(
9f95a23c
TL
131 'This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD.'
132 );
133 });
134
135 it('should list all protected snapshots', () => {
136 component.selection.add({
137 id: '-1',
138 name: 'rbd1',
139 pool_name: 'rbd',
140 snapshots: [
141 {
142 name: 'snap1',
143 is_protected: false
144 },
145 {
146 name: 'snap2',
147 is_protected: true
148 }
149 ]
150 });
151
152 expect(component.listProtectedSnapshots()).toEqual(['snap2']);
153 });
154 });
155
11fdf7f2
TL
156 describe('handling of executing tasks', () => {
157 let images: RbdModel[];
158
9f95a23c 159 const addImage = (name: string) => {
11fdf7f2
TL
160 const model = new RbdModel();
161 model.id = '-1';
162 model.name = name;
163 model.pool_name = 'rbd';
164 images.push(model);
165 };
166
167 const addTask = (name: string, image_name: string) => {
168 const task = new ExecutingTask();
169 task.name = name;
170 switch (task.name) {
171 case 'rbd/copy':
172 task.metadata = {
173 dest_pool_name: 'rbd',
9f95a23c 174 dest_namespace: null,
11fdf7f2
TL
175 dest_image_name: 'd'
176 };
177 break;
178 case 'rbd/clone':
179 task.metadata = {
180 child_pool_name: 'rbd',
9f95a23c 181 child_namespace: null,
11fdf7f2
TL
182 child_image_name: 'd'
183 };
184 break;
9f95a23c 185 case 'rbd/create':
11fdf7f2
TL
186 task.metadata = {
187 pool_name: 'rbd',
9f95a23c 188 namespace: null,
11fdf7f2
TL
189 image_name: image_name
190 };
191 break;
9f95a23c
TL
192 default:
193 task.metadata = {
194 image_spec: `rbd/${image_name}`
195 };
196 break;
11fdf7f2
TL
197 }
198 summaryService.addRunningTask(task);
199 };
200
11fdf7f2
TL
201 beforeEach(() => {
202 images = [];
203 addImage('a');
204 addImage('b');
205 addImage('c');
206 component.images = images;
207 refresh({ executing_tasks: [], finished_tasks: [] });
208 spyOn(rbdService, 'list').and.callFake(() =>
2a845540 209 of([{ pool_name: 'rbd', value: images, headers: headers }])
11fdf7f2
TL
210 );
211 fixture.detectChanges();
212 });
213
214 it('should gets all images without tasks', () => {
215 expect(component.images.length).toBe(3);
9f95a23c 216 expect(component.images.every((image: any) => !image.cdExecuting)).toBeTruthy();
11fdf7f2
TL
217 });
218
219 it('should add a new image from a task', () => {
220 addTask('rbd/create', 'd');
221 expect(component.images.length).toBe(4);
eafe8130
TL
222 expectItemTasks(component.images[0], undefined);
223 expectItemTasks(component.images[1], undefined);
224 expectItemTasks(component.images[2], undefined);
225 expectItemTasks(component.images[3], 'Creating');
11fdf7f2
TL
226 });
227
228 it('should show when a image is being cloned', () => {
229 addTask('rbd/clone', 'd');
230 expect(component.images.length).toBe(4);
eafe8130
TL
231 expectItemTasks(component.images[0], undefined);
232 expectItemTasks(component.images[1], undefined);
233 expectItemTasks(component.images[2], undefined);
234 expectItemTasks(component.images[3], 'Cloning');
11fdf7f2
TL
235 });
236
237 it('should show when a image is being copied', () => {
238 addTask('rbd/copy', 'd');
239 expect(component.images.length).toBe(4);
eafe8130
TL
240 expectItemTasks(component.images[0], undefined);
241 expectItemTasks(component.images[1], undefined);
242 expectItemTasks(component.images[2], undefined);
243 expectItemTasks(component.images[3], 'Copying');
11fdf7f2
TL
244 });
245
246 it('should show when an existing image is being modified', () => {
247 addTask('rbd/edit', 'a');
eafe8130 248 expectItemTasks(component.images[0], 'Updating');
2a845540 249 addTask('rbd/delete', 'b');
eafe8130 250 expectItemTasks(component.images[1], 'Deleting');
2a845540 251 addTask('rbd/flatten', 'c');
eafe8130 252 expectItemTasks(component.images[2], 'Flattening');
2a845540 253 expect(component.images.length).toBe(3);
11fdf7f2
TL
254 });
255 });
256
9f95a23c
TL
257 it('should test all TableActions combinations', () => {
258 const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
259 const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
260 component.tableActions
261 );
262
263 expect(tableActions).toEqual({
264 'create,update,delete': {
2a845540
TL
265 actions: [
266 'Create',
267 'Edit',
268 'Copy',
269 'Flatten',
270 'Resync',
271 'Delete',
272 'Move to Trash',
273 'Remove Scheduling',
274 'Promote',
275 'Demote'
276 ],
9f95a23c
TL
277 primary: { multiple: 'Create', executing: 'Edit', single: 'Edit', no: 'Create' }
278 },
279 'create,update': {
2a845540
TL
280 actions: [
281 'Create',
282 'Edit',
283 'Copy',
284 'Flatten',
285 'Resync',
286 'Remove Scheduling',
287 'Promote',
288 'Demote'
289 ],
9f95a23c
TL
290 primary: { multiple: 'Create', executing: 'Edit', single: 'Edit', no: 'Create' }
291 },
292 'create,delete': {
293 actions: ['Create', 'Copy', 'Delete', 'Move to Trash'],
294 primary: { multiple: 'Create', executing: 'Copy', single: 'Copy', no: 'Create' }
295 },
296 create: {
297 actions: ['Create', 'Copy'],
298 primary: { multiple: 'Create', executing: 'Copy', single: 'Copy', no: 'Create' }
299 },
300 'update,delete': {
2a845540
TL
301 actions: [
302 'Edit',
303 'Flatten',
304 'Resync',
305 'Delete',
306 'Move to Trash',
307 'Remove Scheduling',
308 'Promote',
309 'Demote'
310 ],
9f95a23c
TL
311 primary: { multiple: 'Edit', executing: 'Edit', single: 'Edit', no: 'Edit' }
312 },
313 update: {
2a845540 314 actions: ['Edit', 'Flatten', 'Resync', 'Remove Scheduling', 'Promote', 'Demote'],
9f95a23c
TL
315 primary: { multiple: 'Edit', executing: 'Edit', single: 'Edit', no: 'Edit' }
316 },
317 delete: {
318 actions: ['Delete', 'Move to Trash'],
319 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' }
320 },
321 'no-permissions': {
322 actions: [],
323 primary: { multiple: '', executing: '', single: '', no: '' }
324 }
11fdf7f2
TL
325 });
326 });
f67539c2
TL
327
328 const getActionDisable = (name: string) =>
329 component.tableActions.find((o) => o.name === name).disable;
330
b3b6e05e
TL
331 const testActions = (selection: any, expected: { [action: string]: string | boolean }) => {
332 expect(getActionDisable('Edit')(selection)).toBe(expected.edit || false);
333 expect(getActionDisable('Delete')(selection)).toBe(expected.delete || false);
334 expect(getActionDisable('Copy')(selection)).toBe(expected.copy || false);
f67539c2 335 expect(getActionDisable('Flatten')(selection)).toBeTruthy();
b3b6e05e 336 expect(getActionDisable('Move to Trash')(selection)).toBe(expected.moveTrash || false);
f67539c2
TL
337 };
338
339 it('should test TableActions with valid/invalid image name', () => {
340 component.selection.selected = [
341 {
342 name: 'foobar',
343 pool_name: 'rbd',
344 snapshots: []
345 }
346 ];
b3b6e05e 347 testActions(component.selection, {});
f67539c2
TL
348
349 component.selection.selected = [
350 {
351 name: 'foo/bar',
352 pool_name: 'rbd',
353 snapshots: []
354 }
355 ];
b3b6e05e
TL
356 const message = `This RBD image has an invalid name and can't be managed by ceph.`;
357 const expected = {
358 edit: message,
359 delete: message,
360 copy: message,
361 moveTrash: message
362 };
363 testActions(component.selection, expected);
364 });
365
366 it('should disable edit, copy, flatten and move action if RBD is in status `Removing`', () => {
367 component.selection.selected = [
368 {
369 name: 'foobar',
370 pool_name: 'rbd',
371 snapshots: [],
372 source: 'REMOVING'
373 }
374 ];
375
376 const message = `Action not possible for an RBD in status 'Removing'`;
377 const expected = {
378 edit: message,
379 copy: message,
380 moveTrash: message
381 };
382 testActions(component.selection, expected);
f67539c2 383 });
11fdf7f2 384});