]> git.apps.os.sepia.ceph.com Git - ceph.git/blob
01f4626686a1a5b50ee82889a5b4b0032be6db1e
[ceph.git] /
1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { FormControl, ReactiveFormsModule } from '@angular/forms';
4 import { Router } from '@angular/router';
5 import { RouterTestingModule } from '@angular/router/testing';
6
7 import _ from 'lodash';
8 import { ToastrModule } from 'ngx-toastr';
9 import { of as observableOf } from 'rxjs';
10
11 import { RgwBucketService } from '~/app/shared/api/rgw-bucket.service';
12 import { RgwSiteService } from '~/app/shared/api/rgw-site.service';
13 import { RgwUserService } from '~/app/shared/api/rgw-user.service';
14 import { NotificationType } from '~/app/shared/enum/notification-type.enum';
15 import { NotificationService } from '~/app/shared/services/notification.service';
16 import { SharedModule } from '~/app/shared/shared.module';
17 import { configureTestBed, FormHelper } from '~/testing/unit-test-helper';
18 import { RgwBucketMfaDelete } from '../models/rgw-bucket-mfa-delete';
19 import { RgwBucketVersioning } from '../models/rgw-bucket-versioning';
20 import { RgwBucketFormComponent } from './rgw-bucket-form.component';
21
22 describe('RgwBucketFormComponent', () => {
23   let component: RgwBucketFormComponent;
24   let fixture: ComponentFixture<RgwBucketFormComponent>;
25   let rgwBucketService: RgwBucketService;
26   let getPlacementTargetsSpy: jasmine.Spy;
27   let rgwBucketServiceGetSpy: jasmine.Spy;
28   let enumerateSpy: jasmine.Spy;
29   let formHelper: FormHelper;
30
31   configureTestBed({
32     declarations: [RgwBucketFormComponent],
33     imports: [
34       HttpClientTestingModule,
35       ReactiveFormsModule,
36       RouterTestingModule,
37       SharedModule,
38       ToastrModule.forRoot()
39     ]
40   });
41
42   beforeEach(() => {
43     fixture = TestBed.createComponent(RgwBucketFormComponent);
44     component = fixture.componentInstance;
45     rgwBucketService = TestBed.inject(RgwBucketService);
46     rgwBucketServiceGetSpy = spyOn(rgwBucketService, 'get');
47     getPlacementTargetsSpy = spyOn(TestBed.inject(RgwSiteService), 'get');
48     enumerateSpy = spyOn(TestBed.inject(RgwUserService), 'enumerate');
49     formHelper = new FormHelper(component.bucketForm);
50   });
51
52   it('should create', () => {
53     expect(component).toBeTruthy();
54   });
55
56   describe('bucketNameValidator', () => {
57     const testValidator = (name: string, valid: boolean) => {
58       const validatorFn = component.bucketNameValidator();
59       const ctrl = new FormControl(name);
60       ctrl.markAsDirty();
61       const validatorPromise = validatorFn(ctrl);
62       expect(validatorPromise instanceof Promise).toBeTruthy();
63       if (validatorPromise instanceof Promise) {
64         validatorPromise.then((resp) => {
65           if (valid) {
66             expect(resp).toBe(null);
67           } else {
68             expect(resp instanceof Object).toBeTruthy();
69             expect(resp.bucketNameInvalid).toBeTruthy();
70           }
71         });
72       }
73     };
74
75     it('should validate empty name', () => {
76       testValidator('', true);
77     });
78
79     it('bucket names cannot be formatted as IP address', () => {
80       testValidator('172.10.4.51', false);
81     });
82
83     it('bucket name must be >= 3 characters long (1/2)', () => {
84       testValidator('ab', false);
85     });
86
87     it('bucket name must be >= 3 characters long (2/2)', () => {
88       testValidator('abc', true);
89     });
90
91     it('bucket name must be <= than 63 characters long (1/2)', () => {
92       testValidator(_.repeat('a', 64), false);
93     });
94
95     it('bucket name must be <= than 63 characters long (2/2)', () => {
96       testValidator(_.repeat('a', 63), true);
97     });
98
99     it('bucket names must not contain uppercase characters or underscores (1/2)', () => {
100       testValidator('iAmInvalid', false);
101     });
102
103     it('bucket names must not contain uppercase characters or underscores (2/2)', () => {
104       testValidator('i_am_invalid', false);
105     });
106
107     it('bucket names with invalid labels (1/3)', () => {
108       testValidator('abc.1def.Ghi2', false);
109     });
110
111     it('bucket names with invalid labels (2/3)', () => {
112       testValidator('abc.1-xy', false);
113     });
114
115     it('bucket names with invalid labels (3/3)', () => {
116       testValidator('abc.*def', false);
117     });
118
119     it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (1/3)', () => {
120       testValidator('xyz.abc', true);
121     });
122
123     it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (2/3)', () => {
124       testValidator('abc.1-def', true);
125     });
126
127     it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (3/3)', () => {
128       testValidator('abc.ghi2', true);
129     });
130
131     it('bucket names must be unique', () => {
132       spyOn(rgwBucketService, 'enumerate').and.returnValue(observableOf(['abcd']));
133       const validatorFn = component.bucketNameValidator();
134       const ctrl = new FormControl('abcd');
135       ctrl.markAsDirty();
136       const validatorPromise = validatorFn(ctrl);
137       expect(validatorPromise instanceof Promise).toBeTruthy();
138       if (validatorPromise instanceof Promise) {
139         validatorPromise.then((resp) => {
140           expect(resp instanceof Object).toBeTruthy();
141           expect(resp.bucketNameExists).toBeTruthy();
142         });
143       }
144     });
145
146     it('should get zonegroup and placement targets', () => {
147       const payload: Record<string, any> = {
148         zonegroup: 'default',
149         placement_targets: [
150           {
151             name: 'default-placement',
152             data_pool: 'default.rgw.buckets.data'
153           },
154           {
155             name: 'placement-target2',
156             data_pool: 'placement-target2.rgw.buckets.data'
157           }
158         ]
159       };
160       getPlacementTargetsSpy.and.returnValue(observableOf(payload));
161       enumerateSpy.and.returnValue(observableOf([]));
162       fixture.detectChanges();
163
164       expect(component.zonegroup).toBe(payload.zonegroup);
165       const placementTargets = [];
166       for (const placementTarget of payload['placement_targets']) {
167         placementTarget[
168           'description'
169         ] = `${placementTarget['name']} (pool: ${placementTarget['data_pool']})`;
170         placementTargets.push(placementTarget);
171       }
172       expect(component.placementTargets).toEqual(placementTargets);
173     });
174   });
175
176   describe('submit form', () => {
177     let notificationService: NotificationService;
178
179     beforeEach(() => {
180       spyOn(TestBed.inject(Router), 'navigate').and.stub();
181       notificationService = TestBed.inject(NotificationService);
182       spyOn(notificationService, 'show');
183     });
184
185     it('should validate name', () => {
186       component.editing = false;
187       component.createForm();
188       const control = component.bucketForm.get('bid');
189       expect(_.isFunction(control.asyncValidator)).toBeTruthy();
190     });
191
192     it('should not validate name', () => {
193       component.editing = true;
194       component.createForm();
195       const control = component.bucketForm.get('bid');
196       expect(control.asyncValidator).toBeNull();
197     });
198
199     it('tests create success notification', () => {
200       spyOn(rgwBucketService, 'create').and.returnValue(observableOf([]));
201       component.editing = false;
202       component.bucketForm.markAsDirty();
203       component.submit();
204       expect(notificationService.show).toHaveBeenCalledWith(
205         NotificationType.success,
206         `Created Object Gateway bucket 'null'`
207       );
208     });
209
210     it('tests update success notification', () => {
211       spyOn(rgwBucketService, 'update').and.returnValue(observableOf([]));
212       component.editing = true;
213       component.bucketForm.markAsDirty();
214       component.submit();
215       expect(notificationService.show).toHaveBeenCalledWith(
216         NotificationType.success,
217         `Updated Object Gateway bucket 'null'.`
218       );
219     });
220   });
221
222   describe('mfa credentials', () => {
223     const checkMfaCredentialsVisibility = (
224       fakeResponse: object,
225       versioningChecked: boolean,
226       mfaDeleteChecked: boolean,
227       expectedVisibility: boolean
228     ) => {
229       component['route'].params = observableOf({ bid: 'bid' });
230       component.editing = true;
231       rgwBucketServiceGetSpy.and.returnValue(observableOf(fakeResponse));
232       enumerateSpy.and.returnValue(observableOf([]));
233       component.ngOnInit();
234       component.bucketForm.patchValue({
235         versioning: versioningChecked,
236         'mfa-delete': mfaDeleteChecked
237       });
238       fixture.detectChanges();
239
240       const mfaTokenSerial = fixture.debugElement.nativeElement.querySelector('#mfa-token-serial');
241       const mfaTokenPin = fixture.debugElement.nativeElement.querySelector('#mfa-token-pin');
242       if (expectedVisibility) {
243         expect(mfaTokenSerial).toBeTruthy();
244         expect(mfaTokenPin).toBeTruthy();
245       } else {
246         expect(mfaTokenSerial).toBeFalsy();
247         expect(mfaTokenPin).toBeFalsy();
248       }
249     };
250
251     it('inputs should be visible when required', () => {
252       checkMfaCredentialsVisibility(
253         {
254           versioning: RgwBucketVersioning.SUSPENDED,
255           mfa_delete: RgwBucketMfaDelete.DISABLED
256         },
257         false,
258         false,
259         false
260       );
261       checkMfaCredentialsVisibility(
262         {
263           versioning: RgwBucketVersioning.SUSPENDED,
264           mfa_delete: RgwBucketMfaDelete.DISABLED
265         },
266         true,
267         false,
268         false
269       );
270       checkMfaCredentialsVisibility(
271         {
272           versioning: RgwBucketVersioning.ENABLED,
273           mfa_delete: RgwBucketMfaDelete.DISABLED
274         },
275         false,
276         false,
277         false
278       );
279       checkMfaCredentialsVisibility(
280         {
281           versioning: RgwBucketVersioning.ENABLED,
282           mfa_delete: RgwBucketMfaDelete.ENABLED
283         },
284         true,
285         true,
286         false
287       );
288       checkMfaCredentialsVisibility(
289         {
290           versioning: RgwBucketVersioning.SUSPENDED,
291           mfa_delete: RgwBucketMfaDelete.DISABLED
292         },
293         false,
294         true,
295         true
296       );
297       checkMfaCredentialsVisibility(
298         {
299           versioning: RgwBucketVersioning.SUSPENDED,
300           mfa_delete: RgwBucketMfaDelete.ENABLED
301         },
302         false,
303         false,
304         true
305       );
306       checkMfaCredentialsVisibility(
307         {
308           versioning: RgwBucketVersioning.SUSPENDED,
309           mfa_delete: RgwBucketMfaDelete.ENABLED
310         },
311         true,
312         true,
313         true
314       );
315       checkMfaCredentialsVisibility(
316         {
317           versioning: RgwBucketVersioning.ENABLED,
318           mfa_delete: RgwBucketMfaDelete.ENABLED
319         },
320         false,
321         true,
322         true
323       );
324     });
325   });
326
327   describe('object locking', () => {
328     const setDaysAndYears = (fn: (name: string) => void) => {
329       ['lock_retention_period_days', 'lock_retention_period_years'].forEach(fn);
330     };
331
332     const expectPatternLockError = (value: string) => {
333       formHelper.setValue('lock_enabled', true, true);
334       setDaysAndYears((name: string) => {
335         formHelper.setValue(name, value);
336         formHelper.expectError(name, 'pattern');
337       });
338     };
339
340     const expectValidLockInputs = (enabled: boolean, mode: string, days: string, years: string) => {
341       formHelper.setValue('lock_enabled', enabled);
342       formHelper.setValue('lock_mode', mode);
343       formHelper.setValue('lock_retention_period_days', days);
344       formHelper.setValue('lock_retention_period_years', years);
345       [
346         'lock_enabled',
347         'lock_mode',
348         'lock_retention_period_days',
349         'lock_retention_period_years'
350       ].forEach((name) => {
351         const control = component.bucketForm.get(name);
352         expect(control.valid).toBeTruthy();
353         expect(control.errors).toBeNull();
354       });
355     };
356
357     it('should check lock enabled checkbox [mode=create]', () => {
358       component.createForm();
359       const control = component.bucketForm.get('lock_enabled');
360       expect(control.disabled).toBeFalsy();
361     });
362
363     it('should check lock enabled checkbox [mode=edit]', () => {
364       component.editing = true;
365       component.createForm();
366       const control = component.bucketForm.get('lock_enabled');
367       expect(control.disabled).toBeTruthy();
368     });
369
370     it('should have the "eitherDaysOrYears" error', () => {
371       formHelper.setValue('lock_enabled', true);
372       setDaysAndYears((name: string) => {
373         const control = component.bucketForm.get(name);
374         control.updateValueAndValidity();
375         expect(control.value).toBe(0);
376         expect(control.invalid).toBeTruthy();
377         formHelper.expectError(control, 'eitherDaysOrYears');
378       });
379     });
380
381     it('should have the "pattern" error [1]', () => {
382       expectPatternLockError('-1');
383     });
384
385     it('should have the "pattern" error [2]', () => {
386       expectPatternLockError('1.2');
387     });
388
389     it('should have valid values [1]', () => {
390       expectValidLockInputs(true, 'Governance', '0', '1');
391     });
392
393     it('should have valid values [2]', () => {
394       expectValidLockInputs(false, 'Compliance', '100', '0');
395     });
396   });
397 });