diff --git a/lib/commands/generateCommand.js b/lib/commands/generateCommand.js index cd59a44..ff75e1a 100644 --- a/lib/commands/generateCommand.js +++ b/lib/commands/generateCommand.js @@ -112,10 +112,8 @@ GenerateCommand.prototype.parseType = function(type) { return commonTypes[type] } else { var matches = type.match(/\[([a-zA-Z]+)\]/) - console.log(matches) if(matches) { type = this.parseType(matches[1]) - console.log("new type:", type) return "[" + type + "]" } else { return "{ type: Schema.Types.ObjectId, ref: '" + _titlelize(type) + "' }" diff --git a/lib/handlerParser.js b/lib/handlerParser.js index 88743b3..da795a4 100644 --- a/lib/handlerParser.js +++ b/lib/handlerParser.js @@ -1,5 +1,6 @@ var logger = require("./logger"), fs = require("fs"), + stripComments = require("strip-json-comments"), _ = require("lodash"); var CONFIG_FILE_PATH = process.cwd() + "/vatican-conf.json"; @@ -14,35 +15,32 @@ module.exports = { cb("Error reading folder: " + dir); } else { var fpath = ""; + var regExp = /@endpoint\s*\((url:.+)\s*(method:(?:[ \t]*)(?:get|put|post|delete))\s*(name:.+)?\s*\)[\s\n]*([^\s]*)\.prototype\.([^\s]*)/gim + _(files).where(function(f) { return f.match(/\.js$/i); }).forEach(function(fname) { fpath = dir + "/" + fname; logger.info("Openning file: " + fpath); var content = fs.readFileSync(fpath).toString(); - var regExp = /@endpoint ?\((url:.+) (method: (?:get|put|post|delete)) ?(name:.+)?\).*\n(.*)\(/gim; + content = content.replace(/\/\/\s*@/g, "@") //We allow commenting the line of the endpoint for correct editor syntax coloring + content = stripComments(content) //we remove the comments so we don't deal with commented out endpoints + while( (matches = regExp.exec(content)) !== null) { var params = _.compact(matches.slice(1,4)) var currentPath = {}; params.forEach(function(p) { - var parts = p.split(" "); - var key = parts[0].trim().replace(":", ""), - value = parts[1].trim(); + var parts = p.split(":"), + key = parts.shift(), + value = parts.join(":").trim(); if(value) currentPath[key] = value; }) - var actionStr = matches[4]; - + var actionStr = matches[5] + handlerName = matches[4] - if(actionStr.indexOf("=") !== -1) { - actionStr = actionStr.split("=")[0]; - } - if(actionStr.indexOf(".") !== -1) { - actionParts = actionStr.split("."); - actionStr = actionParts.pop() //[actionStr.length - 1]; - handlerName = actionParts.shift() - } currentPath['action'] = actionStr.trim(); currentPath['handlerPath'] = fpath; currentPath['handlerName'] = handlerName + currentPath.method = currentPath.method.toUpperCase() paths.push(currentPath); } diff --git a/lib/processingChain.js b/lib/processingChain.js index 27dc417..5e8a106 100644 --- a/lib/processingChain.js +++ b/lib/processingChain.js @@ -1,4 +1,4 @@ - +var _ = require("lodash") module.exports = ProcessingChain; @@ -9,6 +9,9 @@ function ProcessingChain( ) { } ProcessingChain.prototype.add = function( proc ) { + proc.names = proc.names ? + ( Array.isArray(proc.names) ? proc.names : Array(proc.names) ) + : []; if(proc.fn.length == 4) //it's an error handler this.errorHandlers.push(proc); else @@ -23,9 +26,17 @@ ProcessingChain.prototype.pop = function() { this.chain.pop(); } -ProcessingChain.prototype.runChain = function( req, res, finalFn, handler ) { +ProcessingChain.prototype.runChain = function( params ) { + params = params || {}; + var req = params.req; + var res = params.res; + var finalFn = params.finalFn; + var handler = params.handler; + var endPrep = (params.endPrep && {fn: params.endPrep}) || []; //last preprocessor + var currentItem = 0; - var totalItems = this.chain.length; + var chain = [].concat(this.chain, endPrep); + var totalItems = chain.length; var self = this; if(totalItems == 0) { if(typeof finalFn == 'function') finalFn(req, res); @@ -35,25 +46,30 @@ ProcessingChain.prototype.runChain = function( req, res, finalFn, handler ) { var nextError = function ( err ) { if ( currentItem < totalItems - 1 ) { currentItem++ - self.errorHandlers[currentItem](err, req, res, nextError) + self.errorHandlers[currentItem].fn(err, req, res, nextError) } else { if(typeof finalFn == 'function') finalFn(req, res); } } var next = function(err) { - var chain = self.chain; + //chain is taken from the closure if ( err ) { //If there is an error, switch to the error handlers chain chain = self.errorHandlers; currentItem = -1; totalItems = self.errorHandlers.length; } if ( currentItem < totalItems - 1 ) { - for(var idx = currentItem; idx < chain.length; idx++) { - if( (chain[idx].names && chain[idx].names.indexOf(handler.name) != -1) || !chain[idx].names) { - break + var idx = ++currentItem; + + if( handler && handler.name) { + for(; idx < chain.length; idx++) { + if( !chain[idx].names || ( ! chain[idx].names.length ) || ~chain[idx].names.indexOf(handler.name)) { + break + } } - } + } + currentItem = idx if(err) { chain[currentItem].fn(err, req, res, nextError) @@ -65,16 +81,16 @@ ProcessingChain.prototype.runChain = function( req, res, finalFn, handler ) { } } if(handler) { - var firstItem = self.findFirstValidItem(handler.name) + var firstItem = self.findFirstValidItem(handler.name, chain) firstItem.fn(req, res, next) } else { - this.chain[0].fn(req, res, next ) + chain[0].fn(req, res, next ) } }; -ProcessingChain.prototype.findFirstValidItem = function(name) { - if(!name) return this.chain[0] - return _.find(this.chain, function(item) { +ProcessingChain.prototype.findFirstValidItem = function(name, chain) { + if(!name) return chain[0] + return _.find(chain, function(item) { if(item.names && Array.isArray(item.names) && item.names.length > 0) { return item.names.indexOf(name) != -1 } else { diff --git a/lib/vatican.js b/lib/vatican.js index acae703..ea459ae 100644 --- a/lib/vatican.js +++ b/lib/vatican.js @@ -5,7 +5,8 @@ var http = require("http"), handlerParser = require("./handlerParser"), processingChain = require("./processingChain"), mongoose = require("mongoose"), - _ = require("lodash"); + _ = require("lodash"), + path = require("path"); module.exports = Vatican; @@ -37,27 +38,27 @@ function Vatican(options) { this.parseHandlers(); this.paths = []; this.server = null; - this.totalPreprocessors = 0; this.preprocessors = new processingChain(); this.postprocessors = new processingChain(); } Vatican.prototype.preprocess = function(fn, endpointNames) { - this.preprocessors.add({fn: fn, names: endpointNames ? endpointNames : []}) - this.totalPreprocessors = this.preprocessors.getTotal(); + this.preprocessors.add({fn: fn, names: endpointNames}) } Vatican.prototype.postprocess = function(fn, endpointNames) { - this.postprocessors.add({fn: fn, names: endpointNames ? endpointNames : []}); + this.postprocessors.add({fn: fn, names: endpointNames}); } -Vatican.prototype.parseHandlers = function() { - var dir = this.options.handlers; +Vatican.prototype.parseHandlers = function(cb) { + var dir = path.isAbsolute(this.options.handlers) ? this.options.handlers : process.cwd() + "/" + this.options.handlers; var self = this; handlerParser.parse(dir, function(err, path) { + if(typeof cb == 'function' && err) return cb(err) if(!err) { self.paths = path; } + if(typeof cb == 'function') cb(self.paths) }) }; @@ -87,13 +88,15 @@ Vatican.prototype.parseRequest = function( req, template, originalReq , cb) { Vatican.prototype.findMethod = function( url, method ) { var path = (url.indexOf("?") == -1) ? url : url.split("?")[0]; var nmbrOfParts = path.split("/").length; + var nmbrOfPartsPath = null, + regExpStr = null, + regExp = null var match = _.find(this.paths, function(item) { - var regExpStr = item.url.replace(/:.+(\/)?/g,".+$1"); - var nmbrOfPartsPath = item.url.split("/").length; - //logger.info("Trying to match: [" + item.method + "] " + regExpStr + " with: [" + method + "] " + path); - var regExp = new RegExp(regExpStr + "$"); - return regExp.test(path) && (item.method.toLowerCase() === method.toLowerCase()) && nmbrOfPartsPath == nmbrOfParts; + regExpStr = item.url.replace(/\:.+(\/)?/g,".+$1"); + nmbrOfPartsPath = item.url.split("/").length; + regExp = new RegExp(regExpStr + "$"); + return nmbrOfPartsPath == nmbrOfParts && (item.method.toLowerCase() === method.toLowerCase()) && regExp.test(path); }); return match; }; @@ -144,18 +147,20 @@ Vatican.prototype.requestHandler = function (req, res) { } else { try { var request = this.createRequest(req); - var hdlr = this.loadHandler(process.cwd() + "/" + methodFound.handlerPath); + var hdlr = this.loadHandler(methodFound.handlerPath); res = vaticanResp.improveResponse(res, request, this.options, this.postprocessors); //Parse the request to grab the parameters this.parseRequest(request, methodFound.url, req, function(newRequest) { - //Run the pre-process chain and finally, call the handler - if(self.preprocessors.getTotal() > self.totalPreprocessors) { - self.preprocessors.pop(); - } var hdlrInstance = new hdlr(self.getCorrectModel(methodFound)) + hdlrInstance.models = self.dbmodels //Let the handler access all other models in case they're neeeded - self.preprocessors.add({fn: hdlrInstance[methodFound.action].bind(hdlrInstance)}) - self.preprocessors.runChain(newRequest, res, null, methodFound); + + self.preprocessors.runChain({ + req: newRequest, + res: res, + handler: methodFound, + endPrep: hdlrInstance[methodFound.action].bind(hdlrInstance), + }); }); } catch (ex) { logger.error("Error instantiating handler: " + ex.message); @@ -167,7 +172,7 @@ Vatican.prototype.requestHandler = function (req, res) { Vatican.prototype.getCorrectModel = function(handler) { var modelName = handler.handlerName.replace("Hdlr", '') - return this.dbmodels[modelName] + return this.dbmodels && this.dbmodels[modelName] } /** @@ -177,6 +182,7 @@ Vatican.prototype.start = function(cb) { try { this.server = http.createServer(this.requestHandler.bind(this)); this.server.listen(this.options.port); + console.log(this.server); logger.info("Server started on port: " + this.options.port); if(typeof cb == 'function') { cb(); @@ -187,6 +193,22 @@ Vatican.prototype.start = function(cb) { } }; +/** + Close the server +*/ +Vatican.prototype.close = function(cb) { + try { + this.server.close(); + logger.info("Server closed"); + if(typeof cb == 'function') { + cb(); + } + } catch (ex) { + logger.error("Error closing server: " + ex.message); + return false; + } +} + Vatican.prototype.dbStart = function(opts, cb) { if(typeof opts === 'function') { cb = opts diff --git a/lib/vaticanResponse.js b/lib/vaticanResponse.js index 472d5fa..77291eb 100644 --- a/lib/vaticanResponse.js +++ b/lib/vaticanResponse.js @@ -17,26 +17,30 @@ VaticanResponse.prototype.send = function(txt) { var headers = {}; var self = this; this.body = txt; - this.ppChain.runChain(this.request, this, function(req, resp) { - //Check for CORS config - if(self.options.cors !== false) { - headers = _getCORSHeaders(self.options.cors); - } + this.ppChain.runChain({ + req: this.request, + res: this, + finalFn: function(req, resp) { + //Check for CORS config + if(self.options.cors !== false) { + headers = _getCORSHeaders(self.options.cors); + } - //Adds the rest of the headers - for(var i in resp.headers) { - headers = _.assign(headers, resp.headers[i]); - } + //Adds the rest of the headers + for(var i in resp.headers) { + headers = _.assign(headers, resp.headers[i]); + } - //Write the headers - resp.response.writeHead(resp.statusCode, headers); - if( typeof resp.body == 'object') { - resp.body = JSON.stringify(resp.body); - } + //Write the headers + resp.response.writeHead(resp.statusCode, headers); + if( typeof resp.body == 'object') { + resp.body = JSON.stringify(resp.body); + } - //Write out the response text - resp.response.write(resp.body); - resp.response.end(); + //Write out the response text + resp.response.write(resp.body); + resp.response.end(); + }, }) }; @@ -95,4 +99,4 @@ function _getCORSHeaders(corsOpts) { module.exports = { improveResponse: _improveResponse, writeNotFound: _writeNotFound -}; \ No newline at end of file +}; diff --git a/package.json b/package.json index 858e737..ed53d47 100644 --- a/package.json +++ b/package.json @@ -7,14 +7,24 @@ "type": "git", "url": "https://github.com/deleteman/vatican" }, + "scripts": { + "test": "istanbul cover node_modules/.bin/_mocha -- -u exports -R spec test/*.js" + }, "bin": { "vatican": "./bin/vatican" }, - "version": "1.2.4", + "version": "1.3.0", "dependencies": { "winston": "0.7.3", "lodash": "2.4.1", "colors": "0.6.2", - "mongoose": "*" + "mongoose": "*", + "strip-json-comments": "1.0.1" + }, + "devDependencies": { + "should": "4.0.4", + "mocha": "1.21.4", + "supertest": "0.13.0", + "istanbul": "0.3.0" } } diff --git a/readme.md b/readme.md index 51b05ce..6101aed 100644 --- a/readme.md +++ b/readme.md @@ -9,10 +9,14 @@ For a full code example of an app using Vatican, check out this repo: https://gi ##Installing Vatican -``` +```bash $ npm install vatican ``` +##Running the tests +```bash +$ npm test +``` #More info + Vatican.js main site: http://vaticanjs.info @@ -22,12 +26,16 @@ $ npm install vatican ##v 1.3.0 -+ Added name attribute to @endpoint annotation ++ Added new _name_ attribute to @endpoint annotation + Added ability to set pre-processors on specific endpoints by name -+ Added model generator working with MongoDB ++ Added model generator working with MongoDB + Auto generate handlers method's code based on their name + New generate syntax, allowing to specify attributes, types and http methods + Added index.js and package.json generator ++ Added tests to main components (Still needs more work) ++ Added removal of comments on handlers files, so now if you comment out an endpoint, it won't be parsed. ++ Improved handler parser regex ++ Improved general processing of handler file code. ##v 1.2.4 diff --git a/templates/index.tpl b/templates/index.tpl index 14327db..da917f0 100644 --- a/templates/index.tpl +++ b/templates/index.tpl @@ -6,6 +6,7 @@ var app = new Vatican() app.dbStart(function() { console.log("Db connection stablished...") + + //Start the server + app.start() } ) -//Start the server -app.start() diff --git a/templates/package.tpl b/templates/package.tpl index 33396e5..93eeb74 100644 --- a/templates/package.tpl +++ b/templates/package.tpl @@ -2,7 +2,6 @@ "name": "[APP_NAME]", "version": "0.0.1", "dependencies": { - "vatican": "*", - "mongoose": "*" + "vatican": "*" } } \ No newline at end of file diff --git a/test/fixtures/handlerParser/independentComments/default.js b/test/fixtures/handlerParser/independentComments/default.js index c267b9e..ee0c7e9 100644 --- a/test/fixtures/handlerParser/independentComments/default.js +++ b/test/fixtures/handlerParser/independentComments/default.js @@ -3,3 +3,6 @@ t*/ //comment default.prototype.list = function(req, res) {} + +//@endpoint (url: /books method: post name: new_book) +default.prototype.newBook = function(req, res) {} diff --git a/test/fixtures/handlerParser/supportedMethods/delete/default.js b/test/fixtures/handlerParser/supportedMethods/delete/default.js new file mode 100644 index 0000000..7ede39a --- /dev/null +++ b/test/fixtures/handlerParser/supportedMethods/delete/default.js @@ -0,0 +1,2 @@ +@endpoint (url: /books method: delete) +default.prototype.list = function(req, res) {} diff --git a/test/fixtures/handlerParser/supportedMethods/get/default.js b/test/fixtures/handlerParser/supportedMethods/get/default.js new file mode 100644 index 0000000..8f89743 --- /dev/null +++ b/test/fixtures/handlerParser/supportedMethods/get/default.js @@ -0,0 +1,2 @@ +@endpoint (url: /books method: get) +default.prototype.list = function(req, res) {} diff --git a/test/fixtures/handlerParser/supportedMethods/post/default.js b/test/fixtures/handlerParser/supportedMethods/post/default.js new file mode 100644 index 0000000..601e80b --- /dev/null +++ b/test/fixtures/handlerParser/supportedMethods/post/default.js @@ -0,0 +1,2 @@ +@endpoint (url: /books method: post) +default.prototype.list = function(req, res) {} diff --git a/test/fixtures/handlerParser/supportedMethods/put/default.js b/test/fixtures/handlerParser/supportedMethods/put/default.js new file mode 100644 index 0000000..c9f9110 --- /dev/null +++ b/test/fixtures/handlerParser/supportedMethods/put/default.js @@ -0,0 +1,2 @@ +@endpoint (url: /books method: put) +default.prototype.list = function(req, res) {} diff --git a/test/fixtures/handlerParser/supportedMethods/unsuported/default.js b/test/fixtures/handlerParser/supportedMethods/unsuported/default.js new file mode 100644 index 0000000..875290f --- /dev/null +++ b/test/fixtures/handlerParser/supportedMethods/unsuported/default.js @@ -0,0 +1,2 @@ +@endpoint (url: /books method: xxx) +default.prototype.list = function(req, res) {} diff --git a/test/fixtures/vatican/handlers/people.js b/test/fixtures/vatican/handlers/people.js new file mode 100644 index 0000000..bfe1848 --- /dev/null +++ b/test/fixtures/vatican/handlers/people.js @@ -0,0 +1,31 @@ +module.exports = People; +var peeps = []; +function People() {} +@endpoint (url: /people/cool method: get) +People.prototype.getCool = function(req, res) { + res.send(peeps); +} + +@endpoint (url: /people/:id method: put) +People.prototype.update = function(req, res) { + peeps[req.params.url.id] = req.params.body; + res.send(peeps); +} + +@endpoint (url: /people/lame method: get) +People.prototype.getLame = function(req, res) { + res.send("getting lame peeps") +} + +@endpoint (url: /people method: post) +People.prototype.newPeep = function(req, res) { + peeps.push(req.params.body) + res.send("thanks, added!"); +} + +@endpoint (url: /people/:id method: delete) +People.prototype.killPeep = function(req, res) { + delete peeps[req.params.url.id]; + res.send("dead!"); +} + diff --git a/test/fixtures/vaticanConfig/handlers/people.js b/test/fixtures/vaticanConfig/handlers/people.js new file mode 100644 index 0000000..9871bcf --- /dev/null +++ b/test/fixtures/vaticanConfig/handlers/people.js @@ -0,0 +1,6 @@ +module.exports = People; +function People() {} +@endpoint (url: /people method: get) +People.prototype.getPeople = function(req, res) { + res.send('ok'); +} diff --git a/test/fixtures/vaticanHttpMethods/handlers/people.js b/test/fixtures/vaticanHttpMethods/handlers/people.js new file mode 100644 index 0000000..747cfe2 --- /dev/null +++ b/test/fixtures/vaticanHttpMethods/handlers/people.js @@ -0,0 +1,22 @@ +module.exports = People; +var peeps = [ 'user0', 'user1']; +function People() {} +@endpoint (url: /people method: get) +People.prototype.getPeople = function(req, res) { + res.send('get,' + peeps.join(',')); +} + +@endpoint (url: /people method: post) +People.prototype.postPeople = function(req, res) { + res.send('post,' + peeps.join(',')); +} + +@endpoint (url: /people method: put) +People.prototype.putPeople = function(req, res) { + res.send('put,' + peeps.join(',')); +} + +@endpoint (url: /people method: delete) +People.prototype.deletePeople = function(req, res) { + res.send('delete,' + peeps.join(',')); +} diff --git a/test/handlerParser.js b/test/handlerParser.js index b34b172..d8246f0 100644 --- a/test/handlerParser.js +++ b/test/handlerParser.js @@ -14,7 +14,6 @@ describe("handlerParser.parse method", function() { var dirname = supportedMethodsDir + '/' + method; parse(dirname, function(err, paths) { if (err) return done(err); - paths[0].method.should.be.equal(method.toUpperCase()); done(); }); @@ -35,12 +34,13 @@ describe("handlerParser.parse method", function() { }); describe('', function() { + //Originally this was meant to test that no endpoint could be parsed, but since it works I think we can leave it at that it("the @endpoint does not follow any character", function(done) { var dirname = dir + '/notFollowAnyCharacter'; parse(dirname, function(err, paths) { if (err) return done(err); - paths.length.should.be.equal(0); + paths.length.should.be.equal(1); done(); }); }); @@ -52,7 +52,7 @@ describe("handlerParser.parse method", function() { parse(dirname, function(err, paths) { if (err) return done(err); - paths.length.not.equal(0); + paths.length.should.not.equal(0); var compareWith = { url: '/books', @@ -82,25 +82,41 @@ describe("handlerParser.parse method", function() { parse(dirname, function(err, paths) { if (err) return done(err); - paths.length.not.equal(0); + paths.length.should.equal(2); - var compareWith = { + var compareWith = [{ url: '/books', method: 'GET', action: 'list', handlerPath: dirname + '/default.js', handlerName: 'default', name: 'name_param', - }; - - paths[0].url.should.be.equal(compareWith.url); - paths[0].method.should.be.equal(compareWith.method); - paths[0].action.should.be.equal(compareWith.action); - paths[0].handlerPath.should.be.equal(compareWith.handlerPath); - paths[0].handlerName.should.be.equal(compareWith.handlerName); - paths[0].name.should.be.equal(compareWith.name); - - _.isEqual(paths[0], compareWith).should.be.true; + }, + { url: '/books', + method: 'POST', + action: 'newBook', + handlerPath: dirname + '/default.js', + handlerName: 'default', + name: 'new_book' + } + ] + + paths[0].url.should.be.equal(compareWith[0].url); + paths[0].method.should.be.equal(compareWith[0].method); + paths[0].action.should.be.equal(compareWith[0].action); + paths[0].handlerPath.should.be.equal(compareWith[0].handlerPath); + paths[0].handlerName.should.be.equal(compareWith[0].handlerName); + paths[0].name.should.be.equal(compareWith[0].name); + + paths[1].url.should.be.equal(compareWith[1].url); + paths[1].method.should.be.equal(compareWith[1].method); + paths[1].action.should.be.equal(compareWith[1].action); + paths[1].handlerPath.should.be.equal(compareWith[1].handlerPath); + paths[1].handlerName.should.be.equal(compareWith[1].handlerName); + paths[1].name.should.be.equal(compareWith[1].name); + + _.isEqual(paths[0], compareWith[0]).should.be.true; + _.isEqual(paths[1], compareWith[1]).should.be.true; done(); }); }); @@ -111,7 +127,7 @@ describe("handlerParser.parse method", function() { parse(dirname, function(err, paths) { if (err) return done(err); - paths.length.not.equal(0); + paths.length.should.not.equal(0); var compareWith = { url: '/books', diff --git a/test/processingChain.js b/test/processingChain.js new file mode 100644 index 0000000..da529c0 --- /dev/null +++ b/test/processingChain.js @@ -0,0 +1,198 @@ +var should = require('should'); //for mocha tests +var ProcessingChain = require('../lib/processingChain.js') +var _ = require('lodash'); + +describe('Processing Chain methods', function() { + + var processingChain = new ProcessingChain() + + var simpleProc = { + fn: function(req, res, next) { + + }, + names: [] + } + var errorProc = { + fn: function(err, req, res, next) { + + } + } + describe("@add", function() { + it("should add a normal function to the right chain", function(done) { + processingChain.add(simpleProc) + processingChain.getTotal().should.equal(1) + done() + }) + + it("should add an error handler to the error chain", function(done) { + processingChain.add(errorProc) + processingChain.errorHandlers.length.should.equal(1) + done() + }) + + }) + describe("@pop", function() { + it("should correctly remove the last item from the chain", function(done) { + processingChain.add({fn: true}) + processingChain.getTotal().should.equal(2) + processingChain.pop() + processingChain.getTotal().should.equal(1) + processingChain.chain[0].should.not.equal(true) + done() + }) + }) + + describe("@findFirstValidItem", function() { + it("should correctly find the first valid process when there is no endpoint name set", function() { + pc = new ProcessingChain() + pc.add({fn: 1}) + pc.add({fn: 2}) + pc.findFirstValidItem(undefined, pc.chain).fn.should.equal(1) + }) + + it("should correctly find the first valid process when there is an endpoint name set", function() { + pc = new ProcessingChain() + pc.add({fn: 1, names: ['first']}) + pc.add({fn: 2, names: ['second']}) + pc.findFirstValidItem('first', pc.chain).fn.should.equal(1) + pc.findFirstValidItem('second', pc.chain).fn.should.equal(2) + }) + }) + + describe("@runChain", function() { + it("should run the chain correctly without handler name", function(done) { + var result = "" + pc = new ProcessingChain() + pc.add({fn: function(req, res, n) { + result+= "1" + n() + }}) + pc.add({fn: function(req, res, n) { + result+= "2" + n() + }, + names: ['ok'], + }) + pc.add({fn: function(req, res, n) { + result+= "3" + n() + }}) + pc.runChain({ + req: {}, + res: {}, + finalFn: function() { + result.should.equal("123") + done() + }, + }) + }) + + it("should run the chain correctly with handler name", function(done) { + var result = "" + pc = new ProcessingChain() + pc.add({fn: function(req, res, n) { + result+= "1" + n() + }}) + pc.add({fn: function(req, res, n) { + result+= "2" + n() + }, + names: ['ok'], + }) + pc.add({fn: function(req, res, n) { + result+= "3" + n() + }}) + + pc.add({fn: function(req, res, n) { + result+= "4" + n() + }, + names: ['not ok', 'nott1 ok'] + }); + + pc.add({fn: function(req, res, n) { + result+= "5" + n() + }, + names: ['not ok', 'ok'] + }); + pc.runChain({ + req: {}, + res: {}, + finalFn: function() { + result.should.equal("1235") + done() + }, + handler: {name: 'ok'} + }) + }) + + it("should switch to the error chain if there is a problem", function(done) { + var result = "" + pc = new ProcessingChain() + pc.add({fn: function(req, res, n) { + result+= "1" + n() + }}) + pc.add({fn: function(req, res, n) { + result+= "2" + n('error') + }}) + pc.add({fn: function(req, res, n) { + result+= "3" + n() + }}) + + pc.add({fn: function(err, req, res, n) { + result += err + n() + }}) + pc.add({fn: function(err, req, res, n) { + result += 'e2' + n() + }}) + pc.runChain({ + req: {}, + res: {}, + finalFn: function() { + result.should.equal("12errore2") + done() + } + }) + }) + + it("should run correctly if there are named endpoints involved", function(done) { + var result = "" + pc = new ProcessingChain() + pc.add({fn: function(req, res, n) { + result+= "1" + n() + }}) + pc.add({fn: function(req, res, n) { + result+= "2" + n('error') + }, names: ['endpoint1']}) + pc.add({fn: function(req, res, n) { + result+= "3" + n() + }, names: ['endpoint2']}) + + pc.add({fn: function(req, res, n) { + result += "4" + n() + }, names: ["endpoint2", "endpoint1"]}) + + pc.runChain({ + req: {}, + res: {}, + finalFn: function() { + result.should.equal("134") + done() + }, + handler: {name: 'endpoint2'} + }) + }) + }) +}) diff --git a/test/vatican.js b/test/vatican.js new file mode 100644 index 0000000..98fac8b --- /dev/null +++ b/test/vatican.js @@ -0,0 +1,74 @@ +var should = require('should'); //for mocha tests +var Vatican = require("../lib/vatican") +var _ = require('lodash'); + + +describe("Vatican methods", function() { + + var vatican = new Vatican({ + handlers: __dirname + '/fixtures/vatican/handlers', + port: 88 + }) + var matchFound = null + + describe("@checkOpptions", function(){ + it("should throw an error if no port is specified", function() { + (function() { + v = new Vatican({handlers: ''}) + }).should.throw("Port not specified") + }) + it("should throw an error if no handlers folder is specified", function() { + (function() { + v = new Vatican({port: 123}) + }).should.throw("Handlers folder not specified") + }) + }) + + describe("@preprocess", function() { + it("should add a processor to the pre-processors chain", function() { + vatican.preprocess(function(req, res, next) { + + }) + + vatican.preprocessors.chain.length.should.equal(1) + }) + }) + + describe("@postprocess", function() { + it("should add a processor to the post-processors chain", function() { + vatican.postprocess(function(req, res, next) { + + }) + + vatican.postprocessors.chain.length.should.equal(1) + }) + }) + + describe("@parseHandlers", function() { + it("should return a structure with the handlers data", function(done) { + vatican.parseHandlers(function(hdlrs) { + hdlrs.length.should.equal(5) + done() + }) + }) + }) + + describe("@findMethod", function() { + it("should find the right method from the paths", function(done) { + matchFound = vatican.findMethod('/people/123', 'DELETE') + matchFound.action.should.equal("killPeep") + matchFound.handlerName.should.equal('People') + done() + }) + }) + + describe("@loadHandler", function() { + it("should load and return the handler data", function(done) { + var handler = vatican.loadHandler(matchFound.handlerPath) + _.keys(vatican.handlers).length.should.equal(1) + var type = typeof handler == 'function' + type.should.be.ok + done() + }) + }) +}) diff --git a/test/vaticanConfig.js b/test/vaticanConfig.js new file mode 100644 index 0000000..489ff26 --- /dev/null +++ b/test/vaticanConfig.js @@ -0,0 +1,43 @@ +var should = require('should'), + vatican = require('../'), + request = require('supertest'); + +describe('vatican configuration', function() { + it('handlers must support a relative path', function( done ) { + var app = new vatican({ + 'handlers': 'fixtures/vaticanConfig/handlers', + 'port': 24000, + }); + + app.start(); + + request('http://localhost:24000') + .get('/people') + .expect(200) + .expect('ok') + .end(function(err) { + ( err == undefined ).should.be.true; + app.close(); + done(); + }); + }); + + it('handlers must support a absolute path', function( done ) { + var app = new vatican({ + 'handlers': __dirname + '/fixtures/vaticanConfig/handlers', + 'port': 24000, + }); + + app.start(); + + request('http://localhost:24000') + .get('/people') + .expect(200) + .expect('ok') + .end(function(err) { + ( err == undefined ).should.be.true; + app.close(); + done(); + }); + }); +}); diff --git a/test/vaticanHttpMethods.js b/test/vaticanHttpMethods.js new file mode 100644 index 0000000..c2d46c2 --- /dev/null +++ b/test/vaticanHttpMethods.js @@ -0,0 +1,32 @@ +var request = require('supertest'), + should = require('should'), + vatican = require('../'), + http = require('http'); + +describe('vatican http methods', function() { + var app = new vatican({ + 'handlers': __dirname + '/fixtures/vaticanHttpMethods/handlers', + 'port': 24000, + }); + + before(function() { + app.start(); + }); + + after(function() { + app.close(); + }); + + [ + 'get', + 'put', + 'post', + 'delete' + ].forEach(function(method) { + it(method + " method without params", function( done ) { + request('http://localhost:24000')[method]('/people') + .expect(200) //statusCode + .expect(method + ',user0,user1', done); //body + }); + }); +});