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