diff --git a/package.json b/package.json index be2fec970..bd52aa85a 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "mqtt": "2.18.0", "multer": "1.3.0", "mustache": "2.3.0", + "node-json-db": "0.7.5", "node-red-node-email": "0.1.*", "node-red-node-feedparser": "0.1.*", "node-red-node-rbe": "0.2.*", @@ -101,6 +102,7 @@ "http-proxy": "^1.16.2", "istanbul": "0.4.5", "mocha": "^5.1.1", + "rewire": "3.0.2", "should": "^8.4.0", "sinon": "1.17.7", "stoppable": "^1.0.6", diff --git a/red/runtime/index.js b/red/runtime/index.js index 6577ee59c..30d9c6f2b 100644 --- a/red/runtime/index.js +++ b/red/runtime/index.js @@ -109,6 +109,8 @@ function start() { events.emit("runtime-event",{id:"runtime-unsupported-version",payload:{type:"error",text:"notification.errors.unsupportedVersion"},retain:true}); } log.info(os.type()+" "+os.release()+" "+os.arch()+" "+os.endianness()); + log.info("Loading external context plugins"); + redNodes.loadContextsPlugin(); return redNodes.load().then(function() { var i; diff --git a/red/runtime/nodes/context/external.js b/red/runtime/nodes/context/external.js deleted file mode 100644 index d969fc53d..000000000 --- a/red/runtime/nodes/context/external.js +++ /dev/null @@ -1,133 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ - -var when = require("when"); -var log = require("../../log"); - -var re = /^(\$.*?)\.(.+)|^(\$.*)/; -var externalContexts; - -function parseKey(key){ - var keys = null; - var temp = re.exec(key); - if(temp){ - keys = []; - if(temp[3]){ - keys[0] = temp[3]; - keys[1] = null; - } else { - keys[0] = temp[1]; - keys[1] = temp[2]; - } - keys[0] = keys[0] === "$" ? "default" : keys[0].substring(1); - } - return keys; -} - -function hasContextStorage(plugin) { - return externalContexts.hasOwnProperty(plugin); -} - -var contextModuleInterface ={ - init: function (settings) { - var plugins = settings.contextStorage; - externalContexts = {}; - if (plugins) { - for(var pluginName in plugins){ - var plugin; - if(plugins[pluginName].hasOwnProperty("module") && plugins[pluginName].hasOwnProperty("config")) { - if(typeof plugins[pluginName].module === "string") { - try{ - plugin = require(plugins[pluginName].module); - }catch(err){ - log.error(err); - continue; - } - } else { - plugin = plugins[pluginName].module; - } - plugin.init(plugins[pluginName].config); - externalContexts[pluginName] = plugin; - } - } - } - }, - get: function(key, flowId) { - var result = parseKey(key); - if(!result){ - throw new Error("Invalid key"); - } - if(hasContextStorage(result[0])){ - return externalContexts[result[0]].get(result[1], flowId); - }else if(hasContextStorage("default")) { - // log.warn(result[1] " is got from default context storage"); - return externalContexts["default"].get(result[1], flowId); - }else{ - throw new Error(result[0] + " is not defined in setting.js"); - } - - }, - set: function(key, value, flowId) { - var result = parseKey(key); - if(!result){ - throw new Error("Invalid key"); - } - if(hasContextStorage(result[0])){ - externalContexts[result[0]].set(result[1], value, flowId); - }else if(hasContextStorage("default")) { - // log.warn(result[1] " is set to default context storage"); - externalContexts["default"].set(result[1], value, flowId); - }else{ - throw new Error(result[0] + " is not defined in setting.js"); - } - }, - keys: function(key, flowId) { - var result = parseKey(key); - if(!result){ - throw new Error("Invalid key"); - } - if(hasContextStorage(result[0])){ - return externalContexts[result[0]].keys(flowId); - }else if(hasContextStorage("default")) { - // log.warn("keys are got from default context storage"); - externalContexts["default"].keys(flowId); - }else{ - throw new Error(result[0] + " is not defined in setting.js"); - } - }, - // run: function(command,key,value,flowId) { - // //todo: run custom method in plugin - // }, - // close: function(){ - // //todo: close connections, streams, etc... - // }, - canUse: function(key){ - var result = parseKey(key); - if(!result){ - return false; - }else{ - if(hasContextStorage(result[0]) || hasContextStorage("default")){ - return true; - }else{ - return false; - } - } - }, - hasContextStorage: hasContextStorage, - parseKey: parseKey -}; - -module.exports = contextModuleInterface; \ No newline at end of file diff --git a/red/runtime/nodes/context/index.js b/red/runtime/nodes/context/index.js index cd78f25de..845408e8f 100644 --- a/red/runtime/nodes/context/index.js +++ b/red/runtime/nodes/context/index.js @@ -14,57 +14,110 @@ * limitations under the License. **/ +var clone = require("clone"); var util = require("../../util"); -var log = require("../../log"); -var externalContext = require("./external"); +var settings; var contexts = {}; var globalContext = null; +var externalContexts = {}; -var re = /^(\$.*?)\.(.+)|^(\$.*)/; +function init(_settings) { + settings = _settings; + externalContexts = {}; -function createContext(id,seed) { - var flowId = id; - var data = seed || {}; - var obj = seed || {}; + // init meomory plugin + externalContexts["_"] = require("./memory"); + externalContexts["_"].init(); + globalContext = createContext("global",settings.functionGlobalContext || {}); +} - function get(key) { - return util.getMessageProperty(data,key); - }; - function set(key, value) { - util.setMessageProperty(data,key,value); - }; - function keys() { - var keysData = Object.keys(data); - if (seed == null) { - return keysData; - } else { - return keysData.filter(function (key) { - return key !== "set" && key !== "get" && key !== "keys"; - }); +function load() { + // load & init plugins in settings.contextStorage + var plugins = settings.contextStorage; + var alias = null; + if (plugins) { + for(var pluginName in plugins){ + if(pluginName === "_"){ + continue; + } + if(pluginName === "default" && typeof plugins[pluginName] === "string"){ + alias = plugins[pluginName]; + continue; + } + var plugin; + if(plugins[pluginName].hasOwnProperty("module")){ + var config = plugins[pluginName].config || {}; + copySettings(config, settings); + try{ + plugin = require("./"+plugins[pluginName].module); + }catch(err){ + throw new Error(plugins[pluginName].module + " could not be loaded"); + } + plugin.init(config); + externalContexts[pluginName] = plugin; + }else{ + throw new Error("module is is not defined in settings.contextStorage." + plugins[pluginName] ); + } } - }; - - obj.get = function(key) { - if(externalContext.canUse(key)) { - return externalContext.get(key, flowId); - }else{ - return get(key); - } - }; - obj.set = function(key, value) { - if(externalContext.canUse(key)) { - externalContext.set(key, value, flowId); - }else{ - set(key, value); + if(alias){ + if(externalContexts.hasOwnProperty(alias)){ + externalContexts["default"] = externalContexts[alias]; + }else{ + throw new Error("default is invalid" + plugins["default"]) + } } } - obj.keys = function(key) { - if(externalContext.canUse(key)) { - return externalContext.keys(key, flowId); - }else{ - return keys(); +} + +function copySettings(config, settings){ + var copy = ["userDir"] + config.settings = {}; + copy.forEach(function(setting){ + config.settings[setting] = clone(settings[setting]); + }); +} + +function createContext(id,seed) { + var scope = id; + var obj = seed || {}; + + obj.get = function(key) { + var result = parseKey(key); + if(!result){ + return externalContexts["_"].get(key, scope); } + if(externalContexts.hasOwnProperty(result[0])){ + return externalContexts[result[0]].get(result[1], scope); + }else if(externalContexts.hasOwnProperty("defalut")){ + return externalContexts["defalut"].get(result[1], scope); + }else{ + throw new Error(result[0] + " is not defined in setting.js"); + } + }; + obj.set = function(key, value) { + var result = parseKey(key); + if(!result){ + return externalContexts["_"].set(key, value, scope); + } + if(externalContexts.hasOwnProperty(result[0])){ + externalContexts[result[0]].set(result[1], value, scope); + }else if(externalContexts.hasOwnProperty("defalut")){ + externalContexts["defalut"].set(result[1], value, scope); + }else{ + throw new Error(result[0] + " is not defined in setting.js"); + } + }; + obj.keys = function() { + //TODO: discuss about keys() behavior + var keys = []; + for(var plugin in externalContexts){ + keys.concat(externalContexts[plugin].keys(scope)); + } + return keys; + }; + if(id === "global"){ + externalContexts["_"].setGlobalContext(seed); } return obj; } @@ -72,7 +125,7 @@ function createContext(id,seed) { function getContext(localId,flowId) { var contextId = localId; if (flowId) { - contextId = localId+"_"+flowId; + contextId = localId+":"+flowId; } if (contexts.hasOwnProperty(contextId)) { return contexts[contextId]; @@ -91,7 +144,10 @@ function getContext(localId,flowId) { function deleteContext(id,flowId) { var contextId = id; if (flowId) { - contextId = id+"_"+flowId; + contextId = id+":"+flowId; + } + for(var plugin in externalContexts){ + externalContexts[plugin].delete(contextId); } delete contexts[contextId]; } @@ -102,19 +158,36 @@ function clean(flowConfig) { var node; for (var id in contexts) { if (contexts.hasOwnProperty(id)) { - var idParts = id.split("_"); + var idParts = id.split(":"); if (!flowConfig.allNodes.hasOwnProperty(idParts[0])) { + for(var plugin in externalContexts){ + externalContexts[plugin].delete(id); + } delete contexts[id]; } } } } +function parseKey(key){ + var keys = null; + if(!key){ + return null; + } + var index_$ = key.indexOf("$"); + var index_dot = key.indexOf(".", 1); + if(index_$ === 0 && index_dot){ + keys = []; + keys[0] = key.substring(1,index_dot); + keys[1] = key.substring(index_dot + 1); + keys[0] = keys[0] || "default"; + } + return keys; +} + module.exports = { - init: function(settings) { - globalContext = createContext("global",settings.functionGlobalContext || {}); - externalContext.init(settings); - }, + init: init, + load: load, get: getContext, delete: deleteContext, clean:clean diff --git a/red/runtime/nodes/context/localfilesystem.js b/red/runtime/nodes/context/localfilesystem.js index 733c619d1..e77954189 100644 --- a/red/runtime/nodes/context/localfilesystem.js +++ b/red/runtime/nodes/context/localfilesystem.js @@ -14,67 +14,105 @@ * limitations under the License. **/ -var storage = require('node-persist'); +var JsonDB = require('node-json-db'); var fs = require('fs-extra'); -var fspath = require("path"); +var path = require("path"); var configs; -var storagePath; -var fileStorages; +var storageBaseDir; +var storages; -function createStorage(path) { - var fileStorage = storage.create({dir: fspath.join(storagePath,path)}); - fileStorage.initSync(); - fileStorages[path] = fileStorage; +function createStorage(scope) { + var i = scope.indexOf(":") + + if(i === -1){ + if(scope === "global"){ + storages[scope] = new JsonDB(path.join(storageBaseDir,"global",scope), true, true); + }else{ // scope:flow + storages[scope] = new JsonDB(path.join(storageBaseDir,scope,"flow"), true, true); + } + }else{ // scope:local + var ids = scope.split(":") + storages[scope] = new JsonDB(path.join(storageBaseDir,ids[1],ids[0]), true, true); + } } var localfilesystem = { init: function(_configs) { configs = _configs; - fileStorages = {}; + storages = {}; if (!configs.dir) { - try { - fs.statSync(fspath.join(process.env.NODE_RED_HOME,".config.json")); - storagePath = fspath.join(process.env.NODE_RED_HOME,"contexts"); - } catch(err) { + if(configs.settings && configs.settings.userDir){ + storageBaseDir = path.join(configs.settings.userDir,"contexts"); + }else{ try { - // Consider compatibility for older versions - if (process.env.HOMEPATH) { - fs.statSync(fspath.join(process.env.HOMEPATH,".node-red",".config.json")); - storagePath = fspath.join(process.env.HOMEPATH,".node-red","contexts"); - } + fs.statSync(path.join(process.env.NODE_RED_HOME,".config.json")); + storageBaseDir = path.join(process.env.NODE_RED_HOME,"contexts"); } catch(err) { - } - if (!configs.dir) { - storagePath = fspath.join(process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH || process.env.NODE_RED_HOME,".node-red","contexts"); + try { + // Consider compatibility for older versions + if (process.env.HOMEPATH) { + fs.statSync(path.join(process.env.HOMEPATH,".node-red",".config.json")); + storageBaseDir = path.join(process.env.HOMEPATH,".node-red","contexts"); + } + } catch(err) { + } + if (!storageBaseDir) { + storageBaseDir = path.join(process.env.HOME || process.env.USERPROFILE || process.env.HOMEPATH || process.env.NODE_RED_HOME,".node-red","contexts"); + } } } }else{ - storagePath = configs.dir; + storageBaseDir = configs.dir; } - }, - stop: function() { - return when.resolve(); - }, - get: function (key, flowId) { - if(!fileStorages[flowId]){ - createStorage(flowId); + get: function (key, scope) { + if(!storages[scope]){ + createStorage(scope); + } + try{ + storages[scope].reload(); + return storages[scope].getData("/" + key.replace(/\./g,"/")); + }catch(err){ + if(err.name === "DataError"){ + return undefined; + }else{ + throw err; + } } - return fileStorages[flowId].getItemSync(key); }, - set: function (key, value, flowId) { - if(!fileStorages[flowId]){ - createStorage(flowId); + set: function (key, value, scope) { + if(!storages[scope]){ + createStorage(scope); + } + if(value){ + storages[scope].push("/" + key.replace(/\./g,"/"), value); + }else{ + storages[scope].delete("/" + key.replace(/\./g,"/")); } - fileStorages[flowId].setItemSync(key, value); }, - keys: function (flowId) { - if(!fileStorages[flowId]){ - createStorage(flowId); + keys: function (scope) { + if(!storages[scope]){ + return []; + } + return Object.keys(storages[scope].getData("/")); + }, + delete: function(scope){ + if(storages[scope]){ + storages[scope].delete("/"); + if(scope.indexOf(":") === -1){ + fs.removeSync(path.dirname(storages[scope].filename)); + }else{ + try{ + fs.statSync(storages[scope].filename); + fs.unlinkSync(storages[scope].filename); + }catch(err){ + console.log("deleted"); + } + } + delete storages[scope]; } - return fileStorages[flowId].keys(); } }; diff --git a/red/runtime/nodes/context/memory.js b/red/runtime/nodes/context/memory.js new file mode 100644 index 000000000..deed13d36 --- /dev/null +++ b/red/runtime/nodes/context/memory.js @@ -0,0 +1,59 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var util = require("../../util"); + +var data; +var seedFlg = false; + +var memory = { + init: function(config) { + data = {}; + }, + get: function(key, scope) { + if(!data[scope]){ + data[scope] = {}; + } + return util.getMessageProperty(data[scope],key); + }, + set: function(key, value, scope) { + if(!data[scope]){ + data[scope] = {}; + } + util.setMessageProperty(data[scope],key,value); + }, + keys: function(scope){ + if(!data[scope]){ + data[scope] = {}; + } + var keysData = Object.keys(data[scope]); + if (scope !== "global") { + return keysData; + } else { + return keysData.filter(function (key) { + return key !== "set" && key !== "get" && key !== "keys"; + }); + } + }, + delete: function(scope){ + delete data[scope]; + }, + setGlobalContext: function(seed){ + data["global"] = seed; + } +}; + +module.exports = memory; \ No newline at end of file diff --git a/red/runtime/nodes/index.js b/red/runtime/nodes/index.js index b2f8ff3b5..139f62ffa 100644 --- a/red/runtime/nodes/index.js +++ b/red/runtime/nodes/index.js @@ -216,5 +216,8 @@ module.exports = { setCredentialSecret: credentials.setKey, clearCredentials: credentials.clear, exportCredentials: credentials.export, - getCredentialKeyType: credentials.getKeyType + getCredentialKeyType: credentials.getKeyType, + + // Contexts + loadContextsPlugin: context.load }; diff --git a/test/red/runtime/nodes/context/external_spec.js b/test/red/runtime/nodes/context/external_spec.js deleted file mode 100644 index 726d1c666..000000000 --- a/test/red/runtime/nodes/context/external_spec.js +++ /dev/null @@ -1,222 +0,0 @@ -/** - * Copyright JS Foundation and other contributors, http://js.foundation - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - **/ -var should = require("should"); -var sinon = require('sinon'); -var external = require("../../../../../red/runtime/nodes/context/external"); - -describe("external", function() { - var stubModule = { - module: { - init: function(){}, - get: function(){}, - set: function(){}, - keys: function(){}, - run: function(){}, - close: function(){} - }, - config: {} - }; - describe('#init()', function() { - it('should load bundle module as default', function() { - external.init({ - contextStorage:{ - default:{ - module: "./localfilesystem", - config:{} - } - }}); - external.hasContextStorage("default").should.be.true(); - }); - - it('should load bundle module as localfile', function() { - external.init({ - contextStorage:{ - localfile:{ - module: "./localfilesystem", - config:{} - } - }}); - external.hasContextStorage("localfile").should.be.true(); - }); - - it('should not load non-existent module', function() { - external.init({ - contextStorage:{ - default:{ - module: "non-existent-module", - config:{} - } - - }}) - external.hasContextStorage("default").should.be.false(); - }); - - it('should load multiple modules', function() { - external.init({ - contextStorage:{ - default:{ - module: "./localfilesystem", - config:{} - }, - test:{ - module: { - init: function() { - return true; - } - }, - config:{} - } - } - }); - external.hasContextStorage("default").should.be.true(); - external.hasContextStorage("test").should.be.true(); - }); - - it('should load multiple modules without non-existent module', function() { - external.init({ - contextStorage:{ - nonexist:{ - module: "non-existent-module", - config:{} - }, - default:{ - module: "./localfilesystem", - config:{} - }, - test:{ - module: { - init: function() { - return true; - } - }, - config:{} - } - } - }); - external.hasContextStorage("nonexist").should.be.false(); - external.hasContextStorage("default").should.be.true(); - external.hasContextStorage("test").should.be.true(); - }); - }); - - // describe('#get()', function() { - // }); - - // describe('#set()', function() { - // }); - - // describe('#keys()', function() { - // }); - - // describe('#hasContextStorage()', function() { - // }); - - describe('#canUse()', function() { - it('should return true if specified module is loaded', function() { - external.init({ - contextStorage:{ - localfilesystem:{ - module: {name:"test",init: function(){return true}}, - config: {} - } - } - }); - external.canUse("$localfilesystem").should.be.true(); - external.canUse("$localfilesystem.foo").should.be.true(); - }); - it('should return false if specified module is not loaded', function() { - external.init({ - contextStorage:{ - localfilesystem:{ - module: {name:"test",init: function(){return true}}, - config: {} - } - } - }); - external.canUse("$file").should.be.false(); - external.canUse("$file.foo").should.be.false(); - }); - it('should return true if specified module is not loaded but default module is loaded', function() { - external.init({ - contextStorage:{ - default:{ - module: {name:"test",init: function(){return true}}, - config: {} - } - } - }); - external.canUse("$file").should.be.true(); - external.canUse("$file.foo").should.be.true(); - }); - it('should return false if argument does not contain module name', function() { - external.init({ - contextStorage:{ - default:{ - module: {name:"test",init: function(){return true}}, - config: {} - } - } - }); - external.canUse("file").should.be.false(); - external.canUse("file.foo").should.be.false(); - }); - }); - - describe('#parseKey()', function() { - function returnModuleAndKey(input, expectedModule, expectedKey) { - var result = external.parseKey(input); - result[0].should.eql(expectedModule); - result[1].should.eql(expectedKey); - }; - - function returnModule(input, expectedModule) { - var result = external.parseKey(input); - result[0].should.eql(expectedModule); - should(result[1]).be.null(); - }; - - it('should retrun module and key', function() { - returnModuleAndKey("$test.aaa","test","aaa"); - returnModuleAndKey("$test.aaa.bbb","test","aaa.bbb"); - returnModuleAndKey("$1.234","1","234"); - returnModuleAndKey("$$test.foo","$test","foo"); - returnModuleAndKey("$test.$foo","test","$foo"); - returnModuleAndKey("$test.$foo.$bar","test","$foo.$bar"); - returnModuleAndKey("$test..foo","test",".foo"); - returnModuleAndKey("$test..","test","."); - }); - - it('should retrun only module', function() { - returnModule("$test","test",null); - returnModule("$1","1",null); - returnModule("$$test","$test",null); - returnModule("$test.","test.",null); - }); - - it('should retrun module as default', function() { - returnModuleAndKey("$default.foo","default","foo"); - returnModuleAndKey("$.foo","default","foo"); - returnModule("$default","default"); - returnModule("$","default"); - }); - - it('should retrun null', function() { - should(external.parseKey("test.aaa")).be.null(); - should(external.parseKey("test")).be.null(); - should(external.parseKey(null)).be.null(); - }); - }); -}); \ No newline at end of file diff --git a/test/red/runtime/nodes/context/index_spec.js b/test/red/runtime/nodes/context/index_spec.js index fce39bcf4..1bcea888d 100644 --- a/test/red/runtime/nodes/context/index_spec.js +++ b/test/red/runtime/nodes/context/index_spec.js @@ -18,6 +18,7 @@ var should = require("should"); var sinon = require('sinon'); var path = require('path'); var fs = require('fs-extra'); +var rewire = require("rewire"); var Context = require("../../../../../red/runtime/nodes/context/index"); describe('context', function() { @@ -147,6 +148,7 @@ describe('context', function() { var stubGet = sinon.stub(); var stubSet = sinon.stub(); var stubKeys = sinon.stub(); + var stubDelete = sinon.stub(); var contextStorage={ test:{ module: { @@ -156,6 +158,7 @@ describe('context', function() { get: stubGet, set: stubSet, keys: stubKeys, + delete: stubDelete }, config:{} } @@ -235,4 +238,52 @@ describe('context', function() { }); }); }); + + describe('#parseKey()', function() { + var parseKey = rewire("../../../../../red/runtime/nodes/context/index").__get__("parseKey"); + + function returnModuleAndKey(input, expectedModule, expectedKey) { + var result = parseKey(input); + result[0].should.eql(expectedModule); + result[1].should.eql(expectedKey); + }; + + function returnModule(input, expectedModule) { + var result = parseKey(input); + result[0].should.eql(expectedModule); + should(result[1]).be.null(); + }; + + it('should retrun module and key', function() { + returnModuleAndKey("$test.aaa","test","aaa"); + returnModuleAndKey("$test.aaa.bbb","test","aaa.bbb"); + returnModuleAndKey("$1.234","1","234"); + returnModuleAndKey("$$test.foo","$test","foo"); + returnModuleAndKey("$test.$foo","test","$foo"); + returnModuleAndKey("$test.$foo.$bar","test","$foo.$bar"); + returnModuleAndKey("$test..foo","test",".foo"); + returnModuleAndKey("$test..","test","."); + }); + + // it('should retrun only module', function() { + // returnModule("$test","test",null); + // returnModule("$1","1",null); + // returnModule("$$test","$test",null); + // returnModule("$test.","test.",null); + // }); + + it('should retrun module as default', function() { + returnModuleAndKey("$default.foo","default","foo"); + returnModuleAndKey("$.foo","default","foo"); + // returnModule("$default","default"); + // returnModule("$","default"); + }); + + it('should retrun null', function() { + should(parseKey("test.aaa")).be.null(); + should(parseKey("test")).be.null(); + should(parseKey(null)).be.null(); + }); + }); + }); diff --git a/test/red/runtime/nodes/context/localfilesystem_spec.js b/test/red/runtime/nodes/context/localfilesystem_spec.js index e69de29bb..d0d545413 100644 --- a/test/red/runtime/nodes/context/localfilesystem_spec.js +++ b/test/red/runtime/nodes/context/localfilesystem_spec.js @@ -0,0 +1,126 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var should = require('should'); +var fs = require('fs-extra'); +var path = require("path"); +var context = require('../../../../../red/runtime/nodes/context/localfilesystem'); + +var resourcesDir = path.resolve(path.join(__dirname,"..","resources","context")); + +describe('localfilesystem',function() { + + beforeEach(function() { + context.init({dir: resourcesDir}); + }); + + afterEach(function() { + context.delete("nodeX"); + context.delete("nodeY"); + }); + + after(function() { + fs.removeSync(resourcesDir); + }); + + describe('#get/set',function() { + it('should store property',function() { + should.not.exist(context.get("foo","nodeX")); + context.set("foo","test","nodeX"); + context.get("foo","nodeX").should.eql("test"); + }); + + it('should store property - creates parent properties',function() { + context.set("foo.bar","test","nodeX"); + context.get("foo","nodeX").should.eql({bar:"test"}); + }); + + it('should delete property',function() { + context.set("foo.abc.bar1","test1","nodeX"); + context.set("foo.abc.bar2","test2","nodeX"); + context.get("foo.abc","nodeX").should.eql({bar1:"test1",bar2:"test2"}); + context.set("foo.abc.bar1",undefined,"nodeX"); + context.get("foo.abc","nodeX").should.eql({bar2:"test2"}); + context.set("foo.abc",undefined,"nodeX"); + should.not.exist(context.get("foo.abc","nodeX")); + context.set("foo",undefined,"nodeX"); + should.not.exist(context.get("foo","nodeX")); + }); + + it('should not shared context with other scope', function() { + should.not.exist(context.get("foo","nodeX")); + should.not.exist(context.get("foo","nodeY")); + context.set("foo","testX","nodeX"); + context.set("foo","testY","nodeY"); + + context.get("foo","nodeX").should.eql("testX"); + context.get("foo","nodeY").should.eql("testY"); + }); + }); + + describe('#keys',function() { + it('should enumerate context keys', function() { + var keys = context.keys("nodeX"); + keys.should.be.an.Array(); + keys.should.be.empty(); + + context.set("foo","bar","nodeX"); + keys = context.keys("nodeX"); + keys.should.have.length(1); + keys[0].should.eql("foo"); + + context.set("abc.def","bar","nodeX"); + keys = context.keys("nodeX"); + keys.should.have.length(2); + keys[1].should.eql("abc"); + }); + + it('should enumerate context keys in each scopes', function() { + var keysX = context.keys("nodeX"); + keysX.should.be.an.Array(); + keysX.should.be.empty(); + + var keysY = context.keys("nodeY"); + keysY.should.be.an.Array(); + keysY.should.be.empty(); + + context.set("foo","bar","nodeX"); + context.set("hoge","piyo","nodeY"); + keysX = context.keys("nodeX"); + keysX.should.have.length(1); + keysX[0].should.eql("foo"); + + keysY = context.keys("nodeY"); + keysY.should.have.length(1); + keysY[0].should.eql("hoge"); + }); + }); + + describe('#delete',function() { + it('should delete context',function() { + should.not.exist(context.get("foo","nodeX")); + should.not.exist(context.get("foo","nodeY")); + context.set("foo","abc","nodeX"); + context.set("foo","abc","nodeY"); + context.get("foo","nodeX").should.eql("abc"); + context.get("foo","nodeY").should.eql("abc"); + + context.delete("nodeX"); + should.not.exist(context.get("foo","nodeX")); + should.exist(context.get("foo","nodeY")); + }); + }); +}); \ No newline at end of file diff --git a/test/red/runtime/nodes/context/memory_spec.js b/test/red/runtime/nodes/context/memory_spec.js new file mode 100644 index 000000000..59acd67e1 --- /dev/null +++ b/test/red/runtime/nodes/context/memory_spec.js @@ -0,0 +1,141 @@ +/** + * Copyright JS Foundation and other contributors, http://js.foundation + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + **/ + +var should = require('should'); +var context = require('../../../../../red/runtime/nodes/context/memory'); + +describe('memory',function() { + + beforeEach(function() { + context.init({}); + }); + + describe('#get/set',function() { + it('should store property',function() { + should.not.exist(context.get("foo","nodeX")); + context.set("foo","test","nodeX"); + context.get("foo","nodeX").should.eql("test"); + }); + + it('should store property - creates parent properties',function() { + context.set("foo.bar","test","nodeX"); + context.get("foo","nodeX").should.eql({bar:"test"}); + }); + + it('should delete property',function() { + context.set("foo.abc.bar1","test1","nodeX"); + context.set("foo.abc.bar2","test2","nodeX"); + context.get("foo.abc","nodeX").should.eql({bar1:"test1",bar2:"test2"}); + context.set("foo.abc.bar1",undefined,"nodeX"); + context.get("foo.abc","nodeX").should.eql({bar2:"test2"}); + context.set("foo.abc",undefined,"nodeX"); + should.not.exist(context.get("foo.abc","nodeX")); + context.set("foo",undefined,"nodeX"); + should.not.exist(context.get("foo","nodeX")); + }); + + it('should not shared context with other scope', function() { + should.not.exist(context.get("foo","nodeX")); + should.not.exist(context.get("foo","nodeY")); + context.set("foo","testX","nodeX"); + context.set("foo","testY","nodeY"); + + context.get("foo","nodeX").should.eql("testX"); + context.get("foo","nodeY").should.eql("testY"); + }); + }); + + describe('#keys',function() { + it('should enumerate context keys', function() { + var keys = context.keys("nodeX"); + keys.should.be.an.Array(); + keys.should.be.empty(); + + context.set("foo","bar","nodeX"); + keys = context.keys("nodeX"); + keys.should.have.length(1); + keys[0].should.eql("foo"); + + context.set("abc.def","bar","nodeX"); + keys = context.keys("nodeX"); + keys.should.have.length(2); + keys[1].should.eql("abc"); + }); + + it('should enumerate context keys in each scopes', function() { + var keysX = context.keys("nodeX"); + keysX.should.be.an.Array(); + keysX.should.be.empty(); + + var keysY = context.keys("nodeY"); + keysY.should.be.an.Array(); + keysY.should.be.empty(); + + context.set("foo","bar","nodeX"); + context.set("hoge","piyo","nodeY"); + keysX = context.keys("nodeX"); + keysX.should.have.length(1); + keysX[0].should.eql("foo"); + + keysY = context.keys("nodeY"); + keysY.should.have.length(1); + keysY[0].should.eql("hoge"); + }); + + it('should enumerate only context keys when GlobalContext was given', function() { + var keys = context.keys("global"); + keys.should.be.an.Array(); + keys.should.be.empty(); + + var data = { + foo: "bar" + } + context.setGlobalContext(data); + keys = context.keys("global"); + keys.should.have.length(1); + keys[0].should.eql("foo"); + }); + }); + + describe('#delete',function() { + it('should delete context',function() { + should.not.exist(context.get("foo","nodeX")); + should.not.exist(context.get("foo","nodeY")); + context.set("foo","abc","nodeX"); + context.set("foo","abc","nodeY"); + context.get("foo","nodeX").should.eql("abc"); + context.get("foo","nodeY").should.eql("abc"); + + context.delete("nodeX"); + should.not.exist(context.get("foo","nodeX")); + should.exist(context.get("foo","nodeY")); + }); + }); + + describe('#setGlobalContext',function() { + it('should initialize global context with argument', function() { + var keys = context.keys("global"); + keys.should.be.an.Array(); + keys.should.be.empty(); + + var data = { + foo: "bar" + } + context.setGlobalContext(data); + context.get("foo","global").should.eql("bar"); + }); + }); +}); \ No newline at end of file