tripleo-ui/src/__tests__/actions/NodesActions.tests.js

422 lines
12 KiB
JavaScript

/**
* Copyright 2017 Red Hat Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain
* a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/
import IronicApiService from '../../js/services/IronicApiService';
import IronicInspectorApiService from '../../js/services/IronicInspectorApiService';
import MistralApiService from '../../js/services/MistralApiService';
import { mockStore } from './utils';
import NodesActions from '../../js/actions/NodesActions';
import * as ErrorActions from '../../js/actions/ErrorActions';
import NodesConstants from '../../js/constants/NodesConstants';
import MistralConstants from '../../js/constants/MistralConstants';
const mockGetNodesResponse = [{ uuid: 1 }, { uuid: 2 }];
describe('Nodes Actions', () => {
it('creates action to request nodes', () => {
const expectedAction = {
type: NodesConstants.REQUEST_NODES
};
expect(NodesActions.requestNodes()).toEqual(expectedAction);
});
it('creates action to receive nodes', () => {
const expectedAction = {
type: NodesConstants.RECEIVE_NODES,
payload: mockGetNodesResponse
};
expect(NodesActions.receiveNodes(mockGetNodesResponse)).toEqual(
expectedAction
);
});
it('creates action to notify that nodes operation started', () => {
const expectedAction = {
type: NodesConstants.START_NODES_OPERATION,
payload: mockGetNodesResponse
};
expect(NodesActions.startOperation(mockGetNodesResponse)).toEqual(
expectedAction
);
});
it('creates action to notify that nodes operation finished', () => {
const expectedAction = {
type: NodesConstants.FINISH_NODES_OPERATION,
payload: mockGetNodesResponse
};
expect(NodesActions.finishOperation(mockGetNodesResponse)).toEqual(
expectedAction
);
});
it('should create an action for pending Node update', () => {
const expectedAction = {
type: NodesConstants.UPDATE_NODE_PENDING,
payload: 'someId'
};
expect(NodesActions.updateNodePending('someId')).toEqual(expectedAction);
});
it('should create an action for successful Node update', () => {
const updatedNode = {
uuid: 1
};
const expectedAction = {
type: NodesConstants.UPDATE_NODE_SUCCESS,
payload: updatedNode
};
expect(NodesActions.updateNodeSuccess(updatedNode)).toEqual(expectedAction);
});
it('should create an action for failed Node update', () => {
const expectedAction = {
type: NodesConstants.UPDATE_NODE_FAILED,
payload: 'someNodeId'
};
expect(NodesActions.updateNodeFailed('someNodeId')).toEqual(expectedAction);
});
it('should create an action to add Nodes', () => {
const registeredNodes = {
uuid1: {
uuid: 'uuid1',
name: 'node1'
},
uuid2: {
uuid: 'uuid2',
name: 'node2'
}
};
const expectedAction = {
type: NodesConstants.ADD_NODES,
payload: registeredNodes
};
expect(NodesActions.addNodes(registeredNodes)).toEqual(expectedAction);
});
});
describe('Fetching Nodes Actions', () => {
beforeEach(() => {
IronicApiService.getNodes = jest
.fn()
.mockReturnValue(() => Promise.resolve({ nodes: [{ uuid: '123' }] }));
IronicApiService.getPorts = jest.fn().mockReturnValue(() =>
Promise.resolve({
ports: [{ uuid: 'port1', address: 'mac', node_uuid: '123' }]
})
);
IronicInspectorApiService.getIntrospectionStatuses = jest
.fn()
.mockReturnValue(() =>
Promise.resolve({
introspection: [{ uuid: 'node1', state: 'finished' }]
})
);
});
it('fetches the data and dispatches actions', () => {
const store = mockStore({});
const normalizedData = {
nodes: {
123: {
uuid: '123'
}
},
ports: {
port1: {
uuid: 'port1',
address: 'mac',
node_uuid: '123'
}
},
introspectionStatuses: {
node1: {
uuid: 'node1',
state: 'finished'
}
}
};
return store.dispatch(NodesActions.fetchNodes()).then(() => {
expect(IronicApiService.getNodes).toHaveBeenCalled();
expect(IronicApiService.getPorts).toHaveBeenCalled();
expect(
IronicInspectorApiService.getIntrospectionStatuses
).toHaveBeenCalled();
expect(store.getActions()).toEqual([
NodesActions.requestNodes(),
NodesActions.receiveNodes(normalizedData)
]);
});
});
});
describe('Fetching Introspection data success', () => {
const response = { interfaces: { eth0: { mac: '00:00:00:00:00:11' } } };
const nodeId = '598612eb-f21b-435e-a868-7bb74e576cc2';
const store = mockStore({});
beforeEach(() => {
IronicInspectorApiService.getIntrospectionData = jest
.fn()
.mockReturnValue(() => Promise.resolve(response));
});
it('dispatches fetchNodeIntrospectionDataSuccess', () =>
store.dispatch(NodesActions.fetchNodeIntrospectionData(nodeId)).then(() => {
expect(
IronicInspectorApiService.getIntrospectionData
).toHaveBeenCalledWith(nodeId);
expect(store.getActions()).toEqual([
NodesActions.fetchNodeIntrospectionDataSuccess(nodeId, response)
]);
}));
});
describe('Fetching Introspection data error', () => {
const store = mockStore({});
const nodeId = '598612eb-f21b-435e-a868-7bb74e576cc2';
const error = {
response: {
data: { error: { message: 'some error message' } },
status: 404
}
};
beforeEach(() => {
IronicInspectorApiService.getIntrospectionData = jest
.fn()
.mockReturnValue(() => Promise.reject(error));
ErrorActions.handleErrors = jest.fn().mockReturnValue(() => {});
});
it('dispatches fetchNodeIntrospectionDataFailed', () =>
store.dispatch(NodesActions.fetchNodeIntrospectionData(nodeId)).then(() => {
expect(
IronicInspectorApiService.getIntrospectionData
).toHaveBeenCalledWith(nodeId);
expect(store.getActions()).toEqual([
NodesActions.fetchNodeIntrospectionDataFailed(nodeId)
]);
}));
});
describe('Asynchronous Introspect Nodes Action', () => {
const store = mockStore({});
const nodeIds = ['598612eb-f21b-435e-a868-7bb74e576cc2'];
beforeEach(() => {
MistralApiService.runWorkflow = jest
.fn()
.mockReturnValue(() => Promise.resolve({ state: 'RUNNING' }));
NodesActions.pollNodeslistDuringProgress = jest
.fn()
.mockReturnValue(() => {});
});
it('dispatches startOperation', () =>
store.dispatch(NodesActions.startNodesIntrospection(nodeIds)).then(() => {
expect(MistralApiService.runWorkflow).toHaveBeenCalledWith(
MistralConstants.BAREMETAL_INTROSPECT,
{
node_uuids: nodeIds
}
);
expect(NodesActions.pollNodeslistDuringProgress).toHaveBeenCalled();
expect(store.getActions()).toEqual([
NodesActions.startOperation(nodeIds)
]);
}));
});
describe('nodesIntrospectionFinished', () => {
beforeEach(() => {
NodesActions.fetchNodes = jest.fn().mockReturnValue(() => {});
});
it('handles successful nodes introspection', () => {
const store = mockStore({});
const messagePayload = {
execution: {
input: {
node_uuids: ['598612eb-f21b-435e-a868-7bb74e576cc2']
}
},
status: 'SUCCESS',
message: 'Nodes Introspection was successful',
introspected_nodes: {
'598612eb-f21b-435e-a868-7bb74e576cc2': { finished: true, error: null }
},
execution_id: '622eb415-a522-4016-b5f6-6e9e0b3f687a',
queue_name: 'tripleo',
ttl: 3600
};
store.dispatch(NodesActions.nodesIntrospectionFinished(messagePayload));
expect(NodesActions.fetchNodes).toHaveBeenCalled();
const actions = store.getActions();
expect(Object.keys(actions).map(key => actions[key].type)).toEqual([
'FINISH_NODES_OPERATION',
'NOTIFY'
]);
});
it('handles failed nodes introspection', () => {
const store = mockStore({});
const messagePayload = {
execution: {
input: {
node_uuids: ['598612eb-f21b-435e-a868-7bb74e576cc2']
}
},
status: 'FAILED',
message: [
{ message: 'Nodes Introspection failed' },
{ message: 'Some error occurred during introspection' }
],
introspected_nodes: {},
execution_id: '622eb415-a522-4016-b5f6-6e9e0b3f687a',
queue_name: 'tripleo',
ttl: 3600
};
store.dispatch(NodesActions.nodesIntrospectionFinished(messagePayload));
expect(NodesActions.fetchNodes).toHaveBeenCalled();
const actions = store.getActions();
expect(Object.keys(actions).map(key => actions[key].type)).toEqual([
'FINISH_NODES_OPERATION',
'NOTIFY'
]);
});
});
describe('startProvideNodes Action', () => {
const store = mockStore({});
const nodeIds = ['598612eb-f21b-435e-a868-7bb74e576cc2'];
beforeEach(() => {
MistralApiService.runWorkflow = jest
.fn()
.mockReturnValue(() => Promise.resolve({ state: 'RUNNING' }));
NodesActions.pollNodeslistDuringProgress = jest
.fn()
.mockReturnValue(() => {});
});
it('dispatches actions', () =>
store.dispatch(NodesActions.startProvideNodes(nodeIds)).then(() => {
expect(MistralApiService.runWorkflow).toHaveBeenCalledWith(
MistralConstants.BAREMETAL_PROVIDE,
{
node_uuids: nodeIds
}
);
expect(NodesActions.pollNodeslistDuringProgress).toHaveBeenCalled();
expect(store.getActions()).toEqual([
NodesActions.startOperation(nodeIds)
]);
}));
});
describe('provideNodesFinished', () => {
beforeEach(() => {
NodesActions.fetchNodes = jest.fn().mockReturnValue(() => {});
});
it('handles success', () => {
const store = mockStore({});
const messagePayload = {
status: 'SUCCESS',
message: 'Nodes were successfully made available',
execution: { input: { node_uuids: [] } }
};
store.dispatch(NodesActions.nodesIntrospectionFinished(messagePayload));
expect(NodesActions.fetchNodes).toHaveBeenCalled();
const actions = store.getActions();
expect(Object.keys(actions).map(key => actions[key].type)).toEqual([
'FINISH_NODES_OPERATION',
'NOTIFY'
]);
});
it('handles failure', () => {
const store = mockStore({});
const messagePayload = {
status: 'FAILED',
message: [{ result: 'Failed to set nodes to available.' }],
execution: { input: { node_uuids: [] } }
};
store.dispatch(NodesActions.nodesIntrospectionFinished(messagePayload));
expect(NodesActions.fetchNodes).toHaveBeenCalled();
const actions = store.getActions();
expect(Object.keys(actions).map(key => actions[key].type)).toEqual([
'FINISH_NODES_OPERATION',
'NOTIFY'
]);
});
});
describe('Update Node thunk action', () => {
const store = mockStore({});
const nodePatch = {
uuid: 'someId',
patches: [
{
op: 'replace',
path: '/properties/capabilities',
value: 'updated value for path'
}
]
};
beforeEach(() => {
IronicApiService.patchNode = jest
.fn()
.mockReturnValue(() => Promise.resolve({ uuid: 'someId' }));
});
it('dispatches required actions', () =>
store.dispatch(NodesActions.updateNode(nodePatch)).then(() => {
expect(store.getActions()).toEqual([
NodesActions.updateNodePending('someId'),
NodesActions.updateNodeSuccess({ uuid: 'someId' })
]);
}));
});
describe('Delete Nodes thunk action', () => {
const store = mockStore({});
const nodeIds = ['598612eb-f21b-435e-a868-7bb74e576cc2'];
beforeEach(() => {
IronicApiService.deleteNode = jest
.fn()
.mockReturnValue(() => Promise.resolve());
});
it('successfully deletes a set of nodes', () =>
store.dispatch(NodesActions.deleteNodes(nodeIds)).then(() => {
expect(store.getActions()).toEqual([
NodesActions.startOperation(nodeIds),
NodesActions.deleteNodeSuccess(nodeIds[0])
]);
}));
});