import { By } from '@angular/platform-browser';
import { RouterTestingModule } from '@angular/router/testing';
-import _ = require('lodash');
+import * as _ from 'lodash';
import { BsModalService } from 'ngx-bootstrap/modal';
import { TabsModule } from 'ngx-bootstrap/tabs';
+import { ToastrModule } from 'ngx-toastr';
import { EMPTY, of } from 'rxjs';
import {
i18nProviders,
PermissionHelper
} from '../../../../../testing/unit-test-helper';
+import { CoreModule } from '../../../../core/core.module';
+import { OrchestratorService } from '../../../../shared/api/orchestrator.service';
import { OsdService } from '../../../../shared/api/osd.service';
import { ConfirmationModalComponent } from '../../../../shared/components/confirmation-modal/confirmation-modal.component';
import { CriticalConfirmationModalComponent } from '../../../../shared/components/critical-confirmation-modal/critical-confirmation-modal.component';
+import { FormModalComponent } from '../../../../shared/components/form-modal/form-modal.component';
import { TableActionsComponent } from '../../../../shared/datatable/table-actions/table-actions.component';
import { CdTableAction } from '../../../../shared/models/cd-table-action';
import { CdTableSelection } from '../../../../shared/models/cd-table-selection';
import { Permissions } from '../../../../shared/models/permissions';
import { AuthStorageService } from '../../../../shared/services/auth-storage.service';
-import { SharedModule } from '../../../../shared/shared.module';
+import { CephModule } from '../../../ceph.module';
import { PerformanceCounterModule } from '../../../performance-counter/performance-counter.module';
-import { OsdDetailsComponent } from '../osd-details/osd-details.component';
-import { OsdPerformanceHistogramComponent } from '../osd-performance-histogram/osd-performance-histogram.component';
import { OsdReweightModalComponent } from '../osd-reweight-modal/osd-reweight-modal.component';
import { OsdListComponent } from './osd-list.component';
const fakeAuthStorageService = {
getPermissions: () => {
- return new Permissions({ osd: ['read', 'update', 'create', 'delete'] });
+ return new Permissions({
+ 'config-opt': ['read', 'update', 'create', 'delete'],
+ osd: ['read', 'update', 'create', 'delete']
+ });
}
};
- const getTableAction = (name) => component.tableActions.find((action) => action.name === name);
+ const getTableAction = (name: string) =>
+ component.tableActions.find((action) => action.name === name);
const setFakeSelection = () => {
// Default data and selection
- const selection = [{ id: 1 }];
- const data = [{ id: 1 }];
+ const selection = [{ id: 1, tree: { device_class: 'ssd' } }];
+ const data = [{ id: 1, tree: { device_class: 'ssd' } }];
// Table data and selection
component.selection = new CdTableSelection();
component.selection.selected = selection;
- component.selection.update();
component.osds = data;
component.permissions = fakeAuthStorageService.getPermissions();
};
- const openActionModal = (actionName) => {
+ const openActionModal = (actionName: string) => {
setFakeSelection();
getTableAction(actionName).click();
};
*/
const mockSafeToDestroy = () => {
spyOn(TestBed.get(OsdService), 'safeToDestroy').and.callFake(() =>
- of({ 'safe-to-destroy': true })
+ of({ is_safe_to_destroy: true })
+ );
+ };
+
+ const mockSafeToDelete = () => {
+ spyOn(TestBed.get(OsdService), 'safeToDelete').and.callFake(() =>
+ of({ is_safe_to_delete: true })
);
};
+ const mockOrchestratorStatus = () => {
+ spyOn(TestBed.get(OrchestratorService), 'status').and.callFake(() => of({ available: true }));
+ };
+
configureTestBed({
imports: [
HttpClientTestingModule,
PerformanceCounterModule,
TabsModule.forRoot(),
- SharedModule,
+ ToastrModule.forRoot(),
+ CephModule,
ReactiveFormsModule,
+ RouterTestingModule,
+ CoreModule,
RouterTestingModule
],
- declarations: [OsdListComponent, OsdDetailsComponent, OsdPerformanceHistogramComponent],
+ declarations: [],
providers: [
{ provide: AuthStorageService, useValue: fakeAuthStorageService },
TableActionsComponent,
beforeEach(() => {
fixture = TestBed.createComponent(OsdListComponent);
- fixture.detectChanges();
component = fixture.componentInstance;
osdService = TestBed.get(OsdService);
modalServiceShowSpy = spyOn(TestBed.get(BsModalService), 'show').and.stub();
});
it('should have columns that are sortable', () => {
- expect(component.columns.every((column) => Boolean(column.prop))).toBeTruthy();
+ fixture.detectChanges();
+ expect(
+ component.columns
+ .filter((column) => !column.checkboxable)
+ .every((column) => Boolean(column.prop))
+ ).toBeTruthy();
});
describe('getOsdList', () => {
- let osds;
-
- const createOsd = (n: number) => ({
- in: 'in',
- up: 'up',
- stats_history: {
- op_out_bytes: [[n, n], [n * 2, n * 2]],
- op_in_bytes: [[n * 3, n * 3], [n * 4, n * 4]]
- },
- stats: {
- stat_bytes_used: n * n,
- stat_bytes: n * n * n
- }
- });
+ let osds: any[];
+
+ const createOsd = (n: number) =>
+ <Record<string, any>>{
+ in: 'in',
+ up: 'up',
+ tree: {
+ device_class: 'ssd'
+ },
+ stats_history: {
+ op_out_bytes: [[n, n], [n * 2, n * 2]],
+ op_in_bytes: [[n * 3, n * 3], [n * 4, n * 4]]
+ },
+ stats: {
+ stat_bytes_used: n * n,
+ stat_bytes: n * n * n
+ },
+ state: []
+ };
const expectAttributeOnEveryOsd = (attr: string) =>
expect(component.osds.every((osd) => Boolean(_.get(osd, attr)))).toBeTruthy();
expect(component.osds[0].collectedStates).toEqual(['in', 'up']);
});
+ it('should have "destroyed" state in "collectedStates"', () => {
+ osds[0].state.push('destroyed');
+ osds[0].up = 0;
+ component.getOsdList();
+
+ expectAttributeOnEveryOsd('collectedStates');
+ expect(component.osds[0].collectedStates).toEqual(['in', 'destroyed']);
+ });
+
it('should have custom attribute "stats_history.out_bytes"', () => {
expectAttributeOnEveryOsd('stats_history.out_bytes');
expect(component.osds[0].stats_history.out_bytes).toEqual([1, 2]);
});
});
- describe('show table actions as defined', () => {
- let tableActions: TableActionsComponent;
- let scenario: { fn; empty; single };
- let permissionHelper: PermissionHelper;
-
- const getTableActionComponent = () => {
+ describe('show osd actions as defined', () => {
+ const getOsdActions = () => {
fixture.detectChanges();
- return fixture.debugElement.query(By.directive(TableActionsComponent)).componentInstance;
+ return fixture.debugElement.query(By.css('#cluster-wide-actions')).componentInstance
+ .dropDownActions;
};
- beforeEach(() => {
- permissionHelper = new PermissionHelper(component.permissions.osd, () =>
- getTableActionComponent()
+ it('shows osd actions after osd-actions', () => {
+ fixture.detectChanges();
+ expect(fixture.debugElement.query(By.css('#cluster-wide-actions'))).toBe(
+ fixture.debugElement.queryAll(By.directive(TableActionsComponent))[1]
);
- scenario = {
- fn: () => tableActions.getCurrentButton().name,
- single: 'Scrub',
- empty: 'Scrub'
- };
- tableActions = permissionHelper.setPermissionsAndGetActions(1, 1, 1);
});
- it('shows action button', () => permissionHelper.testScenarios(scenario));
+ it('shows both osd actions', () => {
+ const osdActions = getOsdActions();
+ expect(osdActions).toEqual(component.clusterWideActions);
+ expect(osdActions.length).toBe(3);
+ });
- it('shows all actions', () => {
- expect(tableActions.tableActions.length).toBe(9);
- expect(tableActions.tableActions).toEqual(component.tableActions);
+ it('shows only "Flags" action', () => {
+ component.permissions.configOpt.read = false;
+ const osdActions = getOsdActions();
+ expect(osdActions[0].name).toBe('Flags');
+ expect(osdActions.length).toBe(1);
+ });
+
+ it('shows only "Recovery Priority" action', () => {
+ component.permissions.osd.read = false;
+ const osdActions = getOsdActions();
+ expect(osdActions[0].name).toBe('Recovery Priority');
+ expect(osdActions[1].name).toBe('PG scrub');
+ expect(osdActions.length).toBe(2);
+ });
+
+ it('shows no osd actions', () => {
+ component.permissions.configOpt.read = false;
+ component.permissions.osd.read = false;
+ const osdActions = getOsdActions();
+ expect(osdActions).toEqual([]);
+ });
+ });
+
+ it('should test all TableActions combinations', () => {
+ const permissionHelper: PermissionHelper = new PermissionHelper(component.permissions.osd);
+ const tableActions: TableActionsComponent = permissionHelper.setPermissionsAndGetActions(
+ component.tableActions
+ );
+
+ expect(tableActions).toEqual({
+ 'create,update,delete': {
+ actions: [
+ 'Create',
+ 'Edit',
+ 'Scrub',
+ 'Deep Scrub',
+ 'Reweight',
+ 'Mark Out',
+ 'Mark In',
+ 'Mark Down',
+ 'Mark Lost',
+ 'Purge',
+ 'Destroy',
+ 'Delete'
+ ],
+ primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Create' }
+ },
+ 'create,update': {
+ actions: [
+ 'Create',
+ 'Edit',
+ 'Scrub',
+ 'Deep Scrub',
+ 'Reweight',
+ 'Mark Out',
+ 'Mark In',
+ 'Mark Down'
+ ],
+ primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Create' }
+ },
+ 'create,delete': {
+ actions: ['Create', 'Mark Lost', 'Purge', 'Destroy', 'Delete'],
+ primary: {
+ multiple: 'Create',
+ executing: 'Mark Lost',
+ single: 'Mark Lost',
+ no: 'Create'
+ }
+ },
+ create: {
+ actions: ['Create'],
+ primary: { multiple: 'Create', executing: 'Create', single: 'Create', no: 'Create' }
+ },
+ 'update,delete': {
+ actions: [
+ 'Edit',
+ 'Scrub',
+ 'Deep Scrub',
+ 'Reweight',
+ 'Mark Out',
+ 'Mark In',
+ 'Mark Down',
+ 'Mark Lost',
+ 'Purge',
+ 'Destroy',
+ 'Delete'
+ ],
+ primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Edit' }
+ },
+ update: {
+ actions: ['Edit', 'Scrub', 'Deep Scrub', 'Reweight', 'Mark Out', 'Mark In', 'Mark Down'],
+ primary: { multiple: 'Scrub', executing: 'Edit', single: 'Edit', no: 'Edit' }
+ },
+ delete: {
+ actions: ['Mark Lost', 'Purge', 'Destroy', 'Delete'],
+ primary: {
+ multiple: 'Mark Lost',
+ executing: 'Mark Lost',
+ single: 'Mark Lost',
+ no: 'Mark Lost'
+ }
+ },
+ 'no-permissions': {
+ actions: [],
+ primary: { multiple: '', executing: '', single: '', no: '' }
+ }
});
});
describe('test table actions in submenu', () => {
+ beforeEach(() => {
+ fixture.detectChanges();
+ });
+
beforeEach(fakeAsync(() => {
// The menu needs a click to render the dropdown!
const dropDownToggle = fixture.debugElement.query(By.css('.dropdown-toggle'));
fixture.detectChanges();
}));
- it('has all menu entries disabled', () => {
+ it('has all menu entries disabled except create', () => {
const tableActionElement = fixture.debugElement.query(By.directive(TableActionsComponent));
const toClassName = TestBed.get(TableActionsComponent).toClassName;
const getActionClasses = (action: CdTableAction) =>
- tableActionElement.query(By.css('.' + toClassName(action.name))).classes;
+ tableActionElement.query(By.css(`.${toClassName(action.name)} .dropdown-item`)).classes;
component.tableActions.forEach((action) => {
+ if (action.name === 'Create') {
+ return;
+ }
expect(getActionClasses(action).disabled).toBe(true);
});
});
*
* @param modalClass - The expected class of the modal
*/
- const expectOpensModal = (actionName: string, modalClass): void => {
+ const expectOpensModal = (actionName: string, modalClass: any): void => {
openActionModal(actionName);
// @TODO: check why tsc is complaining when passing 'expectationFailOutput' param.
expectOpensModal('Reweight', OsdReweightModalComponent);
});
+ it('opens the form modal', () => {
+ expectOpensModal('Edit', FormModalComponent);
+ });
+
it('opens all confirmation modals', () => {
const modalClass = ConfirmationModalComponent;
expectOpensModal('Mark Out', modalClass);
expectOpensModal('Mark Lost', modalClass);
expectOpensModal('Purge', modalClass);
expectOpensModal('Destroy', modalClass);
+ mockOrchestratorStatus();
+ mockSafeToDelete();
+ expectOpensModal('Delete', modalClass);
});
});
describe('tests if the correct methods are called on confirmation', () => {
const expectOsdServiceMethodCalled = (
actionName: string,
- osdServiceMethodName: 'markOut' | 'markIn' | 'markDown' | 'markLost' | 'purge' | 'destroy'
+ osdServiceMethodName:
+ | 'markOut'
+ | 'markIn'
+ | 'markDown'
+ | 'markLost'
+ | 'purge'
+ | 'destroy'
+ | 'delete'
): void => {
const osdServiceSpy = spyOn(osdService, osdServiceMethodName).and.callFake(() => EMPTY);
openActionModal(actionName);
expectOsdServiceMethodCalled('Mark Lost', 'markLost');
expectOsdServiceMethodCalled('Purge', 'purge');
expectOsdServiceMethodCalled('Destroy', 'destroy');
+ mockOrchestratorStatus();
+ mockSafeToDelete();
+ expectOsdServiceMethodCalled('Delete', 'delete');
});
});
});