Skip to content

Commit

Permalink
taskflows tests
Browse files Browse the repository at this point in the history
✓ simple actions
✓ async actions
  • Loading branch information
TristanWright committed Jun 14, 2016
1 parent 514c61c commit 1fe0047
Show file tree
Hide file tree
Showing 6 changed files with 558 additions and 19 deletions.
18 changes: 7 additions & 11 deletions src/redux/actions/taskflows.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,10 @@ export function updateTaskflowJobLog(taskflowId, jobId) {
// ----------------------------------------------------------------------------
// STATUSES
export function updateTaskflowJobStatus(taskflowId, jobId, status) {
if (status) {
return { type: UPDATE_TASKFLOW_JOB_STATUS, taskflowId, jobId, status };
}
return dispatch => {
if (status) {
return { type: UPDATE_TASKFLOW_JOB_STATUS, taskflowId, jobId, status };
}
const action = netActions.addNetworkCall(`taskflow_job_status_${jobId}`, 'Check job status');

client.getJobStatus(jobId)
Expand Down Expand Up @@ -117,13 +117,9 @@ export function startTaskflow(id, payload, simulationStep, location) {
dispatch(netActions.successNetworkCall(action.id, resp));

if (simulationStep) {
const data = Object.assign(
{},
simulationStep.data,
{ metadata: Object.assign(
{},
simulationStep.data.metadata,
{ taskflowId: id }) });
const data = Object.assign({}, simulationStep.data,
{ metadata: Object.assign({}, simulationStep.data.metadata, { taskflowId: id }),
});
dispatch(projActions.updateSimulationStep(simulationStep.id, simulationStep.step, data, location));
}
},
Expand Down Expand Up @@ -195,7 +191,7 @@ export function fetchTaskflow(id) {
});
}
if (taskflow.meta.cluster) {
clusterActions.fetchClusters();
dispatch(clusterActions.fetchClusters());
}
}
},
Expand Down
10 changes: 5 additions & 5 deletions src/redux/reducers/taskflows.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
import * as Actions from '../actions/taskflows';

