diff --git a/app/js/models/models.html b/app/js/models/models.html index 72a083c2..7ab38476 100644 --- a/app/js/models/models.html +++ b/app/js/models/models.html @@ -82,7 +82,7 @@
Primary model - + diff --git a/app/js/models/models.js b/app/js/models/models.js index d6f60a1a..03f97d8a 100644 --- a/app/js/models/models.js +++ b/app/js/models/models.js @@ -7,6 +7,7 @@ define([ './createNetworkModelController', './addComparisonFunnelPlotController', './setBaselineDistributionController', + './editModelTitleController', './standaloneModelResource', './standaloneModelBaselineResource', './standaloneProblemResource', @@ -41,6 +42,7 @@ define([ CreateNetworkModelController, AddComparisonFunnelPlotController, SetBaselineDistributionController, + EditModelTitleController, ModelResource, ModelBaselineResource, ProblemResource, @@ -77,6 +79,7 @@ define([ .controller('CreateNetworkModelController', CreateNetworkModelController) .controller('AddComparisonFunnelPlotController', AddComparisonFunnelPlotController) .controller('SetBaselineDistributionController', SetBaselineDistributionController) + .controller('EditModelTitleController', EditModelTitleController) // resources .factory('ModelResource', ModelResource) diff --git a/standalone-app/modelHandlers.js b/standalone-app/modelHandlers.js index 9de304de..e771d9fb 100644 --- a/standalone-app/modelHandlers.js +++ b/standalone-app/modelHandlers.js @@ -14,7 +14,6 @@ function find(request, response, next) { var analysisId = request.params.analysisId; modelRepository.findByAnalysis(analysisId, function(error, modelsResult) { - if (error) { return next({ statusCode: httpStatus.INTERNAL_SERVER_ERROR, @@ -22,14 +21,9 @@ function find(request, response, next) { }); } - var modelsWithTasks = _.filter(modelsResult, function(model) { - return model.taskUrl !== null && model.taskUrl !== undefined; - }); - var modelsWithoutTasks = _.filter(modelsResult, function(model) { - return model.taskUrl === null || model.taskUrl === undefined; - }); - if (modelsWithTasks.length) { - var taskUrls = _.map(modelsWithTasks, 'taskUrl'); + var { modelsWithTask, modelsWithoutTask } = modelService.partitionModels(modelsResult); + if (modelsWithTask.length) { + var taskUrls = _.map(modelsWithTask, 'taskUrl'); pataviTaskRepository.getPataviTasksStatus(taskUrls, function(error, pataviResult) { if (error) { next({ @@ -37,8 +31,8 @@ function find(request, response, next) { message: error }); } else { - var decoratedResult = decorateWithRunStatus(modelsWithTasks, pataviResult); - response.json(decoratedResult.concat(modelsWithoutTasks)); + var decoratedResult = modelService.decorateWithRunStatus(modelsWithTask, pataviResult); + response.json(decoratedResult.concat(modelsWithoutTask)); } }); } else { @@ -47,15 +41,6 @@ function find(request, response, next) { }); } -function decorateWithRunStatus(modelsResult, pataviResult) { - var pataviTasks = _.keyBy(pataviResult, 'id'); - return _.map(modelsResult, function(model) { - return _.extend(model, { - runStatus: pataviTasks[model.taskUrl].runStatus - }); - }); -} - function getResult(request, response, next) { logger.debug('modelHandler.getResult'); logger.debug('request.params.analysisId' + request.params.analysisId); @@ -68,7 +53,11 @@ function getResult(request, response, next) { }, function(model, callback) { modelCache = model; - callback(model.taskUrl === null || model.taskUrl === undefined); + if (model.taskUrl === null || model.taskUrl === undefined) { + callback('Error, model ' + modelId + ' does not have a task url'); + } else { + callback(); + } }, function(callback) { pataviTaskRepository.getResult(modelCache.taskUrl, callback); @@ -77,16 +66,7 @@ function getResult(request, response, next) { response.status(httpStatus.OK); response.json(pataviResult); } - ], function(error) { - if (error) { - next({ - statusCode: httpStatus.NOT_FOUND, - message: 'no result found for model with id ' + modelId - }); - } else { - next(); - } - }); + ], _.partial(asyncCallback, next)); } function createModel(request, response, next) { @@ -104,16 +84,7 @@ function createModel(request, response, next) { id: createdId }); } - ], function(error) { - if (error) { - next({ - statusCode: httpStatus.NOT_FOUND, - message: 'Error creating model for analysis: ' + analysisId - }); - } else { - next(); - } - }); + ], _.partial(asyncCallback, next)); } function extendRunLength(request, response, next) { @@ -142,16 +113,7 @@ function extendRunLength(request, response, next) { function() { response.sendStatus(httpStatus.OK); } - ], function(error) { - if (error) { - next({ - statusCode: httpStatus.NOT_FOUND, - message: 'Error extending run length of model: ' + modelId - }); - } else { - next(); - } - }); + ], _.partial(asyncCallback, next)); } function addFunnelPlot(request, response, next) { @@ -172,44 +134,26 @@ function addFunnelPlot(request, response, next) { function() { response.sendStatus(httpStatus.CREATED); } - ], function(error) { - if (error) { - next({ - statusCode: httpStatus.NOT_FOUND, - message: 'Error adding funnelplot for model: ' + modelId - }); - } else { - next(); - } - }); + ], _.partial(asyncCallback, next)); } -function queryFunnnelPlots(request, response, next) { +function queryFunnelPlots(request, response, next) { var modelId = Number.parseInt(request.params.modelId); - getFunnelPlotsById(request, response, next, funnelPlotRepository.findByModelId, modelId); + getFromGetterById(response, next, funnelPlotRepository.findByModelId, modelId); } function getFunnelPlot(request, response, next) { var plotId = Number.parseInt(request.params.plotId); - getFunnelPlotsById(request, response, next, funnelPlotRepository.findByPlotId, plotId); -} - -function getFunnelPlotsById(request, response, next, getter, id) { - getter(id, function(error, result) { - if (error) { - next({ - statusCode: httpStatus.INTERNAL_SERVER_ERROR, - message: error - }); - } else { - response.json(result); - } - }); + getFromGetterById(response, next, funnelPlotRepository.findByPlotId, plotId); } function getBaseline(request, response, next) { var modelId = Number.parseInt(request.params.modelId); - modelBaselineRepository.get(modelId, function(error, result) { + getFromGetterById(response, next, modelBaselineRepository.get, modelId); +} + +function getFromGetterById(response, next, getter, id) { + getter(id, function(error, result) { if (error) { next({ statusCode: httpStatus.INTERNAL_SERVER_ERROR, @@ -240,25 +184,13 @@ function setBaseline(request, response, next) { function() { response.sendStatus(httpStatus.OK); } - ], function(error) { - if (error) { - next(error.hasOwnProperty('message') ? error : { - statusCode: httpStatus.NOT_FOUND, - message: 'Error setting baseline for model: ' + modelId - }); - } else { - next(); - } - }); + ], _.partial(asyncCallback, next)); } function checkCoordinates(analysisId, model, callback) { logger.debug('check analysisId = ' + analysisId + ' and model.analysisId = ' + model.analysisId); if (analysisId !== model.analysisId) { - callback({ - statusCode: httpStatus.NOT_FOUND, - message: 'Error, could not find analysis/model combination' - }); + callback('Error, could not find analysis/model combination'); } else { callback(); } @@ -285,7 +217,7 @@ function setAttributes(request, response, next) { function() { response.sendStatus(httpStatus.OK); } - ], next); + ], _.partial(asyncCallback, next)); } function getModel(request, response, next) { @@ -318,6 +250,17 @@ function setTitle(request, response, next) { }); } +function asyncCallback(next, error) { + if (error) { + next({ + statusCode: httpStatus.INTERNAL_SERVER_ERROR, + message: error + }); + } else { + next(); + } +} + module.exports = { find: find, createModel: createModel, @@ -329,6 +272,6 @@ module.exports = { setTitle: setTitle, setAttributes: setAttributes, addFunnelPlot: addFunnelPlot, - queryFunnnelPlots: queryFunnnelPlots, + queryFunnelPlots: queryFunnelPlots, getFunnelPlot: getFunnelPlot }; diff --git a/standalone-app/modelRouter.js b/standalone-app/modelRouter.js index 00f1c1e9..49f36840 100644 --- a/standalone-app/modelRouter.js +++ b/standalone-app/modelRouter.js @@ -16,7 +16,7 @@ module.exports = express.Router({ .put('/:modelId/setTitle', modelHandlers.setTitle) .post('/:modelId/attributes', modelHandlers.setAttributes) .post('/:modelId/funnelPlots', modelHandlers.addFunnelPlot) - .get('/:modelId/funnelPlots', modelHandlers.queryFunnnelPlots) + .get('/:modelId/funnelPlots', modelHandlers.queryFunnelPlots) .get('/:modelId/funnelPlots/:plotId', modelHandlers.getFunnelPlot) .use('/:modelId/task', pataviTaskRouter) ; diff --git a/standalone-app/modelService.js b/standalone-app/modelService.js index 91c0198b..0910d1b2 100644 --- a/standalone-app/modelService.js +++ b/standalone-app/modelService.js @@ -1,18 +1,38 @@ 'use strict'; var modelRepository = require('./modelRepository'); - -module.exports = { - update: update -}; +var _ = require('lodash'); function update(oldModel, newModel, callback) { if (newModel.burnInIterations < oldModel.burnInIterations || newModel.inferenceIterations < oldModel.inferenceIterations) { - callback({ - statusCode: 500, - message: 'may not update model with lower number of iterations' - }); + var errorMessage = 'Error: may not update model with lower number of iterations'; + callback(errorMessage); } else { modelRepository.update(newModel, callback); } } + +function partitionModels(models) { + var partition = _.partition(models, function(model) { + return model.taskUrl !== null && model.taskUrl !== undefined; + }); + return { + modelsWithTask: partition[0], + modelsWithoutTask: partition[1] + }; +} + +function decorateWithRunStatus(models, pataviResult) { + var tasks = _.keyBy(pataviResult, 'id'); + return _.map(models, function(model) { + return _.extend(model, { + runStatus: tasks[model.taskUrl].runStatus + }); + }); +} + +module.exports = { + update: update, + partitionModels: partitionModels, + decorateWithRunStatus: decorateWithRunStatus +}; diff --git a/test/modelHandlersSpec.js b/test/modelHandlersSpec.js index 3bc9bf93..1241ad16 100644 --- a/test/modelHandlersSpec.js +++ b/test/modelHandlersSpec.js @@ -10,14 +10,18 @@ chai.use(spies); var modelRepositoryStub = chai.spy(); var modelBaselineRepositoryStub = chai.spy(); var pataviTaskRepositoryStub = chai.spy(); +var modelServiceStub = chai.spy(); +var funnelPlotRepositoryStub = chai.spy(); var modelHandlers = proxyquire('../standalone-app/modelHandlers', { './modelRepository': modelRepositoryStub, './modelBaselineRepository': modelBaselineRepositoryStub, './pataviTaskRepository': pataviTaskRepositoryStub, + './modelService': modelServiceStub, + './funnelPlotRepository': funnelPlotRepositoryStub }); -var errorMessage = 'error'; +var errorMessage = 'error message'; var error500 = { statusCode: 500, message: errorMessage @@ -26,37 +30,21 @@ var error404 = { statusCode: 404, message: errorMessage }; +var coordinateError = { + statusCode: 500, + message: 'Error, could not find analysis/model combination' +}; var analysisId = 1; +var modelId = -1; describe('the model handlers', function() { - describe('find', function() { + describe.only('find', function() { var request = { params: { analysisId: 1 } }; - it('should call next with an error object when an error occurs while retrieving models from the repository', function() { - var response = {}; - var next = chai.spy(); - modelRepositoryStub.findByAnalysis = sinon.fake.yields(errorMessage); - modelHandlers.find(request, response, next); - expect(next).to.have.been.called.with(error500); - }); - - it('should call next with an error object when an error occurs while gettig the status of Patavi tasks', function() { - var response = {}; - var next = chai.spy(); - var modelsResult = [ - { taskUrl: 'taskUrl' } - ]; - modelRepositoryStub.findByAnalysis = sinon.fake.yields(null, modelsResult); - pataviTaskRepositoryStub.getPataviTasksStatus = sinon.fake.yields(errorMessage); - - modelHandlers.find(request, response, next); - expect(next).to.have.been.called.with(error500); - }); - it('should call response.json with models with and without tasks', function() { var response = { json: chai.spy() @@ -75,7 +63,13 @@ describe('the model handlers', function() { id: taskUrl, runStatus: runStatus }]; + var partitionResult = { + modelsWithTask: [modelsResult[0]], + modelsWithoutTask: [] + }; + modelServiceStub.partitionModels = sinon.fake.returns(partitionResult); modelRepositoryStub.findByAnalysis = sinon.fake.yields(null, modelsResult); + pataviTaskRepositoryStub.getPataviTasksStatus = sinon.fake.yields(null, pataviResult); modelHandlers.find(request, response, next); @@ -89,31 +83,56 @@ describe('the model handlers', function() { var next = chai.spy(); var expectedModelArray = [{}]; var modelsResult = [{}]; + var partitionResult = { + modelsWithTask: [], + modelsWithoutTask: [modelsResult[0]] + }; + modelServiceStub.partitionModels = sinon.fake.returns(partitionResult); modelRepositoryStub.findByAnalysis = sinon.fake.yields(null, modelsResult); modelHandlers.find(request, response, next); expect(response.json).to.have.been.called.with(expectedModelArray); }); + + it('should call next with an error object when an error occurs while retrieving models from the repository', function() { + var response = {}; + var next = chai.spy(); + modelRepositoryStub.findByAnalysis = sinon.fake.yields(errorMessage); + modelHandlers.find(request, response, next); + expect(next).to.have.been.called.with(error500); + }); + + it('should call next with an error object when an error occurs while gettig the status of Patavi tasks', function() { + var response = {}; + var next = chai.spy(); + var modelsResult = [ + { taskUrl: 'taskUrl' } + ]; + var partitionResult = { + modelsWithTask: [modelsResult[0]], + modelsWithoutTask: [] + }; + modelServiceStub.partitionModels = sinon.fake.returns(partitionResult); + modelRepositoryStub.findByAnalysis = sinon.fake.yields(null, modelsResult); + pataviTaskRepositoryStub.getPataviTasksStatus = sinon.fake.yields(errorMessage); + + modelHandlers.find(request, response, next); + expect(next).to.have.been.called.with(error500); + }); }); describe('getResult', function() { - var modelId = 1; var request = { params: { analysisId: analysisId, modelId: modelId } }; - var message = 'no result found for model with id '; - var error = { - statusCode: 404, - message: message + modelId - }; + var response; it('should pass an error to next when attempting to get a model from the repository', function(done) { - var response; var next = function(thrownError) { - expect(thrownError).to.deep.equal(error); + expect(thrownError).to.deep.equal(error500); done(); }; modelRepositoryStub.get = sinon.fake.yields(errorMessage); @@ -122,9 +141,12 @@ describe('the model handlers', function() { }); it('should pass an error to next when attempting to get results of model with no task', function(done) { - var response; + var noTaskError = { + message: 'Error, model ' + modelId + ' does not have a task url', + statusCode: 500 + }; var next = function(thrownError) { - expect(thrownError).to.deep.equal(error); + expect(thrownError).to.deep.equal(noTaskError); done(); }; var modelResult = {}; @@ -134,10 +156,9 @@ describe('the model handlers', function() { }); it('should pass an error to next when attempting to get patavi result', function(done) { - var response; var taskUrl = 'url'; var next = function(thrownError) { - expect(thrownError).to.deep.equal(error); + expect(thrownError).to.deep.equal(error500); done(); }; var modelResult = { @@ -174,7 +195,7 @@ describe('the model handlers', function() { }); describe('createModel', function() { - var request = { + var request = { params: { analysisId: analysisId }, @@ -182,7 +203,6 @@ describe('the model handlers', function() { }; it('should call the repository to create a new model', function(done) { - var modelId = -1; var next = chai.spy(); var expectations = function(returnId) { expect(returnId).to.deep.equal({ @@ -190,6 +210,7 @@ describe('the model handlers', function() { }); expect(response.location).to.have.been.called.with('/analyses/' + analysisId + '/models/' + modelId); expect(response.status).to.have.been.called.with(201); + expect(next).to.have.not.been.called(); done(); }; var response = { @@ -203,12 +224,8 @@ describe('the model handlers', function() { }); it('should call next with an error if the model can\'t be created', function(done) { - var error = { - statusCode: 404, - message: 'Error creating model for analysis: ' + analysisId - }; var next = function(thrownError) { - expect(thrownError).to.deep.equal(error); + expect(thrownError).to.deep.equal(error500); done(); }; var response = {}; @@ -217,6 +234,308 @@ describe('the model handlers', function() { }); }); + describe('extendRunLength', function() { + + var request = { + params: { + analysisId: analysisId, + modelId: modelId + }, + body: {} + }; + var model = { + analysisId: analysisId, + taskUrl: 'url' + }; + + it('should update an existing model to have more iterations and call response.sendStatus when successful', function(done) { + var next = chai.spy(); + var expectations = function(status) { + expect(status).to.equal(200); + expect(next).to.have.not.been.called(); + done(); + }; + var response = { + sendStatus: expectations + }; + + modelRepositoryStub.get = sinon.fake.yields(null, model); + modelServiceStub.update = sinon.fake.yields(null); + pataviTaskRepositoryStub.deleteTask = sinon.fake.yields(null); + + modelHandlers.extendRunLength(request, response, next); + }); + + it('should call next with an error if the model cannot be retrieved', function(done) { + var next = function(thrownError) { + expect(thrownError).to.deep.equal(error500); + done(); + }; + var response = {}; + modelRepositoryStub.get = sinon.fake.yields(errorMessage); + + modelHandlers.extendRunLength(request, response, next); + }); + + it('should call next with an error if the model cannot be updated', function(done) { + var next = function(thrownError) { + expect(thrownError).to.deep.equal(error500); + done(); + }; + var response = {}; + modelRepositoryStub.get = sinon.fake.yields(null, model); + modelServiceStub.update = sinon.fake.yields(errorMessage); + + modelHandlers.extendRunLength(request, response, next); + }); + + it('should call next with an error if the task cannot be deleted', function(done) { + var next = function(thrownError) { + expect(thrownError).to.deep.equal(error500); + done(); + }; + var response = {}; + modelRepositoryStub.get = sinon.fake.yields(null, model); + modelServiceStub.update = sinon.fake.yields(null); + pataviTaskRepositoryStub.deleteTask = sinon.fake.yields(errorMessage); + + modelHandlers.extendRunLength(request, response, next); + }); + + it('should call next with an error if the coordinates do not match', function(done) { + var next = function(thrownError) { + expect(thrownError).to.deep.equal(coordinateError); + done(); + }; + var modelWithWrongAnalysis = { + analysisId: 1337, + taskUrl: 'url' + }; + var response = {}; + modelRepositoryStub.get = sinon.fake.yields(null, modelWithWrongAnalysis); + + modelHandlers.extendRunLength(request, response, next); + }); + }); + + describe('addFunnelPlot', function() { + + var request = { + params: { + analysisId: analysisId, + modelId: modelId + }, + body: {} + }; + + var model = { + analysisId: analysisId, + taskUrl: 'url' + }; + + it('should add a funnel plot and call response.sendStatus when successful', function(done) { + var next = chai.spy(); + var expectations = function(status) { + expect(status).to.equal(201); + expect(next).to.have.not.been.called(); + done(); + }; + var response = { + sendStatus: expectations + }; + + modelRepositoryStub.get = sinon.fake.yields(null, model); + funnelPlotRepositoryStub.create = sinon.fake.yields(null); + + modelHandlers.addFunnelPlot(request, response, next); + }); + + it('should call next with an error if the model cannot be retrieved', function(done) { + var next = function(thrownError) { + expect(thrownError).to.deep.equal(error500); + done(); + }; + var response = {}; + modelRepositoryStub.get = sinon.fake.yields(errorMessage); + modelHandlers.addFunnelPlot(request, response, next); + }); + + it('should call next with an error if the funnel plot can not be created', function(done) { + var next = function(thrownError) { + expect(thrownError).to.deep.equal(error500); + done(); + }; + var response = {}; + modelRepositoryStub.get = sinon.fake.yields(null, model); + funnelPlotRepositoryStub.create = sinon.fake.yields(errorMessage); + modelHandlers.addFunnelPlot(request, response, next); + }); + + it('should call next with an error if the coordinates do not match', function(done) { + var next = function(thrownError) { + expect(thrownError).to.deep.equal(coordinateError); + done(); + }; + var modelWithWrongAnalysis = { + analysisId: 1337, + taskUrl: 'url' + }; + var response = {}; + modelRepositoryStub.get = sinon.fake.yields(null, modelWithWrongAnalysis); + modelHandlers.addFunnelPlot(request, response, next); + }); + }); + + describe('queryFunnelPlots', function() { + var request = { + params: { + modelId: modelId + } + }; + + it('should query a funnel plot given a model id and call response.json with the result', function() { + var response = { + json: chai.spy() + }; + var next = chai.spy(); + var result = {}; + funnelPlotRepositoryStub.findByModelId = sinon.fake.yields(null, result); + modelHandlers.queryFunnelPlots(request, response, next); + expect(response.json).to.have.been.called.with(result); + }); + + it('should call next with an error', function() { + var response = {}; + var next = chai.spy(); + funnelPlotRepositoryStub.findByModelId = sinon.fake.yields(errorMessage); + modelHandlers.queryFunnelPlots(request, response, next); + expect(next).to.have.been.called.with(error500); + }); + }); + + describe('getFunnelPlot', function() { + var request = { + params: { + plotId: -2 + } + }; + + it('should query a funnel plot given a model id and call response.json with the result', function() { + var response = { + json: chai.spy() + }; + var next = chai.spy(); + var result = {}; + funnelPlotRepositoryStub.findByPlotId = sinon.fake.yields(null, result); + modelHandlers.getFunnelPlot(request, response, next); + expect(response.json).to.have.been.called.with(result); + }); + + it('should call next with an error', function() { + var response = {}; + var next = chai.spy(); + funnelPlotRepositoryStub.findByPlotId = sinon.fake.yields(errorMessage); + modelHandlers.getFunnelPlot(request, response, next); + expect(next).to.have.been.called.with(error500); + }); + }); + + describe('setAttributes', function() { + var request = { + params: { + analysisId: analysisId, + modelId: modelId + }, + body: { + isArchived: false + } + }; + var model = { + id: modelId, + analysisId: analysisId + }; + + it('should set the model attributes and call response.sendStatus when successful', function(done) { + var next = chai.spy(); + var expectations = function(status) { + expect(status).to.equal(200); + expect(next).to.not.have.been.called(); + done(); + }; + var response = { + sendStatus: expectations + }; + modelRepositoryStub.get = sinon.fake.yields(null, model); + modelRepositoryStub.setArchive = sinon.fake.yields(null); + modelHandlers.setAttributes(request, response, next); + }); + + it('should call next with an error if the model could not be retrieved', function(done) { + var response; + var next = function(thrownError) { + expect(thrownError).to.deep.equal(error500); + done(); + }; + modelRepositoryStub.get = sinon.fake.yields(errorMessage); + modelHandlers.setAttributes(request, response, next); + }); + + it('should call next with an error if the archived status could not be set', function(done) { + var response; + var next = function(thrownError) { + expect(thrownError).to.deep.equal(error500); + done(); + }; + modelRepositoryStub.get = sinon.fake.yields(null, model); + modelRepositoryStub.setArchive = sinon.fake.yields(errorMessage); + modelHandlers.setAttributes(request, response, next); + }); + + it('should call next with an error if the coordinates do not match', function(done) { + var response; + var next = function(thrownError) { + expect(thrownError).to.deep.equal(coordinateError); + done(); + }; + var modelWithWrongAnalysis = { + analysisId: 1337, + modelId: modelId + }; + modelRepositoryStub.get = sinon.fake.yields(null, modelWithWrongAnalysis); + modelRepositoryStub.setArchive = sinon.fake.yields(errorMessage); + modelHandlers.setAttributes(request, response, next); + }); + }); + + describe('getModel', function() { + var request = { + params: { + modelId: 1 + } + }; + it('should query the model repository and call response.json with the result', function() { + var response = { + json: chai.spy() + }; + var next = chai.spy(); + var result = {}; + modelRepositoryStub.get = sinon.fake.yields(null, result); + + modelHandlers.getModel(request, response, next); + expect(response.json).to.have.been.called.with(result); + expect(next).to.not.have.been.called(); + }); + + it('should, if an error occurs, pass it to next', function() { + var response = {}; + var next = chai.spy(); + modelRepositoryStub.get = sinon.fake.yields(errorMessage); + modelHandlers.getModel(request, response, next); + + expect(next).to.have.been.called.with(error404); + }); + }); + describe('getBaseline', function() { var request = { params: { @@ -273,19 +592,14 @@ describe('the model handlers', function() { expect(next).to.not.have.been.called(); }); - it('should, if an illegal analysis, model combination occurs pass an error to next', function(done) { - var message = 'Error, could not find analysis/model combination'; - var error = { - statusCode: 404, - message: message - }; + it('should call next with an error if the coordinates do not match', function(done) { var response = {}; var next = function(thrownError) { - expect(thrownError).to.deep.equal(error); + expect(thrownError).to.deep.equal(coordinateError); done(); }; modelRepositoryStub.get = sinon.fake.yields(null, 'someModel'); - modelBaselineRepositoryStub.get = sinon.fake.yields(message); + modelBaselineRepositoryStub.get = sinon.fake.yields(errorMessage); modelHandlers.setBaseline(request, response, next); }); @@ -293,7 +607,7 @@ describe('the model handlers', function() { var response = {}; var message = 'Error setting baseline for model: ' + modelId; var error = { - statusCode: 404, + statusCode: 500, message: message }; var next = function(thrownError) { diff --git a/test/modelRepositorySpec.js b/test/modelRepositorySpec.js index 5651d0aa..ced455e4 100644 --- a/test/modelRepositorySpec.js +++ b/test/modelRepositorySpec.js @@ -18,7 +18,7 @@ var modelRepository = proxyquire('../standalone-app/modelRepository', { './db': dbStub }); -var expectedError = 'error'; +var expectedError = 'error message'; var columnString = 'title, analysisId, linearModel,' + ' burn_in_iterations, inference_iterations, ' + ' thinning_factor, modelType, likelihood, link,' + diff --git a/test/modelServiceSpec.js b/test/modelServiceSpec.js index caabb89c..19809434 100644 --- a/test/modelServiceSpec.js +++ b/test/modelServiceSpec.js @@ -1,7 +1,9 @@ 'use strict'; -var assert = require('assert'), - sinon = require('sinon'), - proxyquire = require('proxyquire'); +var assert = require('assert'); +var sinon = require('sinon'); +var proxyquire = require('proxyquire'); +var chai = require('chai'); +var expect = chai.expect; var modelRepository = {}; var modelService = proxyquire('../standalone-app//modelService', { @@ -10,43 +12,87 @@ var modelService = proxyquire('../standalone-app//modelService', { describe('the model service', function() { - describe('for too-low runlength settings', function() { - it('should call the callback with a non-null error parameter', function(done) { - var oldModel = { - burnInIterations: 20, - inferenceIterations: 30 - }; - var newModel = { - burnInIterations: 10, - inferenceIterations: 20 - }; - modelService.update(oldModel, newModel, function(error) { - assert(error); - done(); + describe('update', function() { + describe('for too-low runlength settings', function() { + it('should call the callback with a non-null error parameter', function(done) { + var oldModel = { + burnInIterations: 20, + inferenceIterations: 30 + }; + var newModel = { + burnInIterations: 10, + inferenceIterations: 20 + }; + var errorMessage = 'Error: may not update model with lower number of iterations'; + var callback = function(error) { + expect(error).to.equal(errorMessage); + done(); + }; + modelService.update(oldModel, newModel, callback); }); }); - }); - - describe('for correct runlength settings', function() { - beforeEach(function() { - sinon.stub(modelRepository, 'update').onCall(0).yields(null); + + describe('for correct runlength settings', function() { + beforeEach(function() { + sinon.stub(modelRepository, 'update').onCall(0).yields(null); + }); + + afterEach(function() { + modelRepository.update.restore(); + }); + + it('should update the model in the repository', function(done) { + var oldModel = { + burnInIterations: 10, + inferenceIterations: 20 + }; + var newModel = { + burnInIterations: 20, + inferenceIterations: 30 + }; + var callback = function(error) { + assert(!error); + assert(modelRepository.update.calledWith(newModel)); + done(); + }; + modelService.update(oldModel, newModel, callback); + }); }); - afterEach(function() { - modelRepository.update.restore(); + + describe('partitionModels', function() { + it('should return two arrays containing models with and without an assigned task', function() { + var models = [{ + id: 1, + taskUrl: 'url' + }, { + id: 2 + }]; + var result = modelService.partitionModels(models); + var expectedResult = { + modelsWithTask: [models[0]], + modelsWithoutTask: [models[1]], + }; + expect(result).to.deep.equal(expectedResult); + }); }); - it('should update the model in the repository', function(done) { - var oldModel = { - burnInIterations: 10, - inferenceIterations: 20 - }; - var newModel = { - burnInIterations: 20, - inferenceIterations: 30 - }; - modelService.update(oldModel, newModel, function(error) { - assert(!error); - assert(modelRepository.update.calledWith(newModel)); - done(); + + describe('decorateWithRunStatus', function() { + it('should append a runStatus parameter to the models', function() { + var taskUrl = 'taskUrl'; + var runStatus = 'runStatus'; + var models = [{ + taskUrl: taskUrl + }]; + var pataviResult = [{ + id: taskUrl, + runStatus: runStatus + }]; + var result = modelService.decorateWithRunStatus(models, pataviResult); + var expectedResult = [{ + taskUrl: taskUrl, + runStatus: runStatus + }]; + expect(result).to.deep.equal(expectedResult); }); }); });