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';
7 import _ from 'lodash';
8 import { ToastrModule } from 'ngx-toastr';
9 import { of as observableOf, throwError } from 'rxjs';
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';
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()
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);
52 it('should create', () => {
53 expect(component).toBeTruthy();
56 describe('bucketNameValidator', () => {
57 const testValidator = (name: string, valid: boolean, expectedError?: string) => {
58 rgwBucketServiceGetSpy.and.returnValue(throwError('foo'));
59 formHelper.setValue('bid', name, true);
62 formHelper.expectValid('bid');
64 formHelper.expectError('bid', expectedError);
68 it('should validate empty name', fakeAsync(() => {
69 formHelper.expectErrorChange('bid', '', 'required', true);
72 it('bucket names cannot be formatted as IP address', fakeAsync(() => {
73 const testIPs = ['1.1.1.01', '001.1.1.01', '127.0.0.1'];
74 for (const ip of testIPs) {
75 testValidator(ip, false, 'ipAddress');
79 it('bucket name must be >= 3 characters long (1/2)', fakeAsync(() => {
80 testValidator('ab', false, 'shouldBeInRange');
83 it('bucket name must be >= 3 characters long (2/2)', fakeAsync(() => {
84 testValidator('abc', true);
87 it('bucket name must be <= than 63 characters long (1/2)', fakeAsync(() => {
88 testValidator(_.repeat('a', 64), false, 'shouldBeInRange');
91 it('bucket name must be <= than 63 characters long (2/2)', fakeAsync(() => {
92 testValidator(_.repeat('a', 63), true);
95 it('bucket names must not contain uppercase characters or underscores (1/2)', fakeAsync(() => {
96 testValidator('iAmInvalid', false, 'containsUpperCase');
99 it('bucket names can only contain lowercase letters, numbers, and hyphens', fakeAsync(() => {
100 testValidator('$$$', false, 'onlyLowerCaseAndNumbers');
103 it('bucket names must not contain uppercase characters or underscores (2/2)', fakeAsync(() => {
104 testValidator('i_am_invalid', false, 'containsUpperCase');
107 it('bucket names must start and end with letters or numbers', fakeAsync(() => {
108 testValidator('abcd-', false, 'lowerCaseOrNumber');
111 it('bucket names with invalid labels (1/3)', fakeAsync(() => {
112 testValidator('abc.1def.Ghi2', false, 'containsUpperCase');
115 it('bucket names with invalid labels (2/3)', fakeAsync(() => {
116 testValidator('abc.1_xy', false, 'containsUpperCase');
119 it('bucket names with invalid labels (3/3)', fakeAsync(() => {
120 testValidator('abc.*def', false, 'lowerCaseOrNumber');
123 it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (1/3)', fakeAsync(() => {
124 testValidator('xyz.abc', true);
127 it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (2/3)', fakeAsync(() => {
128 testValidator('abc.1-def', true);
131 it('bucket names must be a series of one or more labels and can contain lowercase letters, numbers, and hyphens (3/3)', fakeAsync(() => {
132 testValidator('abc.ghi2', true);
135 it('bucket names must be unique', fakeAsync(() => {
136 testValidator('bucket-name-is-unique', true);
139 it('should get zonegroup and placement targets', () => {
140 const payload: Record<string, any> = {
141 zonegroup: 'default',
144 name: 'default-placement',
145 data_pool: 'default.rgw.buckets.data'
148 name: 'placement-target2',
149 data_pool: 'placement-target2.rgw.buckets.data'
153 getPlacementTargetsSpy.and.returnValue(observableOf(payload));
154 enumerateSpy.and.returnValue(observableOf([]));
155 fixture.detectChanges();
157 expect(component.zonegroup).toBe(payload.zonegroup);
158 const placementTargets = [];
159 for (const placementTarget of payload['placement_targets']) {
162 ] = `${placementTarget['name']} (pool: ${placementTarget['data_pool']})`;
163 placementTargets.push(placementTarget);
165 expect(component.placementTargets).toEqual(placementTargets);
169 describe('submit form', () => {
170 let notificationService: NotificationService;
173 spyOn(TestBed.inject(Router), 'navigate').and.stub();
174 notificationService = TestBed.inject(NotificationService);
175 spyOn(notificationService, 'show');
178 it('should validate name', () => {
179 component.editing = false;
180 component.createForm();
181 const control = component.bucketForm.get('bid');
182 expect(_.isFunction(control.asyncValidator)).toBeTruthy();
185 it('should not validate name', () => {
186 component.editing = true;
187 component.createForm();
188 const control = component.bucketForm.get('bid');
189 expect(control.asyncValidator).toBeNull();
192 it('tests create success notification', () => {
193 spyOn(rgwBucketService, 'create').and.returnValue(observableOf([]));
194 component.editing = false;
195 component.bucketForm.markAsDirty();
197 expect(notificationService.show).toHaveBeenCalledWith(
198 NotificationType.success,
199 `Created Object Gateway bucket 'null'`
203 it('tests update success notification', () => {
204 spyOn(rgwBucketService, 'update').and.returnValue(observableOf([]));
205 component.editing = true;
206 component.bucketForm.markAsDirty();
208 expect(notificationService.show).toHaveBeenCalledWith(
209 NotificationType.success,
210 `Updated Object Gateway bucket 'null'.`
215 describe('mfa credentials', () => {
216 const checkMfaCredentialsVisibility = (
217 fakeResponse: object,
218 versioningChecked: boolean,
219 mfaDeleteChecked: boolean,
220 expectedVisibility: boolean
222 component['route'].params = observableOf({ bid: 'bid' });
223 component.editing = true;
224 rgwBucketServiceGetSpy.and.returnValue(observableOf(fakeResponse));
225 enumerateSpy.and.returnValue(observableOf([]));
226 component.ngOnInit();
227 component.bucketForm.patchValue({
228 versioning: versioningChecked,
229 'mfa-delete': mfaDeleteChecked
231 fixture.detectChanges();
233 const mfaTokenSerial = fixture.debugElement.nativeElement.querySelector('#mfa-token-serial');
234 const mfaTokenPin = fixture.debugElement.nativeElement.querySelector('#mfa-token-pin');
235 if (expectedVisibility) {
236 expect(mfaTokenSerial).toBeTruthy();
237 expect(mfaTokenPin).toBeTruthy();
239 expect(mfaTokenSerial).toBeFalsy();
240 expect(mfaTokenPin).toBeFalsy();
244 it('inputs should be visible when required', () => {
245 checkMfaCredentialsVisibility(
247 versioning: RgwBucketVersioning.SUSPENDED,
248 mfa_delete: RgwBucketMfaDelete.DISABLED
254 checkMfaCredentialsVisibility(
256 versioning: RgwBucketVersioning.SUSPENDED,
257 mfa_delete: RgwBucketMfaDelete.DISABLED
263 checkMfaCredentialsVisibility(
265 versioning: RgwBucketVersioning.ENABLED,
266 mfa_delete: RgwBucketMfaDelete.DISABLED
272 checkMfaCredentialsVisibility(
274 versioning: RgwBucketVersioning.ENABLED,
275 mfa_delete: RgwBucketMfaDelete.ENABLED
281 checkMfaCredentialsVisibility(
283 versioning: RgwBucketVersioning.SUSPENDED,
284 mfa_delete: RgwBucketMfaDelete.DISABLED
290 checkMfaCredentialsVisibility(
292 versioning: RgwBucketVersioning.SUSPENDED,
293 mfa_delete: RgwBucketMfaDelete.ENABLED
299 checkMfaCredentialsVisibility(
301 versioning: RgwBucketVersioning.SUSPENDED,
302 mfa_delete: RgwBucketMfaDelete.ENABLED
308 checkMfaCredentialsVisibility(
310 versioning: RgwBucketVersioning.ENABLED,
311 mfa_delete: RgwBucketMfaDelete.ENABLED
320 describe('object locking', () => {
321 const expectPatternLockError = (value: string) => {
322 formHelper.setValue('lock_enabled', true, true);
323 formHelper.setValue('lock_retention_period_days', value);
324 formHelper.expectError('lock_retention_period_days', 'pattern');
327 const expectValidLockInputs = (enabled: boolean, mode: string, days: string) => {
328 formHelper.setValue('lock_enabled', enabled);
329 formHelper.setValue('lock_mode', mode);
330 formHelper.setValue('lock_retention_period_days', days);
331 ['lock_enabled', 'lock_mode', 'lock_retention_period_days'].forEach((name) => {
332 const control = component.bucketForm.get(name);
333 expect(control.valid).toBeTruthy();
334 expect(control.errors).toBeNull();
338 it('should check lock enabled checkbox [mode=create]', () => {
339 component.createForm();
340 const control = component.bucketForm.get('lock_enabled');
341 expect(control.disabled).toBeFalsy();
344 it('should check lock enabled checkbox [mode=edit]', () => {
345 component.editing = true;
346 component.createForm();
347 const control = component.bucketForm.get('lock_enabled');
348 expect(control.disabled).toBeTruthy();
351 it('should have the "lockDays" error', () => {
352 formHelper.setValue('lock_enabled', true);
353 const control = component.bucketForm.get('lock_retention_period_days');
354 control.updateValueAndValidity();
355 expect(control.value).toBe(0);
356 expect(control.invalid).toBeTruthy();
357 formHelper.expectError(control, 'lockDays');
360 it('should have the "pattern" error [1]', () => {
361 expectPatternLockError('-1');
364 it('should have the "pattern" error [2]', () => {
365 expectPatternLockError('1.2');
368 it('should have valid values [1]', () => {
369 expectValidLockInputs(true, 'Governance', '1');
372 it('should have valid values [2]', () => {
373 expectValidLockInputs(false, 'Compliance', '2');