1
0
mirror of https://github.com/node-red/node-red.git synced 2023-10-10 13:36:53 +02:00

Add node module update api and expose in palette editor

This commit is contained in:
Nick O'Leary 2017-01-21 23:46:44 +00:00
parent 0720128bd4
commit e27f5d0460
No known key found for this signature in database
GPG Key ID: 4F2157149161A6C9
10 changed files with 210 additions and 50 deletions

View File

@ -24,7 +24,6 @@
url: 'nodes', url: 'nodes',
success: function(data) { success: function(data) {
RED.nodes.setNodeList(data); RED.nodes.setNodeList(data);
var nsCount = 0; var nsCount = 0;
for (var i=0;i<data.length;i++) { for (var i=0;i<data.length;i++) {
var ns = data[i]; var ns = data[i];
@ -158,6 +157,9 @@
typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>"; typeList = "<ul><li>"+msg.types.join("</li><li>")+"</li></ul>";
RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success"); RED.notify(RED._("palette.event.nodeDisabled", {count:msg.types.length})+typeList,"success");
} }
} else if (topic == "node/upgraded") {
RED.notify(RED._("palette.event.nodeUpgraded", {module:msg.module,version:msg.version}),"success");
RED.nodes.registry.setModulePendingUpdated(msg.module,msg.version);
} }
// Refresh flow library to ensure any examples are updated // Refresh flow library to ensure any examples are updated
RED.library.loadFlowLibrary(); RED.library.loadFlowLibrary();

View File

