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