1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { ReactiveFormsModule } from '@angular/forms';
4 import { By } from '@angular/platform-browser';
5 import { RouterTestingModule } from '@angular/router/testing';
7 import { NgbActiveModal, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
8 import _ from 'lodash';
9 import { ToastrModule } from 'ngx-toastr';
10 import { of } from 'rxjs';
12 import { CephServiceService } from '~/app/shared/api/ceph-service.service';
13 import { PaginateObservable } from '~/app/shared/api/paginate.model';
14 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
15 import { SharedModule } from '~/app/shared/shared.module';
16 import { configureTestBed, FormHelper, Mocks } from '~/testing/unit-test-helper';
17 import { ServiceFormComponent } from './service-form.component';
18 import { PoolService } from '~/app/shared/api/pool.service';
20 // for 'nvmeof' service
22 Mocks.getPool('pool-1', 1, ['cephfs']),
23 Mocks.getPool('rbd', 2),
24 Mocks.getPool('pool-2', 3)
26 class MockPoolService {
32 describe('ServiceFormComponent', () => {
33 let component: ServiceFormComponent;
34 let fixture: ComponentFixture<ServiceFormComponent>;
35 let cephServiceService: CephServiceService;
36 let form: CdFormGroup;
37 let formHelper: FormHelper;
40 declarations: [ServiceFormComponent],
41 providers: [NgbActiveModal, { provide: PoolService, useClass: MockPoolService }],
43 HttpClientTestingModule,
48 ToastrModule.forRoot()
53 fixture = TestBed.createComponent(ServiceFormComponent);
54 component = fixture.componentInstance;
56 form = component.serviceForm;
57 formHelper = new FormHelper(form);
58 fixture.detectChanges();
61 it('should create', () => {
62 expect(component).toBeTruthy();
65 describe('should test form', () => {
67 cephServiceService = TestBed.inject(CephServiceService);
68 spyOn(cephServiceService, 'create').and.stub();
71 it('should test placement (host)', () => {
72 formHelper.setValue('service_type', 'crash');
73 formHelper.setValue('placement', 'hosts');
74 formHelper.setValue('hosts', ['mgr0', 'mon0', 'osd0']);
75 formHelper.setValue('count', 2);
77 expect(cephServiceService.create).toHaveBeenCalledWith({
78 service_type: 'crash',
80 hosts: ['mgr0', 'mon0', 'osd0'],
87 it('should test placement (label)', () => {
88 formHelper.setValue('service_type', 'mgr');
89 formHelper.setValue('placement', 'label');
90 formHelper.setValue('label', 'foo');
92 expect(cephServiceService.create).toHaveBeenCalledWith({
101 it('should submit valid count', () => {
102 formHelper.setValue('count', 1);
103 component.onSubmit();
104 formHelper.expectValid('count');
107 it('should submit invalid count (1)', () => {
108 formHelper.setValue('count', 0);
109 component.onSubmit();
110 formHelper.expectError('count', 'min');
113 it('should submit invalid count (2)', () => {
114 formHelper.setValue('count', 'abc');
115 component.onSubmit();
116 formHelper.expectError('count', 'pattern');
119 it('should test unmanaged', () => {
120 formHelper.setValue('service_type', 'mgr');
121 formHelper.setValue('service_id', 'svc');
122 formHelper.setValue('placement', 'label');
123 formHelper.setValue('label', 'bar');
124 formHelper.setValue('unmanaged', true);
125 component.onSubmit();
126 expect(cephServiceService.create).toHaveBeenCalledWith({
134 it('should test various services', () => {
136 ['alertmanager', 'crash', 'mds', 'mgr', 'mon', 'node-exporter', 'prometheus', 'rbd-mirror'],
138 formHelper.setValue('service_type', serviceType);
139 component.onSubmit();
140 expect(cephServiceService.create).toHaveBeenCalledWith({
141 service_type: serviceType,
149 describe('should test service grafana', () => {
151 formHelper.setValue('service_type', 'grafana');
154 it('should sumbit grafana', () => {
155 component.onSubmit();
156 expect(cephServiceService.create).toHaveBeenCalledWith({
157 service_type: 'grafana',
160 initial_admin_password: null,
165 it('should sumbit grafana with custom port and initial password', () => {
166 formHelper.setValue('grafana_port', 1234);
167 formHelper.setValue('grafana_admin_password', 'foo');
168 component.onSubmit();
169 expect(cephServiceService.create).toHaveBeenCalledWith({
170 service_type: 'grafana',
173 initial_admin_password: 'foo',
179 describe('should test service nfs', () => {
181 formHelper.setValue('service_type', 'nfs');
184 it('should submit nfs', () => {
185 component.onSubmit();
186 expect(cephServiceService.create).toHaveBeenCalledWith({
194 describe('should test service rgw', () => {
196 formHelper.setValue('service_type', 'rgw');
197 formHelper.setValue('service_id', 'svc');
200 it('should test rgw valid service id', () => {
201 formHelper.setValue('service_id', 'svc.realm.zone');
202 formHelper.expectValid('service_id');
203 formHelper.setValue('service_id', 'svc');
204 formHelper.expectValid('service_id');
207 it('should submit rgw with realm, zonegroup and zone', () => {
208 formHelper.setValue('service_id', 'svc');
209 formHelper.setValue('realm_name', 'my-realm');
210 formHelper.setValue('zone_name', 'my-zone');
211 formHelper.setValue('zonegroup_name', 'my-zonegroup');
212 component.onSubmit();
213 expect(cephServiceService.create).toHaveBeenCalledWith({
216 rgw_realm: 'my-realm',
218 rgw_zonegroup: 'my-zonegroup',
225 it('should submit rgw with port and ssl enabled', () => {
226 formHelper.setValue('rgw_frontend_port', 1234);
227 formHelper.setValue('ssl', true);
228 component.onSubmit();
229 expect(cephServiceService.create).toHaveBeenCalledWith({
237 rgw_frontend_port: 1234,
238 rgw_frontend_ssl_certificate: '',
243 it('should submit valid rgw port (1)', () => {
244 formHelper.setValue('rgw_frontend_port', 1);
245 component.onSubmit();
246 formHelper.expectValid('rgw_frontend_port');
249 it('should submit valid rgw port (2)', () => {
250 formHelper.setValue('rgw_frontend_port', 65535);
251 component.onSubmit();
252 formHelper.expectValid('rgw_frontend_port');
255 it('should submit invalid rgw port (1)', () => {
256 formHelper.setValue('rgw_frontend_port', 0);
257 fixture.detectChanges();
258 formHelper.expectError('rgw_frontend_port', 'min');
261 it('should submit invalid rgw port (2)', () => {
262 formHelper.setValue('rgw_frontend_port', 65536);
263 fixture.detectChanges();
264 formHelper.expectError('rgw_frontend_port', 'max');
267 it('should submit invalid rgw port (3)', () => {
268 formHelper.setValue('rgw_frontend_port', 'abc');
269 component.onSubmit();
270 formHelper.expectError('rgw_frontend_port', 'pattern');
273 it('should submit rgw w/o port', () => {
274 formHelper.setValue('ssl', false);
275 component.onSubmit();
276 expect(cephServiceService.create).toHaveBeenCalledWith({
288 it('should not show private key field', () => {
289 formHelper.setValue('ssl', true);
290 fixture.detectChanges();
291 const ssl_key = fixture.debugElement.query(By.css('#ssl_key'));
292 expect(ssl_key).toBeNull();
295 it('should test .pem file', () => {
297 -----BEGIN CERTIFICATE-----
298 iJ5IbgzlKPssdYwuAEI3yPZxX/g5vKBrgcyD3LttLL/DlElq/1xCnwVrv7WROSNu
299 -----END CERTIFICATE-----
300 -----BEGIN CERTIFICATE-----
301 mn/S7BNBEC7AGe5ajmN+8hBTGdACUXe8rwMNrtTy/MwBZ0VpJsAAjJh+aptZh5yB
302 -----END CERTIFICATE-----
303 -----BEGIN RSA PRIVATE KEY-----
304 x4Ea7kGVgx9kWh5XjWz9wjZvY49UKIT5ppIAWPMbLl3UpfckiuNhTA==
305 -----END RSA PRIVATE KEY-----`;
306 formHelper.setValue('ssl', true);
307 formHelper.setValue('ssl_cert', pemCert);
308 fixture.detectChanges();
309 formHelper.expectValid('ssl_cert');
313 describe('should test service iscsi', () => {
315 formHelper.setValue('service_type', 'iscsi');
316 formHelper.setValue('pool', 'xyz');
317 formHelper.setValue('api_user', 'user');
318 formHelper.setValue('api_password', 'password');
319 formHelper.setValue('ssl', false);
322 it('should submit iscsi', () => {
323 component.onSubmit();
324 expect(cephServiceService.create).toHaveBeenCalledWith({
325 service_type: 'iscsi',
330 api_password: 'password',
335 it('should submit iscsi with trusted ips', () => {
336 formHelper.setValue('ssl', true);
337 formHelper.setValue('trusted_ip_list', ' 172.16.0.5, 192.1.1.10 ');
338 component.onSubmit();
339 expect(cephServiceService.create).toHaveBeenCalledWith({
340 service_type: 'iscsi',
345 api_password: 'password',
349 trusted_ip_list: '172.16.0.5, 192.1.1.10'
353 it('should submit iscsi with port', () => {
354 formHelper.setValue('api_port', 456);
355 component.onSubmit();
356 expect(cephServiceService.create).toHaveBeenCalledWith({
357 service_type: 'iscsi',
362 api_password: 'password',
368 it('should submit valid iscsi port (1)', () => {
369 formHelper.setValue('api_port', 1);
370 component.onSubmit();
371 formHelper.expectValid('api_port');
374 it('should submit valid iscsi port (2)', () => {
375 formHelper.setValue('api_port', 65535);
376 component.onSubmit();
377 formHelper.expectValid('api_port');
380 it('should submit invalid iscsi port (1)', () => {
381 formHelper.setValue('api_port', 0);
382 fixture.detectChanges();
383 formHelper.expectError('api_port', 'min');
386 it('should submit invalid iscsi port (2)', () => {
387 formHelper.setValue('api_port', 65536);
388 fixture.detectChanges();
389 formHelper.expectError('api_port', 'max');
392 it('should submit invalid iscsi port (3)', () => {
393 formHelper.setValue('api_port', 'abc');
394 component.onSubmit();
395 formHelper.expectError('api_port', 'pattern');
398 it('should throw error when there is no pool', () => {
399 formHelper.expectErrorChange('pool', '', 'required');
403 describe('should test service nvmeof', () => {
405 component.serviceType = 'nvmeof';
406 formHelper.setValue('service_type', 'nvmeof');
407 component.ngOnInit();
408 fixture.detectChanges();
411 it('should set rbd pools correctly onInit', () => {
412 expect(component.pools.length).toBe(3);
413 expect(component.rbdPools.length).toBe(2);
416 it('should set default values correctly onInit', () => {
417 expect(form.get('service_type').value).toBe('nvmeof');
418 expect(form.get('group').value).toBe('default');
419 expect(form.get('pool').value).toBe('rbd');
420 expect(form.get('service_id').value).toBe('rbd.default');
423 it('should reflect correct values on group change', () => {
424 // Initially the group value should be 'default'
425 expect(component.serviceForm.get('group')?.value).toBe('default');
426 const groupInput = fixture.debugElement.query(By.css('#group')).nativeElement;
427 // Simulate input value change
428 groupInput.value = 'foo';
429 // Trigger the input event
430 groupInput.dispatchEvent(new Event('input'));
431 // Trigger the change event
432 groupInput.dispatchEvent(new Event('change'));
433 fixture.detectChanges();
434 // Verify values after change
435 expect(form.get('group').value).toBe('foo');
436 expect(form.get('service_id').value).toBe('rbd.foo');
439 it('should reflect correct values on pool change', () => {
440 // Initially the pool value should be 'rbd'
441 expect(component.serviceForm.get('pool')?.value).toBe('rbd');
442 const poolInput = fixture.debugElement.query(By.css('#pool')).nativeElement;
443 // Simulate input value change
444 poolInput.value = 'pool-2';
445 // Trigger the input event
446 poolInput.dispatchEvent(new Event('input'));
447 // Trigger the change event
448 poolInput.dispatchEvent(new Event('change'));
449 fixture.detectChanges();
450 // Verify values after change
451 expect(form.get('pool').value).toBe('pool-2');
452 expect(form.get('service_id').value).toBe('pool-2.default');
455 it('should throw error when there is no service id', () => {
456 formHelper.expectErrorChange('service_id', '', 'required');
459 it('should throw error when there is no pool', () => {
460 formHelper.expectErrorChange('pool', '', 'required');
463 it('should throw error when there is no group', () => {
464 formHelper.expectErrorChange('group', '', 'required');
467 it('should hide the count element when service_type is "nvmeof"', () => {
468 const countEl = fixture.debugElement.query(By.css('#count'));
469 expect(countEl).toBeNull();
472 it('should submit nvmeof', () => {
473 component.onSubmit();
474 expect(cephServiceService.create).toHaveBeenCalledWith({
475 service_type: 'nvmeof',
476 service_id: 'rbd.default',
485 describe('should test service smb', () => {
487 formHelper.setValue('service_type', 'smb');
488 formHelper.setValue('service_id', 'foo');
489 formHelper.setValue('cluster_id', 'cluster_foo');
490 formHelper.setValue('config_uri', 'rados://.smb/foo/scc.toml');
493 it('should submit smb', () => {
494 component.onSubmit();
495 expect(cephServiceService.create).toHaveBeenCalledWith({
500 cluster_id: 'cluster_foo',
501 config_uri: 'rados://.smb/foo/scc.toml'
506 describe('should test service ingress', () => {
508 formHelper.setValue('service_type', 'ingress');
509 formHelper.setValue('backend_service', 'rgw.foo');
510 formHelper.setValue('virtual_ip', '192.168.20.1/24');
511 formHelper.setValue('ssl', false);
514 it('should submit ingress', () => {
515 component.onSubmit();
516 expect(cephServiceService.create).toHaveBeenCalledWith({
517 service_type: 'ingress',
520 backend_service: 'rgw.foo',
521 service_id: 'rgw.foo',
522 virtual_ip: '192.168.20.1/24',
523 virtual_interface_networks: null,
528 it('should pre-populate the service id', () => {
529 component.prePopulateId();
530 const prePopulatedID = component.serviceForm.getValue('service_id');
531 expect(prePopulatedID).toBe('rgw.foo');
534 it('should submit valid frontend and monitor port', () => {
536 formHelper.setValue('frontend_port', 1);
537 formHelper.setValue('monitor_port', 1);
538 fixture.detectChanges();
539 formHelper.expectValid('frontend_port');
540 formHelper.expectValid('monitor_port');
543 formHelper.setValue('frontend_port', 65535);
544 formHelper.setValue('monitor_port', 65535);
545 fixture.detectChanges();
546 formHelper.expectValid('frontend_port');
547 formHelper.expectValid('monitor_port');
550 it('should submit invalid frontend and monitor port', () => {
552 formHelper.setValue('frontend_port', 0);
553 formHelper.setValue('monitor_port', 0);
554 fixture.detectChanges();
555 formHelper.expectError('frontend_port', 'min');
556 formHelper.expectError('monitor_port', 'min');
559 formHelper.setValue('frontend_port', 65536);
560 formHelper.setValue('monitor_port', 65536);
561 fixture.detectChanges();
562 formHelper.expectError('frontend_port', 'max');
563 formHelper.expectError('monitor_port', 'max');
566 formHelper.setValue('frontend_port', 'abc');
567 formHelper.setValue('monitor_port', 'abc');
568 component.onSubmit();
569 formHelper.expectError('frontend_port', 'pattern');
570 formHelper.expectError('monitor_port', 'pattern');
573 it('should not show private key field with ssl enabled', () => {
574 formHelper.setValue('ssl', true);
575 fixture.detectChanges();
576 const ssl_key = fixture.debugElement.query(By.css('#ssl_key'));
577 expect(ssl_key).toBeNull();
580 it('should test .pem file with ssl enabled', () => {
582 -----BEGIN CERTIFICATE-----
583 iJ5IbgzlKPssdYwuAEI3yPZxX/g5vKBrgcyD3LttLL/DlElq/1xCnwVrv7WROSNu
584 -----END CERTIFICATE-----
585 -----BEGIN CERTIFICATE-----
586 mn/S7BNBEC7AGe5ajmN+8hBTGdACUXe8rwMNrtTy/MwBZ0VpJsAAjJh+aptZh5yB
587 -----END CERTIFICATE-----
588 -----BEGIN RSA PRIVATE KEY-----
589 x4Ea7kGVgx9kWh5XjWz9wjZvY49UKIT5ppIAWPMbLl3UpfckiuNhTA==
590 -----END RSA PRIVATE KEY-----`;
591 formHelper.setValue('ssl', true);
592 formHelper.setValue('ssl_cert', pemCert);
593 fixture.detectChanges();
594 formHelper.expectValid('ssl_cert');
598 describe('should test service snmp-gateway', () => {
600 formHelper.setValue('service_type', 'snmp-gateway');
601 formHelper.setValue('snmp_destination', '192.168.20.1:8443');
604 it('should test snmp-gateway service with V2c', () => {
605 formHelper.setValue('snmp_version', 'V2c');
606 formHelper.setValue('snmp_community', 'public');
607 component.onSubmit();
608 expect(cephServiceService.create).toHaveBeenCalledWith({
609 service_type: 'snmp-gateway',
613 snmp_destination: '192.168.20.1:8443',
615 snmp_community: 'public'
620 it('should test snmp-gateway service with V3', () => {
621 formHelper.setValue('snmp_version', 'V3');
622 formHelper.setValue('engine_id', '800C53F00000');
623 formHelper.setValue('auth_protocol', 'SHA');
624 formHelper.setValue('privacy_protocol', 'DES');
625 formHelper.setValue('snmp_v3_auth_username', 'testuser');
626 formHelper.setValue('snmp_v3_auth_password', 'testpass');
627 formHelper.setValue('snmp_v3_priv_password', 'testencrypt');
628 component.onSubmit();
629 expect(cephServiceService.create).toHaveBeenCalledWith({
630 service_type: 'snmp-gateway',
634 snmp_destination: '192.168.20.1:8443',
635 engine_id: '800C53F00000',
636 auth_protocol: 'SHA',
637 privacy_protocol: 'DES',
639 snmp_v3_auth_username: 'testuser',
640 snmp_v3_auth_password: 'testpass',
641 snmp_v3_priv_password: 'testencrypt'
646 it('should submit invalid snmp destination', () => {
647 formHelper.setValue('snmp_version', 'V2c');
648 formHelper.setValue('snmp_destination', '192.168.20.1');
649 formHelper.setValue('snmp_community', 'public');
650 formHelper.expectError('snmp_destination', 'snmpDestinationPattern');
653 it('should submit invalid snmp engine id', () => {
654 formHelper.setValue('snmp_version', 'V3');
655 formHelper.setValue('snmp_destination', '192.168.20.1');
656 formHelper.setValue('engine_id', 'AABBCCDDE');
657 formHelper.setValue('auth_protocol', 'SHA');
658 formHelper.setValue('privacy_protocol', 'DES');
659 formHelper.setValue('snmp_v3_auth_username', 'testuser');
660 formHelper.setValue('snmp_v3_auth_password', 'testpass');
662 formHelper.expectError('engine_id', 'snmpEngineIdPattern');
666 describe('should test service mds', () => {
668 formHelper.setValue('service_type', 'mds');
669 const paginate_obs = new PaginateObservable<any>(of({}));
670 spyOn(cephServiceService, 'list').and.returnValue(paginate_obs);
673 it('should test mds valid service id', () => {
674 formHelper.setValue('service_id', 'svc123');
675 formHelper.expectValid('service_id');
676 formHelper.setValue('service_id', 'svc_id-1');
677 formHelper.expectValid('service_id');
680 it('should test mds invalid service id', () => {
681 formHelper.setValue('service_id', '123');
682 formHelper.expectError('service_id', 'mdsPattern');
683 formHelper.setValue('service_id', '123svc');
684 formHelper.expectError('service_id', 'mdsPattern');
685 formHelper.setValue('service_id', 'svc#1');
686 formHelper.expectError('service_id', 'mdsPattern');
690 describe('check edit fields', () => {
692 component.editing = true;
695 it('should check whether edit field is correctly loaded', () => {
696 const paginate_obs = new PaginateObservable<any>(of({}));
697 const cephServiceSpy = spyOn(cephServiceService, 'list').and.returnValue(paginate_obs);
698 component.ngOnInit();
699 expect(cephServiceSpy).toBeCalledTimes(2);
700 expect(component.action).toBe('Edit');
701 const serviceType = fixture.debugElement.query(By.css('#service_type')).nativeElement;
702 const serviceId = fixture.debugElement.query(By.css('#service_id')).nativeElement;
703 expect(serviceType.disabled).toBeTruthy();
704 expect(serviceId.disabled).toBeTruthy();
707 it('should not edit pools for nvmeof service', () => {
708 component.serviceType = 'nvmeof';
709 formHelper.setValue('service_type', 'nvmeof');
710 component.ngOnInit();
711 fixture.detectChanges();
712 const poolId = fixture.debugElement.query(By.css('#pool')).nativeElement;
713 expect(poolId.disabled).toBeTruthy();
716 it('should not edit groups for nvmeof service', () => {
717 component.serviceType = 'nvmeof';
718 formHelper.setValue('service_type', 'nvmeof');
719 component.ngOnInit();
720 fixture.detectChanges();
721 const groupId = fixture.debugElement.query(By.css('#group')).nativeElement;
722 expect(groupId.disabled).toBeTruthy();