]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
5eaed1c1ef64c813a4161f2b98a766baa118c180
[ceph.git] /
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
3 import { By } from '@angular/platform-browser';
4 import { RouterTestingModule } from '@angular/router/testing';
5
6 import { I18n } from '@ngx-translate/i18n-polyfill';
7 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
8 import { ToastrModule } from 'ngx-toastr';
9 import { Subject, throwError as observableThrowError } from 'rxjs';
10
11 import {
12   configureTestBed,
13   expectItemTasks,
14   i18nProviders,
15   PermissionHelper
16 } from '../../../../testing/unit-test-helper';
17 import { ApiModule } from '../../../shared/api/api.module';
18 import { RbdService } from '../../../shared/api/rbd.service';
19 import { ComponentsModule } from '../../../shared/components/components.module';
20 import { ActionLabelsI18n } from '../../../shared/constants/app.constants';
21 import { DataTableModule } from '../../../shared/datatable/datatable.module';
22 import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
23 import { ExecutingTask } from '../../../shared/models/executing-task';
24 import { Permissions } from '../../../shared/models/permissions';
25 import { PipesModule } from '../../../shared/pipes/pipes.module';
26 import { AuthStorageService } from '../../../shared/services/auth-storage.service';
27 import { NotificationService } from '../../../shared/services/notification.service';
28 import { SummaryService } from '../../../shared/services/summary.service';
29 import { TaskListService } from '../../../shared/services/task-list.service';
30 import { RbdSnapshotFormComponent } from '../rbd-snapshot-form/rbd-snapshot-form.component';
31 import { RbdSnapshotListComponent } from './rbd-snapshot-list.component';
32 import { RbdSnapshotModel } from './rbd-snapshot.model';
33
34 describe('RbdSnapshotListComponent', () => {
35   let component: RbdSnapshotListComponent;
36   let fixture: ComponentFixture<RbdSnapshotListComponent>;
37   let summaryService: SummaryService;
38
39   const fakeAuthStorageService = {
40     isLoggedIn: () => {
41       return true;
42     },
43     getPermissions: () => {
44       return new Permissions({ 'rbd-image': ['read', 'update', 'create', 'delete'] });
45     }
46   };
47
48   configureTestBed({
49     declarations: [RbdSnapshotListComponent],
50     imports: [
51       DataTableModule,
52       ComponentsModule,
53       ToastrModule.forRoot(),
54       ApiModule,
55       HttpClientTestingModule,
56       RouterTestingModule,
57       PipesModule
58     ],
59     providers: [
60       { provide: AuthStorageService, useValue: fakeAuthStorageService },
61       TaskListService,
62       i18nProviders
63     ]
64   });
65
66   beforeEach(() => {
67     fixture = TestBed.createComponent(RbdSnapshotListComponent);
68     component = fixture.componentInstance;
69     summaryService = TestBed.get(SummaryService);
70   });
71
72   it('should create', () => {
73     fixture.detectChanges();
74     expect(component).toBeTruthy();
75   });
76
77   describe('api delete request', () => {
78     let called;
79     let rbdService: RbdService;
80     let notificationService: NotificationService;
81     let authStorageService: AuthStorageService;
82
83     beforeEach(() => {
84       fixture.detectChanges();
85       const i18n = TestBed.get(I18n);
86       const actionLabelsI18n = TestBed.get(ActionLabelsI18n);
87       called = false;
88       rbdService = new RbdService(null, null);
89       notificationService = new NotificationService(null, null, null);
90       authStorageService = new AuthStorageService();
91       authStorageService.set('user', '', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
92       component = new RbdSnapshotListComponent(
93         authStorageService,
94         null,
95         null,
96         null,
97         rbdService,
98         null,
99         notificationService,
100         null,
101         null,
102         i18n,
103         actionLabelsI18n
104       );
105       spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
106       spyOn(notificationService, 'notifyTask').and.stub();
107       component.modalRef = new BsModalRef();
108       component.modalRef.content = {
109         stopLoadingSpinner: () => (called = true)
110       };
111     });
112
113     it('should call stopLoadingSpinner if the request fails', <any>fakeAsync(() => {
114       expect(called).toBe(false);
115       component._asyncTask('deleteSnapshot', 'rbd/snap/delete', 'someName');
116       tick(500);
117       expect(called).toBe(true);
118     }));
119   });
120
121   describe('handling of executing tasks', () => {
122     let snapshots: RbdSnapshotModel[];
123
124     const addSnapshot = (name) => {
125       const model = new RbdSnapshotModel();
126       model.id = 1;
127       model.name = name;
128       snapshots.push(model);
129     };
130
131     const addTask = (task_name: string, snapshot_name: string) => {
132       const task = new ExecutingTask();
133       task.name = task_name;
134       task.metadata = {
135         pool_name: 'rbd',
136         image_name: 'foo',
137         snapshot_name: snapshot_name
138       };
139       summaryService.addRunningTask(task);
140     };
141
142     const refresh = (data) => {
143       summaryService['summaryDataSource'].next(data);
144     };
145
146     beforeEach(() => {
147       fixture.detectChanges();
148       snapshots = [];
149       addSnapshot('a');
150       addSnapshot('b');
151       addSnapshot('c');
152       component.snapshots = snapshots;
153       component.poolName = 'rbd';
154       component.rbdName = 'foo';
155       refresh({ executing_tasks: [], finished_tasks: [] });
156       component.ngOnChanges();
157       fixture.detectChanges();
158     });
159
160     it('should gets all snapshots without tasks', () => {
161       expect(component.snapshots.length).toBe(3);
162       expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy();
163     });
164
165     it('should add a new image from a task', () => {
166       addTask('rbd/snap/create', 'd');
167       expect(component.snapshots.length).toBe(4);
168       expectItemTasks(component.snapshots[0], undefined);
169       expectItemTasks(component.snapshots[1], undefined);
170       expectItemTasks(component.snapshots[2], undefined);
171       expectItemTasks(component.snapshots[3], 'Creating');
172     });
173
174     it('should show when an existing image is being modified', () => {
175       addTask('rbd/snap/edit', 'a');
176       addTask('rbd/snap/delete', 'b');
177       addTask('rbd/snap/rollback', 'c');
178       expect(component.snapshots.length).toBe(3);
179       expectItemTasks(component.snapshots[0], 'Updating');
180       expectItemTasks(component.snapshots[1], 'Deleting');
181       expectItemTasks(component.snapshots[2], 'Rolling back');
182     });
183   });
184
185   describe('snapshot modal dialog', () => {
186     beforeEach(() => {
187       component.poolName = 'pool01';
188       component.rbdName = 'image01';
189       spyOn(TestBed.get(BsModalService), 'show').and.callFake(() => {
190         const ref = new BsModalRef();
191         ref.content = new RbdSnapshotFormComponent(
192           null,
193           null,
194           null,
195           null,
196           TestBed.get(I18n),
197           TestBed.get(ActionLabelsI18n)
198         );
199         ref.content.onSubmit = new Subject();
200         return ref;
201       });
202     });
203
204     it('should display old snapshot name', () => {
205       component.selection.selected = [{ name: 'oldname' }];
206       component.selection.update();
207       component.openEditSnapshotModal();
208       expect(component.modalRef.content.snapName).toBe('oldname');
209       expect(component.modalRef.content.editing).toBeTruthy();
210     });
211
212     it('should display suggested snapshot name', () => {
213       component.openCreateSnapshotModal();
214       expect(component.modalRef.content.snapName).toMatch(
215         RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+[\\+-][\\d:]+$`)
216       );
217     });
218   });
219
220   describe('show action buttons and drop down actions depending on permissions', () => {
221     let tableActions: TableActionsComponent;
222     let scenario: { fn; empty; single };
223     let permissionHelper: PermissionHelper;
224
225     const getTableActionComponent = (): TableActionsComponent => {
226       fixture.detectChanges();
227       return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
228     };
229
230     beforeEach(() => {
231       permissionHelper = new PermissionHelper(component.permission, () =>
232         getTableActionComponent()
233       );
234       scenario = {
235         fn: () => tableActions.getCurrentButton().name,
236         single: 'Rename',
237         empty: 'Create'
238       };
239     });
240
241     describe('with all', () => {
242       beforeEach(() => {
243         tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
244       });
245
246       it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
247         permissionHelper.testScenarios(scenario));
248
249       it('shows all actions', () => {
250         expect(tableActions.tableActions.length).toBe(8);
251         expect(tableActions.tableActions).toEqual(component.tableActions);
252       });
253     });
254
255     describe('with read, create and update', () => {
256       beforeEach(() => {
257         tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 0);
258       });
259
260       it(`shows 'Rename' for single selection else 'Create' as main action`, () =>
261         permissionHelper.testScenarios(scenario));
262
263       it(`shows all actions except for 'Delete'`, () => {
264         expect(tableActions.tableActions.length).toBe(7);
265         component.tableActions.pop();
266         expect(tableActions.tableActions).toEqual(component.tableActions);
267       });
268     });
269
270     describe('with read, create and delete', () => {
271       beforeEach(() => {
272         tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 1);
273       });
274
275       it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
276         scenario.single = 'Clone';
277         permissionHelper.testScenarios(scenario);
278       });
279
280       it(`shows 'Create', 'Clone', 'Copy' and 'Delete' action`, () => {
281         expect(tableActions.tableActions.length).toBe(4);
282         expect(tableActions.tableActions).toEqual([
283           component.tableActions[0],
284           component.tableActions[4],
285           component.tableActions[5],
286           component.tableActions[7]
287         ]);
288       });
289     });
290
291     describe('with read, edit and delete', () => {
292       beforeEach(() => {
293         tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 1);
294       });
295
296       it(`shows always 'Rename' as main action`, () => {
297         scenario.empty = 'Rename';
298         permissionHelper.testScenarios(scenario);
299       });
300
301       it(`shows 'Rename', 'Protect', 'Unprotect', 'Rollback' and 'Delete' action`, () => {
302         expect(tableActions.tableActions.length).toBe(5);
303         expect(tableActions.tableActions).toEqual([
304           component.tableActions[1],
305           component.tableActions[2],
306           component.tableActions[3],
307           component.tableActions[6],
308           component.tableActions[7]
309         ]);
310       });
311     });
312
313     describe('with read and create', () => {
314       beforeEach(() => {
315         tableActions = permissionHelper.setPermissionsAndGetActions(1, 0, 0);
316       });
317
318       it(`shows 'Clone' for single selection else 'Create' as main action`, () => {
319         scenario.single = 'Clone';
320         permissionHelper.testScenarios(scenario);
321       });
322
323       it(`shows 'Create', 'Clone' and 'Copy' actions`, () => {
324         expect(tableActions.tableActions.length).toBe(3);
325         expect(tableActions.tableActions).toEqual([
326           component.tableActions[0],
327           component.tableActions[4],
328           component.tableActions[5]
329         ]);
330       });
331     });
332
333     describe('with read and edit', () => {
334       beforeEach(() => {
335         tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
336       });
337
338       it(`shows always 'Rename' as main action`, () => {
339         scenario.empty = 'Rename';
340         permissionHelper.testScenarios(scenario);
341       });
342
343       it(`shows 'Rename', 'Protect', 'Unprotect' and 'Rollback' actions`, () => {
344         expect(tableActions.tableActions.length).toBe(4);
345         expect(tableActions.tableActions).toEqual([
346           component.tableActions[1],
347           component.tableActions[2],
348           component.tableActions[3],
349           component.tableActions[6]
350         ]);
351       });
352     });
353
354     describe('with read and delete', () => {
355       beforeEach(() => {
356         tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 1);
357       });
358
359       it(`shows always 'Delete' as main action`, () => {
360         scenario.single = 'Delete';
361         scenario.empty = 'Delete';
362         permissionHelper.testScenarios(scenario);
363       });
364
365       it(`shows only 'Delete' action`, () => {
366         expect(tableActions.tableActions.length).toBe(1);
367         expect(tableActions.tableActions).toEqual([component.tableActions[7]]);
368       });
369     });
370
371     describe('with only read', () => {
372       beforeEach(() => {
373         tableActions = permissionHelper.setPermissionsAndGetActions(0, 0, 0);
374       });
375
376       it('shows no main action', () => {
377         permissionHelper.testScenarios({
378           fn: () => tableActions.getCurrentButton(),
379           single: undefined,
380           empty: undefined
381         });
382       });
383
384       it('shows no actions', () => {
385         expect(tableActions.tableActions.length).toBe(0);
386         expect(tableActions.tableActions).toEqual([]);
387       });
388     });
389
390     describe('test unprotected and protected action cases', () => {
391       beforeEach(() => {
392         tableActions = permissionHelper.setPermissionsAndGetActions(0, 1, 0);
393       });
394
395       it(`shows none of them if nothing is selected`, () => {
396         permissionHelper.setSelection([]);
397         fixture.detectChanges();
398         expect(tableActions.dropDownActions).toEqual([
399           component.tableActions[1],
400           component.tableActions[6]
401         ]);
402       });
403
404       it(`shows 'Protect' of them if nothing is selected`, () => {
405         permissionHelper.setSelection([{ is_protected: false }]);
406         fixture.detectChanges();
407         expect(tableActions.dropDownActions).toEqual([
408           component.tableActions[1],
409           component.tableActions[2],
410           component.tableActions[6]
411         ]);
412       });
413
414       it(`shows 'Unprotect' of them if nothing is selected`, () => {
415         permissionHelper.setSelection([{ is_protected: true }]);
416         fixture.detectChanges();
417         expect(tableActions.dropDownActions).toEqual([
418           component.tableActions[1],
419           component.tableActions[3],
420           component.tableActions[6]
421         ]);
422       });
423     });
424   });
425 });