]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-snapshot-list/rbd-snapshot-list.component.spec.ts
ea95a5540a6bcb77ca908f86a4dd5e9eddd2bd89
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / rbd-snapshot-list / rbd-snapshot-list.component.spec.ts
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
3 import { RouterTestingModule } from '@angular/router/testing';
4
5 import { I18n } from '@ngx-translate/i18n-polyfill';
6 import { BsModalRef, BsModalService } from 'ngx-bootstrap/modal';
7 import { TabsModule } from 'ngx-bootstrap/tabs';
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 { RbdSnapshotFormModalComponent } from '../rbd-snapshot-form/rbd-snapshot-form-modal.component';
31 import { RbdTabsComponent } from '../rbd-tabs/rbd-tabs.component';
32 import { RbdSnapshotListComponent } from './rbd-snapshot-list.component';
33 import { RbdSnapshotModel } from './rbd-snapshot.model';
34
35 describe('RbdSnapshotListComponent', () => {
36 let component: RbdSnapshotListComponent;
37 let fixture: ComponentFixture<RbdSnapshotListComponent>;
38 let summaryService: SummaryService;
39
40 const fakeAuthStorageService = {
41 isLoggedIn: () => {
42 return true;
43 },
44 getPermissions: () => {
45 return new Permissions({ 'rbd-image': ['read', 'update', 'create', 'delete'] });
46 }
47 };
48
49 configureTestBed({
50 declarations: [RbdSnapshotListComponent, RbdTabsComponent],
51 imports: [
52 ApiModule,
53 ComponentsModule,
54 DataTableModule,
55 HttpClientTestingModule,
56 PipesModule,
57 RouterTestingModule,
58 TabsModule.forRoot(),
59 ToastrModule.forRoot()
60 ],
61 providers: [
62 { provide: AuthStorageService, useValue: fakeAuthStorageService },
63 TaskListService,
64 i18nProviders
65 ]
66 });
67
68 beforeEach(() => {
69 fixture = TestBed.createComponent(RbdSnapshotListComponent);
70 component = fixture.componentInstance;
71 component.ngOnChanges();
72 summaryService = TestBed.get(SummaryService);
73 });
74
75 it('should create', () => {
76 fixture.detectChanges();
77 expect(component).toBeTruthy();
78 });
79
80 describe('api delete request', () => {
81 let called: boolean;
82 let rbdService: RbdService;
83 let notificationService: NotificationService;
84 let authStorageService: AuthStorageService;
85
86 beforeEach(() => {
87 fixture.detectChanges();
88 const i18n = TestBed.get(I18n);
89 const actionLabelsI18n = TestBed.get(ActionLabelsI18n);
90 called = false;
91 rbdService = new RbdService(null, null);
92 notificationService = new NotificationService(null, null, null);
93 authStorageService = new AuthStorageService();
94 authStorageService.set('user', '', { 'rbd-image': ['create', 'read', 'update', 'delete'] });
95 component = new RbdSnapshotListComponent(
96 authStorageService,
97 null,
98 null,
99 null,
100 rbdService,
101 null,
102 notificationService,
103 null,
104 null,
105 i18n,
106 actionLabelsI18n
107 );
108 spyOn(rbdService, 'deleteSnapshot').and.returnValue(observableThrowError({ status: 500 }));
109 spyOn(notificationService, 'notifyTask').and.stub();
110 component.modalRef = new BsModalRef();
111 component.modalRef.content = {
112 stopLoadingSpinner: () => (called = true)
113 };
114 });
115
116 it('should call stopLoadingSpinner if the request fails', fakeAsync(() => {
117 expect(called).toBe(false);
118 component._asyncTask('deleteSnapshot', 'rbd/snap/delete', 'someName');
119 tick(500);
120 expect(called).toBe(true);
121 }));
122 });
123
124 describe('handling of executing tasks', () => {
125 let snapshots: RbdSnapshotModel[];
126
127 const addSnapshot = (name: string) => {
128 const model = new RbdSnapshotModel();
129 model.id = 1;
130 model.name = name;
131 snapshots.push(model);
132 };
133
134 const addTask = (task_name: string, snapshot_name: string) => {
135 const task = new ExecutingTask();
136 task.name = task_name;
137 task.metadata = {
138 image_spec: 'rbd/foo',
139 snapshot_name: snapshot_name
140 };
141 summaryService.addRunningTask(task);
142 };
143
144 const refresh = (data: any) => {
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 expectItemTasks(component.snapshots[0], undefined);
171 expectItemTasks(component.snapshots[1], undefined);
172 expectItemTasks(component.snapshots[2], undefined);
173 expectItemTasks(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 expectItemTasks(component.snapshots[0], 'Updating');
182 expectItemTasks(component.snapshots[1], 'Deleting');
183 expectItemTasks(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(() => {
192 const ref = new BsModalRef();
193 ref.content = new RbdSnapshotFormModalComponent(
194 null,
195 null,
196 null,
197 null,
198 TestBed.get(I18n),
199 TestBed.get(ActionLabelsI18n)
200 );
201 ref.content.onSubmit = new Subject();
202 return ref;
203 });
204 });
205
206 it('should display old snapshot name', () => {
207 component.selection.selected = [{ name: 'oldname' }];
208 component.openEditSnapshotModal();
209 expect(component.modalRef.content.snapName).toBe('oldname');
210 expect(component.modalRef.content.editing).toBeTruthy();
211 });
212
213 it('should display suggested snapshot name', () => {
214 component.openCreateSnapshotModal();
215 expect(component.modalRef.content.snapName).toMatch(
216 RegExp(`^${component.rbdName}_[\\d-]+T[\\d.:]+[\\+-][\\d:]+$`)
217 );
218 });
219 });
220
221 it('should test all TableActions combinations', () => {
222 const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
223 const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
224 component.tableActions
225 );
226
227 expect(tableActions).toEqual({
228 'create,update,delete': {
229 actions: [
230 'Create',
231 'Rename',
232 'Protect',
233 'Unprotect',
234 'Clone',
235 'Copy',
236 'Rollback',
237 'Delete'
238 ],
239 primary: { multiple: 'Create', executing: 'Rename', single: 'Rename', no: 'Create' }
240 },
241 'create,update': {
242 actions: ['Create', 'Rename', 'Protect', 'Unprotect', 'Clone', 'Copy', 'Rollback'],
243 primary: { multiple: 'Create', executing: 'Rename', single: 'Rename', no: 'Create' }
244 },
245 'create,delete': {
246 actions: ['Create', 'Clone', 'Copy', 'Delete'],
247 primary: { multiple: 'Create', executing: 'Clone', single: 'Clone', no: 'Create' }
248 },
249 create: {
250 actions: ['Create', 'Clone', 'Copy'],
251 primary: { multiple: 'Create', executing: 'Clone', single: 'Clone', no: 'Create' }
252 },
253 'update,delete': {
254 actions: ['Rename', 'Protect', 'Unprotect', 'Rollback', 'Delete'],
255 primary: { multiple: 'Rename', executing: 'Rename', single: 'Rename', no: 'Rename' }
256 },
257 update: {
258 actions: ['Rename', 'Protect', 'Unprotect', 'Rollback'],
259 primary: { multiple: 'Rename', executing: 'Rename', single: 'Rename', no: 'Rename' }
260 },
261 delete: {
262 actions: ['Delete'],
263 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' }
264 },
265 'no-permissions': {
266 actions: [],
267 primary: { multiple: '', executing: '', single: '', no: '' }
268 }
269 });
270 });
271 });