const initialState = {
export const initialState = {
mapById: {},
taskflowMapByTaskId: {},
taskflowMapByJobId: {},
updateLogs: [],
};

const initialTaskflow = {
export const taskflowTemplate = {
taskMapById: {},
log: [],
actions: [],
Expand All @@ -28,14 +28,14 @@ export default function taskflowsReducer(state = initialState, action) {
}
const taskflow = Object.assign(
{},
initialTaskflow,
taskflowTemplate,
state.mapById[action.taskflow._id],
{
flow: action.taskflow,
jobMapById,
primaryJob: action.primaryJob,

});
}
);
const mapById = Object.assign(
{},
state.mapById,
Expand Down
2 changes: 1 addition & 1 deletion test/karma.redux.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ module.exports = function(config) {
reporters: ['spec'],
plugins: [
'karma-jasmine',
'karma-phantomjs-launcher',
'karma-spec-reporter',
'karma-webpack',
'karma-phantomjs-launcher'
],
preprocessors: {
'tests.webpack.js': ['webpack'],
Expand Down
244 changes: 244 additions & 0 deletions test/redux/taskflows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,244 @@
import * as Actions from '../../src/redux/actions/taskflows';
import taskflowsReducer, { initialState } from '../../src/redux/reducers/taskflows';
import client from '../../src/network';

import taskflowState from '../sampleData/basicTaskflowState';

import expect from 'expect';
import thunk from 'redux-thunk';
import complete from '../helpers/complete';
import { registerMiddlewares } from 'redux-actions-assertions';
import { registerAssertions } from 'redux-actions-assertions/expect';
import deepClone from 'mout/src/lang/deepClone';
/* global describe it afterEach */

registerMiddlewares([thunk]);
registerAssertions();

function setSpy(target, method, data) {
expect.spyOn(target, method)
.andReturn(Promise.resolve({ data }));
}

Object.freeze(initialState);

describe('taskflow actions', () => {
const taskflowId = '574c9d900640fd6e133b4b57';
const taskflow = deepClone(taskflowState.mapById[taskflowId]);
const task = Object.assign({}, taskflow.taskMapById['574c9f350640fd6e13b11e39']);
describe('simple actions', () => {
it('should clear update logs', (done) => {
const expectedAction = { type: Actions.CLEAR_UPDATE_LOG };
expect(Actions.clearUpdateLog())
.toDispatchActions(expectedAction, complete(done));

const givenState = deepClone(initialState);
givenState.updateLogs = ['a1', 'b2'];
expect(taskflowsReducer(givenState, expectedAction))
.toEqual(initialState);
});

it('should update a task\'s status for in a taskflow', (done) => {
// console.log(task, taskflow.taskMapById['574c9f350640fd6e13b11e39']);
const newStatus = 'running';
const expectedAction = {
type: Actions.UPDATE_TASKFLOW_TASK_STATUS,
taskflowId,
status: newStatus,
taskId: task._id,
};
expect(Actions.updateTaskflowTaskStatus(taskflowId, task._id, newStatus))
.toDispatchActions(expectedAction, complete(done));

const expectedTask = Object.assign({}, task);
expectedTask.status = newStatus;
expect(taskflowsReducer(taskflowState, expectedAction).mapById[taskflowId].taskMapById[task._id])
.toEqual(expectedTask);
});

it('should add a taskflow', (done) => {
const expectedAction = { type: Actions.ADD_TASKFLOW, taskflow: taskflow.flow, primaryJob: 'pyfr_run' };
expect(Actions.addTaskflow(taskflow.flow, 'pyfr_run'))
.toDispatchActions(expectedAction, complete(done));

const newState = taskflowsReducer(deepClone(initialState), expectedAction);
const expectedTaskflow = deepClone(taskflowState);
// we don't have these properties from a taskflow that's just been added
expectedTaskflow.mapById[taskflowId].taskMapById = {};
expectedTaskflow.mapById[taskflowId].log = [];
expectedTaskflow.taskflowMapByTaskId = {};
expectedTaskflow.taskflowMapByJobId = {};
delete expectedTaskflow.mapById[taskflowId].allComplete;
delete expectedTaskflow.mapById[taskflowId].stepName;
delete expectedTaskflow.mapById[taskflowId].simulation;
expect(newState)
.toEqual(expectedTaskflow);
});

it('should attach a simulation to a taskflow', (done) => {
const expectedAction = { type: Actions.BIND_SIMULATION_TO_TASKFLOW,
taskflowId, simulationId: 'a1', stepName: 'visuzlization' };
expect(Actions.attachSimulationToTaskflow('a1', taskflowId, 'visuzlization'))
.toDispatchActions(expectedAction, complete(done));

const newState = deepClone(taskflowState);
newState.mapById[taskflowId].simulation = expectedAction.simulationId;
newState.mapById[taskflowId].stepName = expectedAction.stepName;
expect(taskflowsReducer(taskflowState, expectedAction))
.toEqual(newState);
});

it('should update a taskflow\'s status', (done) => {
const newStatus = 'running';
const expectedAction = { type: Actions.UPDATE_TASKFLOW_STATUS, id: taskflowId, status: newStatus };
expect(Actions.updateTaskflowStatus(taskflowId, newStatus))
.toDispatchActions(expectedAction, complete(done));

expect(taskflowsReducer(taskflowState, expectedAction).mapById[taskflowId].flow.status)
.toEqual(newStatus);
});

it('should update taskflow properties', (done) => {
const newMeta = {
actions: ['stop', 'terminate'],
allComplete: false,
outputDirectory: '/my/dir/wow',
primaryJob: 'some_new_primary_job',
};
const expectedAction = { type: Actions.UPDATE_TASKFLOW_METADATA, id: taskflowId, metadata: newMeta };
expect(Actions.updateTaskflowMetadata(taskflowId, newMeta))
.toDispatchActions(expectedAction, complete(done));

const newState = deepClone(taskflowState);
newState.mapById[taskflowId].allComplete = newMeta.allComplete;
newState.mapById[taskflowId].actions = newMeta.actions;
newState.mapById[taskflowId].outputDirectory = newMeta.outputDirectory;
newState.mapById[taskflowId].primaryJob = newMeta.primaryJob;

expect(taskflowsReducer(taskflowState, expectedAction))
.toEqual(newState);
});
});

// ----------------------------------------------------------------------------
// AYSYNCHRONUS ACTIONS
// ----------------------------------------------------------------------------

describe('async actions', () => {
afterEach(() => {
expect.restoreSpies();
});

it('should update taskflow log', (done) => {
const log = [{ entry: 'created...' }, { entry: 'running...' }];
const expectedAction = { type: Actions.UPDATE_TASKFLOW_LOG, taskflowId, log };
setSpy(client, 'getTaskflowLog', { log });
expect(Actions.updateTaskflowLog(taskflowId))
.toDispatchActions(expectedAction, complete(done));
});

it('should update taskflow job log', (done) => {
const log = [{ entry: 'created...' }, { entry: 'running...' }];
const expectedAction = { type: Actions.UPDATE_TASKFLOW_JOB_LOG, taskflowId,
jobId: 'a1', log };
setSpy(client, 'getJobLog', { log });
expect(Actions.updateTaskflowJobLog(taskflowId, 'a1'))
.toDispatchActions(expectedAction, complete(done));
});

it('should update taskflow job status', (done) => {
const newStatus = 'running';
const expectedAction = { type: Actions.UPDATE_TASKFLOW_JOB_STATUS, taskflowId,
jobId: 'a1', status: newStatus };
expect(Actions.updateTaskflowJobStatus(taskflowId, 'a1', newStatus))
.toDispatchActions(expectedAction, complete(done));

// can be called without status parameter, calls async if it is
setSpy(client, 'getJobStatus', { status: newStatus });
expect(Actions.updateTaskflowJobStatus(taskflowId, 'a1'))
.toDispatchActions(expectedAction, complete(done));

expect(client.getJobStatus)
.toHaveBeenCalled();
});

it('should start taskflow', (done) => {
setSpy(client, 'startTaskflow', '');
// calls projects @updateSimulationStep which we're testing elsewhere
// let's just make sure it's calling startTaskflow
expect(Actions.startTaskflow(taskflowId, {}, 'Visuzlization'))
.toDispatchActions([], complete(done));

expect(client.startTaskflow)
.toHaveBeenCalled();
});

it('shuold create a taskflow', (done) => {
const expectedActions = [
{ type: Actions.ADD_TASKFLOW, primaryJob: 'pyfr' },
{ type: Actions.BIND_SIMULATION_TO_TASKFLOW, taskflowId, simulationId: 'mySimStep', stepName: 'Visuzlization' },
// also starts taskflow
];
setSpy(client, 'createTaskflow', taskflow.flow);
expect(Actions.createTaskflow('myFlow', 'pyfr', {}, { id: 'mySimStep', step: 'Visuzlization' }))
.toDispatchActions(expectedActions, complete(done));
});

it('should fetch taskflow tasks', (done) => {
const tasks = [{ name: 'task1' }, { name: 'task2' }];
const expectedAction = { type: Actions.UPDATE_TASKFLOW_TASKS, taskflowId, tasks };
setSpy(client, 'getTaskflowTasks', tasks);
expect(Actions.fetchTaskflowTasks(taskflowId))
.toDispatchActions(expectedAction, complete(done));
});

// big test, this dispatches a lot of actions
it('should fetch a taskflow', (done) => {
const clusters = [{ _id: 'a1' }, { _id: 'b2' }];
const log = [{ entry: 'created...' }, { entry: 'running...' }];
const flow = deepClone(taskflow.flow);
flow.meta.jobs = [{ _id: 'job1', status: 'running' }];
const expectedActions = [
{ type: Actions.ADD_TASKFLOW, taskflow: flow },
{ type: Actions.UPDATE_TASKFLOW_JOB_STATUS, taskflowId, jobId: 'job1', status: 'running' },
{ type: Actions.UPDATE_TASKFLOW_JOB_LOG, taskflowId, jobId: 'job1', log },
{ type: 'UPDATE_CLUSTERS', clusters },
];
setSpy(client, 'getTaskflow', flow);
setSpy(client, 'getJobLog', { log });
setSpy(client, 'getJobStatus', { status: 'running' });
setSpy(client, 'listClusters', clusters);
expect(Actions.fetchTaskflow(taskflowId))
.toDispatchActions(expectedActions, complete(done));
});

it('should update a taskflow from a simulation', (done) => {
const simulation = {
_id: 'mySimulationId',
steps: {
Visuzlization: {
metadata: { taskflowId },
},
},
};
const flow = deepClone(taskflow.flow);
const expectedActions = [
{ type: Actions.ADD_TASKFLOW, taskflow: flow },
{ type: Actions.BIND_SIMULATION_TO_TASKFLOW, taskflowId, simulationId: 'mySimulationId', stepName: 'Visuzlization' },
];
setSpy(client, 'getTaskflow', flow);
expect(Actions.updateTaskflowFromSimulation(simulation))
.toDispatchActions(expectedActions, complete(done));
});

it('should delete a taskflow', (done) => {
const expectedAction = { type: Actions.DELETE_TASKFLOW, id: taskflowId };
setSpy(client, 'deleteTaskflow', null);
expect(Actions.deleteTaskflow(taskflowId))
.toDispatchActions(expectedAction, complete(done));

expect(client.deleteTaskflow)
.toHaveBeenCalled();
});
});
});
Loading

0 comments on commit 1fe0047

Please sign in to comment.