]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
44f4130183f0c7966cc607511960a74266d65575
[ceph.git] /
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
3 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
4 import { RouterTestingModule } from '@angular/router/testing';
5
6 import { NgbModalModule, NgbNavModule } from '@ng-bootstrap/ng-bootstrap';
7 import { MockComponent } from 'ng-mocks';
8 import { ToastrModule } from 'ngx-toastr';
9 import { Subject, throwError as observableThrowError } from 'rxjs';
10
11 import {
12   configureTestBed,
13   expectItemTasks,
14   PermissionHelper
15 } from '../../../../testing/unit-test-helper';
16 import { RbdService } from '../../../shared/api/rbd.service';
17 import { ComponentsModule } from '../../../shared/components/components.module';
18 import { CriticalConfirmationModalComponent } from '../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
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 { CdTableSelection } from '../../../shared/models/cd-table-selection';
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 { ModalService } from '../../../shared/services/modal.service';
28 import { NotificationService } from '../../../shared/services/notification.service';
29 import { SummaryService } from '../../../shared/services/summary.service';
30 import { TaskListService } from '../../../shared/services/task-list.service';
31 import { RbdSnapshotFormModalComponent } from '../rbd-snapshot-form/rbd-snapshot-form-modal.component';
32 import { RbdTabsComponent } from '../rbd-tabs/rbd-tabs.component';
33 import { RbdSnapshotActionsModel } from './rbd-snapshot-actions.model';
34 import { RbdSnapshotListComponent } from './rbd-snapshot-list.component';
35 import { RbdSnapshotModel } from './rbd-snapshot.model';
36
37 describe('RbdSnapshotListComponent', () => {
38   let component: RbdSnapshotListComponent;
39   let fixture: ComponentFixture<RbdSnapshotListComponent>;
40   let summaryService: SummaryService;
41
42   const fakeAuthStorageService = {
43     isLoggedIn: () => {
44       return true;
45     },
46     getPermissions: () => {
47       return new Permissions({ 'rbd-image': ['read', 'update', 'create', 'delete'] });
48     }
49   };
50
51   configureTestBed(
52     {
53       declarations: [
54         RbdSnapshotListComponent,
55         RbdTabsComponent,
56         MockComponent(RbdSnapshotFormModalComponent)
57       ],
58       imports: [
59         BrowserAnimationsModule,
60         ComponentsModule,
61         DataTableModule,
62         HttpClientTestingModule,
63         PipesModule,
64         RouterTestingModule,
65         NgbNavModule,
66         ToastrModule.forRoot(),
67         NgbModalModule
68       ],
69       providers: [
70         { provide: AuthStorageService, useValue: fakeAuthStorageService },
71         TaskListService
72       ]
73     },
74     [CriticalConfirmationModalComponent]
75   );
76
77   beforeEach(() => {
78     fixture = TestBed.createComponent(RbdSnapshotListComponent);
79     component = fixture.componentInstance;
80     component.ngOnChanges();
81     summaryService = TestBed.inject(SummaryService);
82   });
83
84   it('should create', () => {
85     fixture.detectChanges();
86     expect(component).toBeTruthy();
87   });
88
89   describe('api delete request', () => {
90     let called: boolean;
91     let rbdService: RbdService;
92     let notificationService: NotificationService;
93     let authStorageService: AuthStorageService;
94
95     beforeEach(() => {
96       fixture.detectChanges();
97       const modalService = TestBed.inject(ModalService);
98       const actionLabelsI18n = TestBed.inject(ActionLabelsI18n);
99       called = false;
100       rbdService = new RbdService(null, null);
101       notificationService = new NotificationService(null, null, null);
102       authStorageService = new AuthStorageService();
103       authStorageService.set('user', '', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
104       component = new RbdSnapshotListComponent(
105         authStorageService,
106         modalService,
107         null,
108         null,
109         rbdService,
110         null,
111         notificationService,
112         null,
113         null,
114         actionLabelsI18n
115       );
116       spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
117       spyOn(notificationService, 'notifyTask').and.stub();
118     });
119
120     it('should call stopLoadingSpinner if the request fails', fakeAsync(() => {
121       component.updateSelection(new CdTableSelection([{ name: 'someName' }]));
122       expect(called).toBe(false);
123       component.deleteSnapshotModal();
124       spyOn(component.modalRef.componentInstance, 'stopLoadingSpinner').and.callFake(() => {
125         called = true;
126       });
127       component.modalRef.componentInstance.submitAction();
128       tick(500);
129       expect(called).toBe(true);
130     }));
131   });
132
133   describe('handling of executing tasks', () => {
134     let snapshots: RbdSnapshotModel[];
135
136     const addSnapshot = (name: string) => {
137       const model = new RbdSnapshotModel();
138       model.id = 1;
139       model.name = name;
140       snapshots.push(model);
141     };
142
143     const addTask = (task_name: string, snapshot_name: string) => {
144       const task = new ExecutingTask();
145       task.name = task_name;
146       task.metadata = {
147         image_spec: 'rbd/foo',
148         snapshot_name: snapshot_name
149       };
150       summaryService.addRunningTask(task);
151     };
152
153     const refresh = (data: any) => {
154       summaryService['summaryDataSource'].next(data);
155     };
156
157     beforeEach(() => {
158       fixture.detectChanges();
159       snapshots = [];
160       addSnapshot('a');
161       addSnapshot('b');
162       addSnapshot('c');
163       component.snapshots = snapshots;
164       component.poolName = 'rbd';
165       component.rbdName = 'foo';
166       refresh({ executing_tasks: [], finished_tasks: [] });
167       component.ngOnChanges();
168       fixture.detectChanges();
169     });
170
171     it('should gets all snapshots without tasks', () => {
172       expect(component.snapshots.length).toBe(3);
173       expect(component.snapshots.every((image) => !image.cdExecuting)).toBeTruthy();
174     });
175
176     it('should add a new image from a task', () => {
177       addTask('rbd/snap/create', 'd');
178       expect(component.snapshots.length).toBe(4);
179       expectItemTasks(component.snapshots[0], undefined);
180       expectItemTasks(component.snapshots[1], undefined);
181       expectItemTasks(component.snapshots[2], undefined);
182       expectItemTasks(component.snapshots[3], 'Creating');
183     });
184
185     it('should show when an existing image is being modified', () => {
186       addTask('rbd/snap/edit', 'a');
187       addTask('rbd/snap/delete', 'b');
188       addTask('rbd/snap/rollback', 'c');
189       expect(component.snapshots.length).toBe(3);
190       expectItemTasks(component.snapshots[0], 'Updating');
191       expectItemTasks(component.snapshots[1], 'Deleting');
192       expectItemTasks(component.snapshots[2], 'Rolling back');
193     });
194   });
195
196   describe('snapshot modal dialog', () => {
197     beforeEach(() => {
198       component.poolName = 'pool01';
199       component.rbdName = 'image01';
200       spyOn(TestBed.inject(ModalService), 'show').and.callFake(() => {
201         const ref: any = {};
202         ref.componentInstance = new RbdSnapshotFormModalComponent(
203           null,
204           null,
205           null,
206           null,
207           TestBed.inject(ActionLabelsI18n)
208         );
209         ref.componentInstance.onSubmit = new Subject();
210         return ref;
211       });
212     });
213
214     it('should display old snapshot name', () => {
215       component.selection.selected = [{ name: 'oldname' }];
216       component.openEditSnapshotModal();
217       expect(component.modalRef.componentInstance.snapName).toBe('oldname');
218       expect(component.modalRef.componentInstance.editing).toBeTruthy();
219     });
220
221     it('should display suggested snapshot name', () => {
222       component.openCreateSnapshotModal();
223       expect(component.modalRef.componentInstance.snapName).toMatch(
224         RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+[\\+-][\\d:]+$`)
225       );
226     });
227   });
228
229   it('should test all TableActions combinations', () => {
230     const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
231     const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
232       component.tableActions
233     );
234
235     expect(tableActions).toEqual({
236       'create,update,delete': {
237         actions: [
238           'Create',
239           'Rename',
240           'Protect',
241           'Unprotect',
242           'Clone',
243           'Copy',
244           'Rollback',
245           'Delete'
246         ],
247         primary: { multiple: 'Create', executing: 'Rename', single: 'Rename', no: 'Create' }
248       },
249       'create,update': {
250         actions: ['Create', 'Rename', 'Protect', 'Unprotect', 'Clone', 'Copy', 'Rollback'],
251         primary: { multiple: 'Create', executing: 'Rename', single: 'Rename', no: 'Create' }
252       },
253       'create,delete': {
254         actions: ['Create', 'Clone', 'Copy', 'Delete'],
255         primary: { multiple: 'Create', executing: 'Clone', single: 'Clone', no: 'Create' }
256       },
257       create: {
258         actions: ['Create', 'Clone', 'Copy'],
259         primary: { multiple: 'Create', executing: 'Clone', single: 'Clone', no: 'Create' }
260       },
261       'update,delete': {
262         actions: ['Rename', 'Protect', 'Unprotect', 'Rollback', 'Delete'],
263         primary: { multiple: 'Rename', executing: 'Rename', single: 'Rename', no: 'Rename' }
264       },
265       update: {
266         actions: ['Rename', 'Protect', 'Unprotect', 'Rollback'],
267         primary: { multiple: 'Rename', executing: 'Rename', single: 'Rename', no: 'Rename' }
268       },
269       delete: {
270         actions: ['Delete'],
271         primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' }
272       },
273       'no-permissions': {
274         actions: [],
275         primary: { multiple: '', executing: '', single: '', no: '' }
276       }
277     });
278   });
279
280   describe('clone button disable state', () => {
281     let actions: RbdSnapshotActionsModel;
282
283     beforeEach(() => {
284       fixture.detectChanges();
285       const rbdService = TestBed.inject(RbdService);
286       const actionLabelsI18n = TestBed.inject(ActionLabelsI18n);
287       actions = new RbdSnapshotActionsModel(actionLabelsI18n, [], rbdService);
288     });
289
290     it('should be disabled with version 1 and protected false', () => {
291       const selection = new CdTableSelection([{ name: 'someName', is_protected: false }]);
292       const disableDesc = actions.getCloneDisableDesc(selection, ['layering']);
293       expect(disableDesc).toBe('Snapshot must be protected in order to clone.');
294     });
295
296     it.each([
297       [1, true],
298       [2, true],
299       [2, false]
300     ])('should be enabled with version %d and protected %s', (version, is_protected) => {
301       actions.cloneFormatVersion = version;
302       const selection = new CdTableSelection([{ name: 'someName', is_protected: is_protected }]);
303       const disableDesc = actions.getCloneDisableDesc(selection, ['layering']);
304       expect(disableDesc).toBe(false);
305     });
306   });
307 });