@ -42,6 +42,10 @@ RED.nodes = (function() {
var nodeDefinitions = {}; var nodeDefinitions = {};
var exports = { var exports = {
setModulePendingUpdated: function(module,version) {
moduleList[module].pending_version = version;
RED.events.emit("registry:module-updated",{module:module,version:version});
},
getModule: function(module) { getModule: function(module) {
return moduleList[module]; return moduleList[module];
}, },
@ -78,6 +82,9 @@ RED.nodes = (function() {
local:ns.local, local:ns.local,
sets:{} sets:{}
}; };
if (ns.pending_version) {
moduleList[ns.module].pending_version = ns.pending_version;
}
moduleList[ns.module].sets[ns.name] = ns; moduleList[ns.module].sets[ns.name] = ns;
RED.events.emit("registry:node-set-added",ns); RED.events.emit("registry:node-set-added",ns);
}, },

View File

@ -31,6 +31,17 @@ RED.palette.editor = (function() {
var eventTimers = {}; var eventTimers = {};
var activeFilter = ""; var activeFilter = "";
function semVerCompare(A,B) {
var aParts = A.split(".").map(function(m) { return parseInt(m);});
var bParts = B.split(".").map(function(m) { return parseInt(m);});
for (var i=0;i<3;i++) {
var j = aParts[i]-bParts[i];
if (j<0) { return -1 }
if (j>0) { return 1 }
}
return 0;
}
function delayCallback(start,callback) { function delayCallback(start,callback) {
var delta = Date.now() - start; var delta = Date.now() - start;
if (delta < 300) { if (delta < 300) {
@ -64,14 +75,21 @@ RED.palette.editor = (function() {
}); });
}) })
} }
function installNodeModule(id,shade,callback) { function installNodeModule(id,version,shade,callback) {
var requestBody = {
module: id
};
if (callback === undefined) {
callback = shade;
shade = version;
} else {
requestBody.version = version;
}
shade.show(); shade.show();
$.ajax({ $.ajax({
url:"nodes", url:"nodes",
type: "POST", type: "POST",
data: JSON.stringify({ data: JSON.stringify(requestBody),
module: id
}),
contentType: "application/json; charset=utf-8" contentType: "application/json; charset=utf-8"
}).done(function(data,textStatus,xhr) { }).done(function(data,textStatus,xhr) {
shade.hide(); shade.hide();
@ -266,19 +284,19 @@ RED.palette.editor = (function() {
nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0)); nodeEntry.container.toggleClass("disabled",(activeTypeCount === 0));
} }
} }
if (moduleInfo.pending_version) {
nodeEntry.updateButton.hide(); nodeEntry.versionSpan.html(moduleInfo.version+' <i class="fa fa-long-arrow-right"></i> '+moduleInfo.pending_version).appendTo(nodeEntry.metaRow)
// if (loadedIndex.hasOwnProperty(module)) { nodeEntry.updateButton.html(RED._('palette.editor.updated')).addClass('disabled').show();
// if (moduleInfo.version !== loadedIndex[module].version) { } else if (loadedIndex.hasOwnProperty(module)) {
// nodeEntry.updateButton.show(); if (semVerCompare(loadedIndex[module].version,moduleInfo.version) === 1) {
// nodeEntry.updateButton.html(RED._('palette.editor.update',{version:loadedIndex[module].version})); nodeEntry.updateButton.show();
// } else { nodeEntry.updateButton.html(RED._('palette.editor.update',{version:loadedIndex[module].version}));
// nodeEntry.updateButton.hide(); } else {
// } nodeEntry.updateButton.hide();
// }
// } else { } else {
// nodeEntry.updateButton.hide(); nodeEntry.updateButton.hide();
// } }
} }
} }
@ -515,7 +533,7 @@ RED.palette.editor = (function() {
var titleRow = $('<div class="palette-module-meta palette-module-name"><i class="fa fa-cube"></i></div>').appendTo(headerRow); var titleRow = $('<div class="palette-module-meta palette-module-name"><i class="fa fa-cube"></i></div>').appendTo(headerRow);
$('<span>').html(entry.name).appendTo(titleRow); $('<span>').html(entry.name).appendTo(titleRow);
var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow); var metaRow = $('<div class="palette-module-meta palette-module-version"><i class="fa fa-tag"></i></div>').appendTo(headerRow);
$('<span>').html(entry.version).appendTo(metaRow); var versionSpan = $('<span>').html(entry.version).appendTo(metaRow);
var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow); var buttonRow = $('<div>',{class:"palette-module-meta"}).appendTo(headerRow);
var setButton = $('<a href="#" class="editor-button editor-button-small palette-module-set-button"><i class="fa fa-angle-right palette-module-node-chevron"></i> </a>').appendTo(buttonRow); var setButton = $('<a href="#" class="editor-button editor-button-small palette-module-set-button"><i class="fa fa-angle-right palette-module-node-chevron"></i> </a>').appendTo(buttonRow);
var setCount = $('<span>').appendTo(setButton); var setCount = $('<span>').appendTo(setButton);
@ -524,6 +542,19 @@ RED.palette.editor = (function() {
var updateButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.update')).appendTo(buttonGroup); var updateButton = $('<a href="#" class="editor-button editor-button-small"></a>').html(RED._('palette.editor.update')).appendTo(buttonGroup);
updateButton.click(function(evt) { updateButton.click(function(evt) {
evt.preventDefault(); evt.preventDefault();
if ($(this).hasClass('disabled')) {
return;
}
$("#palette-module-install-confirm").data('module',entry.name);
$("#palette-module-install-confirm").data('version',loadedIndex[entry.name].version);
$("#palette-module-install-confirm").data('shade',shade);
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.update.body"));
$(".palette-module-install-confirm-button-install").hide();
$(".palette-module-install-confirm-button-remove").hide();
$(".palette-module-install-confirm-button-update").show();
$("#palette-module-install-confirm")
.dialog('option', 'title', RED._("palette.editor.confirm.update.title"))
.dialog('open');
}) })
@ -536,6 +567,7 @@ RED.palette.editor = (function() {
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.remove.body")); $("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.remove.body"));
$(".palette-module-install-confirm-button-install").hide(); $(".palette-module-install-confirm-button-install").hide();
$(".palette-module-install-confirm-button-remove").show(); $(".palette-module-install-confirm-button-remove").show();
$(".palette-module-install-confirm-button-update").hide();
$("#palette-module-install-confirm") $("#palette-module-install-confirm")
.dialog('option', 'title', RED._("palette.editor.confirm.remove.title")) .dialog('option', 'title', RED._("palette.editor.confirm.remove.title"))
.dialog('open'); .dialog('open');
@ -555,6 +587,7 @@ RED.palette.editor = (function() {
setCount: setCount, setCount: setCount,
container: container, container: container,
shade: shade, shade: shade,
versionSpan: versionSpan,
sets: {} sets: {}
} }
setButton.click(function(evt) { setButton.click(function(evt) {
@ -732,6 +765,7 @@ RED.palette.editor = (function() {
$("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.install.body")); $("#palette-module-install-confirm-body").html(RED._("palette.editor.confirm.install.body"));
$(".palette-module-install-confirm-button-install").show(); $(".palette-module-install-confirm-button-install").show();
$(".palette-module-install-confirm-button-remove").hide(); $(".palette-module-install-confirm-button-remove").hide();
$(".palette-module-install-confirm-button-update").hide();
$("#palette-module-install-confirm") $("#palette-module-install-confirm")
.dialog('option', 'title', RED._("palette.editor.confirm.install.title")) .dialog('option', 'title', RED._("palette.editor.confirm.install.title"))
.dialog('open'); .dialog('open');
@ -807,12 +841,34 @@ RED.palette.editor = (function() {
} }
}) })
$( this ).dialog( "close" );
}
},
{
text: RED._("palette.editor.confirm.button.update"),
class: "primary palette-module-install-confirm-button-update",
click: function() {
var id = $(this).data('module');
var version = $(this).data('version');
var shade = $(this).data('shade');
shade.show();
installNodeModule(id,version,shade,function(xhr) {
if (xhr) {
if (xhr.responseJSON) {
RED.notify(RED._('palette.editor.errors.updateFailed',{module: id,message:xhr.responseJSON.message}));
}
}
});
$( this ).dialog( "close" ); $( this ).dialog( "close" );
} }
} }
] ]
}) })
RED.events.on('registry:module-updated', function(ns) {
refreshNodeModule(ns.module);
});
RED.events.on('registry:node-set-enabled', function(ns) { RED.events.on('registry:node-set-enabled', function(ns) {
refreshNodeModule(ns.module); refreshNodeModule(ns.module);
}); });

