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