]> git.proxmox.com Git - ceph.git/blob - ceph/src/pybind/mgr/dashboard/frontend/src/app/ceph/pool/erasure-code-profile-form/erasure-code-profile-form-modal.component.spec.ts
0d4ce97a210165c8673c00f16fd7263326f77180
[ceph.git] / ceph / src / pybind / mgr / dashboard / frontend / src / app / ceph / pool / erasure-code-profile-form / erasure-code-profile-form-modal.component.spec.ts
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { By } from '@angular/platform-browser';
4 import { RouterTestingModule } from '@angular/router/testing';
5
6 import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
7 import { BsModalRef } from 'ngx-bootstrap/modal';
8 import { ToastrModule } from 'ngx-toastr';
9 import { of } from 'rxjs';
10
11 import {
12 configureTestBed,
13 FixtureHelper,
14 FormHelper,
15 i18nProviders
16 } from '../../../../testing/unit-test-helper';
17 import { ErasureCodeProfileService } from '../../../shared/api/erasure-code-profile.service';
18 import { ErasureCodeProfile } from '../../../shared/models/erasure-code-profile';
19 import { TaskWrapperService } from '../../../shared/services/task-wrapper.service';
20 import { PoolModule } from '../pool.module';
21 import { ErasureCodeProfileFormModalComponent } from './erasure-code-profile-form-modal.component';
22
23 describe('ErasureCodeProfileFormModalComponent', () => {
24 let component: ErasureCodeProfileFormModalComponent;
25 let ecpService: ErasureCodeProfileService;
26 let fixture: ComponentFixture<ErasureCodeProfileFormModalComponent>;
27 let formHelper: FormHelper;
28 let fixtureHelper: FixtureHelper;
29 let data: {};
30
31 configureTestBed({
32 imports: [
33 HttpClientTestingModule,
34 RouterTestingModule,
35 ToastrModule.forRoot(),
36 PoolModule,
37 NgBootstrapFormValidationModule.forRoot()
38 ],
39 providers: [ErasureCodeProfileService, BsModalRef, i18nProviders]
40 });
41
42 beforeEach(() => {
43 fixture = TestBed.createComponent(ErasureCodeProfileFormModalComponent);
44 fixtureHelper = new FixtureHelper(fixture);
45 component = fixture.componentInstance;
46 formHelper = new FormHelper(component.form);
47 ecpService = TestBed.get(ErasureCodeProfileService);
48 data = {
49 failure_domains: ['host', 'osd'],
50 plugins: ['isa', 'jerasure', 'shec', 'lrc'],
51 names: ['ecp1', 'ecp2'],
52 devices: ['ssd', 'hdd']
53 };
54 spyOn(ecpService, 'getInfo').and.callFake(() => of(data));
55 fixture.detectChanges();
56 });
57
58 it('should create', () => {
59 expect(component).toBeTruthy();
60 });
61
62 it('calls listing to get ecps on ngInit', () => {
63 expect(ecpService.getInfo).toHaveBeenCalled();
64 expect(component.names.length).toBe(2);
65 });
66
67 describe('form validation', () => {
68 it(`isn't valid if name is not set`, () => {
69 expect(component.form.invalid).toBeTruthy();
70 formHelper.setValue('name', 'someProfileName');
71 expect(component.form.valid).toBeTruthy();
72 });
73
74 it('sets name invalid', () => {
75 component.names = ['awesomeProfileName'];
76 formHelper.expectErrorChange('name', 'awesomeProfileName', 'uniqueName');
77 formHelper.expectErrorChange('name', 'some invalid text', 'pattern');
78 formHelper.expectErrorChange('name', null, 'required');
79 });
80
81 it('sets k to min error', () => {
82 formHelper.expectErrorChange('k', 0, 'min');
83 });
84
85 it('sets m to min error', () => {
86 formHelper.expectErrorChange('m', 0, 'min');
87 });
88
89 it(`should show all default form controls`, () => {
90 const showDefaults = (plugin: string) => {
91 formHelper.setValue('plugin', plugin);
92 fixtureHelper.expectIdElementsVisible(
93 [
94 'name',
95 'plugin',
96 'k',
97 'm',
98 'crushFailureDomain',
99 'crushRoot',
100 'crushDeviceClass',
101 'directory'
102 ],
103 true
104 );
105 };
106 showDefaults('jerasure');
107 showDefaults('shec');
108 showDefaults('lrc');
109 showDefaults('isa');
110 });
111
112 describe(`for 'jerasure' plugin (default)`, () => {
113 it(`requires 'm' and 'k'`, () => {
114 formHelper.expectErrorChange('k', null, 'required');
115 formHelper.expectErrorChange('m', null, 'required');
116 });
117
118 it(`should show 'packetSize' and 'technique'`, () => {
119 fixtureHelper.expectIdElementsVisible(['packetSize', 'technique'], true);
120 });
121
122 it(`should not show any other plugin specific form control`, () => {
123 fixtureHelper.expectIdElementsVisible(['c', 'l', 'crushLocality'], false);
124 });
125 });
126
127 describe(`for 'isa' plugin`, () => {
128 beforeEach(() => {
129 formHelper.setValue('plugin', 'isa');
130 });
131
132 it(`does not require 'm' and 'k'`, () => {
133 formHelper.setValue('k', null);
134 formHelper.expectValidChange('k', null);
135 formHelper.expectValidChange('m', null);
136 });
137
138 it(`should show 'technique'`, () => {
139 fixtureHelper.expectIdElementsVisible(['technique'], true);
140 expect(fixture.debugElement.query(By.css('#technique'))).toBeTruthy();
141 });
142
143 it(`should not show any other plugin specific form control`, () => {
144 fixtureHelper.expectIdElementsVisible(['c', 'l', 'crushLocality', 'packetSize'], false);
145 });
146 });
147
148 describe(`for 'lrc' plugin`, () => {
149 beforeEach(() => {
150 formHelper.setValue('plugin', 'lrc');
151 });
152
153 it(`requires 'm', 'l' and 'k'`, () => {
154 formHelper.expectErrorChange('k', null, 'required');
155 formHelper.expectErrorChange('m', null, 'required');
156 });
157
158 it(`should show 'l' and 'crushLocality'`, () => {
159 fixtureHelper.expectIdElementsVisible(['l', 'crushLocality'], true);
160 });
161
162 it(`should not show any other plugin specific form control`, () => {
163 fixtureHelper.expectIdElementsVisible(['c', 'packetSize', 'technique'], false);
164 });
165 });
166
167 describe(`for 'shec' plugin`, () => {
168 beforeEach(() => {
169 formHelper.setValue('plugin', 'shec');
170 });
171
172 it(`does not require 'm' and 'k'`, () => {
173 formHelper.expectValidChange('k', null);
174 formHelper.expectValidChange('m', null);
175 });
176
177 it(`should show 'c'`, () => {
178 fixtureHelper.expectIdElementsVisible(['c'], true);
179 });
180
181 it(`should not show any other plugin specific form control`, () => {
182 fixtureHelper.expectIdElementsVisible(
183 ['l', 'crushLocality', 'packetSize', 'technique'],
184 false
185 );
186 });
187 });
188 });
189
190 describe('submission', () => {
191 let ecp: ErasureCodeProfile;
192
193 const testCreation = () => {
194 fixture.detectChanges();
195 component.onSubmit();
196 expect(ecpService.create).toHaveBeenCalledWith(ecp);
197 };
198
199 beforeEach(() => {
200 ecp = new ErasureCodeProfile();
201 const taskWrapper = TestBed.get(TaskWrapperService);
202 spyOn(taskWrapper, 'wrapTaskAroundCall').and.callThrough();
203 spyOn(ecpService, 'create').and.stub();
204 });
205
206 describe(`'jerasure' usage`, () => {
207 beforeEach(() => {
208 ecp.name = 'jerasureProfile';
209 });
210
211 it('should be able to create a profile with only required fields', () => {
212 formHelper.setMultipleValues(ecp, true);
213 ecp.k = 4;
214 ecp.m = 2;
215 testCreation();
216 });
217
218 it(`does not create with missing 'k' or invalid form`, () => {
219 ecp.k = 0;
220 formHelper.setMultipleValues(ecp, true);
221 component.onSubmit();
222 expect(ecpService.create).not.toHaveBeenCalled();
223 });
224
225 it('should be able to create a profile with m, k, name, directory and packetSize', () => {
226 ecp.m = 3;
227 ecp.directory = '/different/ecp/path';
228 formHelper.setMultipleValues(ecp, true);
229 ecp.k = 4;
230 formHelper.setValue('packetSize', 8192, true);
231 ecp.packetsize = 8192;
232 testCreation();
233 });
234
235 it('should not send the profile with unsupported fields', () => {
236 formHelper.setMultipleValues(ecp, true);
237 ecp.k = 4;
238 ecp.m = 2;
239 formHelper.setValue('crushLocality', 'osd', true);
240 testCreation();
241 });
242 });
243
244 describe(`'isa' usage`, () => {
245 beforeEach(() => {
246 ecp.name = 'isaProfile';
247 ecp.plugin = 'isa';
248 });
249
250 it('should be able to create a profile with only plugin and name', () => {
251 formHelper.setMultipleValues(ecp, true);
252 testCreation();
253 });
254
255 it('should send profile with plugin, name, failure domain and technique only', () => {
256 ecp.technique = 'cauchy';
257 formHelper.setMultipleValues(ecp, true);
258 formHelper.setValue('crushFailureDomain', 'osd', true);
259 ecp['crush-failure-domain'] = 'osd';
260 testCreation();
261 });
262
263 it('should not send the profile with unsupported fields', () => {
264 formHelper.setMultipleValues(ecp, true);
265 formHelper.setValue('packetSize', 'osd', true);
266 testCreation();
267 });
268 });
269
270 describe(`'lrc' usage`, () => {
271 beforeEach(() => {
272 ecp.name = 'lreProfile';
273 ecp.plugin = 'lrc';
274 });
275
276 it('should be able to create a profile with only required fields', () => {
277 formHelper.setMultipleValues(ecp, true);
278 ecp.k = 4;
279 ecp.m = 2;
280 ecp.l = 3;
281 testCreation();
282 });
283
284 it('should send profile with all required fields and crush root and locality', () => {
285 ecp.l = 8;
286 formHelper.setMultipleValues(ecp, true);
287 ecp.k = 4;
288 ecp.m = 2;
289 formHelper.setValue('crushLocality', 'osd', true);
290 formHelper.setValue('crushRoot', 'rack', true);
291 ecp['crush-locality'] = 'osd';
292 ecp['crush-root'] = 'rack';
293 testCreation();
294 });
295
296 it('should not send the profile with unsupported fields', () => {
297 formHelper.setMultipleValues(ecp, true);
298 ecp.k = 4;
299 ecp.m = 2;
300 ecp.l = 3;
301 formHelper.setValue('c', 4, true);
302 testCreation();
303 });
304 });
305
306 describe(`'shec' usage`, () => {
307 beforeEach(() => {
308 ecp.name = 'shecProfile';
309 ecp.plugin = 'shec';
310 });
311
312 it('should be able to create a profile with only plugin and name', () => {
313 formHelper.setMultipleValues(ecp, true);
314 testCreation();
315 });
316
317 it('should send profile with plugin, name, c and crush device class only', () => {
318 ecp.c = 4;
319 formHelper.setMultipleValues(ecp, true);
320 formHelper.setValue('crushDeviceClass', 'ssd', true);
321 ecp['crush-device-class'] = 'ssd';
322 testCreation();
323 });
324
325 it('should not send the profile with unsupported fields', () => {
326 formHelper.setMultipleValues(ecp, true);
327 formHelper.setValue('l', 8, true);
328 testCreation();
329 });
330 });
331 });
332 });