1 import { HttpClientTestingModule } from '@angular/common/http/testing';
2 import { ComponentFixture, TestBed } from '@angular/core/testing';
3 import { Validators } from '@angular/forms';
4 import { RouterTestingModule } from '@angular/router/testing';
6 import { NgBootstrapFormValidationModule } from 'ng-bootstrap-form-validation';
7 import { NodeEvent, Tree, TreeModel, TreeModule } from 'ng2-tree';
8 import { BsModalRef, BsModalService, ModalModule } from 'ngx-bootstrap/modal';
9 import { ToastrModule } from 'ngx-toastr';
10 import { of } from 'rxjs';
17 } from '../../../../testing/unit-test-helper';
18 import { CephfsService } from '../../../shared/api/cephfs.service';
19 import { ConfirmationModalComponent } from '../../../shared/components/confirmation-modal/confirmation-modal.component';
20 import { FormModalComponent } from '../../../shared/components/form-modal/form-modal.component';
21 import { NotificationType } from '../../../shared/enum/notification-type.enum';
22 import { CdValidators } from '../../../shared/forms/cd-validators';
23 import { CdTableAction } from '../../../shared/models/cd-table-action';
24 import { CdTableSelection } from '../../../shared/models/cd-table-selection';
29 } from '../../../shared/models/cephfs-directory-models';
30 import { NotificationService } from '../../../shared/services/notification.service';
31 import { SharedModule } from '../../../shared/shared.module';
32 import { CephfsDirectoriesComponent } from './cephfs-directories.component';
34 describe('CephfsDirectoriesComponent', () => {
35 let component: CephfsDirectoriesComponent;
36 let fixture: ComponentFixture<CephfsDirectoriesComponent>;
37 let cephfsService: CephfsService;
38 let lsDirSpy: jasmine.Spy;
39 let modalShowSpy: jasmine.Spy;
40 let notificationShowSpy: jasmine.Spy;
41 let minValidator: jasmine.Spy;
42 let maxValidator: jasmine.Spy;
43 let minBinaryValidator: jasmine.Spy;
44 let maxBinaryValidator: jasmine.Spy;
47 // Get's private attributes or functions
49 nodeIds: (): { [path: string]: CephfsDir } => component['nodeIds'],
50 dirs: (): CephfsDir[] => component['dirs'],
51 requestedPaths: (): string[] => component['requestedPaths']
54 // Object contains mock data that will be reset before each test.
58 createdSnaps: CephfsSnapshot[] | any[];
59 deletedSnaps: CephfsSnapshot[] | any[];
60 updatedQuotas: { [path: string]: CephfsQuotas };
63 // Object contains mock functions
65 quotas: (max_bytes: number, max_files: number): CephfsQuotas => ({ max_bytes, max_files }),
66 snapshots: (dirPath: string, howMany: number): CephfsSnapshot[] => {
67 const name = 'someSnapshot';
69 const oneDay = 3600 * 24 * 1000;
70 for (let i = 0; i < howMany; i++) {
71 const snapName = `${name}${i + 1}`;
72 const path = `${dirPath}/.snap/${snapName}`;
73 const created = new Date(+new Date() - oneDay * i).toString();
74 snapshots.push({ name: snapName, path, created });
78 dir: (parentPath: string, name: string, modifier: number): CephfsDir => {
79 const dirPath = `${parentPath === '/' ? '' : parentPath}/${name}`;
80 let snapshots = mockLib.snapshots(parentPath, modifier);
81 const extraSnapshots = mockData.createdSnaps.filter((s) => s.path === dirPath);
82 if (extraSnapshots.length > 0) {
83 snapshots = snapshots.concat(extraSnapshots);
85 const deletedSnapshots = mockData.deletedSnaps
86 .filter((s) => s.path === dirPath)
88 if (deletedSnapshots.length > 0) {
89 snapshots = snapshots.filter((s) => !deletedSnapshots.includes(s.name));
95 quotas: Object.assign(
96 mockLib.quotas(1024 * modifier, 10 * modifier),
97 mockData.updatedQuotas[dirPath] || {}
102 // Only used inside other mocks
103 lsSingleDir: (path = ''): CephfsDir[] => {
104 if (path.includes('b')) {
105 // 'b' has no sub directories
109 // Directories are not sorted!
110 mockLib.dir(path, 'c', 3),
111 mockLib.dir(path, 'a', 1),
112 mockLib.dir(path, 'b', 2)
115 lsDir: (_id: number, path = '') => {
116 // will return 2 levels deep
117 let data = mockLib.lsSingleDir(path);
118 const paths = data.map((dir) => dir.path);
119 paths.forEach((pathL2) => {
120 data = data.concat(mockLib.lsSingleDir(pathL2));
124 mkSnapshot: (_id: any, path: string, name: string) => {
125 mockData.createdSnaps.push({
128 created: new Date().toString()
132 rmSnapshot: (_id: any, path: string, name: string) => {
133 mockData.deletedSnaps.push({
136 created: new Date().toString()
140 updateQuota: (_id: any, path: string, updated: CephfsQuotas) => {
141 mockData.updatedQuotas[path] = Object.assign(mockData.updatedQuotas[path] || {}, updated);
142 return of('Response');
144 modalShow: (comp: any, init: any) => {
145 modal = modalServiceShow(comp, init);
148 getControllerByPath: (path: string) => {
150 expand: () => mockLib.expand(path),
151 select: () => component.onNodeSelected(mockLib.getNodeEvent(path))
154 // Only used inside other mocks to mock "tree.expand" of every node
155 expand: (path: string) => {
156 component.updateDirectory(path, (nodes) => {
157 mockData.nodes = mockData.nodes.concat(nodes);
160 getNodeEvent: (path: string): NodeEvent => {
161 const tree = mockData.nodes.find((n) => n.id === path) as Tree;
162 if (mockData.parent) {
163 tree.parent = mockData.parent;
165 const dir = get.nodeIds()[path];
166 const parentNode = mockData.nodes.find((n) => n.id === dir.parent);
167 tree.parent = parentNode as Tree;
169 return { node: tree } as NodeEvent;
171 changeId: (id: number) => {
173 component.ngOnChanges();
174 mockData.nodes = [component.tree].concat(component.tree.children);
176 selectNode: (path: string) => {
177 mockLib.getControllerByPath(path).select();
179 mkDir: (path: string, name: string, maxFiles: number, maxBytes: number) => {
180 const dir = mockLib.dir(path, name, 3);
181 dir.quotas.max_bytes = maxBytes * 1024;
182 dir.quotas.max_files = maxFiles;
183 get.nodeIds()[dir.path] = dir;
184 mockData.nodes.push({
189 createSnapshotThroughModal: (name: string) => {
190 component.createSnapshot();
191 modal.component.onSubmitForm({ name });
193 deleteSnapshotsThroughModal: (snapshots: CephfsSnapshot[]) => {
194 component.snapshot.selection.selected = snapshots;
195 component.deleteSnapshotModal();
196 modal.component.callSubmitAction();
198 updateQuotaThroughModal: (attribute: string, value: number) => {
199 component.quota.selection.selected = component.settings.filter(
200 (q) => q.quotaKey === attribute
202 component.updateQuotaModal();
203 modal.component.onSubmitForm({ [attribute]: value });
205 unsetQuotaThroughModal: (attribute: string) => {
206 component.quota.selection.selected = component.settings.filter(
207 (q) => q.quotaKey === attribute
209 component.unsetQuotaModal();
210 modal.component.onSubmit();
212 setFourQuotaDirs: (quotas: number[][]) => {
213 expect(quotas.length).toBe(4); // Make sure this function is used correctly
215 quotas.forEach((quota, index) => {
217 mockLib.mkDir(path, index.toString(), quota[0], quota[1]);
229 parent: { value: '/', id: '/' }
233 mockLib.selectNode('/1/2/3/4');
237 // Expects that are used frequently
239 dirLength: (n: number) => expect(get.dirs().length).toBe(n),
240 nodeLength: (n: number) => expect(mockData.nodes.length).toBe(n),
241 lsDirCalledTimes: (n: number) => expect(lsDirSpy).toHaveBeenCalledTimes(n),
242 requestedPaths: (expected: string[]) => expect(get.requestedPaths()).toEqual(expected),
243 snapshotsByName: (snaps: string[]) =>
244 expect(component.selectedDir.snapshots.map((s) => s.name)).toEqual(snaps),
245 dirQuotas: (bytes: number, files: number) => {
246 expect(component.selectedDir.quotas).toEqual({ max_bytes: bytes, max_files: files });
248 noQuota: (key: 'bytes' | 'files') => {
249 assert.quotaRow(key, '', 0, '');
251 quotaIsNotInherited: (key: 'bytes' | 'files', shownValue: any, nextMaximum: number) => {
252 const dir = component.selectedDir;
253 const path = dir.path;
254 assert.quotaRow(key, shownValue, nextMaximum, path);
256 quotaIsInherited: (key: 'bytes' | 'files', shownValue: any, path: string) => {
257 const isBytes = key === 'bytes';
258 const nextMaximum = get.nodeIds()[path].quotas[isBytes ? 'max_bytes' : 'max_files'];
259 assert.quotaRow(key, shownValue, nextMaximum, path);
262 key: 'bytes' | 'files',
263 shownValue: number | string,
264 nextTreeMaximum: number,
267 const isBytes = key === 'bytes';
268 expect(component.settings[isBytes ? 1 : 0]).toEqual({
270 name: `Max ${isBytes ? 'size' : key}`,
274 quotaKey: `max_${key}`,
275 dirValue: expect.any(Number),
277 value: nextTreeMaximum,
278 path: expect.any(String)
282 quotaUnsetModalTexts: (titleText: string, message: string, notificationMsg: string) => {
283 expect(modalShowSpy).toHaveBeenCalledWith(ConfirmationModalComponent, {
284 initialState: expect.objectContaining({
286 description: message,
290 expect(notificationShowSpy).toHaveBeenCalledWith(NotificationType.success, notificationMsg);
292 quotaUpdateModalTexts: (titleText: string, message: string, notificationMsg: string) => {
293 expect(modalShowSpy).toHaveBeenCalledWith(FormModalComponent, {
294 initialState: expect.objectContaining({
297 submitButtonText: 'Save'
300 expect(notificationShowSpy).toHaveBeenCalledWith(NotificationType.success, notificationMsg);
302 quotaUpdateModalField: (
308 errors?: { [key: string]: string }
310 expect(modalShowSpy).toHaveBeenCalledWith(FormModalComponent, {
311 initialState: expect.objectContaining({
319 validators: expect.anything(),
325 if (type === 'binary') {
326 expect(minBinaryValidator).toHaveBeenCalledWith(0);
327 expect(maxBinaryValidator).toHaveBeenCalledWith(max);
329 expect(minValidator).toHaveBeenCalledWith(0);
330 expect(maxValidator).toHaveBeenCalledWith(max);
337 HttpClientTestingModule,
341 NgBootstrapFormValidationModule.forRoot(),
342 ToastrModule.forRoot(),
343 ModalModule.forRoot()
345 declarations: [CephfsDirectoriesComponent],
346 providers: [i18nProviders, BsModalRef]
358 cephfsService = TestBed.get(CephfsService);
359 lsDirSpy = spyOn(cephfsService, 'lsDir').and.callFake(mockLib.lsDir);
360 spyOn(cephfsService, 'mkSnapshot').and.callFake(mockLib.mkSnapshot);
361 spyOn(cephfsService, 'rmSnapshot').and.callFake(mockLib.rmSnapshot);
362 spyOn(cephfsService, 'updateQuota').and.callFake(mockLib.updateQuota);
364 modalShowSpy = spyOn(TestBed.get(BsModalService), 'show').and.callFake(mockLib.modalShow);
365 notificationShowSpy = spyOn(TestBed.get(NotificationService), 'show').and.stub();
367 fixture = TestBed.createComponent(CephfsDirectoriesComponent);
368 component = fixture.componentInstance;
369 fixture.detectChanges();
371 spyOn(component.treeComponent, 'getControllerByNodeId').and.callFake((id) =>
372 mockLib.getControllerByPath(id)
376 it('should create', () => {
377 expect(component).toBeTruthy();
380 describe('mock self test', () => {
381 it('tests snapshots mock', () => {
382 expect(mockLib.snapshots('/a', 1).map((s) => ({ name: s.name, path: s.path }))).toEqual([
384 name: 'someSnapshot1',
385 path: '/a/.snap/someSnapshot1'
388 expect(mockLib.snapshots('/a/b', 3).map((s) => ({ name: s.name, path: s.path }))).toEqual([
390 name: 'someSnapshot1',
391 path: '/a/b/.snap/someSnapshot1'
394 name: 'someSnapshot2',
395 path: '/a/b/.snap/someSnapshot2'
398 name: 'someSnapshot3',
399 path: '/a/b/.snap/someSnapshot3'
404 it('tests dir mock', () => {
405 const path = '/a/b/c';
406 mockData.createdSnaps = [{ path, name: 's1' }, { path, name: 's2' }];
407 mockData.deletedSnaps = [{ path, name: 'someSnapshot2' }, { path, name: 's2' }];
408 const dir = mockLib.dir('/a/b', 'c', 2);
409 expect(dir.path).toBe('/a/b/c');
410 expect(dir.parent).toBe('/a/b');
411 expect(dir.quotas).toEqual({ max_bytes: 2048, max_files: 20 });
412 expect(dir.snapshots.map((s) => s.name)).toEqual(['someSnapshot1', 's1']);
415 it('tests lsdir mock', () => {
416 let dirs: CephfsDir[] = [];
417 mockLib.lsDir(2, '/a').subscribe((x) => (dirs = x));
418 expect(dirs.map((d) => d.path)).toEqual([
431 describe('test quota update mock', () => {
435 const updateQuota = (quotas: CephfsQuotas) => mockLib.updateQuota(ID, PATH, quotas);
437 const expectMockUpdate = (max_bytes?: number, max_files?: number) =>
438 expect(mockData.updatedQuotas[PATH]).toEqual({
443 const expectLsUpdate = (max_bytes?: number, max_files?: number) => {
445 mockLib.lsDir(ID, '/').subscribe((dirs) => (dir = dirs.find((d) => d.path === PATH)));
446 expect(dir.quotas).toEqual({
452 it('tests to set quotas', () => {
453 expectLsUpdate(1024, 10);
455 updateQuota({ max_bytes: 512 });
456 expectMockUpdate(512);
457 expectLsUpdate(512, 10);
459 updateQuota({ max_files: 100 });
460 expectMockUpdate(512, 100);
461 expectLsUpdate(512, 100);
464 it('tests to unset quotas', () => {
465 updateQuota({ max_files: 0 });
466 expectMockUpdate(undefined, 0);
467 expectLsUpdate(1024, 0);
469 updateQuota({ max_bytes: 0 });
470 expectMockUpdate(0, 0);
471 expectLsUpdate(0, 0);
476 it('calls lsDir only if an id exits', () => {
477 component.ngOnChanges();
478 assert.lsDirCalledTimes(0);
481 assert.lsDirCalledTimes(1);
482 expect(lsDirSpy).toHaveBeenCalledWith(1, '/');
485 assert.lsDirCalledTimes(2);
486 expect(lsDirSpy).toHaveBeenCalledWith(2, '/');
489 describe('listing sub directories', () => {
493 * Tree looks like this:
501 it('expands first level', () => {
502 // Tree will only show '*' if nor 'loadChildren' or 'children' are defined
504 mockData.nodes.map((node) => ({ [node.id]: Boolean(node.loadChildren || node.children) }))
505 ).toEqual([{ '/': true }, { '/a': true }, { '/b': false }, { '/c': true }]);
508 it('resets all dynamic content on id change', () => {
509 mockLib.selectNode('/a');
511 * Tree looks like this:
520 assert.requestedPaths(['/', '/a']);
521 assert.nodeLength(7);
522 assert.dirLength(15);
523 expect(component.selectedDir).toBeDefined();
525 mockLib.changeId(undefined);
527 assert.requestedPaths([]);
528 expect(component.selectedDir).not.toBeDefined();
531 it('should select a node and show the directory contents', () => {
532 mockLib.selectNode('/a');
533 const dir = get.dirs().find((d) => d.path === '/a');
534 expect(component.selectedDir).toEqual(dir);
535 assert.quotaIsNotInherited('files', 10, 0);
536 assert.quotaIsNotInherited('bytes', '1 KiB', 0);
539 it('should extend the list by subdirectories when expanding and omit already called path', () => {
540 mockLib.selectNode('/a');
541 mockLib.selectNode('/a/c');
543 * Tree looks like this:
555 assert.lsDirCalledTimes(3);
556 assert.requestedPaths(['/', '/a', '/a/c']);
557 assert.dirLength(21);
558 assert.nodeLength(10);
561 it('should select parent by path', () => {
562 mockLib.selectNode('/a');
563 mockLib.selectNode('/a/c');
564 mockLib.selectNode('/a/c/a');
565 component.selectOrigin('/a');
566 expect(component.selectedDir.path).toBe('/a');
569 it('should omit call for directories that have no sub directories', () => {
570 mockLib.selectNode('/b');
572 * Tree looks like this:
578 assert.lsDirCalledTimes(1);
579 assert.requestedPaths(['/']);
580 assert.nodeLength(4);
583 describe('used quotas', () => {
584 it('should use no quota if none is set', () => {
585 mockLib.setFourQuotaDirs([[0, 0], [0, 0], [0, 0], [0, 0]]);
586 assert.noQuota('files');
587 assert.noQuota('bytes');
588 assert.dirQuotas(0, 0);
591 it('should use quota from upper parents', () => {
592 mockLib.setFourQuotaDirs([[100, 0], [0, 8], [0, 0], [0, 0]]);
593 assert.quotaIsInherited('files', 100, '/1');
594 assert.quotaIsInherited('bytes', '8 KiB', '/1/2');
595 assert.dirQuotas(0, 0);
598 it('should use quota from the parent with the lowest value (deep inheritance)', () => {
599 mockLib.setFourQuotaDirs([[200, 1], [100, 4], [400, 3], [300, 2]]);
600 assert.quotaIsInherited('files', 100, '/1/2');
601 assert.quotaIsInherited('bytes', '1 KiB', '/1');
602 assert.dirQuotas(2048, 300);
605 it('should use current value', () => {
606 mockLib.setFourQuotaDirs([[200, 2], [300, 4], [400, 3], [100, 1]]);
607 assert.quotaIsNotInherited('files', 100, 200);
608 assert.quotaIsNotInherited('bytes', '1 KiB', 2048);
609 assert.dirQuotas(1024, 100);
614 describe('snapshots', () => {
617 mockLib.selectNode('/a');
620 it('should create a snapshot', () => {
621 mockLib.createSnapshotThroughModal('newSnap');
622 expect(cephfsService.mkSnapshot).toHaveBeenCalledWith(1, '/a', 'newSnap');
623 assert.snapshotsByName(['someSnapshot1', 'newSnap']);
626 it('should delete a snapshot', () => {
627 mockLib.createSnapshotThroughModal('deleteMe');
628 mockLib.deleteSnapshotsThroughModal([component.selectedDir.snapshots[1]]);
629 assert.snapshotsByName(['someSnapshot1']);
632 it('should delete all snapshots', () => {
633 mockLib.createSnapshotThroughModal('deleteAll');
634 mockLib.deleteSnapshotsThroughModal(component.selectedDir.snapshots);
635 assert.snapshotsByName([]);
639 it('should test all snapshot table actions combinations', () => {
640 const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
641 const tableActions = permissionHelper.setPermissionsAndGetActions(
642 component.snapshot.tableActions
645 expect(tableActions).toEqual({
646 'create,update,delete': {
647 actions: ['Create', 'Delete'],
648 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Create' }
652 primary: { multiple: 'Create', executing: 'Create', single: 'Create', no: 'Create' }
655 actions: ['Create', 'Delete'],
656 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Create' }
660 primary: { multiple: 'Create', executing: 'Create', single: 'Create', no: 'Create' }
664 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' }
668 primary: { multiple: '', executing: '', single: '', no: '' }
672 primary: { multiple: 'Delete', executing: 'Delete', single: 'Delete', no: 'Delete' }
676 primary: { multiple: '', executing: '', single: '', no: '' }
681 describe('quotas', () => {
684 minValidator = spyOn(Validators, 'min').and.callThrough();
685 maxValidator = spyOn(Validators, 'max').and.callThrough();
686 minBinaryValidator = spyOn(CdValidators, 'binaryMin').and.callThrough();
687 maxBinaryValidator = spyOn(CdValidators, 'binaryMax').and.callThrough();
690 mockLib.selectNode('/a');
691 mockLib.selectNode('/a/c');
692 mockLib.selectNode('/a/c/b');
693 // Quotas after selection
694 assert.quotaIsInherited('files', 10, '/a');
695 assert.quotaIsInherited('bytes', '1 KiB', '/a');
696 assert.dirQuotas(2048, 20);
699 describe('update modal', () => {
700 describe('max_files', () => {
702 mockLib.updateQuotaThroughModal('max_files', 5);
705 it('should update max_files correctly', () => {
706 expect(cephfsService.updateQuota).toHaveBeenCalledWith(1, '/a/c/b', { max_files: 5 });
707 assert.quotaIsNotInherited('files', 5, 10);
710 it('uses the correct form field', () => {
711 assert.quotaUpdateModalField('number', 'Max files', 'max_files', 20, 10, {
712 min: 'Value has to be at least 0 or more',
713 max: 'Value has to be at most 10 or less'
717 it('shows the right texts', () => {
718 assert.quotaUpdateModalTexts(
719 "Update CephFS files quota for '/a/c/b'",
720 "The inherited files quota 10 from '/a' is the maximum value to be used.",
721 "Updated CephFS files quota for '/a/c/b'"
726 describe('max_bytes', () => {
728 mockLib.updateQuotaThroughModal('max_bytes', 512);
731 it('should update max_files correctly', () => {
732 expect(cephfsService.updateQuota).toHaveBeenCalledWith(1, '/a/c/b', { max_bytes: 512 });
733 assert.quotaIsNotInherited('bytes', '512 B', 1024);
736 it('uses the correct form field', () => {
737 mockLib.updateQuotaThroughModal('max_bytes', 512);
738 assert.quotaUpdateModalField('binary', 'Max size', 'max_bytes', 2048, 1024);
741 it('shows the right texts', () => {
742 assert.quotaUpdateModalTexts(
743 "Update CephFS size quota for '/a/c/b'",
744 "The inherited size quota 1 KiB from '/a' is the maximum value to be used.",
745 "Updated CephFS size quota for '/a/c/b'"
750 describe('action behaviour', () => {
751 it('opens with next maximum as maximum if directory holds the current maximum', () => {
752 mockLib.updateQuotaThroughModal('max_bytes', 512);
753 mockLib.updateQuotaThroughModal('max_bytes', 888);
754 assert.quotaUpdateModalField('binary', 'Max size', 'max_bytes', 512, 1024);
757 it("uses 'Set' action instead of 'Update' if the quota is not set (0)", () => {
758 mockLib.updateQuotaThroughModal('max_bytes', 0);
759 mockLib.updateQuotaThroughModal('max_bytes', 200);
760 assert.quotaUpdateModalTexts(
761 "Set CephFS size quota for '/a/c/b'",
762 "The inherited size quota 1 KiB from '/a' is the maximum value to be used.",
763 "Set CephFS size quota for '/a/c/b'"
769 describe('unset modal', () => {
770 describe('max_files', () => {
772 mockLib.updateQuotaThroughModal('max_files', 5); // Sets usable quota
773 mockLib.unsetQuotaThroughModal('max_files');
776 it('should unset max_files correctly', () => {
777 expect(cephfsService.updateQuota).toHaveBeenCalledWith(1, '/a/c/b', { max_files: 0 });
778 assert.dirQuotas(2048, 0);
781 it('shows the right texts', () => {
782 assert.quotaUnsetModalTexts(
783 "Unset CephFS files quota for '/a/c/b'",
784 "Unset files quota 5 from '/a/c/b' in order to inherit files quota 10 from '/a'.",
785 "Unset CephFS files quota for '/a/c/b'"
790 describe('max_bytes', () => {
792 mockLib.updateQuotaThroughModal('max_bytes', 512); // Sets usable quota
793 mockLib.unsetQuotaThroughModal('max_bytes');
796 it('should unset max_files correctly', () => {
797 expect(cephfsService.updateQuota).toHaveBeenCalledWith(1, '/a/c/b', { max_bytes: 0 });
798 assert.dirQuotas(0, 20);
801 it('shows the right texts', () => {
802 assert.quotaUnsetModalTexts(
803 "Unset CephFS size quota for '/a/c/b'",
804 "Unset size quota 512 B from '/a/c/b' in order to inherit size quota 1 KiB from '/a'.",
805 "Unset CephFS size quota for '/a/c/b'"
810 describe('action behaviour', () => {
811 it('uses different Text if no quota is inherited', () => {
812 mockLib.selectNode('/a');
813 mockLib.unsetQuotaThroughModal('max_bytes');
814 assert.quotaUnsetModalTexts(
815 "Unset CephFS size quota for '/a'",
816 "Unset size quota 1 KiB from '/a' in order to have no quota on the directory.",
817 "Unset CephFS size quota for '/a'"
821 it('uses different Text if quota is already inherited', () => {
822 mockLib.unsetQuotaThroughModal('max_bytes');
823 assert.quotaUnsetModalTexts(
824 "Unset CephFS size quota for '/a/c/b'",
825 "Unset size quota 2 KiB from '/a/c/b' which isn't used because of the inheritance " +
826 "of size quota 1 KiB from '/a'.",
827 "Unset CephFS size quota for '/a/c/b'"
834 describe('table actions', () => {
835 let actions: CdTableAction[];
837 const empty = (): CdTableSelection => {
838 const selection = new CdTableSelection();
842 const select = (value: number): CdTableSelection => {
843 const selection = new CdTableSelection();
844 selection.selected = [{ dirValue: value }];
849 actions = component.quota.tableActions;
852 it("shows 'Set' for empty and not set quotas", () => {
853 const isSetVisible = actions[0].visible;
854 expect(isSetVisible(empty())).toBe(true);
855 expect(isSetVisible(select(0))).toBe(true);
856 expect(isSetVisible(select(1))).toBe(false);
859 it("shows 'Update' for set quotas only", () => {
860 const isUpdateVisible = actions[1].visible;
861 expect(isUpdateVisible(empty())).toBeFalsy();
862 expect(isUpdateVisible(select(0))).toBe(false);
863 expect(isUpdateVisible(select(1))).toBe(true);
866 it("only enables 'Unset' for set quotas only", () => {
867 const isUnsetDisabled = actions[2].disable;
868 expect(isUnsetDisabled(empty())).toBe(true);
869 expect(isUnsetDisabled(select(0))).toBe(true);
870 expect(isUnsetDisabled(select(1))).toBe(false);
873 it('should test all quota table actions permission combinations', () => {
874 const permissionHelper: PermissionHelper = new PermissionHelper(component.permission);
875 const tableActions = permissionHelper.setPermissionsAndGetActions(
876 component.quota.tableActions
879 expect(tableActions).toEqual({
880 'create,update,delete': {
881 actions: ['Set', 'Update', 'Unset'],
882 primary: { multiple: 'Set', executing: 'Set', single: 'Set', no: 'Set' }
885 actions: ['Set', 'Update', 'Unset'],
886 primary: { multiple: 'Set', executing: 'Set', single: 'Set', no: 'Set' }
890 primary: { multiple: '', executing: '', single: '', no: '' }
894 primary: { multiple: '', executing: '', single: '', no: '' }
897 actions: ['Set', 'Update', 'Unset'],
898 primary: { multiple: 'Set', executing: 'Set', single: 'Set', no: 'Set' }
901 actions: ['Set', 'Update', 'Unset'],
902 primary: { multiple: 'Set', executing: 'Set', single: 'Set', no: 'Set' }
906 primary: { multiple: '', executing: '', single: '', no: '' }
910 primary: { multiple: '', executing: '', single: '', no: '' }