]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/rbd-list/rbd-list.component.spec.ts
9da640e4822a1f2124a95fabadb3eb11523fc035
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / rbd-list / rbd-list.component.spec.ts
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { RouterTestingModule } from '@angular/router/testing';
4
5 import { AlertModule } from 'ngx-bootstrap/alert';
6 import { BsDropdownModule } from 'ngx-bootstrap/dropdown';
7 import { ModalModule } from 'ngx-bootstrap/modal';
8 import { TabsModule } from 'ngx-bootstrap/tabs';
9 import { TooltipModule } from 'ngx-bootstrap/tooltip';
10 import { ToastrModule } from 'ngx-toastr';
11 import { BehaviorSubject, of } from 'rxjs';
12
13 import {
14 configureTestBed,
15 expectItemTasks,
16 i18nProviders,
17 PermissionHelper
18 } from '../../../../testing/unit-test-helper';
19 import { RbdService } from '../../../shared/api/rbd.service';
20 import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
21 import { ViewCacheStatus } from '../../../shared/enum/view-cache-status.enum';
22 import { ExecutingTask } from '../../../shared/models/executing-task';
23 import { SummaryService } from '../../../shared/services/summary.service';
24 import { TaskListService } from '../../../shared/services/task-list.service';
25 import { SharedModule } from '../../../shared/shared.module';
26 import { RbdConfigurationListComponent } from '../rbd-configuration-list/rbd-configuration-list.component';
27 import { RbdDetailsComponent } from '../rbd-details/rbd-details.component';
28 import { RbdSnapshotListComponent } from '../rbd-snapshot-list/rbd-snapshot-list.component';
29 import { RbdTabsComponent } from '../rbd-tabs/rbd-tabs.component';
30 import { RbdListComponent } from './rbd-list.component';
31 import { RbdModel } from './rbd-model';
32
33 describe('RbdListComponent', () => {
34 let fixture: ComponentFixture<RbdListComponent>;
35 let component: RbdListComponent;
36 let summaryService: SummaryService;
37 let rbdService: RbdService;
38
39 const refresh = (data: any) => {
40 summaryService['summaryDataSource'].next(data);
41 };
42
43 configureTestBed({
44 imports: [
45 SharedModule,
46 BsDropdownModule.forRoot(),
47 TabsModule.forRoot(),
48 ModalModule.forRoot(),
49 TooltipModule.forRoot(),
50 ToastrModule.forRoot(),
51 AlertModule.forRoot(),
52 RouterTestingModule,
53 HttpClientTestingModule
54 ],
55 declarations: [
56 RbdListComponent,
57 RbdDetailsComponent,
58 RbdSnapshotListComponent,
59 RbdConfigurationListComponent,
60 RbdTabsComponent
61 ],
62 providers: [TaskListService, i18nProviders]
63 });
64
65 beforeEach(() => {
66 fixture = TestBed.createComponent(RbdListComponent);
67 component = fixture.componentInstance;
68 summaryService = TestBed.get(SummaryService);
69 rbdService = TestBed.get(RbdService);
70
71 // this is needed because summaryService isn't being reset after each test.
72 summaryService['summaryDataSource'] = new BehaviorSubject(null);
73 summaryService['summaryData$'] = summaryService['summaryDataSource'].asObservable();
74 });
75
76 it('should create', () => {
77 expect(component).toBeTruthy();
78 });
79
80 describe('after ngOnInit', () => {
81 beforeEach(() => {
82 fixture.detectChanges();
83 spyOn(rbdService, 'list').and.callThrough();
84 });
85
86 it('should load images on init', () => {
87 refresh({});
88 expect(rbdService.list).toHaveBeenCalled();
89 });
90
91 it('should not load images on init because no data', () => {
92 refresh(undefined);
93 expect(rbdService.list).not.toHaveBeenCalled();
94 });
95
96 it('should call error function on init when summary service fails', () => {
97 spyOn(component.table, 'reset');
98 summaryService['summaryDataSource'].error(undefined);
99 expect(component.table.reset).toHaveBeenCalled();
100 expect(component.viewCacheStatusList).toEqual([{ status: ViewCacheStatus.ValueException }]);
101 });
102 });
103
104 describe('handling of deletion', () => {
105 beforeEach(() => {
106 fixture.detectChanges();
107 });
108
109 it('should check if there are no snapshots', () => {
110 component.selection.add({
111 id: '-1',
112 name: 'rbd1',
113 pool_name: 'rbd'
114 });
115 expect(component.hasSnapshots()).toBeFalsy();
116 });
117
118 it('should check if there are snapshots', () => {
119 component.selection.add({
120 id: '-1',
121 name: 'rbd1',
122 pool_name: 'rbd',
123 snapshots: [{}, {}]
124 });
125 expect(component.hasSnapshots()).toBeTruthy();
126 });
127
128 it('should get delete disable description', () => {
129 component.selection.add({
130 id: '-1',
131 name: 'rbd1',
132 pool_name: 'rbd',
133 snapshots: [
134 {
135 children: [{}]
136 }
137 ]
138 });
139 expect(component.getDeleteDisableDesc()).toBe(
140 'This RBD has cloned snapshots. Please delete related RBDs before deleting this RBD.'
141 );
142 });
143
144 it('should list all protected snapshots', () => {
145 component.selection.add({
146 id: '-1',
147 name: 'rbd1',
148 pool_name: 'rbd',
149 snapshots: [
150 {
151 name: 'snap1',
152 is_protected: false
153 },
154 {
155 name: 'snap2',
156 is_protected: true
157 }
158 ]
159 });
160
161 expect(component.listProtectedSnapshots()).toEqual(['snap2']);
162 });
163 });
164
165 describe('handling of executing tasks', () => {
166 let images: RbdModel[];
167
168 const addImage = (name: string) => {
169 const model = new RbdModel();
170 model.id = '-1';
171 model.name = name;
172 model.pool_name = 'rbd';
173 images.push(model);
174 };
175
176 const addTask = (name: string, image_name: string) => {
177 const task = new ExecutingTask();
178 task.name = name;
179 switch (task.name) {
180 case 'rbd/copy':
181 task.metadata = {
182 dest_pool_name: 'rbd',
183 dest_namespace: null,
184 dest_image_name: 'd'
185 };
186 break;
187 case 'rbd/clone':
188 task.metadata = {
189 child_pool_name: 'rbd',
190 child_namespace: null,
191 child_image_name: 'd'
192 };
193 break;
194 case 'rbd/create':
195 task.metadata = {
196 pool_name: 'rbd',
197 namespace: null,
198 image_name: image_name
199 };
200 break;
201 default:
202 task.metadata = {
203 image_spec: `rbd/${image_name}`
204 };
205 break;
206 }
207 summaryService.addRunningTask(task);
208 };
209
210 beforeEach(() => {
211 images = [];
212 addImage('a');
213 addImage('b');
214 addImage('c');
215 component.images = images;
216 refresh({ executing_tasks: [], finished_tasks: [] });
217 spyOn(rbdService, 'list').and.callFake(() =>
218 of([{ pool_name: 'rbd', status: 1, value: images }])
219 );
220 fixture.detectChanges();
221 });
222
223 it('should gets all images without tasks', () => {
224 expect(component.images.length).toBe(3);
225 expect(component.images.every((image: any) => !image.cdExecuting)).toBeTruthy();
226 });
227
228 it('should add a new image from a task', () => {
229 addTask('rbd/create', 'd');
230 expect(component.images.length).toBe(4);
231 expectItemTasks(component.images[0], undefined);
232 expectItemTasks(component.images[1], undefined);
233 expectItemTasks(component.images[2], undefined);
234 expectItemTasks(component.images[3], 'Creating');
235 });
236
237 it('should show when a image is being cloned', () => {
238 addTask('rbd/clone', 'd');
239 expect(component.images.length).toBe(4);
240 expectItemTasks(component.images[0], undefined);
241 expectItemTasks(component.images[1], undefined);
242 expectItemTasks(component.images[2], undefined);
243 expectItemTasks(component.images[3], 'Cloning');
244 });
245
246 it('should show when a image is being copied', () => {
247 addTask('rbd/copy', 'd');
248 expect(component.images.length).toBe(4);
249 expectItemTasks(component.images[0], undefined);
250 expectItemTasks(component.images[1], undefined);
251 expectItemTasks(component.images[2], undefined);
252 expectItemTasks(component.images[3], 'Copying');
253 });
254
255 it('should show when an existing image is being modified', () => {
256 addTask('rbd/edit', 'a');
257 addTask('rbd/delete', 'b');
258 addTask('rbd/flatten', 'c');
259 expect(component.images.length).toBe(3);
260 expectItemTasks(component.images[0], 'Updating');
261 expectItemTasks(component.images[1], 'Deleting');
262 expectItemTasks(component.images[2], 'Flattening');
263 });
264 });
265
266 it('should test all TableActions combinations', () => {
267 const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
268 const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
269 component.tableActions
270 );
271
272 expect(tableActions).toEqual({
273 'create,update,delete': {
274 actions: ['Create', 'Edit', 'Copy', 'Flatten', 'Delete', 'Move to Trash'],
275 primary: { multiple: 'Create', executing: 'Edit', single: 'Edit', no: 'Create' }
276 },
277 'create,update': {
278 actions: ['Create', 'Edit', 'Copy', 'Flatten'],
279 primary: { multiple: 'Create', executing: 'Edit', single: 'Edit', no: 'Create' }
280 },
281 'create,delete': {
282 actions: ['Create', 'Copy', 'Delete', 'Move to Trash'],
283 primary: { multiple: 'Create', executing: 'Copy', single: 'Copy', no: 'Create' }
284 },
285 create: {
286 actions: ['Create', 'Copy'],
287 primary: { multiple: 'Create', executing: 'Copy', single: 'Copy', no: 'Create' }
288 },
289 'update,delete': {
290 actions: ['Edit', 'Flatten', 'Delete', 'Move to Trash'],
291 primary: { multiple: 'Edit', executing: 'Edit', single: 'Edit', no: 'Edit' }
292 },
293 update: {
294 actions: ['Edit', 'Flatten'],
295 primary: { multiple: 'Edit', executing: 'Edit', single: 'Edit', no: 'Edit' }
296 },
297 delete: {
298 actions: ['Delete', 'Move to Trash'],
299 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' }
300 },
301 'no-permissions': {
302 actions: [],
303 primary: { multiple: '', executing: '', single: '', no: '' }
304 }
305 });
306 });
307 });