1 import { HttpClientTestingModule, HttpTestingController } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { ReactiveFormsModule } from '@angular/forms';
4 import { ActivatedRoute } from '@angular/router';
5 import { RouterTestingModule } from '@angular/router/testing';
7 import { ToastrModule } from 'ngx-toastr';
9 import { LoadingPanelComponent } from '~/app/shared/components/loading-panel/loading-panel.component';
10 import { SelectOption } from '~/app/shared/components/select/select-option.model';
11 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
12 import { SharedModule } from '~/app/shared/shared.module';
13 import { ActivatedRouteStub } from '~/testing/activated-route-stub';
14 import { configureTestBed, FormHelper, IscsiHelper } from '~/testing/unit-test-helper';
15 import { IscsiTargetFormComponent } from './iscsi-target-form.component';
17 describe('IscsiTargetFormComponent', () => {
18 let component: IscsiTargetFormComponent;
19 let fixture: ComponentFixture<IscsiTargetFormComponent>;
20 let httpTesting: HttpTestingController;
21 let activatedRoute: ActivatedRouteStub;
24 config: { minimum_gateways: 2 },
25 disk_default_controls: {
34 target_default_controls: {
39 required_rbd_features: {
43 unsupported_rbd_features: {
47 backstores: ['backstore:1', 'backstore:2'],
48 default_backstore: 'backstore:1',
52 const LIST_TARGET: any[] = [
54 target_iqn: 'iqn.2003-01.com.redhat.iscsi-gw:iscsi-igw',
55 portals: [{ host: 'node1', ip: '192.168.100.201' }],
61 backstore: 'backstore:1',
62 wwn: '64af6678-9694-4367-bacc-f8eb0baa'
67 client_iqn: 'iqn.1994-05.com.redhat:rh7-client',
68 luns: [{ pool: 'rbd', image: 'disk_1', lun: 0 }],
70 user: 'myiscsiusername',
71 password: 'myiscsipassword',
83 { name: 'node1', ip_addresses: ['192.168.100.201', '10.0.2.15'] },
84 { name: 'node2', ip_addresses: ['192.168.100.202'] }
88 ceph_iscsi_config_version: 11
91 const RBD_LIST: any[] = [
92 { value: [], pool_name: 'ganesha' },
100 block_name_prefix: 'rbd_data.148162fb31a8',
105 features_name: ['deep-flatten', 'exclusive-lock', 'fast-diff', 'layering', 'object-map'],
106 timestamp: '2019-01-18T10:44:26Z',
108 stripe_unit: 4194304,
120 block_name_prefix: 'rbd_data.14b292cee6cb',
125 features_name: ['deep-flatten', 'exclusive-lock', 'fast-diff', 'layering', 'object-map'],
126 timestamp: '2019-01-18T10:45:56Z',
128 stripe_unit: 4194304,
142 declarations: [IscsiTargetFormComponent],
146 HttpClientTestingModule,
148 ToastrModule.forRoot()
152 provide: ActivatedRoute,
153 useValue: new ActivatedRouteStub({ target_iqn: undefined })
157 [LoadingPanelComponent]
161 fixture = TestBed.createComponent(IscsiTargetFormComponent);
162 component = fixture.componentInstance;
163 httpTesting = TestBed.inject(HttpTestingController);
164 activatedRoute = <ActivatedRouteStub>TestBed.inject(ActivatedRoute);
165 fixture.detectChanges();
167 httpTesting.expectOne('ui-api/iscsi/settings').flush(SETTINGS);
168 httpTesting.expectOne('ui-api/iscsi/portals').flush(PORTALS);
169 httpTesting.expectOne('ui-api/iscsi/version').flush(VERSION);
170 httpTesting.expectOne('api/block/image?offset=0&limit=-1&search=&sort=+name').flush(RBD_LIST);
171 httpTesting.expectOne('api/iscsi/target').flush(LIST_TARGET);
172 httpTesting.verify();
175 it('should create', () => {
176 expect(component).toBeTruthy();
179 it('should only show images not used in other targets', () => {
180 expect(component.imagesAll).toEqual([RBD_LIST[1]['value'][1]]);
181 expect(component.imagesSelections).toEqual([
182 { description: '', name: 'rbd/disk_2', selected: false, enabled: true }
186 it('should generate portals selectOptions', () => {
187 expect(component.portalsSelections).toEqual([
188 { description: '', name: 'node1:192.168.100.201', selected: false, enabled: true },
189 { description: '', name: 'node1:10.0.2.15', selected: false, enabled: true },
190 { description: '', name: 'node2:192.168.100.202', selected: false, enabled: true }
194 it('should create the form', () => {
195 expect(component.targetForm.value).toEqual({
208 target_iqn: component.targetForm.value.target_iqn
212 it('should prepare data when selecting an image', () => {
213 expect(component.imagesSettings).toEqual({});
214 component.onImageSelection({ option: { name: 'rbd/disk_2', selected: true } });
215 expect(component.imagesSettings).toEqual({
218 backstore: 'backstore:1',
224 it('should clean data when removing an image', () => {
225 component.onImageSelection({ option: { name: 'rbd/disk_2', selected: true } });
226 component.addGroup();
227 component.groups.controls[0].patchValue({
229 disks: ['rbd/disk_2']
232 expect(component.groups.controls[0].value).toEqual({
233 disks: ['rbd/disk_2'],
238 component.onImageSelection({ option: { name: 'rbd/disk_2', selected: false } });
240 expect(component.groups.controls[0].value).toEqual({ disks: [], group_id: 'foo', members: [] });
241 expect(component.imagesSettings).toEqual({
244 backstore: 'backstore:1',
250 it('should validate authentication', () => {
251 const control = component.targetForm;
252 const formHelper = new FormHelper(control as CdFormGroup);
253 formHelper.expectValid('auth');
254 validateAuth(formHelper);
257 describe('should test initiators', () => {
259 component.onImageSelection({ option: { name: 'rbd/disk_2', selected: true } });
260 component.targetForm.patchValue({ disks: ['rbd/disk_2'], acl_enabled: true });
261 component.addGroup().patchValue({ name: 'group_1' });
262 component.addGroup().patchValue({ name: 'group_2' });
264 component.addInitiator();
265 component.initiators.controls[0].patchValue({
266 client_iqn: 'iqn.initiator'
268 component.updatedInitiatorSelector();
271 it('should prepare data when creating an initiator', () => {
272 expect(component.initiators.controls.length).toBe(1);
273 expect(component.initiators.controls[0].value).toEqual({
274 auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
276 client_iqn: 'iqn.initiator',
279 expect(component.imagesInitiatorSelections).toEqual([
280 [{ description: '', name: 'rbd/disk_2', selected: false, enabled: true }]
282 expect(component.groupMembersSelections).toEqual([
283 [{ description: '', name: 'iqn.initiator', selected: false, enabled: true }],
284 [{ description: '', name: 'iqn.initiator', selected: false, enabled: true }]
288 it('should update data when changing an initiator name', () => {
289 expect(component.groupMembersSelections).toEqual([
290 [{ description: '', name: 'iqn.initiator', selected: false, enabled: true }],
291 [{ description: '', name: 'iqn.initiator', selected: false, enabled: true }]
294 component.initiators.controls[0].patchValue({
295 client_iqn: 'iqn.initiator_new'
297 component.updatedInitiatorSelector();
299 expect(component.groupMembersSelections).toEqual([
300 [{ description: '', name: 'iqn.initiator_new', selected: false, enabled: true }],
301 [{ description: '', name: 'iqn.initiator_new', selected: false, enabled: true }]
305 it('should clean data when removing an initiator', () => {
306 component.groups.controls[0].patchValue({
308 members: ['iqn.initiator']
311 expect(component.groups.controls[0].value).toEqual({
314 members: ['iqn.initiator']
317 component.removeInitiator(0);
319 expect(component.groups.controls[0].value).toEqual({
324 expect(component.groupMembersSelections).toEqual([[], []]);
325 expect(component.imagesInitiatorSelections).toEqual([]);
328 it('should remove images in the initiator when added in a group', () => {
329 component.initiators.controls[0].patchValue({
332 component.imagesInitiatorSelections[0] = [
340 expect(component.initiators.controls[0].value).toEqual({
341 auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
343 client_iqn: 'iqn.initiator',
347 component.groups.controls[0].patchValue({
349 members: ['iqn.initiator']
351 component.onGroupMemberSelection(
354 name: 'iqn.initiator',
361 expect(component.initiators.controls[0].value).toEqual({
362 auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
364 client_iqn: 'iqn.initiator',
367 expect(component.imagesInitiatorSelections[0]).toEqual([
377 it('should disabled the initiator when selected', () => {
378 expect(component.groupMembersSelections).toEqual([
379 [{ description: '', enabled: true, name: 'iqn.initiator', selected: false }],
380 [{ description: '', enabled: true, name: 'iqn.initiator', selected: false }]
383 component.groupMembersSelections[0][0].selected = true;
384 component.onGroupMemberSelection({ option: { name: 'iqn.initiator', selected: true } }, 0);
386 expect(component.groupMembersSelections).toEqual([
387 [{ description: '', enabled: false, name: 'iqn.initiator', selected: true }],
388 [{ description: '', enabled: false, name: 'iqn.initiator', selected: false }]
392 describe('should remove from group', () => {
394 component.onGroupMemberSelection(
395 { option: new SelectOption(true, 'iqn.initiator', '') },
398 component.groupDiskSelections[0][0].selected = true;
399 component.groups.controls[0].patchValue({
400 disks: ['rbd/disk_2'],
401 members: ['iqn.initiator']
404 expect(component.initiators.value[0].luns).toEqual([]);
405 expect(component.imagesInitiatorSelections[0]).toEqual([
406 { description: '', enabled: true, name: 'rbd/disk_2', selected: false }
408 expect(component.initiators.value[0].cdIsInGroup).toBe(true);
411 it('should update initiator images when deselecting', () => {
412 component.onGroupMemberSelection(
413 { option: new SelectOption(false, 'iqn.initiator', '') },
417 expect(component.initiators.value[0].luns).toEqual(['rbd/disk_2']);
418 expect(component.imagesInitiatorSelections[0]).toEqual([
419 { description: '', enabled: true, name: 'rbd/disk_2', selected: true }
421 expect(component.initiators.value[0].cdIsInGroup).toBe(false);
424 it('should update initiator when removing', () => {
425 component.removeGroupInitiator(component.groups.controls[0] as CdFormGroup, 0, 0);
427 expect(component.initiators.value[0].luns).toEqual(['rbd/disk_2']);
428 expect(component.imagesInitiatorSelections[0]).toEqual([
429 { description: '', enabled: true, name: 'rbd/disk_2', selected: true }
431 expect(component.initiators.value[0].cdIsInGroup).toBe(false);
435 it('should validate authentication', () => {
436 const control = component.initiators.controls[0];
437 const formHelper = new FormHelper(control as CdFormGroup);
438 formHelper.expectValid(control);
439 validateAuth(formHelper);
443 describe('should submit request', () => {
445 component.onImageSelection({ option: { name: 'rbd/disk_2', selected: true } });
446 component.targetForm.patchValue({ disks: ['rbd/disk_2'], acl_enabled: true });
447 component.portals.setValue(['node1:192.168.100.201', 'node2:192.168.100.202']);
448 component.addInitiator().patchValue({
449 client_iqn: 'iqn.initiator'
451 component.addGroup().patchValue({
453 members: ['iqn.initiator'],
454 disks: ['rbd/disk_2']
458 it('should call update', () => {
459 activatedRoute.setParams({ target_iqn: 'iqn.iscsi' });
460 component.isEdit = true;
461 component.target_iqn = 'iqn.iscsi';
465 const req = httpTesting.expectOne('api/iscsi/target/iqn.iscsi');
466 expect(req.request.method).toBe('PUT');
467 expect(req.request.body).toEqual({
470 auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
471 client_iqn: 'iqn.initiator',
477 backstore: 'backstore:1',
486 { disks: [{ image: 'disk_2', pool: 'rbd' }], group_id: 'foo', members: ['iqn.initiator'] }
488 new_target_iqn: component.targetForm.value.target_iqn,
490 { host: 'node1', ip: '192.168.100.201' },
491 { host: 'node2', ip: '192.168.100.202' }
494 target_iqn: component.target_iqn,
505 it('should call create', () => {
508 const req = httpTesting.expectOne('api/iscsi/target');
509 expect(req.request.method).toBe('POST');
510 expect(req.request.body).toEqual({
513 auth: { mutual_password: '', mutual_user: '', password: '', user: '' },
514 client_iqn: 'iqn.initiator',
520 backstore: 'backstore:1',
530 disks: [{ image: 'disk_2', pool: 'rbd' }],
532 members: ['iqn.initiator']
536 { host: 'node1', ip: '192.168.100.201' },
537 { host: 'node2', ip: '192.168.100.202' }
540 target_iqn: component.targetForm.value.target_iqn,
551 it('should call create with acl_enabled disabled', () => {
552 component.targetForm.patchValue({ acl_enabled: false });
555 const req = httpTesting.expectOne('api/iscsi/target');
556 expect(req.request.method).toBe('POST');
557 expect(req.request.body).toEqual({
561 backstore: 'backstore:1',
578 { host: 'node1', ip: '192.168.100.201' },
579 { host: 'node2', ip: '192.168.100.202' }
582 target_iqn: component.targetForm.value.target_iqn
587 function validateAuth(formHelper: FormHelper) {
588 IscsiHelper.validateUser(formHelper, 'auth.user');
589 IscsiHelper.validatePassword(formHelper, 'auth.password');
590 IscsiHelper.validateUser(formHelper, 'auth.mutual_user');
591 IscsiHelper.validatePassword(formHelper, 'auth.mutual_password');