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