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';
7 import * as _ from 'lodash';
8 import { ToastrModule } from 'ngx-toastr';
9 import { of as observableOf } from 'rxjs';
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';
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;
32 declarations: [RgwBucketFormComponent],
34 HttpClientTestingModule,
38 ToastrModule.forRoot()
40 providers: [i18nProviders]
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);
53 it('should create', () => {
54 expect(component).toBeTruthy();
57 describe('bucketNameValidator', () => {
58 const testValidator = (name: string, valid: boolean) => {
59 const validatorFn = component.bucketNameValidator();
60 const ctrl = new FormControl(name);
62 const validatorPromise = validatorFn(ctrl);
63 expect(validatorPromise instanceof Promise).toBeTruthy();
64 if (validatorPromise instanceof Promise) {
65 validatorPromise.then((resp) => {
67 expect(resp).toBe(null);
69 expect(resp instanceof Object).toBeTruthy();
70 expect(resp.bucketNameInvalid).toBeTruthy();
76 it('should validate empty name', () => {
77 testValidator('', true);
80 it('bucket names cannot be formatted as IP address', () => {
81 testValidator('172.10.4.51', false);
84 it('bucket name must be >= 3 characters long (1/2)', () => {
85 testValidator('ab', false);
88 it('bucket name must be >= 3 characters long (2/2)', () => {
89 testValidator('abc', true);
92 it('bucket name must be <= than 63 characters long (1/2)', () => {
93 testValidator(_.repeat('a', 64), false);
96 it('bucket name must be <= than 63 characters long (2/2)', () => {
97 testValidator(_.repeat('a', 63), true);
100 it('bucket names must not contain uppercase characters or underscores (1/2)', () => {
101 testValidator('iAmInvalid', false);
104 it('bucket names must not contain uppercase characters or underscores (2/2)', () => {
105 testValidator('i_am_invalid', false);
108 it('bucket names with invalid labels (1/3)', () => {
109 testValidator('abc.1def.Ghi2', false);
112 it('bucket names with invalid labels (2/3)', () => {
113 testValidator('abc.1-xy', false);
116 it('bucket names with invalid labels (3/3)', () => {
117 testValidator('abc.*def', false);
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);
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);
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);
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');
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();
147 it('should get zonegroup and placement targets', () => {
148 const payload: Record<string, any> = {
149 zonegroup: 'default',
152 name: 'default-placement',
153 data_pool: 'default.rgw.buckets.data'
156 name: 'placement-target2',
157 data_pool: 'placement-target2.rgw.buckets.data'
161 getPlacementTargetsSpy.and.returnValue(observableOf(payload));
162 enumerateSpy.and.returnValue(observableOf([]));
163 fixture.detectChanges();
165 expect(component.zonegroup).toBe(payload.zonegroup);
166 const placementTargets = [];
167 for (const placementTarget of payload['placement_targets']) {
170 ] = `${placementTarget['name']} (pool: ${placementTarget['data_pool']})`;
171 placementTargets.push(placementTarget);
173 expect(component.placementTargets).toEqual(placementTargets);
177 describe('submit form', () => {
178 let notificationService: NotificationService;
181 spyOn(TestBed.get(Router), 'navigate').and.stub();
182 notificationService = TestBed.get(NotificationService);
183 spyOn(notificationService, 'show');
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();
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();
200 it('tests create success notification', () => {
201 spyOn(rgwBucketService, 'create').and.returnValue(observableOf([]));
202 component.editing = false;
203 component.bucketForm.markAsDirty();
205 expect(notificationService.show).toHaveBeenCalledWith(
206 NotificationType.success,
207 'Created Object Gateway bucket ""'
211 it('tests update success notification', () => {
212 spyOn(rgwBucketService, 'update').and.returnValue(observableOf([]));
213 component.editing = true;
214 component.bucketForm.markAsDirty();
216 expect(notificationService.show).toHaveBeenCalledWith(
217 NotificationType.success,
218 'Updated Object Gateway bucket "".'
223 describe('mfa credentials', () => {
224 const checkMfaCredentialsVisibility = (
225 fakeResponse: object,
226 versioningChecked: boolean,
227 mfaDeleteChecked: boolean,
228 expectedVisibility: boolean
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
239 fixture.detectChanges();
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();
247 expect(mfaTokenSerial).toBeFalsy();
248 expect(mfaTokenPin).toBeFalsy();
252 it('inputs should be visible when required', () => {
253 checkMfaCredentialsVisibility(
255 versioning: RgwBucketVersioning.SUSPENDED,
256 mfa_delete: RgwBucketMfaDelete.DISABLED
262 checkMfaCredentialsVisibility(
264 versioning: RgwBucketVersioning.SUSPENDED,
265 mfa_delete: RgwBucketMfaDelete.DISABLED
271 checkMfaCredentialsVisibility(
273 versioning: RgwBucketVersioning.ENABLED,
274 mfa_delete: RgwBucketMfaDelete.DISABLED
280 checkMfaCredentialsVisibility(
282 versioning: RgwBucketVersioning.ENABLED,
283 mfa_delete: RgwBucketMfaDelete.ENABLED
289 checkMfaCredentialsVisibility(
291 versioning: RgwBucketVersioning.SUSPENDED,
292 mfa_delete: RgwBucketMfaDelete.DISABLED
298 checkMfaCredentialsVisibility(
300 versioning: RgwBucketVersioning.SUSPENDED,
301 mfa_delete: RgwBucketMfaDelete.ENABLED
307 checkMfaCredentialsVisibility(
309 versioning: RgwBucketVersioning.SUSPENDED,
310 mfa_delete: RgwBucketMfaDelete.ENABLED
316 checkMfaCredentialsVisibility(
318 versioning: RgwBucketVersioning.ENABLED,
319 mfa_delete: RgwBucketMfaDelete.ENABLED
328 describe('object locking', () => {
329 const setDaysAndYears = (fn: (name: string) => void) => {
330 ['lock_retention_period_days', 'lock_retention_period_years'].forEach(fn);
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');
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);
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();
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();
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();
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');
382 it('should have the "pattern" error [1]', () => {
383 expectPatternLockError('-1');
386 it('should have the "pattern" error [2]', () => {
387 expectPatternLockError('1.2');
390 it('should have valid values [1]', () => {
391 expectValidLockInputs(true, 'Governance', '0', '1');
394 it('should have valid values [2]', () => {
395 expectValidLockInputs(false, 'Compliance', '100', '0');