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