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