]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-ci.git/blob
f3fae1748a3297cfcea04747305a8643d33afb57
[ceph-ci.git] /
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ReactiveFormsModule } from '@angular/forms';
3 import { RouterTestingModule } from '@angular/router/testing';
4 import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
5 import { Router } from '@angular/router';
6 import { CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
7
8 import { ToastrModule } from 'ngx-toastr';
9 import { of } from 'rxjs';
10
11 import { NgbActiveModal, NgbTypeaheadModule } from '@ng-bootstrap/ng-bootstrap';
12
13 import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
14 import { SharedModule } from '~/app/shared/shared.module';
15
16 import { NvmeofGroupFormComponent } from './nvmeof-group-form.component';
17 import { GridModule, InputModule, SelectModule } from 'carbon-components-angular';
18 import { PoolService } from '~/app/shared/api/pool.service';
19 import { TaskWrapperService } from '~/app/shared/services/task-wrapper.service';
20 import { CephServiceService } from '~/app/shared/api/ceph-service.service';
21 import { FormHelper } from '~/testing/unit-test-helper';
22
23 describe('NvmeofGroupFormComponent', () => {
24   let component: NvmeofGroupFormComponent;
25   let fixture: ComponentFixture<NvmeofGroupFormComponent>;
26   let form: CdFormGroup;
27   let formHelper: FormHelper;
28   let poolService: PoolService;
29   let taskWrapperService: TaskWrapperService;
30   let cephServiceService: CephServiceService;
31   let router: Router;
32
33   const mockPools = [
34     { pool_name: 'rbd', application_metadata: ['rbd'] },
35     { pool_name: 'rbd', application_metadata: ['rbd'] },
36     { pool_name: 'pool2', application_metadata: ['rgw'] }
37   ];
38
39   beforeEach(async () => {
40     await TestBed.configureTestingModule({
41       declarations: [NvmeofGroupFormComponent],
42       providers: [NgbActiveModal],
43       imports: [
44         HttpClientTestingModule,
45         NgbTypeaheadModule,
46         ReactiveFormsModule,
47         RouterTestingModule,
48         SharedModule,
49         GridModule,
50         InputModule,
51         SelectModule,
52         ToastrModule.forRoot()
53       ],
54       schemas: [CUSTOM_ELEMENTS_SCHEMA]
55     }).compileComponents();
56
57     fixture = TestBed.createComponent(NvmeofGroupFormComponent);
58     component = fixture.componentInstance;
59     poolService = TestBed.inject(PoolService);
60     taskWrapperService = TestBed.inject(TaskWrapperService);
61     cephServiceService = TestBed.inject(CephServiceService);
62     router = TestBed.inject(Router);
63
64     spyOn(poolService, 'list').and.returnValue(Promise.resolve(mockPools));
65
66     component.ngOnInit();
67     form = component.groupForm;
68     formHelper = new FormHelper(form);
69     fixture.detectChanges();
70   });
71
72   it('should create', () => {
73     expect(component).toBeTruthy();
74   });
75
76   it('should initialize form with empty fields', () => {
77     expect(form.controls.groupName.value).toBeNull();
78     expect(form.controls.unmanaged.value).toBe(false);
79   });
80
81   it('should set action to CREATE on init', () => {
82     expect(component.action).toBe('Create');
83   });
84
85   it('should set resource to gateway group', () => {
86     expect(component.resource).toBe('gateway group');
87   });
88
89   describe('form validation', () => {
90     it('should require groupName', () => {
91       formHelper.setValue('groupName', '');
92       formHelper.expectError('groupName', 'required');
93     });
94
95     it('should require pool', () => {
96       formHelper.setValue('pool', null);
97       formHelper.expectError('pool', 'required');
98     });
99
100     it('should be valid when groupName and pool are set', () => {
101       formHelper.setValue('groupName', 'test-group');
102       formHelper.setValue('pool', 'rbd');
103       expect(form.valid).toBe(true);
104     });
105   });
106
107   describe('loadPools', () => {
108     it('should load pools and filter by rbd application metadata', fakeAsync(() => {
109       component.loadPools();
110       tick();
111       expect(component.pools.length).toBe(2);
112       expect(component.pools.map((p) => p.pool_name)).toEqual(['rbd', 'rbd']);
113     }));
114
115     it('should set default pool to rbd if available', fakeAsync(() => {
116       component.groupForm.get('pool').setValue(null);
117       component.loadPools();
118       tick();
119       expect(component.groupForm.get('pool').value).toBe('rbd');
120     }));
121
122     it('should set first pool if rbd is not available', fakeAsync(() => {
123       component.groupForm.get('pool').setValue(null);
124       const poolsWithoutRbd = [{ pool_name: 'custom-pool', application_metadata: ['rbd'] }];
125       (poolService.list as jasmine.Spy).and.returnValue(Promise.resolve(poolsWithoutRbd));
126       component.loadPools();
127       tick();
128       expect(component.groupForm.get('pool').value).toBe('custom-pool');
129     }));
130
131     it('should handle empty pools', fakeAsync(() => {
132       (poolService.list as jasmine.Spy).and.returnValue(Promise.resolve([]));
133       component.loadPools();
134       tick();
135       expect(component.pools.length).toBe(0);
136       expect(component.poolsLoading).toBe(false);
137     }));
138
139     it('should handle pool loading error', fakeAsync(() => {
140       (poolService.list as jasmine.Spy).and.returnValue(Promise.reject('error'));
141       component.loadPools();
142       tick();
143       expect(component.pools).toEqual([]);
144       expect(component.poolsLoading).toBe(false);
145     }));
146   });
147
148   describe('onSubmit', () => {
149     beforeEach(() => {
150       spyOn(cephServiceService, 'create').and.returnValue(of({}));
151       spyOn(taskWrapperService, 'wrapTaskAroundCall').and.callFake(({ call }) => call);
152       spyOn(router, 'navigateByUrl');
153     });
154
155     it('should not call create if no hosts are selected', () => {
156       component.gatewayNodeComponent = {
157         getSelectedHosts: (): any[] => [],
158         getSelectedHostnames: (): string[] => []
159       } as any;
160
161       component.groupForm.get('groupName').setValue('test-group');
162       component.groupForm.get('pool').setValue('rbd');
163       component.onSubmit();
164
165       expect(cephServiceService.create).not.toHaveBeenCalled();
166     });
167
168     it('should create service with correct spec', () => {
169       component.gatewayNodeComponent = {
170         getSelectedHosts: (): any[] => [{ hostname: 'host1' }, { hostname: 'host2' }],
171         getSelectedHostnames: (): string[] => ['host1', 'host2']
172       } as any;
173
174       component.groupForm.get('groupName').setValue('defalut');
175       component.groupForm.get('pool').setValue('rbd');
176       component.groupForm.get('unmanaged').setValue(false);
177       component.onSubmit();
178
179       expect(cephServiceService.create).toHaveBeenCalledWith({
180         service_type: 'nvmeof',
181         service_id: 'rbd.defalut',
182         pool: 'rbd',
183         group: 'defalut',
184         placement: {
185           hosts: ['host1', 'host2']
186         },
187         unmanaged: false
188       });
189     });
190
191     it('should create service with unmanaged flag set to true', () => {
192       component.gatewayNodeComponent = {
193         getSelectedHosts: (): any[] => [{ hostname: 'host1' }],
194         getSelectedHostnames: (): string[] => ['host1']
195       } as any;
196
197       component.groupForm.get('groupName').setValue('unmanaged-group');
198       component.groupForm.get('pool').setValue('rbd');
199       component.groupForm.get('unmanaged').setValue(true);
200       component.onSubmit();
201
202       expect(cephServiceService.create).toHaveBeenCalledWith(
203         jasmine.objectContaining({
204           unmanaged: true,
205           group: 'unmanaged-group',
206           pool: 'rbd'
207         })
208       );
209     });
210
211     it('should navigate to list view on success', () => {
212       component.gatewayNodeComponent = {
213         getSelectedHosts: (): any[] => [{ hostname: 'host1' }],
214         getSelectedHostnames: (): string[] => ['host1']
215       } as any;
216
217       component.groupForm.get('groupName').setValue('test-group');
218       component.groupForm.get('pool').setValue('rbd');
219       component.onSubmit();
220
221       expect(router.navigateByUrl).toHaveBeenCalledWith('/block/nvmeof/gateways');
222     });
223   });
224 });