diff --git a/Shovel/controllers/Shovel.js b/Shovel/controllers/Shovel.js index dc3f83d..c201d7b 100644 --- a/Shovel/controllers/Shovel.js +++ b/Shovel/controllers/Shovel.js @@ -1,3 +1,5 @@ +// Copyright 2015, EMC, Inc. + 'use strict'; var url = require('url'); @@ -6,12 +8,11 @@ var ironic = require('./../lib/api/openstack/ironic'); var config = require('./../config.json'); var glance = require('./../lib/api/openstack/glance'); var keystone = require('./../lib/api/openstack/keystone'); -var logger = require('./../lib/services/logger').Logger; +var logger = require('./../lib/services/logger').Logger('info'); var encryption = require('./../lib/services/encryption'); var jsonfile = require('jsonfile'); var _ = require('underscore'); var Promise = require('bluebird'); - var ironicConfig = config.ironic; var glanceConfig = config.glance; @@ -44,12 +45,8 @@ module.exports.driversGet = function driversGet(req, res, next) { return ironic.get_driver_list(token); }). then(function (result) { - if (typeof result !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(result); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(result); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -71,12 +68,8 @@ module.exports.ironicnodesGet = function ironicnodesGet(req, res, next) { return ironic.get_node_list(token); }). then(function (result) { - if (typeof result !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(result); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(result); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -98,12 +91,8 @@ module.exports.ironicchassisGet = function ironicchassisGet(req, res, next) { return ironic.get_chassis_by_id(token, req.swagger.params.identifier.value); }). then(function (result) { - if (typeof result !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(result); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(result); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -125,12 +114,8 @@ module.exports.ironicnodeGet = function ironicnodeGet(req, res, next) { return ironic.get_node(token, req.swagger.params.identifier.value); }). then(function (result) { - if (typeof result !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(result); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(result); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -153,10 +138,8 @@ module.exports.ironicnodePatch = function ironicnodePatch(req, res, next) { return ironic.patch_node(token, req.swagger.params.identifier.value, data); }). then(function (result) { - if (result) { - res.setHeader('Content-Type', 'application/json'); - res.end(result); - } + res.setHeader('Content-Type', 'application/json'); + res.end(result); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -173,12 +156,8 @@ module.exports.ironicnodePatch = function ironicnodePatch(req, res, next) { module.exports.catalogsGet = function catalogsGet(req, res, next) { return monorail.request_catalogs_get(req.swagger.params.identifier.value). then(function (catalogs) { - if (typeof catalogs !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(catalogs); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(catalogs); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -196,12 +175,8 @@ module.exports.catalogsbysourceGet = function catalogsbysourceGet(req, res, next return monorail.get_catalog_data_by_source(req.swagger.params.identifier.value, req.swagger.params.source.value). then(function (catalogs) { - if (typeof catalogs !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(catalogs); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(catalogs); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -218,12 +193,8 @@ module.exports.catalogsbysourceGet = function catalogsbysourceGet(req, res, next module.exports.nodeGet = function nodeGet(req, res, next) { return monorail.request_node_get(req.swagger.params.identifier.value). then(function (node) { - if (typeof node !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(node); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(node); }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -244,12 +215,8 @@ module.exports.nodesGet = function nodesGet(req, res, next) { return monorail.lookupCatalog(node); }) .then(function (discoveredNodes) { - if (typeof discoveredNodes !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(JSON.stringify(discoveredNodes)); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(JSON.stringify(discoveredNodes)); }); }) .catch(function (err) { @@ -267,21 +234,16 @@ module.exports.nodesGet = function nodesGet(req, res, next) { module.exports.getSeldata = function getSeldata(req, res, next) { return monorail.request_poller_get(req.swagger.params.identifier.value). then(function (pollers) { - if (typeof pollers !== 'undefined') { - pollers = JSON.parse(pollers); - for (var i in pollers) { - if (pollers[i]['config']['command'] === 'sel') { - return monorail.request_poller_data_get(pollers[i]['id']). - then(function (data) { - res.setHeader('Content-Type', 'application/json'); - res.end(data); - }); - } + pollers = JSON.parse(pollers); + for (var i in pollers) { + if (pollers[i]['config']['command'] === 'sel') { + return monorail.request_poller_data_get(pollers[i]['id']). + then(function (data) { + res.setHeader('Content-Type', 'application/json'); + res.end(data); + }); } } - else { - res.end(); - } }) .catch(function (err) { logger.error({ message: err, path: req.url }); @@ -460,7 +422,6 @@ module.exports.unregisterdel = function unregisterdel(req, res, next) { res.setHeader('Content-Type', 'application/json'); res.end(JSON.stringify(err)); }); - }; /* @@ -567,7 +528,6 @@ function setConfig(keyValue, entry) { var content = (keyValue == null) ? output : output[keyValue]; var filteredList = _.pick(content, Object.keys(entry)); _.each(Object.keys(filteredList), function (key) { - logger.info(key); content[key] = entry[key]; }); @@ -581,7 +541,6 @@ function setConfig(keyValue, entry) { return false; } }); - return true; } @@ -594,7 +553,6 @@ module.exports.configget = function configget(req, res, next) { var filename = 'config.json'; jsonfile.readFile(filename, function (err, content) { try { - delete content['key']; if (content.ironic.hasOwnProperty("os_password")) { content.ironic.os_password = '[REDACTED]'; @@ -607,7 +565,8 @@ module.exports.configget = function configget(req, res, next) { } catch (err) { logger.error(err); - res.end(); + res.setHeader('content-type', 'text/plain'); + res.end('failed to get config'); }; }); }; @@ -624,12 +583,8 @@ module.exports.imagesGet = function imagesGet(req, res, next) { return glance.get_images(token); }). then(function (result) { - if (typeof result !== 'undefined') { - res.setHeader('Content-Type', 'application/json'); - res.end(result); - } - else - res.end(); + res.setHeader('Content-Type', 'application/json'); + res.end(result); }) .catch(function (err) { logger.error({ message: err, path: req.url }); diff --git a/Shovel/lib/services/logger.js b/Shovel/lib/services/logger.js index 2fc0ffa..24d5516 100644 --- a/Shovel/lib/services/logger.js +++ b/Shovel/lib/services/logger.js @@ -1,26 +1,30 @@ +// Copyright 2015, EMC, Inc. + var winston = require('winston'); -var logger = new (winston.Logger)({ - levels: { - verbose: 4, - debug: 3, - info: 2, - warn: 1, - error: 0 - }, - colors: { - verbose: 'cyan', - debug: 'blue', - info: 'green', - warn: 'yellow', - error: 'red' - } -}).add(winston.transports.Console, { - level: 'info', - prettyPrint: true, - colorize: true, - silent: false, - timestamp: true -}); - -exports.Logger = logger; +module.exports.Logger = function Logger(level) { + var logger = new (winston.Logger)({ + levels: { + verbose: 5, + debug: 4, + info: 3, + warn: 2, + error: 1, + mask: 0 + }, + colors: { + verbose: 'cyan', + debug: 'blue', + info: 'green', + warn: 'yellow', + error: 'red' + } + }).add(winston.transports.Console, { + level: level, + prettyPrint: true, + colorize: true, + silent: false, + timestamp: true + }); + return logger; +} \ No newline at end of file diff --git a/Shovel/lib/services/poller.js b/Shovel/lib/services/poller.js index 2aef102..94a98ea 100644 --- a/Shovel/lib/services/poller.js +++ b/Shovel/lib/services/poller.js @@ -1,9 +1,11 @@ +// Copyright 2015, EMC, Inc. + var monorail = require('./../api/monorail/monorail'); var ironic = require('./../api/openstack/ironic'); var keystone = require('./../api/openstack/keystone'); var _ = require('underscore'); var config = require('./../../config.json'); -var logger = require('./logger').Logger; +var logger = require('./logger').Logger('error'); module.exports = Poller; var ironicConfig = config.ironic; @@ -15,23 +17,23 @@ function Poller(timeInterval) { Poller.prototype.getToken = function () { var self = this; return keystone.authenticatePassword(ironicConfig.os_tenant_name, - ironicConfig.os_username,ironicConfig.os_password). + ironicConfig.os_username, ironicConfig.os_password). then(function (token) { self._ironicToken = token = JSON.parse(token).access.token.id; return token; }) .catch(function (err) { logger.error(err); - return null; + return (null); }); }; Poller.prototype.stopServer = function () { - try{ + try { clearInterval(this.timeObj); this.timeObj = 0; } - catch(err){ + catch (err) { logger.error(err); } }; @@ -46,9 +48,8 @@ function Poller(timeInterval) { Poller.prototype.searchIronic = function (ironic_node) { var self = this; - return ironic.get_node(self._ironicToken, ironic_node.uuid). - then(function (node_data) { - node_data = JSON.parse(node_data); + var node_data = ironic_node; + try { if (node_data != undefined && node_data.extra && node_data.extra.timer) { if (!node_data.extra.timer.stop) { @@ -61,23 +62,23 @@ function Poller(timeInterval) { node_data.extra.timer.start = new Date().toJSON(); if (node_data.extra.timer.isDone) { node_data.extra.timer.isDone = false; - self.updateInfo(self._ironicToken, node_data). + return self.updateInfo(self._ironicToken, node_data). then(function (data) { return self.patchData(node_data.uuid, JSON.stringify(data)); }). then(function (result) { - return result; + return (result); }); - } } } } - }) - .catch(function (err) { + return Promise.resolve(null); + } + catch (err) { logger.error(err); - return null; - }); + return Promise.resolve(null); + }; }; Poller.prototype.getNodes = function (token) { @@ -87,7 +88,7 @@ function Poller(timeInterval) { }) .catch(function (err) { logger.error(err); - return null; + return (null); }); }; @@ -97,21 +98,20 @@ function Poller(timeInterval) { then(function (result) { result = JSON.parse(result); if (result != undefined) { - return result.extra; + return (result.extra); } - return null; + return (null); }) .catch(function (err) { logger.error(err); - return null; + return (null); }); }; Poller.prototype.updateInfo = function (token, node_data) { - var self = this; return this.getSeldata(node_data.extra.nodeid). then(function (result) { - if (result != undefined) { + if (result != null) { var lastEvent = {}; result = JSON.parse(result); if (result[0] && result[0].hasOwnProperty('sel')) { @@ -131,11 +131,11 @@ function Poller(timeInterval) { node_data.extra.timer.isDone = true; node_data.extra.events = lastEvent; var data = [{ 'path': '/extra', 'value': node_data.extra, 'op': 'replace' }]; - return data; + return (data); }) .catch(function (err) { logger.error(err); - return null; + return (null); }); } diff --git a/Shovel/package.json b/Shovel/package.json index 2ac825f..472faa9 100644 --- a/Shovel/package.json +++ b/Shovel/package.json @@ -17,19 +17,18 @@ "should": "~7.0.1", "mocha": "^2.1.0", "sinon": "1.16.1", - "sinon-as-promised": "^2.0.3", - "sinon-chai": "^2.7.0", "supertest": "^0.15.0", "underscore": "^1.8.3", "xunit-file": "0.0.6", "winston": "2.1.1", "jsonfile": "2.2.3", "crypto": "0.0.3", - "istanbul": "0.4.1" + "istanbul": "0.4.1", + "nock": "3.6.0" }, "scripts": { "postinstall": "scripts/post-install.sh", "start": "start shovel", - "test": "istanbul cover -x '**/test/**' ./node_modules/.bin/_mocha test/* && istanbul report cobertura" + "test": "istanbul cover -x '**/test/**' node_modules/mocha/bin/_mocha test/api/* test/services/* test/controllers/* && istanbul report cobertura" } } diff --git a/Shovel/test/api/client.js b/Shovel/test/api/client.js new file mode 100644 index 0000000..b7d0ca4 --- /dev/null +++ b/Shovel/test/api/client.js @@ -0,0 +1,199 @@ +// Copyright 2015, EMC, Inc. + +var should = require('should'); +var client = require('./../../lib/api/client'); +var Promise = require('bluebird'); +var nock = require('nock'); +Promise.promisifyAll(client); + +describe('client with http requests', function () { + var option = { host: 'localhost', port: 7070, path: '/api/', token: '', data: '{"auth":{"tenantName":"admin" }}', api: '' }; + it('get return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .get('/') + .reply(200, { data: 'data from server' }); + return client.GetAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('get return data from targeted server with a token', function (done) { + nock('http://localhost:7070/api') + .get('/') + .reply(200, { data: 'data from server' }); + option.token = '1234'; + return client.GetAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('get return data from targeted server', function (done) { + return client.GetAsync(option) + .catch(function (result) { + result.should.have.property('errorMessage'); + done(); + }); + }); + + it('post return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .post('/') + .reply(200, { data: 'data from server' }); + + return client.PostAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('post return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .post('/') + .reply(200, { data: 'data from server' }); + option.token = '1234'; + return client.PostAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('post return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .post('/') + .reply(200, { data: 'data from server' }); + return client.PostAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('post return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .post('/') + .reply(200, { data: 'data from server' }); + option.token = '1234'; + return client.PostAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('post return data from targeted server', function (done) { + return client.PostAsync(option) + .catch(function (result) { + result.should.have.property('errorMessage'); + done(); + }); + }); + + it('delete return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .delete('/') + .reply(200, { data: 'data from server' }); + + return client.DeleteAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('delete return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .delete('/') + .reply(200, { data: 'data from server' }); + option.token = '1234'; + return client.DeleteAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('delete return data from targeted server', function (done) { + return client.DeleteAsync(option) + .catch(function (result) { + result.should.have.property('errorMessage'); + done(); + }); + }); + + it('put return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .put('/') + .reply(200, { data: 'data from server' }); + + return client.PutAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('put return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .put('/') + .reply(200, { data: 'data from server' }); + option.token = '1234'; + return client.PutAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('put return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .put('/') + .reply(200, { data: 'data from server' }); + return client.PutAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('put return data from targeted server', function (done) { + return client.PutAsync(option) + .catch(function (result) { + result.should.have.property('errorMessage'); + done(); + }); + }); + + it('path return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .patch('/') + .reply(200, { data: 'data from server' }); + return client.PatchAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('patch return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .patch('/') + .reply(200, { data: 'data from server' }); + option.token = '1234'; + return client.PatchAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('patch return data from targeted server', function (done) { + nock('http://localhost:7070/api') + .patch('/') + .reply(200, { data: 'data from server' }); + return client.PatchAsync(option) + .then(function (result) { + JSON.parse(result).should.have.property('data'); + done(); + }); + }); + it('patch return data from targeted server', function (done) { + return client.PatchAsync(option) + .catch(function (result) { + result.should.have.property('errorMessage'); + done(); + }); + }); +}); \ No newline at end of file diff --git a/Shovel/test/api/ironic.js b/Shovel/test/api/ironic.js new file mode 100644 index 0000000..810701e --- /dev/null +++ b/Shovel/test/api/ironic.js @@ -0,0 +1,113 @@ +// Copyright 2015, EMC, Inc. + +var request = require('supertest'); +var should = require('should'); +var sinon = require('sinon'); +var ironic = require('./../../lib/api/openstack/ironic'); +var client = require('./../../lib/api/client'); +var Promise = require('bluebird'); +Promise.promisifyAll(client); + +describe('****Ironic Lib****', function () { + beforeEach('set up mocks', function () { + var output = ({ data: 'ironic service' }); + sinon.stub(client, 'GetAsync').returns(Promise.resolve(output)); + sinon.stub(client, 'PostAsync').returns(Promise.resolve(output)); + sinon.stub(client, 'PatchAsync').returns(Promise.resolve(output)); + sinon.stub(client, 'PutAsync').returns(Promise.resolve(output)); + sinon.stub(client, 'DeleteAsync').returns(Promise.resolve(output)); + }); + afterEach('teardown mocks', function () { + client['GetAsync'].restore(); + client['PostAsync'].restore(); + client['PatchAsync'].restore(); + client['PutAsync'].restore(); + client['DeleteAsync'].restore(); + + }); + it('ironic.get_chassis return data from ironic', function (done) { + return ironic.get_chassis('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('get_chassis_by_id return data from ironic', function (done) { + return ironic.get_chassis_by_id('123', '123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('get_chassis_by_id return data from ironic', function (done) { + return ironic.get_node_list('123') + .then(function (result) { + result.should.have.property('data'); + done(); + + }); + }); + it('get_node return data from ironic', function (done) { + return ironic.get_node('123', '123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('patch_node return data from ironic', function (done) { + return ironic.patch_node('123', '123', {}) + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('delete_node return data from ironic', function (done) { + return ironic.delete_node('123', '123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('get_port_list return data from ironic', function (done) { + return ironic.get_port_list('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('get_port return data from ironic', function (done) { + return ironic.get_port('123', 'identifier') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('set_power_state on return data from ironic', function (done) { + return ironic.set_power_state('123', 'identifier', 'on') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('set_power_state off return data from ironic', function (done) { + return ironic.set_power_state('123', 'identifier', 'off') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('set_power_state reboot return data from ironic', function (done) { + return ironic.set_power_state('123', 'identifier', 'reboot') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('get_driver_list return data from ironic', function (done) { + return ironic.get_driver_list('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); +}); \ No newline at end of file diff --git a/Shovel/test/api/monorail.js b/Shovel/test/api/monorail.js index 850f8e4..6b07072 100644 --- a/Shovel/test/api/monorail.js +++ b/Shovel/test/api/monorail.js @@ -1,83 +1,76 @@ -var request = require('supertest'); +// Copyright 2015, EMC, Inc. + var should = require('should'); -var assert = require('assert'); var sinon = require('sinon'); var monorail = require('./../../lib/api/monorail/monorail'); var Promise = require('bluebird'); var _ = require('underscore'); +var client = require('./../../lib/api/client'); +var Promise = require('bluebird'); +Promise.promisifyAll(client); +describe('****Monorail Lib****',function(){ + var rackhdNode = [{ workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; + var catalogSource = [{ source: 'dmi', data: {'Memory Device': [{ Size: '1 GB' }, { Size: '1 GB' }], + 'Processor Information': [{}, {}] }}, { source: 'lsscsi', data: [{ peripheralType: 'disk', size: '1GB' }] }]; + var rackhdNode =[ { workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; + var identifier = '123456789'; -var rackhdNode = [{ workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; -var catalogSource = [{ source: 'dmi', data: {'Memory Device': [{ Size: '1 GB' }, { Size: '1 GB' }], - 'Processor Information': [{}, {}] }}, { source: 'lsscsi', data: [{ peripheralType: 'disk', size: '1GB' }] }]; -var rackhdNode =[ { workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; -var identifier = '123456789'; + describe('monorail nodeDiskSize get_node_memory_cpu', function () { + describe('monorail get_node_memory_cpu', function () { + afterEach('teardown mocks', function () { + //monorail + monorail['get_catalog_data_by_source'].restore(); + }); -describe('monorail nodeDiskSize', function () { - - beforeEach('set up mocks', function () { - //monorail - sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[1]))); - - }); - - afterEach('teardown mocks', function () { - //monorail - monorail['get_catalog_data_by_source'].restore(); - }); - - describe('nodeDiskSize', function () { - it('response should returns an integer with value equal to disk size 1GB', function (done) { - return monorail.nodeDiskSize(rackhdNode). - then(function (result) { - result.should.be.exactly(1); - done(); + it('response should returns an integer with value equal to memory size 2000MB and cpus=2', function (done) { + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); + return monorail.get_node_memory_cpu(rackhdNode). + then(function (result) { + result.cpus.should.be.exactly(2); + result.memory.should.be.exactly(2000); + done(); + }); }); }); + describe('nodeDiskSize', function () { + beforeEach('set up mocks', function () { + }); + + afterEach('teardown mocks', function () { + //monorail + monorail['get_catalog_data_by_source'].restore(); + }); + it('response should returns an integer with value equal to disk size 1GB', function (done) { + //monorail + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[1]))); + return monorail.nodeDiskSize(rackhdNode). + then(function (result) { + result.should.be.exactly(1); + done(); + }); + }); + + it('response should returns an integer with value equal to 0', function (done) { + //monorail + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify({}))); + return monorail.nodeDiskSize(rackhdNode). + then(function (result) { + result.should.be.exactly(0); + done(); + }); + }); + }); }); -}); - -describe('monorail get_node_memory_cpu', function () { - beforeEach('set up mocks', function () { - //monorail - sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); - - }); - - afterEach('teardown mocks', function () { - //monorail - monorail['get_catalog_data_by_source'].restore(); - }); - - describe('get_node_memory_cpu', function () { - it('response should returns an integer with value equal to memory size 2000MB and cpus=2', function (done) { - return monorail.get_node_memory_cpu(rackhdNode). - then(function (result) { - result.cpus.should.be.exactly(2); - result.memory.should.be.exactly(2000); - done(); - }) - }); - }); -}); - -describe('lookupCatalog true', function () { - beforeEach('set up mocks', function () { - //monorail - sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); - - }); - - afterEach('teardown mocks', function () { - //monorail - monorail['get_catalog_data_by_source'].restore(); - }); - describe('lookupCatalog', function () { + afterEach('teardown mocks', function () { + //monorail + monorail['get_catalog_data_by_source'].restore(); + }); it('lookupCatalog response should be equal to true', function (done) { + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); return monorail.lookupCatalog(rackhdNode[0]). then(function (result) { - console.log(result); JSON.parse(result).should.be.exactly(true); done(); }) @@ -85,26 +78,10 @@ describe('lookupCatalog true', function () { throw err; }) }); - }); -}); - -describe('lookupCatalog false', function () { - beforeEach('set up mocks', function () { - //monorail - sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify({}))); - - }); - - afterEach('teardown mocks', function () { - //monorail - monorail['get_catalog_data_by_source'].restore(); - }); - - describe('lookupCatalog', function () { it('lookupCatalog response should be equal to fasle if catalog does not have property data', function (done) { + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify({}))); return monorail.lookupCatalog(rackhdNode[0]). then(function (result) { - console.log(result); JSON.parse(result).should.be.exactly(false); done(); }) @@ -113,7 +90,79 @@ describe('lookupCatalog false', function () { }) }); }); + describe('monorail with client get/post/patch/delete returns data', function () { + beforeEach('set up mocks', function () { + var output = ({ data: 'monorail service' }); + sinon.stub(client, 'GetAsync').returns(Promise.resolve(output)); + sinon.stub(client, 'PostAsync').returns(Promise.resolve(output)); + sinon.stub(client, 'PatchAsync').returns(Promise.resolve(output)); + sinon.stub(client, 'PutAsync').returns(Promise.resolve(output)); + sinon.stub(client, 'DeleteAsync').returns(Promise.resolve(output)); + }); + afterEach('teardown mocks', function () { + client['GetAsync'].restore(); + client['PostAsync'].restore(); + client['PatchAsync'].restore(); + client['PutAsync'].restore(); + client['DeleteAsync'].restore(); + }); - + it('monorail.request_nodes_get return data from monorail', function (done) { + return monorail.request_nodes_get() + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('monorail.request_node_get return data from monorail', function (done) { + return monorail.request_node_get('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('monorail.request_whitelist_set return data from monorail', function (done) { + return monorail.request_whitelist_set('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('monorail.request_whitelist_del return data from monorail', function (done) { + return monorail.request_whitelist_del('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('monorail.request_catalogs_get return data from monorail', function (done) { + return monorail.request_catalogs_get('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('monorail.get_catalog_data_by_source return data from monorail', function (done) { + return monorail.get_catalog_data_by_source('123', 'bmc') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('monorail.request_poller_get return data from monorail', function (done) { + return monorail.request_poller_get('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + it('monorail.request_poller_data_get return data from monorail', function (done) { + return monorail.request_poller_data_get('123') + .then(function (result) { + result.should.have.property('data'); + done(); + }); + }); + }); }); \ No newline at end of file diff --git a/Shovel/test/controllers/Shovel.js b/Shovel/test/controllers/Shovel.js index f868a9d..451d24b 100644 --- a/Shovel/test/controllers/Shovel.js +++ b/Shovel/test/controllers/Shovel.js @@ -1,284 +1,242 @@ +// Copyright 2015, EMC, Inc. + var request = require('supertest'); var should = require('should'); -var assert = require('assert'); var sinon = require('sinon'); var monorail = require('./../../lib/api/monorail/monorail'); var ironic = require('./../../lib/api/openstack/ironic'); var keystone = require('./../../lib/api/openstack/keystone'); +var glance = require('./../../lib/api/openstack/glance'); var Promise = require('bluebird'); -var _ = require('underscore'); +var _ = require('underscore'); var helper = require('./../helper'); var url = 'http://localhost:9008'; - - - -var rackhdNode =[ { workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; -var identifier = '9a761508-4eee-4065-b47b-45c22dff54c2'; -var ironic_node_list = [ { uuid: "9a761508-4eee-4065-b47b-45c22dff54c2", extra: { name: "D51B-2U (dual 10G LoM)", eventre: "", nodeid: "564cefa014ee77be18e48efd", - timer: { start: "2015-11-30T21:14:11.753Z", finish: "2015-11-30T21:14:11.772Z", stop: false, isDone: true, timeInteval: 5000 }, eventcnt: 0 } }]; -var nodePollers = [{ config: { command: "sel" }, id: "564dd86285fb1e7c72721543" }]; -var _sel = { sel:[ { logId: "1", date: "12/03/2015", time: "08:54:11", sensorType: "Memory", sensorNumber: "#0x53", event: "Correctable ECC", value: "Asserted"} ] }; -var keyToken = { access:{ token:{id:'123456'} } }; -var selEvent = { message: "There is no cache record for the poller with ID 564cf02a4978dadc187976f5.Perhaps it has not been run yet?" }; -var extraPatch = {extra: {name: "QuantaPlex T41S-2U", eventre: "Correctable ECC",nodeid: "565f3f3b4c95bce26f35c6a0", - timer: {timeInterval: 15000, start: "2015-12-03T17:38:20.569Z",finish: "2015-12-03T17:38:20.604Z",stop: false,isDone: true} } }; -var patchedData = [{ 'path': '/extra', 'value': extraPatch.extra, 'op': 'replace' }]; -var catalog = [{ node: "9a761508-4eee-4065-b47b-45c22dff54c2", source: "dmi", data: {} }]; -var ironicDrivers = { drivers: [{ "hosts": ["localhost"], "name": "pxe_ssh", "links": [] }] }; -var ironicChassis = { uuid: "1ac07daf-264e-4bd5-b0c4-d53095c217ac", link: [], extra: {}, created_at: "", "nodes": [], description: "ironic test chassis" }; -var catalogSource = [{ source: 'dmi', data: {'Memory Device': [{ Size: '1 GB' }, { Size: '1 GB' }], - 'Processor Information': [{}, {}] }}, { source: 'lsscsi', data: [{ peripheralType: 'disk', size: '1GB' }] }]; +describe('****SHOVEL API Interface****', function () { -describe('Shovel api unit testing', function () { - var dmiData = { cpus: 1, memory: 1 }; + var rackhdNode = [{ workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; + var identifier = '9a761508-4eee-4065-b47b-45c22dff54c2'; + var ironic_node_list = [{ uuid: "9a761508-4eee-4065-b47b-45c22dff54c2", extra: { name: "D51B-2U (dual 10G LoM)", eventre: "", nodeid: "564cefa014ee77be18e48efd", + timer: { start: "2015-11-30T21:14:11.753Z", finish: "2015-11-30T21:14:11.772Z", stop: false, isDone: true, timeInteval: 5000 }, eventcnt: 0 } } ]; + var nodePollers = [{ config: { command: "sel" }, id: "564dd86285fb1e7c72721543" }]; + var _sel = { sel: [{ logId: "1", date: "12/03/2015", time: "08:54:11", sensorType: "Memory", sensorNumber: "#0x53", event: "Correctable ECC", value: "Asserted" }] }; + var keyToken = { access: { token: { id: '123456' } } }; + var selEvent = { message: "There is no cache record for the poller with ID 564cf02a4978dadc187976f5.Perhaps it has not been run yet?" }; + var extraPatch = { extra: { name: "QuantaPlex T41S-2U", eventre: "Correctable ECC", nodeid: "565f3f3b4c95bce26f35c6a0", + timer: { timeInterval: 15000, start: "2015-12-03T17:38:20.569Z", finish: "2015-12-03T17:38:20.604Z", stop: false, isDone: true } } }; + var patchedData = [{ 'path': '/extra', 'value': extraPatch.extra, 'op': 'replace' }]; + var catalog = [{ node: "9a761508-4eee-4065-b47b-45c22dff54c2", source: "dmi", data: {} }]; + var ironicDrivers = { drivers: [{ "hosts": ["localhost"], "name": "pxe_ssh", "links": [] }] }; + var ironicChassis = { uuid: "1ac07daf-264e-4bd5-b0c4-d53095c217ac", link: [], extra: {}, created_at: "", "nodes": [], description: "ironic test chassis" }; + var catalogSource = [{ source: 'dmi', data: { 'Memory Device': [{ Size: '1 GB' }, { Size: '1 GB' }], 'Processor Information': [{}, {}] } }, + { source: 'lsscsi', data: [{ peripheralType: 'disk', size: '1GB' }] }]; + var glanceImages = { "images": [{ "name": "ir-deploy-pxe_ssh.initramfs", "container_format": "ari", "disk_format": "ari" }] }; before('start HTTP server', function () { helper.startServer(); }); - - beforeEach('set up mocks', function () { - //monorail - sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); - sinon.stub(monorail, 'request_nodes_get').returns(Promise.resolve(JSON.stringify(rackhdNode))); - sinon.stub(monorail, 'lookupCatalog').returns(Promise.resolve(true)); - sinon.stub(monorail, 'request_catalogs_get').returns(Promise.resolve(JSON.stringify(catalog))); - sinon.stub(monorail, 'request_poller_get').returns(Promise.resolve(JSON.stringify(nodePollers))); - sinon.stub(monorail, 'request_poller_data_get').returns(Promise.resolve(JSON.stringify(_sel))); - sinon.stub(monorail, 'request_whitelist_del').returns(Promise.resolve('')); - sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(0)); - sinon.stub(monorail, 'get_node_memory_cpu').returns(Promise.resolve(dmiData)); - sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); - //keystone - sinon.stub(keystone, 'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); - //ironic - sinon.stub(ironic, 'get_driver_list').returns(Promise.resolve(JSON.stringify(ironicDrivers))); - sinon.stub(ironic, 'get_chassis_by_id').returns(Promise.resolve(JSON.stringify(ironicChassis))); - sinon.stub(ironic, 'patch_node').returns(Promise.resolve(JSON.stringify(extraPatch))); - sinon.stub(ironic, 'get_node_list').returns(Promise.resolve(JSON.stringify(ironic_node_list))); - sinon.stub(ironic, 'get_node').returns(Promise.resolve(JSON.stringify(ironic_node_list[0]))); - sinon.stub(ironic, 'delete_node').returns(Promise.resolve('')); - }); - - afterEach('teardown mocks', function () { - //monorail - monorail['request_node_get'].restore(); - monorail['request_nodes_get'].restore(); - monorail['request_poller_get'].restore(); - monorail['request_poller_data_get'].restore(); - monorail['request_catalogs_get'].restore(); - monorail['lookupCatalog'].restore(); - monorail['request_whitelist_del'].restore(); - monorail['nodeDiskSize'].restore(); - monorail['get_node_memory_cpu'].restore(); - monorail['get_catalog_data_by_source'].restore(); - //ironic - ironic['patch_node'].restore(); - ironic['get_node_list'].restore(); - ironic['get_node'].restore(); - ironic['get_chassis_by_id'].restore(); - ironic['get_driver_list'].restore(); - ironic['delete_node'].restore(); - //keystone - keystone['authenticatePassword'].restore(); - }); - after('stop HTTP server', function () { helper.stopServer(); }); + describe('Shovel api unit testing', function () { + var dmiData = { cpus: 1, memory: 1 }; - describe('shovel-info', function () { - it('response should have property \'name\': \'shovel\'', function (done) { + beforeEach('set up mocks', function () { + //monorail + sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + sinon.stub(monorail, 'request_nodes_get').returns(Promise.resolve(JSON.stringify(rackhdNode))); + sinon.stub(monorail, 'lookupCatalog').returns(Promise.resolve(true)); + sinon.stub(monorail, 'request_catalogs_get').returns(Promise.resolve(JSON.stringify(catalog))); + sinon.stub(monorail, 'request_poller_get').returns(Promise.resolve(JSON.stringify(nodePollers))); + sinon.stub(monorail, 'request_poller_data_get').returns(Promise.resolve(JSON.stringify(_sel))); + sinon.stub(monorail, 'request_whitelist_del').returns(Promise.resolve('')); + sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(0)); + sinon.stub(monorail, 'get_node_memory_cpu').returns(Promise.resolve(dmiData)); + sinon.stub(monorail, 'get_catalog_data_by_source').returns(Promise.resolve(JSON.stringify(catalogSource[0]))); + //glance + sinon.stub(glance, 'get_images').returns(Promise.resolve(JSON.stringify(glanceImages))); + //keystone + sinon.stub(keystone, 'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); + //ironic + sinon.stub(ironic, 'get_driver_list').returns(Promise.resolve(JSON.stringify(ironicDrivers))); + sinon.stub(ironic, 'get_chassis_by_id').returns(Promise.resolve(JSON.stringify(ironicChassis))); + sinon.stub(ironic, 'patch_node').returns(Promise.resolve(JSON.stringify(extraPatch))); + sinon.stub(ironic, 'get_node_list').returns(Promise.resolve(JSON.stringify(ironic_node_list))); + sinon.stub(ironic, 'get_node').returns(Promise.resolve(JSON.stringify(ironic_node_list[0]))); + sinon.stub(ironic, 'delete_node').returns(Promise.resolve('')); + }); + + afterEach('teardown mocks', function () { + //monorail + monorail['request_node_get'].restore(); + monorail['request_nodes_get'].restore(); + monorail['request_poller_get'].restore(); + monorail['request_poller_data_get'].restore(); + monorail['request_catalogs_get'].restore(); + monorail['lookupCatalog'].restore(); + monorail['request_whitelist_del'].restore(); + monorail['nodeDiskSize'].restore(); + monorail['get_node_memory_cpu'].restore(); + monorail['get_catalog_data_by_source'].restore(); + //ironic + ironic['patch_node'].restore(); + ironic['get_node_list'].restore(); + ironic['get_node'].restore(); + ironic['get_chassis_by_id'].restore(); + ironic['get_driver_list'].restore(); + ironic['delete_node'].restore(); + //glance + glance['get_images'].restore(); + //keystone + keystone['authenticatePassword'].restore(); + }); + + it('shovel-info response should have property \'name\': \'shovel\'', function (done) { request(url) .get('/api/1.1/info') - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).should.have.property('name', 'shovel'); done(); }); }); - }); - describe('shovel-catalogs/{identifier}', function () { - it('in case of a correct rackHD id, response should include property node, source and data', function (done) { + it('shovel-catalogs/{identifier} in case of a correct rackHD id, response should include property node, source and data', function (done) { request(url) .get('/api/1.1/catalogs/' + identifier) - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text)[0].should.have.property('node', identifier); JSON.parse(res.text)[0].should.have.property('data'); JSON.parse(res.text)[0].should.have.property('source'); done(); }); }); - }); - describe('shovel-catalogs/{identifier}/source', function () { - it('in case of a correct rackHD id, response should include property source and data', function (done) { + it('shovel-catalogs/{identifier}/source in case of a correct rackHD id, response should include property source and data', function (done) { request(url) .get('/api/1.1/catalogs/' + identifier + '/dmi') - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).should.have.property('data'); JSON.parse(res.text).should.have.property('source'); done(); }); }); - }); - describe('shovel-nodes/{identifier}', function () { - it('in case of correct id, response should include property: id,identifiers', function (done) { + it('shovel-nodes/{identifier} in case of correct id, response should include property: id,identifiers', function (done) { request(url) .get('/api/1.1/nodes/' + identifier) - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).should.have.property('id'); JSON.parse(res.text).should.have.property('identifiers'); done(); }); }); - }); - describe('shovel-ironic/chassis/{identifier}', function () { - it('in case of a correct id, response should include property: uuid , description ', function (done) { + it('shovel-ironic/chassis/{identifier} in case of a correct id, response should include property: uuid , description ', function (done) { request(url) .get('/api/1.1/ironic/chassis/' + identifier) - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).should.have.property('uuid'); JSON.parse(res.text).should.have.property('description'); done(); }); }); - }); - describe('shovel-ironic/drivers', function () { - it('response should have property \'drivers\'', function (done) { + it('shovel-ironic/drivers response should have property \'drivers\'', function (done) { request(url) .get('/api/1.1/ironic/drivers') - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).should.have.property('drivers'); done(); }); }); - }); - describe('shovel-ironic/nodes', function () { - it('response should have property \'uuid\'', function (done) { + it('shovel-ironic/nodes response should have property \'uuid\'', function (done) { request(url) .get('/api/1.1/ironic/nodes') - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text)[0].should.have.property('uuid'); done(); }); }); - }); - describe('shovel-ironic/nodes/identifier', function () { - it('response should have property \'uuid\'', function (done) { + it('shovel-ironic/nodes/identifier response should have property \'uuid\'', function (done) { request(url) .get('/api/1.1/ironic/nodes/' + identifier) - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).should.have.property('uuid'); done(); }); }); - }); - describe('shovel-ironic/patch', function () { - it('response should have property nodeid timer', function (done) { + it('shovel-ironic/patch response should have property nodeid timer', function (done) { var body = {}; request(url) .patch('/api/1.1/ironic/node/' + identifier) .send(body) - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).extra.should.have.property('nodeid'); JSON.parse(res.text).extra.should.have.property('timer'); done(); }); }); - }); - describe('shovel-nodes', function () { - it('response should have property "identifiers"', function (done) { + it('shovel-nodes response should have property "identifiers"', function (done) { request(url) .get('/api/1.1/nodes') - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear for (item in JSON.parse(res.text)) { JSON.parse(res.text)[item].should.have.property('identifiers'); } done(); }); }); - }); - describe('shovel-unregister/{identifier}', function () { - it('if ironic id exist, response should include property: result: success', function (done) { + it('shovel-unregister/{identifier} if ironic id exist, response should include property: result: success', function (done) { request(url) .delete('/api/1.1/unregister/' + identifier) - // end handles the response .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).result.should.be.equal('success'); done(); }); }); - }); - describe('shovel-getconfig', function () { - it('', function (done) { + it('shovel-getconfig return success', function (done) { request(url) .get('/api/1.1/shovel/config') - // end handles the response .end(function (err, res) { if (err) { throw err; @@ -290,17 +248,14 @@ describe('Shovel api unit testing', function () { done(); }); }); - }); - describe('shovel-set ironic config', function () { - it('', function (done) { + it('shovel-set ironic config return success', function (done) { var body = { httpHost: "localhost" }; request(url) .post('/api/1.1/shovel/ironic/set-config') .send(body) .expect('Content-Type', /text/) - .expect(200) //Status code - // end handles the response + .expect(200) .end(function (err, res) { if (err) { throw err; @@ -309,17 +264,14 @@ describe('Shovel api unit testing', function () { done(); }); }); - }); - describe('shovel-set monorail config', function () { - it('', function (done) { + it('shovel-set monorail config return success', function (done) { var body = { httpHost: "localhost" }; request(url) .post('/api/1.1/shovel/monorail/set-config') .send(body) .expect('Content-Type', /text/) - .expect(200) //Status code - // end handles the response + .expect(200) .end(function (err, res) { if (err) { throw err; @@ -328,17 +280,14 @@ describe('Shovel api unit testing', function () { done(); }); }); - }); - describe('shovel-set glance config', function () { - it('', function (done) { + it('shovel-set glance config return success', function (done) { var body = { httpHost: "localhost" }; request(url) .post('/api/1.1/shovel/glance/set-config') .send(body) .expect('Content-Type', /text/) - .expect(200) //Status code - // end handles the response + .expect(200) .end(function (err, res) { if (err) { throw err; @@ -347,17 +296,14 @@ describe('Shovel api unit testing', function () { done(); }); }); - }); - describe('shovel-set keystone config', function () { - it('', function (done) { + it('shovel-set keystone config return success', function (done) { var body = { httpHost: "localhost" }; request(url) .post('/api/1.1/shovel/keystone/set-config') .send(body) .expect('Content-Type', /text/) - .expect(200) //Status code - // end handles the response + .expect(200) .end(function (err, res) { if (err) { throw err; @@ -366,53 +312,223 @@ describe('Shovel api unit testing', function () { done(); }); }); - }); -}); -describe('Shovel api register', function () { - var error_message = '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\": \\"Client\\", \\"faultstring\\": \\"A node with name 5668b42d8bee16a10989e4e4 already exists.\\"}"}'; - var body = { "id": identifier, "driver": "string", "ipmihost": "string", "ipmiusername": "string", "ipmipasswd": "string" }; - - - beforeEach('set up mocks', function () { - //monorail - //keystone - sinon.stub(keystone, 'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); - //ironic - sinon.stub(ironic, 'create_node').returns(Promise.resolve(error_message)); - }); - - afterEach('teardown mocks', function () { - //monorail - monorail['nodeDiskSize'].restore(); - monorail['get_node_memory_cpu'].restore(); - monorail['request_node_get'].restore(); - //keystone - keystone['authenticatePassword'].restore(); - //ironic - ironic['create_node'].restore(); - - }); - - describe('shovel-resgister', function () { - it('response in register should have property error_message when any of node info equal to 0 ', function (done) { - sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); - sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(0)); - sinon.stub(monorail, 'get_node_memory_cpu').returns(Promise.resolve({ cpus: 0, memory: 0 })); - + it('/api/1.1/glance/images should return property name', function (done) { request(url) - .post('/api/1.1/register') - .send(body) - .expect('Content-Type', /json/) - .expect(200) //Status code - // end handles the response + .get('/api/1.1/glance/images') .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear - JSON.parse(res.text).should.have.property('error_message'); + JSON.parse(res.text).images[0].should.have.property('name'); + done(); + }); + }); + }); + + describe('Shovel error handling test', function () { + var client = require('./../../lib/api/client'); + Promise.promisifyAll(client); + var body = { "id": identifier, "driver": "string", "ipmihost": "string", "ipmiusername": "string", "ipmipasswd": "string" }; + before('set up mocks', function () { + //set client to return an error + var output = ({ error: 'error_message' }); + sinon.stub(client, 'GetAsync').returns(Promise.reject(output)); + sinon.stub(client, 'PostAsync').returns(Promise.reject(output)); + }); + after('teardown mocks', function () { + client['GetAsync'].restore(); + client['PostAsync'].restore(); + }); + + it('/api/1.1/nodes/identifier should return error message', function (done) { + request(url) + .get('/api/1.1/nodes/' + identifier) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/nodes/catalogs/identifier should return error message', function (done) { + request(url) + .get('/api/1.1/catalogs/' + identifier) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/catalogs/{identifier}/{source} should return error message', function (done) { + request(url) + .get('/api/1.1/catalogs/123/bmc') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/nodes/{identifier} should return error message', function (done) { + request(url) + .get('/api/1.1/nodes/123') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/nodes/123/sel should return error message', function (done) { + request(url) + .get('/api/1.1/nodes/123/sel') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/ironic/nodes should return error message', function (done) { + request(url) + .get('/api/1.1/ironic/nodes') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/ironic/chassis/123 should return error message', function (done) { + request(url) + .get('/api/1.1/ironic/chassis/123') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/ironic/nodes/123 should return error message', function (done) { + request(url) + .get('/api/1.1/ironic/nodes/123') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/ironic/drivers should return error message', function (done) { + request(url) + .get('/api/1.1/ironic/drivers') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/glance/images should return error message', function (done) { + request(url) + .get('/api/1.1/glance/images') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/register should have property error when cant connect to server', function (done) { + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/api/1.1/unregister/ should fail if no connection to server', function (done) { + request(url) + .delete('/api/1.1/unregister/123') + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + it('/ironic/node/{identifier} should return error message', function (done) { + request(url) + .patch('/api/1.1/ironic/node/123') + .send([{}]) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error'); + done(); + }); + }); + }); + + describe('Shovel api unit test for register', function () { + var error_message = '{"error_message": "{\\"debuginfo\\": null, \\"faultcode\\": \\"Client\\", \\"faultstring\\": \\"A node with name 5668b42d8bee16a10989e4e4 already exists.\\"}"}'; + var body = { "id": identifier, "driver": "string", "ipmihost": "string", "ipmiusername": "string", "ipmipasswd": "string" }; + + beforeEach('set up mocks', function () { + //monorail + //keystone + sinon.stub(keystone, 'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); + //ironic + sinon.stub(ironic, 'create_node').returns(Promise.resolve(error_message)); + }); + afterEach('teardown mocks', function () { + //monorail + monorail['nodeDiskSize'].restore(); + monorail['get_node_memory_cpu'].restore(); + monorail['request_node_get'].restore(); + //keystone + keystone['authenticatePassword'].restore(); + //ironic + ironic['create_node'].restore(); + + }); + it('response in register should have property error_message when any of node info equal to 0 ', function (done) { + sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); + sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(0)); + sinon.stub(monorail, 'get_node_memory_cpu').returns(Promise.resolve({ cpus: 0, memory: 0 })); + + request(url) + .post('/api/1.1/register') + .send(body) + .expect('Content-Type', /json/) + .expect(200) + .end(function (err, res) { + if (err) { + throw err; + } + JSON.parse(res.text).should.have.property('error_message'); done(); }); }); @@ -420,23 +536,19 @@ describe('Shovel api register', function () { sinon.stub(monorail, 'request_node_get').returns(Promise.resolve(JSON.stringify(rackhdNode[0]))); sinon.stub(monorail, 'nodeDiskSize').returns(Promise.resolve(1)); sinon.stub(monorail, 'get_node_memory_cpu').returns(Promise.resolve({ cpus: 1, memory: 1 })); - + request(url) .post('/api/1.1/register') .send(body) .expect('Content-Type', /json/) - .expect(200) //Status code - // end handles the response + .expect(200) .end(function (err, res) { if (err) { throw err; } - // this is should.js syntax, very clear JSON.parse(res.text).should.have.property('error_message'); - done(); }); }); }); - }); diff --git a/Shovel/test/helper.js b/Shovel/test/helper.js index bb06159..c4ff2a7 100644 --- a/Shovel/test/helper.js +++ b/Shovel/test/helper.js @@ -1,7 +1,16 @@ +// Copyright 2015, EMC, Inc. + var app = require('connect')(); var http = require('http'); var swaggerTools = require('swagger-tools'); - +var sinon = require('sinon'); +var winston = require('winston'); +var logger = require('./../lib/services/logger'); +var loggerVar = new (winston.Logger)({ + levels: { verbose: 5, debug: 4, info: 3, warn: 2, error: 1, mask: 0 }, + colors: { verbose: 'cyan', debug: 'blue', info: 'green', warn: 'yellow', error: 'red' } +}) +.add(winston.transports.Console, { level: 'mask' }); var options = { swaggerUi: '/swagger.json', controllers: './controllers', @@ -9,7 +18,16 @@ var options = { }; var swaggerDoc = require('./../api/swagger.json'); +module.exports.maskLogger = function maskLogger() { + sinon.stub(logger, 'Logger').returns(loggerVar); +}; + +module.exports.restoreLogger = function restoreLogger() { + logger['Logger'].restore(); +}; + module.exports.startServer = function startServer() { + this.maskLogger(); swaggerTools.initializeMiddleware(swaggerDoc, function (middleware) { app.use(middleware.swaggerMetadata()); app.use(middleware.swaggerRouter(options)); @@ -17,11 +35,8 @@ module.exports.startServer = function startServer() { }); }; module.exports.stopServer = function stopServer() { + this.restoreLogger(); var net = require('net'); var socket = net.createConnection(9008); socket.end(); -}; - - - - +}; \ No newline at end of file diff --git a/Shovel/test/services/poller.js b/Shovel/test/services/poller.js index 6f9684d..8c997bf 100644 --- a/Shovel/test/services/poller.js +++ b/Shovel/test/services/poller.js @@ -1,20 +1,24 @@ -var request = require('supertest'); +// Copyright 2015, EMC, Inc. + var should = require('should'); -var assert = require('assert'); var sinon = require('sinon'); var monorail = require('./../../lib/api/monorail/monorail'); var ironic = require('./../../lib/api/openstack/ironic'); var keystone = require('./../../lib/api/openstack/keystone'); var Promise = require('bluebird'); -var Poller = require('./../../lib/services/poller'); +var client = require('./../../lib/api/client'); +Promise.promisifyAll(client); var _ = require('underscore'); +var helper = require('./../helper'); +//lib to be tested +var Poller; -describe('Shovel poller unit testing', function () { +describe('*****Shovel poller Class****', function () { var rackhdNode =[ { workflows: [], autoDiscover: false, identifiers: ["2c:60:0c:83:f5:d1"], name: "2c:60:0c:83:f5:d1", sku: null, type: "compute", id: "5668b6ad8bee16a10989e4e5" }]; var identifier = '9a761508-4eee-4065-b47b-45c22dff54c2'; var ironic_node_list = [ { uuid: "9a761508-4eee-4065-b47b-45c22dff54c2", extra: { name: "D51B-2U (dual 10G LoM)", eventre: "", nodeid: "564cefa014ee77be18e48efd", - timer: { start: "2015-11-30T21:14:11.753Z", finish: "2015-11-30T21:14:11.772Z", stop: false, isDone: true, timeInteval: 5000 }, eventcnt: 0 } }]; + timer: { start: "2015-11-30T21:14:11.753Z", finish: "2015-11-30T21:14:11.753Z", stop: false, isDone: true, timeInterval: 500 }, eventcnt: 0}}]; var nodePollers = [{ config: { command: "sel" }, id: "564dd86285fb1e7c72721543" }]; var _sel = { sel:[ { logId: "1", date: "12/03/2015", time: "08:54:11", sensorType: "Memory", sensorNumber: "#0x53", event: "Correctable ECC", value: "Asserted"} ] }; var keyToken = { access:{ token:{id:'123456'} } }; @@ -24,95 +28,198 @@ describe('Shovel poller unit testing', function () { var patchedData = [{ 'path': '/extra', 'value': extraPatch.extra, 'op': 'replace' }]; var pollerInstance; - before('start Poller service', function () { - pollerInstance = new Poller(5000);//timeInterval to 5s + before('mask logger', function () { + helper.maskLogger(); + Poller = require('./../../lib/services/poller'); }); - - beforeEach('set up mocks', function () { - sinon.stub(monorail, 'request_poller_get').returns(Promise.resolve(JSON.stringify(nodePollers))); - sinon.stub(monorail, 'request_poller_data_get').returns(Promise.resolve(JSON.stringify(_sel))); - sinon.stub(ironic,'patch_node').returns(Promise.resolve(JSON.stringify(extraPatch))); - sinon.stub(keystone,'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); - sinon.stub(ironic,'get_node_list').returns(Promise.resolve(JSON.stringify(ironic_node_list))); + after('restore logger', function () { + helper.restoreLogger(); }); + describe('Poller unit tests', function () { + before('start Poller service', function () { + pollerInstance = new Poller(5000);//timeInterval to 5s + }); - afterEach('teardown mocks', function () { - monorail['request_poller_get'].restore(); - monorail['request_poller_data_get'].restore(); - ironic['patch_node'].restore(); - keystone['authenticatePassword'].restore(); - ironic['get_node_list'].restore(); + beforeEach('set up mocks', function () { + sinon.stub(monorail, 'request_poller_get').returns(Promise.resolve(JSON.stringify(nodePollers))); + sinon.stub(monorail, 'request_poller_data_get').returns(Promise.resolve(JSON.stringify(_sel))); + sinon.stub(ironic, 'patch_node').returns(Promise.resolve(JSON.stringify(extraPatch))); + sinon.stub(keystone, 'authenticatePassword').returns(Promise.resolve(JSON.stringify(keyToken))); + sinon.stub(ironic, 'get_node_list').returns(Promise.resolve(JSON.stringify(ironic_node_list))); + sinon.stub(ironic, 'get_node').returns(Promise.resolve(JSON.stringify(ironic_node_list[0]))); + }); - }); + afterEach('teardown mocks', function () { + monorail['request_poller_get'].restore(); + monorail['request_poller_data_get'].restore(); + ironic['patch_node'].restore(); + keystone['authenticatePassword'].restore(); + ironic['get_node_list'].restore(); + ironic['get_node'].restore(); - it('pollerInstance.getSeldata()', function (done) { - return pollerInstance.getSeldata(identifier). - then(function (data) { - var result = JSON.parse(data); - result.should.have.property('sel'); - _.each(result[0],function(item){ - console.info(item); - item.should.have.property('sensorNumber'); - item.should.have.property('event'); + }); + + it('Poller.prototype.searchIronic have property name, timer', function (done) { + return pollerInstance.searchIronic(ironic_node_list[0]). + then(function (result) { + result.should.have.property('name'); + result.should.have.property('timer'); + done(); }); - done(); - }) - .catch (function(err){ - throw(err); }); - }); - it('Poller.prototype.updateInfo',function(done){ - return pollerInstance.updateInfo(identifier,extraPatch). - then(function(data){ - var result = data; - _.each(result,function(item){ - item.should.have.property('path'); - item.should.have.property('value'); + it('start server should call get nodes once', function (done) { + pollerInstance.startServer(0); + pollerInstance.stopServer(); + var callback = sinon.spy(); + pollerInstance.getNodes(callback); + callback.should.be.calledOnce; + done(); + }); + + it('start server should call runPoller once', function (done) { + pollerInstance.startServer(0); + pollerInstance.stopServer(); + var callback = sinon.spy(); + pollerInstance.runPoller(ironic_node_list, callback); + callback.should.be.calledOnce; + done(); + + }); + + it('start server should call searchIronic once', function (done) { + pollerInstance.startServer(0); + pollerInstance.stopServer(); + var callback = sinon.spy(); + pollerInstance.searchIronic(ironic_node_list[0], callback); + callback.should.be.calledOnce; + done(); + + }); + + it('pollerInstance.getSeldata() have property sensorNumber, event', function (done) { + return pollerInstance.getSeldata(identifier). + then(function (data) { + var result = JSON.parse(data); + result.should.have.property('sel'); + _.each(result[0], function (item) { + item.should.have.property('sensorNumber'); + item.should.have.property('event'); + }); + done(); + }) + .catch(function (err) { + throw (err); }); - done(); - }) - .catch(function(err){ - throw err; - }); - }); - - it('Poller.prototype.patchData',function(done){ - return Poller.prototype.patchData('uuid',JSON.stringify(patchedData)). - then(function(data){ - data.should.have.property('nodeid'); - data.should.have.property('timer'); - done(); - }) - .catch(function(err){ - throw(err); - done(); }); - }); - it('Poller.prototype.getNodes',function(done){ - return pollerInstance.getNodes(). - then(function(result){ - _.each(result,function(item){ - item.should.have.property('uuid'); - item.should.have.property('extra'); + it('Poller.prototype.updateInfo have property path, value', function (done) { + return pollerInstance.updateInfo(identifier, extraPatch). + then(function (data) { + var result = data; + _.each(result, function (item) { + item.should.have.property('path'); + item.should.have.property('value'); + }); + done(); + }) + .catch(function (err) { + throw err; }); - done(); - }) - .catch(function(err){ - throw(err); }); - }); - it('Poller.prototype.getToken',function(done){ - return pollerInstance.getToken(). - then(function(token){ - token.should.be.equal('123456'); - done(); - }) - .catch(function(err){ - throw err; + it('Poller.prototype.patchData should have property nodeid, timer', function (done) { + return Poller.prototype.patchData('uuid', JSON.stringify(patchedData)). + then(function (data) { + data.should.have.property('nodeid'); + data.should.have.property('timer'); + done(); + }) + .catch(function (err) { + throw (err); + done(); + }); + }); + + it('Poller.prototype.getNodes should have property uuid, extra', function (done) { + return pollerInstance.getNodes(). + then(function (result) { + _.each(result, function (item) { + item.should.have.property('uuid'); + item.should.have.property('extra'); + }); + done(); + }) + .catch(function (err) { + throw (err); + }); + }); + + it('Poller.prototype.getToken should return token 123456', function (done) { + return pollerInstance.getToken(). + then(function (token) { + token.should.be.equal('123456'); + done(); + }) + .catch(function (err) { + throw err; + }); + + }); + + }); + describe('client get/post returns error', function () { + before('set up mocks', function () { + //set client to return an error + pollerInstance = new Poller(5000);//timeInterval to 5s + var output = ({ error: 'error_message' }); + sinon.stub(client, 'GetAsync').returns(Promise.reject(output)); + sinon.stub(client, 'PostAsync').returns(Promise.reject(output)); + sinon.stub(client, 'PatchAsync').returns(Promise.reject(output)); + sinon.stub(client, 'PutAsync').returns(Promise.reject(output)); + sinon.stub(client, 'DeleteAsync').returns(Promise.reject(output)); + }); + after('teardown mocks', function () { + client['GetAsync'].restore(); + client['PostAsync'].restore(); + client['PatchAsync'].restore(); + client['PutAsync'].restore(); + client['DeleteAsync'].restore(); + }); + it('poller.get_token return null', function (done) { + return pollerInstance.getToken('123') + .then(function (result) { + (result === null).should.be.exactly(true); + done(); + }); + }); + it('poller.getSeldata return null', function (done) { + return pollerInstance.getSeldata('123') + .then(function (result) { + (result === null).should.be.exactly(true); + done(); + }); + }); + it('poller.updateInfo return json obj with property : path', function (done) { + return pollerInstance.updateInfo(identifier, extraPatch) + .then(function (result) { + result[0].should.have.property('path', '/extra'); + done(); + }); + }); + it('poller.patchData return null', function (done) { + return pollerInstance.patchData(identifier, extraPatch) + .then(function (result) { + (result === null).should.be.exactly(true); + done(); + }); + }); + it('poller.getNodes return null', function (done) { + return pollerInstance.getNodes() + .then(function (result) { + (result === null).should.be.exactly(true); + done(); + }); }); - }); });