]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/block/iscsi-target-list/iscsi-target-list.component.spec.ts
16ff57abd2319d381a0a06540ee5bb33b9e3cceb
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / block / iscsi-target-list / iscsi-target-list.component.spec.ts
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
4 import { RouterTestingModule } from '@angular/router/testing';
5
6 import { TreeModule } from 'angular-tree-component';
7 import { TabsModule } from 'ngx-bootstrap/tabs';
8 import { ToastrModule } from 'ngx-toastr';
9 import { BehaviorSubject, of } from 'rxjs';
10
11 import {
12 configureTestBed,
13 expectItemTasks,
14 i18nProviders,
15 PermissionHelper
16 } from '../../../../testing/unit-test-helper';
17 import { IscsiService } from '../../../shared/api/iscsi.service';
18 import { TableActionsComponent } from '../../../shared/datatable/table-actions/table-actions.component';
19 import { CdTableAction } from '../../../shared/models/cd-table-action';
20 import { ExecutingTask } from '../../../shared/models/executing-task';
21 import { SummaryService } from '../../../shared/services/summary.service';
22 import { TaskListService } from '../../../shared/services/task-list.service';
23 import { SharedModule } from '../../../shared/shared.module';
24 import { IscsiTabsComponent } from '../iscsi-tabs/iscsi-tabs.component';
25 import { IscsiTargetDetailsComponent } from '../iscsi-target-details/iscsi-target-details.component';
26 import { IscsiTargetListComponent } from './iscsi-target-list.component';
27
28 describe('IscsiTargetListComponent', () => {
29 let component: IscsiTargetListComponent;
30 let fixture: ComponentFixture<IscsiTargetListComponent>;
31 let summaryService: SummaryService;
32 let iscsiService: IscsiService;
33
34 const refresh = (data: any) => {
35 summaryService['summaryDataSource'].next(data);
36 };
37
38 configureTestBed({
39 imports: [
40 BrowserAnimationsModule,
41 HttpClientTestingModule,
42 RouterTestingModule,
43 SharedModule,
44 TabsModule.forRoot(),
45 TreeModule,
46 ToastrModule.forRoot()
47 ],
48 declarations: [IscsiTargetListComponent, IscsiTabsComponent, IscsiTargetDetailsComponent],
49 providers: [TaskListService, i18nProviders]
50 });
51
52 beforeEach(() => {
53 fixture = TestBed.createComponent(IscsiTargetListComponent);
54 component = fixture.componentInstance;
55 summaryService = TestBed.get(SummaryService);
56 iscsiService = TestBed.get(IscsiService);
57
58 // this is needed because summaryService isn't being reset after each test.
59 summaryService['summaryDataSource'] = new BehaviorSubject(null);
60 summaryService['summaryData$'] = summaryService['summaryDataSource'].asObservable();
61
62 spyOn(iscsiService, 'status').and.returnValue(of({ available: true }));
63 spyOn(iscsiService, 'version').and.returnValue(of({ ceph_iscsi_config_version: 11 }));
64 });
65
66 it('should create', () => {
67 expect(component).toBeTruthy();
68 });
69
70 describe('after ngOnInit', () => {
71 beforeEach(() => {
72 spyOn(iscsiService, 'listTargets').and.callThrough();
73 fixture.detectChanges();
74 });
75
76 it('should load targets on init', () => {
77 refresh({});
78 expect(iscsiService.status).toHaveBeenCalled();
79 expect(iscsiService.listTargets).toHaveBeenCalled();
80 });
81
82 it('should not load targets on init because no data', () => {
83 refresh(undefined);
84 expect(iscsiService.listTargets).not.toHaveBeenCalled();
85 });
86
87 it('should call error function on init when summary service fails', () => {
88 spyOn(component.table, 'reset');
89 summaryService['summaryDataSource'].error(undefined);
90 expect(component.table.reset).toHaveBeenCalled();
91 });
92 });
93
94 describe('handling of executing tasks', () => {
95 let targets: any[];
96
97 const addTarget = (name: string) => {
98 const model: any = {
99 target_iqn: name,
100 portals: [{ host: 'node1', ip: '192.168.100.201' }],
101 disks: [{ pool: 'rbd', image: 'disk_1', controls: {} }],
102 clients: [
103 {
104 client_iqn: 'iqn.1994-05.com.redhat:rh7-client',
105 luns: [{ pool: 'rbd', image: 'disk_1' }],
106 auth: {
107 user: 'myiscsiusername',
108 password: 'myiscsipassword',
109 mutual_user: null,
110 mutual_password: null
111 }
112 }
113 ],
114 groups: [],
115 target_controls: {}
116 };
117 targets.push(model);
118 };
119
120 const addTask = (name: string, target_iqn: string) => {
121 const task = new ExecutingTask();
122 task.name = name;
123 switch (task.name) {
124 case 'iscsi/target/create':
125 task.metadata = {
126 target_iqn: target_iqn
127 };
128 break;
129 case 'iscsi/target/delete':
130 task.metadata = {
131 target_iqn: target_iqn
132 };
133 break;
134 default:
135 task.metadata = {
136 target_iqn: target_iqn
137 };
138 break;
139 }
140 summaryService.addRunningTask(task);
141 };
142
143 beforeEach(() => {
144 targets = [];
145 addTarget('iqn.a');
146 addTarget('iqn.b');
147 addTarget('iqn.c');
148
149 component.targets = targets;
150 refresh({ executing_tasks: [], finished_tasks: [] });
151 spyOn(iscsiService, 'listTargets').and.callFake(() => of(targets));
152 fixture.detectChanges();
153 });
154
155 it('should gets all targets without tasks', () => {
156 expect(component.targets.length).toBe(3);
157 expect(component.targets.every((target) => !target.cdExecuting)).toBeTruthy();
158 });
159
160 it('should add a new target from a task', () => {
161 addTask('iscsi/target/create', 'iqn.d');
162 expect(component.targets.length).toBe(4);
163 expectItemTasks(component.targets[0], undefined);
164 expectItemTasks(component.targets[1], undefined);
165 expectItemTasks(component.targets[2], undefined);
166 expectItemTasks(component.targets[3], 'Creating');
167 });
168
169 it('should show when an existing target is being modified', () => {
170 addTask('iscsi/target/delete', 'iqn.b');
171 expect(component.targets.length).toBe(3);
172 expectItemTasks(component.targets[1], 'Deleting');
173 });
174 });
175
176 describe('handling of actions', () => {
177 beforeEach(() => {
178 fixture.detectChanges();
179 });
180
181 let action: CdTableAction;
182
183 const getAction = (name: string): CdTableAction => {
184 return component.tableActions.find((tableAction) => tableAction.name === name);
185 };
186
187 describe('edit', () => {
188 beforeEach(() => {
189 action = getAction('Edit');
190 });
191
192 it('should be disabled if no gateways', () => {
193 component.selection.selected = [
194 {
195 id: '-1'
196 }
197 ];
198 expect(action.disable(undefined)).toBeTruthy();
199 expect(action.disableDesc(undefined)).toBe('Unavailable gateway(s)');
200 });
201
202 it('should be enabled if active sessions', () => {
203 component.selection.selected = [
204 {
205 id: '-1',
206 info: {
207 num_sessions: 1
208 }
209 }
210 ];
211 expect(action.disable(undefined)).toBeFalsy();
212 expect(action.disableDesc(undefined)).toBeUndefined();
213 });
214
215 it('should be enabled if no active sessions', () => {
216 component.selection.selected = [
217 {
218 id: '-1',
219 info: {
220 num_sessions: 0
221 }
222 }
223 ];
224 expect(action.disable(undefined)).toBeFalsy();
225 expect(action.disableDesc(undefined)).toBeUndefined();
226 });
227 });
228
229 describe('delete', () => {
230 beforeEach(() => {
231 action = getAction('Delete');
232 });
233
234 it('should be disabled if no gateways', () => {
235 component.selection.selected = [
236 {
237 id: '-1'
238 }
239 ];
240 expect(action.disable(undefined)).toBeTruthy();
241 expect(action.disableDesc(undefined)).toBe('Unavailable gateway(s)');
242 });
243
244 it('should be disabled if active sessions', () => {
245 component.selection.selected = [
246 {
247 id: '-1',
248 info: {
249 num_sessions: 1
250 }
251 }
252 ];
253 expect(action.disable(undefined)).toBeTruthy();
254 expect(action.disableDesc(undefined)).toBe('Target has active sessions');
255 });
256
257 it('should be enabled if no active sessions', () => {
258 component.selection.selected = [
259 {
260 id: '-1',
261 info: {
262 num_sessions: 0
263 }
264 }
265 ];
266 expect(action.disable(undefined)).toBeFalsy();
267 expect(action.disableDesc(undefined)).toBeUndefined();
268 });
269 });
270 });
271
272 it('should test all TableActions combinations', () => {
273 const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
274 const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
275 component.tableActions
276 );
277
278 expect(tableActions).toEqual({
279 'create,update,delete': {
280 actions: ['Create', 'Edit', 'Delete'],
281 primary: { multiple: 'Create', executing: 'Edit', single: 'Edit', no: 'Create' }
282 },
283 'create,update': {
284 actions: ['Create', 'Edit'],
285 primary: { multiple: 'Create', executing: 'Edit', single: 'Edit', no: 'Create' }
286 },
287 'create,delete': {
288 actions: ['Create', 'Delete'],
289 primary: { multiple: 'Create', executing: 'Delete', single: 'Delete', no: 'Create' }
290 },
291 create: {
292 actions: ['Create'],
293 primary: { multiple: 'Create', executing: 'Create', single: 'Create', no: 'Create' }
294 },
295 'update,delete': {
296 actions: ['Edit', 'Delete'],
297 primary: { multiple: 'Edit', executing: 'Edit', single: 'Edit', no: 'Edit' }
298 },
299 update: {
300 actions: ['Edit'],
301 primary: { multiple: 'Edit', executing: 'Edit', single: 'Edit', no: 'Edit' }
302 },
303 delete: {
304 actions: ['Delete'],
305 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' }
306 },
307 'no-permissions': {
308 actions: [],
309 primary: { multiple: '', executing: '', single: '', no: '' }
310 }
311 });
312 });
313 });