View File

@ -68,7 +68,8 @@
"warnings": { "warnings": {
"undeployedChanges": "node has undeployed changes", "undeployedChanges": "node has undeployed changes",
"nodeActionDisabled": "node actions disabled within subflow", "nodeActionDisabled": "node actions disabled within subflow",
"missing-types": "Flows stopped due to missing node types. Check logs for details." "missing-types": "Flows stopped due to missing node types. Check logs for details.",
"restartRequired": "Node-RED must be restarted to enable upgraded modules"
}, },
"error": "<strong>Error</strong>: __message__", "error": "<strong>Error</strong>: __message__",
@ -250,7 +251,8 @@
"nodeEnabled": "Node enabled:", "nodeEnabled": "Node enabled:",
"nodeEnabled_plural": "Nodes enabled:", "nodeEnabled_plural": "Nodes enabled:",
"nodeDisabled": "Node disabled:", "nodeDisabled": "Node disabled:",
"nodeDisabled_plural": "Nodes disabled:" "nodeDisabled_plural": "Nodes disabled:",
"nodeUpgraded": "Node module __module__ upgraded to version __version__"
}, },
"editor": { "editor": {
"title": "Manage palette", "title": "Manage palette",
@ -283,6 +285,7 @@
"disable": "disable", "disable": "disable",
"remove": "remove", "remove": "remove",
"update": "update to __version__", "update": "update to __version__",
"updated": "updated",
"install": "install", "install": "install",
"installed": "installed", "installed": "installed",
"loading": "Loading catalogues...", "loading": "Loading catalogues...",
@ -295,7 +298,8 @@
"errors": { "errors": {
"catalogLoadFailed": "Failed to load node catalogue.<br>Check the browser console for more information", "catalogLoadFailed": "Failed to load node catalogue.<br>Check the browser console for more information",
"installFailed": "Failed to install: __module__<br>__message__<br>Check the log for more information", "installFailed": "Failed to install: __module__<br>__message__<br>Check the log for more information",
"removeFailed": "Failed to remove: __module__<br>__message__<br>Check the log for more information" "removeFailed": "Failed to remove: __module__<br>__message__<br>Check the log for more information",
"updateFailed": "Failed to update: __module__<br>__message__<br>Check the log for more information"
}, },
"confirm": { "confirm": {
"install": { "install": {
@ -306,10 +310,15 @@
"body":"Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.", "body":"Removing the node will uninstall it from Node-RED. The node may continue to use resources until Node-RED is restarted.",
"title": "Remove nodes" "title": "Remove nodes"
}, },
"update": {
"body":"Updating the node will require a restart of Node-RED to complete the update. This must be done manually.",
"title": "Update nodes"
},
"button": { "button": {
"review": "Open node information", "review": "Open node information",
"install": "Install", "install": "Install",
"remove": "Remove" "remove": "Remove",
"update": "Update"
} }
} }

View File

@ -48,34 +48,42 @@ module.exports = {
} }
var node = req.body; var node = req.body;
var promise; var promise;
var isUpgrade = false;
if (node.module) { if (node.module) {
var module = redNodes.getModuleInfo(node.module); var module = redNodes.getModuleInfo(node.module);
if (module) { if (module) {
log.audit({event: "nodes.install",module:node.module,error:"module_already_loaded"},req); if (!node.version || module.version === node.version) {
res.status(400).json({error:"module_already_loaded", message:"Module already loaded"}); log.audit({event: "nodes.install",module:node.module, version:node.version, error:"module_already_loaded"},req);
return; res.status(400).json({error:"module_already_loaded", message:"Module already loaded"});
return;
}
isUpgrade = true;
} }
promise = redNodes.installModule(node.module); promise = redNodes.installModule(node.module,node.version);
} else { } else {
log.audit({event: "nodes.install",module:node.module,error:"invalid_request"},req); log.audit({event: "nodes.install",module:node.module,error:"invalid_request"},req);
res.status(400).json({error:"invalid_request", message:"Invalid request"}); res.status(400).json({error:"invalid_request", message:"Invalid request"});
return; return;
} }
promise.then(function(info) { promise.then(function(info) {
comms.publish("node/added",info.nodes,false); if (isUpgrade) {
comms.publish("node/upgraded",{module:node.module,version:node.version},false);
} else {
comms.publish("node/added",info.nodes,false);
}
if (node.module) { if (node.module) {
log.audit({event: "nodes.install",module:node.module},req); log.audit({event: "nodes.install",module:node.module,version:node.version},req);
res.json(info); res.json(info);
} }
}).otherwise(function(err) { }).otherwise(function(err) {
if (err.code === 404) { if (err.code === 404) {
log.audit({event: "nodes.install",module:node.module,error:"not_found"},req); log.audit({event: "nodes.install",module:node.module,version:node.version,error:"not_found"},req);
res.status(404).end(); res.status(404).end();
} else if (err.code) { } else if (err.code) {
log.audit({event: "nodes.install",module:node.module,error:err.code},req); log.audit({event: "nodes.install",module:node.module,version:node.version,error:err.code},req);
res.status(400).json({error:err.code, message:err.message}); res.status(400).json({error:err.code, message:err.message});
} else { } else {
log.audit({event: "nodes.install",module:node.module,error:err.code||"unexpected_error",message:err.toString()},req); log.audit({event: "nodes.install",module:node.module,version:node.version,error:err.code||"unexpected_error",message:err.toString()},req);
res.status(400).json({error:err.code||"unexpected_error", message:err.toString()}); res.status(400).json({error:err.code||"unexpected_error", message:err.toString()});
} }
}); });

View File

@ -24,12 +24,14 @@
"removed-types": "Removed node types:", "removed-types": "Removed node types:",
"install": { "install": {
"invalid": "Invalid module name", "invalid": "Invalid module name",
"installing": "Installing module: __name__", "installing": "Installing module: __name__, version: __version__",
"installed": "Installed module: __name__", "installed": "Installed module: __name__",
"install-failed": "Install failed", "install-failed": "Install failed",
"install-failed-long": "Installation of module __name__ failed:", "install-failed-long": "Installation of module __name__ failed:",
"install-failed-not-found": "$t(install-failed-long) module not found", "install-failed-not-found": "$t(install-failed-long) module not found",
"upgrading": "Upgrading module: __name__ to version: __version__",
"upgraded": "Upgraded module: __name__. Restart Node-RED to use the new version",
"upgrade-failed-not-found": "$t(server.install.install-failed-long) version not found",
"uninstalling": "Uninstalling module: __name__", "uninstalling": "Uninstalling module: __name__",
"uninstall-failed": "Uninstall failed", "uninstall-failed": "Uninstall failed",
"uninstall-failed-long": "Uninstall of module __name__ failed:", "uninstall-failed-long": "Uninstall of module __name__ failed:",

View File

@ -76,7 +76,7 @@ module.exports = {
addModule: addModule, addModule: addModule,
removeModule: registry.removeModule, removeModule: registry.removeModule,
installModule: installer.installModule, installModule: installer.installModule,
uninstallModule: installer.uninstallModule, uninstallModule: installer.uninstallModule,

View File

@ -59,33 +59,44 @@ function checkModulePath(folder) {
return moduleName; return moduleName;
} }
function checkExistingModule(module) { function checkExistingModule(module,version) {
if (registry.getModuleInfo(module)) { var info = registry.getModuleInfo(module);
// TODO: nls if (info) {
var err = new Error("Module already loaded"); if (!version || info.version === version) {
err.code = "module_already_loaded"; var err = new Error("Module already loaded");
throw err; err.code = "module_already_loaded";
throw err;
}
return true;
} }
return false;
} }
function installModule(module) { function installModule(module,version) {
//TODO: ensure module is 'safe' //TODO: ensure module is 'safe'
return when.promise(function(resolve,reject) { return when.promise(function(resolve,reject) {
var installName = module; var installName = module;
var isUpgrade = false;
try { try {
if (moduleRe.test(module)) { if (moduleRe.test(module)) {
// Simple module name - assume it can be npm installed // Simple module name - assume it can be npm installed
if (version) {
installName += "@"+version;
}
} else if (slashRe.test(module)) { } else if (slashRe.test(module)) {
// A path - check if there's a valid package.json // A path - check if there's a valid package.json
installName = module; installName = module;
module = checkModulePath(module); module = checkModulePath(module);
} }
checkExistingModule(module); isUpgrade = checkExistingModule(module,version);
} catch(err) { } catch(err) {
return reject(err); return reject(err);
} }
log.info(log._("server.install.installing",{name: module})); if (!isUpgrade) {
log.info(log._("server.install.installing",{name: module,version: version||"latest"}));
} else {
log.info(log._("server.install.upgrading",{name: module,version: version||"latest"}));
}
var installDir = settings.userDir || process.env.NODE_RED_HOME || "."; var installDir = settings.userDir || process.env.NODE_RED_HOME || ".";
var child = child_process.execFile(npmCommand,['install','--production',installName], var child = child_process.execFile(npmCommand,['install','--production',installName],
@ -94,10 +105,17 @@ function installModule(module) {
}, },
function(err, stdin, stdout) { function(err, stdin, stdout) {
if (err) { if (err) {
var lookFor404 = new RegExp(" 404 .*"+installName+"$","m"); var e;
var lookFor404 = new RegExp(" 404 .*"+module+"$","m");
var lookForVersionNotFound = new RegExp("version not found: "+module+"@"+version,"m");
if (lookFor404.test(stdout)) { if (lookFor404.test(stdout)) {
log.warn(log._("server.install.install-failed-not-found",{name:module})); log.warn(log._("server.install.install-failed-not-found",{name:module}));
var e = new Error("Module not found"); e = new Error("Module not found");
e.code = 404;
reject(e);
} else if (isUpgrade && lookForVersionNotFound.test(stdout)) {
log.warn(log._("server.install.upgrade-failed-not-found",{name:module}));
e = new Error("Module not found");
e.code = 404; e.code = 404;
reject(e); reject(e);
} else { } else {
@ -108,8 +126,14 @@ function installModule(module) {
reject(new Error(log._("server.install.install-failed"))); reject(new Error(log._("server.install.install-failed")));
} }
} else { } else {
log.info(log._("server.install.installed",{name:module})); if (!isUpgrade) {
resolve(require("./index").addModule(module).then(reportAddedModules)); log.info(log._("server.install.installed",{name:module}));
resolve(require("./index").addModule(module).then(reportAddedModules));
} else {
log.info(log._("server.install.upgraded",{name:module, version:version}));
events.emit("runtime-event",{id:"restart-required",type:"warning",text:"notification.warnings.restartRequired"});
resolve(require("./registry").setModulePendingUpdated(module,version));
}
} }
} }
); );

View File

@ -82,7 +82,8 @@ function getNode(id) {
function saveNodeList() { function saveNodeList() {
var moduleList = {}; var moduleList = {};
var hadPending = false;
var hasPending = false;
for (var module in moduleConfigs) { for (var module in moduleConfigs) {
/* istanbul ignore else */ /* istanbul ignore else */
if (moduleConfigs.hasOwnProperty(module)) { if (moduleConfigs.hasOwnProperty(module)) {
@ -94,6 +95,15 @@ function saveNodeList() {
local: moduleConfigs[module].local||false, local: moduleConfigs[module].local||false,
nodes: {} nodes: {}
}; };
if (moduleConfigs[module].hasOwnProperty('pending_version')) {
hadPending = true;
if (moduleConfigs[module].pending_version !== moduleConfigs[module].version) {
moduleList[module].pending_version = moduleConfigs[module].pending_version;
hasPending = true;
} else {
delete moduleConfigs[module].pending_version;
}
}
} }
var nodes = moduleConfigs[module].nodes; var nodes = moduleConfigs[module].nodes;
for(var node in nodes) { for(var node in nodes) {
@ -111,6 +121,9 @@ function saveNodeList() {
} }
} }
} }
if (hadPending && !hasPending) {
events.emit("runtime-event",{id:"restart-required"});
}
if (settings.available()) { if (settings.available()) {
return settings.set("nodes",moduleList); return settings.set("nodes",moduleList);
} else { } else {
@ -280,6 +293,9 @@ function getNodeList(filter) {
if (nodes.hasOwnProperty(node)) { if (nodes.hasOwnProperty(node)) {
var nodeInfo = filterNodeInfo(nodes[node]); var nodeInfo = filterNodeInfo(nodes[node]);
nodeInfo.version = moduleConfigs[module].version; nodeInfo.version = moduleConfigs[module].version;
if (moduleConfigs[module].pending_version) {
nodeInfo.pending_version = moduleConfigs[module].pending_version;
}
if (!filter || filter(nodes[node])) { if (!filter || filter(nodes[node])) {
list.push(nodeInfo); list.push(nodeInfo);
} }
@ -539,6 +555,12 @@ function cleanModuleList() {
saveNodeList(); saveNodeList();
} }
} }
function setModulePendingUpdated(module,version) {
moduleConfigs[module].pending_version = version;
return saveNodeList().then(function() {
return getModuleInfo(module);
});
}
var registry = module.exports = { var registry = module.exports = {
init: init, init: init,
@ -552,6 +574,7 @@ var registry = module.exports = {
enableNodeSet: enableNodeSet, enableNodeSet: enableNodeSet,
disableNodeSet: disableNodeSet, disableNodeSet: disableNodeSet,
setModulePendingUpdated: setModulePendingUpdated,
removeModule: removeModule, removeModule: removeModule,
getNodeInfo: getNodeInfo, getNodeInfo: getNodeInfo,

View File

@ -46,6 +46,9 @@ describe('nodes/registry/installer', function() {
if (registry.getModuleInfo.restore) { if (registry.getModuleInfo.restore) {
registry.getModuleInfo.restore(); registry.getModuleInfo.restore();
} }
if (typeRegistry.getModuleInfo.restore) {
typeRegistry.getModuleInfo.restore();
}
if (require('fs').statSync.restore) { if (require('fs').statSync.restore) {
require('fs').statSync.restore(); require('fs').statSync.restore();
@ -64,6 +67,32 @@ describe('nodes/registry/installer', function() {
done(); done();
}); });
}); });
it("rejects when npm does not find specified version", function(done) {
sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
cb(new Error(),""," version not found: this_wont_exist@0.1.2");
});
sinon.stub(typeRegistry,"getModuleInfo", function() {
return {
version: "0.1.1"
}
});
installer.installModule("this_wont_exist","0.1.2").otherwise(function(err) {
err.code.should.be.eql(404);
done();
});
});
it("rejects when update requested to existing version", function(done) {
sinon.stub(typeRegistry,"getModuleInfo", function() {
return {
version: "0.1.1"
}
});
installer.installModule("this_wont_exist","0.1.1").otherwise(function(err) {
err.code.should.be.eql('module_already_loaded');
done();
});
});
it("rejects with generic error", function(done) { it("rejects with generic error", function(done) {
sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) { sinon.stub(child_process,"execFile",function(cmd,args,opt,cb) {
cb(new Error("test_error"),"",""); cb(new Error("test_error"),"","");