diff --git a/README.md b/README.md index ac172d59..725443f2 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## Documentation -See our [API Documentation](https://github.com/pelias/api/blob/master/DOCS.md). +See our [API Documentation](https://github.com/pelias/api/blob/master/public/apiDoc.md). ## Install Dependencies diff --git a/controller/place.js b/controller/place.js index 5d3a2402..97461e71 100644 --- a/controller/place.js +++ b/controller/place.js @@ -1,10 +1,18 @@ var service = { mget: require('../service/mget') }; function setup( backend ){ + // allow overriding of dependencies backend = backend || require('../src/backend'); function controller( req, res, next ){ + + // do not run controller when a request + // validation error has occurred. + if( req.errors && req.errors.length ){ + return next(); + } + var query = req.clean.ids.map( function(id) { return { _index: 'pelias', @@ -17,9 +25,8 @@ function setup( backend ){ // error handler if( err ){ return next( err ); } - req.results = { - data: docs - }; + // set response data + res.data = docs; next(); }); diff --git a/controller/search.js b/controller/search.js index c4c54754..2495c37a 100644 --- a/controller/search.js +++ b/controller/search.js @@ -8,6 +8,12 @@ function setup( backend, query ){ function controller( req, res, next ){ + // do not run controller when a request + // validation error has occurred. + if( req.errors && req.errors.length ){ + return next(); + } + // backend command var cmd = { index: 'pelias', @@ -26,10 +32,9 @@ function setup( backend, query ){ // error handler if( err ){ return next( err ); } - req.results = { - data: docs, - meta: meta - }; + // set response data + res.data = docs; + res.meta = meta; next(); }); diff --git a/helper/geojsonify.js b/helper/geojsonify.js index 18e11b3d..9ce77ad4 100644 --- a/helper/geojsonify.js +++ b/helper/geojsonify.js @@ -8,7 +8,6 @@ var GeoJSON = require('geojson'), var DETAILS_PROPS = [ 'housenumber', 'street', - 'category', 'postalcode', 'country_a', 'country', diff --git a/middleware/confidenceScore.js b/middleware/confidenceScore.js index 21b9b5e6..04b87b7d 100644 --- a/middleware/confidenceScore.js +++ b/middleware/confidenceScore.js @@ -23,17 +23,17 @@ function setup(peliasConfig) { function computeScores(req, res, next) { // do nothing if no result data set - if (!req.results || !req.results.data || !req.results.meta) { + if (!res || !res.data || !res.meta) { return next(); } // compute standard deviation and mean from all scores - var scores = req.results.meta.scores; + var scores = res.meta.scores; var stdev = computeStandardDeviation(scores); var mean = stats.mean(scores); // loop through data items and determine confidence scores - req.results.data = req.results.data.map(computeConfidenceScore.bind(null, req, mean, stdev)); + res.data = res.data.map(computeConfidenceScore.bind(null, req, mean, stdev)); next(); } diff --git a/middleware/distance.js b/middleware/distance.js index 3783119d..389b724b 100644 --- a/middleware/distance.js +++ b/middleware/distance.js @@ -9,7 +9,7 @@ function setup() { function computeDistances(req, res, next) { // do nothing if no result data set - if (!req.results || !req.results.data) { + if (!res || !res.data) { return next(); } @@ -17,7 +17,7 @@ function computeDistances(req, res, next) { return next(); } - req.results.data.forEach(function (place) { + res.data.forEach(function (place) { // the result of getDistance is in meters, so convert to kilometers place.distance = geolib.getDistance( { latitude: req.clean.lat, longitude: req.clean.lon }, diff --git a/middleware/geocodeJSON.js b/middleware/geocodeJSON.js index 4f7943e4..80d69e86 100644 --- a/middleware/geocodeJSON.js +++ b/middleware/geocodeJSON.js @@ -1,71 +1,89 @@ +var url = require('url'); var extend = require('extend'); var geojsonify = require('../helper/geojsonify').search; -function setup(peliasConfig) { - - peliasConfig = peliasConfig || require( 'pelias-config' ).generate().api; +/** + * Returns a middleware function that converts elasticsearch + * results into geocodeJSON format. + * + * @param {object} [peliasConfig] api portion of pelias config + * @param {string} [basePath] + * @returns {middleware} + */ +function setup(peliasConfig, basePath) { + + var opts = { + config: peliasConfig || require('pelias-config').generate().api, + basePath: basePath || '/' + }; function middleware(req, res, next) { - return convertToGeocodeJSON(peliasConfig, req, next); + return convertToGeocodeJSON(req, res, next, opts); } return middleware; } -function convertToGeocodeJSON(peliasConfig, req, next) { - - // do nothing if no result data set - if (!req.results || !req.results.data) { - return next(); - } - - req.results.geojson = { geocoding: {} }; +/** + * Converts elasticsearch results into geocodeJSON format + * + * @param {object} req + * @param {object} res + * @param {object} next + * @param {object} opts + * @param {string} opts.basePath e.g. '/v1/' + * @param {string} opts.config.host e.g. 'pelias.mapzen.com' + * @param {string} opts.config.version e.g. 1.0 + * @returns {*} + */ +function convertToGeocodeJSON(req, res, next, opts) { + + res.body = { geocoding: {} }; // REQUIRED. A semver.org compliant version number. Describes the version of // the GeocodeJSON spec that is implemented by this instance. - req.results.geojson.geocoding.version = '0.1'; + res.body.geocoding.version = '0.1'; // OPTIONAL. Default: null. The attribution of the data. In case of multiple sources, // and then multiple attributions, can be an object with one key by source. // Can be a URI on the server, which outlines attribution details. - req.results.geojson.geocoding.attribution = peliasConfig.host + 'attribution'; + res.body.geocoding.attribution = url.resolve(opts.config.host, opts.basePath + 'attribution'); // OPTIONAL. Default: null. The query that has been issued to trigger the // search. // Freeform object. // This is the equivalent of how the engine interpreted the incoming request. // Helpful for debugging and understanding how the input impacts results. - req.results.geojson.geocoding.query = req.clean; + res.body.geocoding.query = req.clean; // OPTIONAL. Warnings and errors. - addMessages(req.results, 'warnings', req.results.geojson.geocoding); - addMessages(req.results, 'errors', req.results.geojson.geocoding); + addMessages(req, 'warnings', res.body.geocoding); + addMessages(req, 'errors', res.body.geocoding); // OPTIONAL // Freeform - addEngine(peliasConfig, req.results.geojson.geocoding); + addEngine(opts.config.version, res.body.geocoding); // response envelope - req.results.geojson.geocoding.timestamp = new Date().getTime(); + res.body.geocoding.timestamp = new Date().getTime(); // convert docs to geojson and merge with geocoding block - extend(req.results.geojson, geojsonify(req.results.data)); + extend(res.body, geojsonify(res.data || [])); next(); } -function addMessages(results, msgType, geocoding) { - if (results.hasOwnProperty(msgType)) { - geocoding.messages = geocoding.messages || {}; - geocoding.messages[msgType] = results[msgType]; +function addMessages(req, msgType, geocoding) { + if (req.hasOwnProperty(msgType) && req[msgType].length) { + geocoding[msgType] = req[msgType]; } } -function addEngine(peliasConfig, geocoding) { +function addEngine(version, geocoding) { geocoding.engine = { name: 'Pelias', author: 'Mapzen', - version: peliasConfig.version + version: version }; } diff --git a/middleware/renamePlacenames.js b/middleware/renamePlacenames.js index 6b27d2fc..6fcba91a 100644 --- a/middleware/renamePlacenames.js +++ b/middleware/renamePlacenames.js @@ -32,12 +32,12 @@ function setup() { function renamePlacenames(req, res, next) { // do nothing if no result data set - if (!req.results || !req.results.data) { + if (!res || !res.data) { return next(); } // loop through data items and remap placenames - req.results.data = req.results.data.map(renameProperties); + res.data = res.data.map(renameProperties); next(); } diff --git a/middleware/sendJSON.js b/middleware/sendJSON.js index 1fd21a4c..9ec3e733 100644 --- a/middleware/sendJSON.js +++ b/middleware/sendJSON.js @@ -1,12 +1,12 @@ function sendJSONResponse(req, res, next) { // do nothing if no result data set - if (!req.results || !req.results.geojson) { + if (!res || !res.body) { return next(); } // respond - return res.status(200).json(req.results.geojson); + return res.status(200).json(res.body); } module.exports = sendJSONResponse; \ No newline at end of file diff --git a/package.json b/package.json index 3acd057d..259528a9 100644 --- a/package.json +++ b/package.json @@ -59,7 +59,7 @@ "through2": "0.6.5" }, "devDependencies": { - "ciao": "^0.3.4", + "ciao": "^0.6.0", "istanbul": "^0.3.13", "jshint": "^2.5.6", "nsp": "^0.3.0", diff --git a/routes/v1.js b/routes/v1.js index 3db635f0..215f1752 100644 --- a/routes/v1.js +++ b/routes/v1.js @@ -43,6 +43,8 @@ var postProc = { */ function addRoutes(app, peliasConfig) { + var base = '/v1/'; + /** ------------------------- routers ------------------------- **/ var routers = { @@ -58,7 +60,7 @@ function addRoutes(app, peliasConfig) { controllers.search(), postProc.confidenceScores(peliasConfig), postProc.renamePlacenames(), - postProc.geocodeJSON(peliasConfig), + postProc.geocodeJSON(peliasConfig, base), postProc.sendJSON ]), autocomplete: createRouter([ @@ -67,7 +69,7 @@ function addRoutes(app, peliasConfig) { controllers.search(null, require('../query/autocomplete')), postProc.confidenceScores(peliasConfig), postProc.renamePlacenames(), - postProc.geocodeJSON(peliasConfig), + postProc.geocodeJSON(peliasConfig, base), postProc.sendJSON ]), reverse: createRouter([ @@ -79,14 +81,14 @@ function addRoutes(app, peliasConfig) { // so it must be calculated first postProc.confidenceScoresReverse(), postProc.renamePlacenames(), - postProc.geocodeJSON(peliasConfig), + postProc.geocodeJSON(peliasConfig, base), postProc.sendJSON ]), place: createRouter([ sanitisers.place.middleware, controllers.place(), postProc.renamePlacenames(), - postProc.geocodeJSON(peliasConfig), + postProc.geocodeJSON(peliasConfig, base), postProc.sendJSON ]), status: createRouter([ @@ -95,18 +97,19 @@ function addRoutes(app, peliasConfig) { }; - var base = '/v1/'; - - // api root + // static data endpoints app.get ( base, routers.index ); app.get ( base + 'attribution', routers.attribution ); + app.get ( '/attribution', routers.attribution ); + app.get ( '/status', routers.status ); + + // backend dependent endpoints app.get ( base + 'place', routers.place ); app.get ( base + 'autocomplete', routers.autocomplete ); app.get ( base + 'search', routers.search ); app.post( base + 'search', routers.search ); app.get ( base + 'reverse', routers.reverse ); - app.get ( '/status', routers.status ); } /** diff --git a/sanitiser/_geo_reverse.js b/sanitiser/_geo_reverse.js index 4b259199..da2bb79c 100644 --- a/sanitiser/_geo_reverse.js +++ b/sanitiser/_geo_reverse.js @@ -14,6 +14,13 @@ module.exports = function sanitize( raw, clean ){ geo_common.sanitize_coord( 'lat', clean, raw['point.lat'], LAT_LON_IS_REQUIRED ); geo_common.sanitize_coord( 'lon', clean, raw['point.lon'], LAT_LON_IS_REQUIRED ); + // remove both if only one is set + // @todo: clean this up! + if( !clean.hasOwnProperty('lat') || !clean.hasOwnProperty('lon') ){ + delete clean.lat; + delete clean.lon; + } + // boundary.circle.* is not mandatory, and only specifying radius is fine, // as point.lat/lon will be used to fill those values by default geo_common.sanitize_boundary_circle( clean, raw, CIRCLE_IS_REQUIRED, CIRCLE_MUST_BE_COMPLETE); diff --git a/sanitiser/_id.js b/sanitiser/_id.js index ff6834ca..4d9c1c71 100644 --- a/sanitiser/_id.js +++ b/sanitiser/_id.js @@ -18,8 +18,18 @@ function sanitize( raw, clean ){ // error & warning messages var messages = { errors: [], warnings: [] }; - // 'raw.id' can be an array!? - var rawIds = check.array( raw.id ) ? _.unique( raw.id ) : [ raw.id ]; + // 'raw.id' can be an array + var rawIds = check.array( raw.id ) ? _.unique( raw.id ) : []; + + // 'raw.id' can be a string + if( check.unemptyString( raw.id ) ){ + rawIds.push( raw.id ); + } + + // no ids provided + if( !rawIds.length ){ + messages.errors.push( errorMessage('id') ); + } // ensure all elements are valid non-empty strings rawIds = rawIds.filter( function( uc ){ @@ -48,25 +58,27 @@ function sanitize( raw, clean ){ } // id text - if( !check.unemptyString( id ) ){ + else if( !check.unemptyString( id ) ){ messages.errors.push( errorMessage( rawId ) ); } // type text - if( !check.unemptyString( type ) ){ + else if( !check.unemptyString( type ) ){ messages.errors.push( errorMessage( rawId ) ); } // type text must be one of the types - if( !_.contains( types, type ) ){ + else if( !_.contains( types, type ) ){ messages.errors.push( errorMessage('type', type + ' is invalid. It must be one of these values - [' + types.join(', ') + ']') ); } - // add valid id to 'clean.ids' array - clean.ids.push({ - id: id, - type: type - }); + else { + clean.ids.push({ + id: id, + type: type + }); + } + }); return messages; diff --git a/sanitiser/_size.js b/sanitiser/_size.js index eb93444b..3fb8115b 100644 --- a/sanitiser/_size.js +++ b/sanitiser/_size.js @@ -1,3 +1,4 @@ +var check = require('check-types'); var MIN_SIZE = 1, MAX_SIZE = 40, @@ -10,35 +11,22 @@ function sanitize( raw, clean ){ var messages = { errors: [], warnings: [] }; // coercions - var _size = parseInt( raw.size, 10 ); + clean.size = parseInt( raw.size, 10 ); // invalid numeric input - // @todo: this can be removed now as queries have default sizes? - if( isNaN( _size ) ){ - - // set the default size - messages.warnings.push('invalid integer \'size\', using DEFAULT_SIZE'); + if( isNaN( clean.size ) ){ clean.size = DEFAULT_SIZE; } - // valid numeric input - else { - - // ensure size falls within defined range - if( _size > MAX_SIZE ){ - // set the max size - messages.warnings.push('out-of-range integer \'size\', using MAX_SIZE'); - clean.size = MAX_SIZE; - } - else if( _size < MIN_SIZE ){ - // set the min size - messages.warnings.push('out-of-range integer \'size\', using MIN_SIZE'); - clean.size = MIN_SIZE; - } - else { - // set the input size - clean.size = _size; - } - + // ensure size falls within defined range + else if( clean.size > MAX_SIZE ){ + // set the max size + messages.warnings.push('out-of-range integer \'size\', using MAX_SIZE'); + clean.size = MAX_SIZE; + } + else if( clean.size < MIN_SIZE ){ + // set the min size + messages.warnings.push('out-of-range integer \'size\', using MIN_SIZE'); + clean.size = MIN_SIZE; } return messages; diff --git a/sanitiser/place.js b/sanitiser/place.js index 8b5e6034..44deb267 100644 --- a/sanitiser/place.js +++ b/sanitiser/place.js @@ -14,10 +14,6 @@ module.exports.sanitiser_list = sanitizers; // middleware module.exports.middleware = function( req, res, next ){ sanitize( req, function( err, clean ){ - if( err ){ - res.status(400); // 400 Bad Request - return next(err); - } next(); }); }; diff --git a/sanitiser/reverse.js b/sanitiser/reverse.js index 532b3652..25e8d71b 100644 --- a/sanitiser/reverse.js +++ b/sanitiser/reverse.js @@ -6,7 +6,6 @@ var sanitizeAll = require('../sanitiser/sanitizeAll'), size: require('../sanitiser/_size'), private: require('../sanitiser/_flag_bool')('private', false), geo_reverse: require('../sanitiser/_geo_reverse'), - categories: require('../sanitiser/_categories'), boundary_country: require('../sanitiser/_boundary_country'), }; @@ -19,10 +18,6 @@ module.exports.sanitiser_list = sanitizers; // middleware module.exports.middleware = function( req, res, next ){ sanitize( req, function( err, clean ){ - if( err ){ - res.status(400); // 400 Bad Request - return next(err); - } next(); }); }; diff --git a/sanitiser/sanitizeAll.js b/sanitiser/sanitizeAll.js index 886a64c3..1c2dd2af 100644 --- a/sanitiser/sanitizeAll.js +++ b/sanitiser/sanitizeAll.js @@ -1,10 +1,16 @@ +var check = require('check-types'); + function sanitize( req, sanitizers, cb ){ // init an object to store clean // (sanitized) input parameters req.clean = {}; + // init erros and warnings arrays + req.errors = []; + req.warnings = []; + // source of input parameters // (in this case from the GET querystring params) var params = req.query || {}; @@ -12,14 +18,21 @@ function sanitize( req, sanitizers, cb ){ for (var s in sanitizers) { var sanity = sanitizers[s]( params, req.clean ); - // errors + // if errors occurred then set them + // on the req object. if( sanity.errors.length ){ - return cb( sanity.errors[0] ); + req.errors = req.errors.concat( sanity.errors ); + } + + // if warnings occurred then set them + // on the req object. + if( sanity.warnings.length ){ + req.warnings = req.warnings.concat( sanity.warnings ); } } - - return cb( undefined, req.clean ); + // @todo remove these args, they do not need to be passed out + return cb( undefined, req.clean ); } // export function diff --git a/sanitiser/search.js b/sanitiser/search.js index 9f22def9..22737180 100644 --- a/sanitiser/search.js +++ b/sanitiser/search.js @@ -7,7 +7,6 @@ var sanitizeAll = require('../sanitiser/sanitizeAll'), sources: require('../sanitiser/_targets')('sources', require( '../query/sources' )), private: require('../sanitiser/_flag_bool')('private', false), geo_search: require('../sanitiser/_geo_search'), - categories: require('../sanitiser/_categories'), boundary_country: require('../sanitiser/_boundary_country'), }; @@ -20,10 +19,6 @@ module.exports.sanitiser_list = sanitizers; // middleware module.exports.middleware = function( req, res, next ){ sanitize( req, function( err, clean ){ - if( err ){ - res.status(400); // 400 Bad Request - return next(err); - } next(); }); }; diff --git a/test/ciao/404.coffee b/test/ciao/404.coffee index 764bc8d5..7a22618d 100644 --- a/test/ciao/404.coffee +++ b/test/ciao/404.coffee @@ -3,7 +3,7 @@ path: '/notexist' #? not found -response.statusCode.should.equal 404 +response.statusCode.should.be.equal 404 #? content-type header correctly set response.should.have.header 'Content-Type','application/json; charset=utf-8' @@ -14,4 +14,4 @@ response.should.have.header 'Cache-Control','public,max-age=300' #? should respond in json with server info should.exist json should.exist json.error -json.error.should.equal 'not found: invalid path' \ No newline at end of file +json.error.should.be.equal 'not found: invalid path' \ No newline at end of file diff --git a/test/ciao/cors.coffee b/test/ciao/CORS/headers_GET.coffee similarity index 79% rename from test/ciao/cors.coffee rename to test/ciao/CORS/headers_GET.coffee index bd97c5de..53866718 100644 --- a/test/ciao/cors.coffee +++ b/test/ciao/CORS/headers_GET.coffee @@ -4,6 +4,6 @@ path: '/' #? access control headers correctly set response.should.have.header 'Access-Control-Allow-Origin','*' -response.should.have.header 'Access-Control-Allow-Methods','GET' +response.should.have.header 'Access-Control-Allow-Methods','GET, OPTIONS' response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type' response.should.have.header 'Access-Control-Allow-Credentials','true' \ No newline at end of file diff --git a/test/ciao/CORS/headers_OPTIONS.coffee b/test/ciao/CORS/headers_OPTIONS.coffee new file mode 100644 index 00000000..5575391a --- /dev/null +++ b/test/ciao/CORS/headers_OPTIONS.coffee @@ -0,0 +1,10 @@ + +#> cross-origin resource sharing +path: '/' +method: 'OPTIONS' + +#? access control headers correctly set +response.should.have.header 'Access-Control-Allow-Origin','*' +response.should.have.header 'Access-Control-Allow-Methods','GET, OPTIONS' +response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type' +response.should.have.header 'Access-Control-Allow-Credentials','true' \ No newline at end of file diff --git a/test/ciao/autocomplete/basic_autocomplete.coffee b/test/ciao/autocomplete/basic_autocomplete.coffee new file mode 100644 index 00000000..5c9278ca --- /dev/null +++ b/test/ciao/autocomplete/basic_autocomplete.coffee @@ -0,0 +1,33 @@ + +#> basic autocomplete +path: '/v1/autocomplete?text=a' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['text'].should.eql 'a' +json.geocoding.query['size'].should.eql 10 \ No newline at end of file diff --git a/test/ciao/autocomplete/null_island.coffee b/test/ciao/autocomplete/null_island.coffee new file mode 100644 index 00000000..ad229069 --- /dev/null +++ b/test/ciao/autocomplete/null_island.coffee @@ -0,0 +1,34 @@ + +#> null island +path: '/v1/autocomplete?text=a&focus.point.lat=0&focus.point.lon=0' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['lat'].should.eql 0 +json.geocoding.query['lon'].should.eql 0 +json.geocoding.query['size'].should.eql 10 \ No newline at end of file diff --git a/test/ciao/index.coffee b/test/ciao/index.coffee index b495c747..8dd9da90 100644 --- a/test/ciao/index.coffee +++ b/test/ciao/index.coffee @@ -3,7 +3,7 @@ path: '/v1/' #? endpoint available -response.statusCode.should.equal 200 +response.statusCode.should.be.equal 200 #? content-type header correctly set response.should.have.header 'Content-Type','text/html; charset=utf-8' diff --git a/test/ciao/place/basic_place.coffee b/test/ciao/place/basic_place.coffee new file mode 100644 index 00000000..b734bff5 --- /dev/null +++ b/test/ciao/place/basic_place.coffee @@ -0,0 +1,33 @@ + +#> basic place +path: '/v1/place?id=geoname:1' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['ids'].should.eql [{ id: '1', type: 'geoname' }] +should.not.exist json.geocoding.query['size'] \ No newline at end of file diff --git a/test/ciao/place/missing_id.coffee b/test/ciao/place/missing_id.coffee new file mode 100644 index 00000000..4e851c3f --- /dev/null +++ b/test/ciao/place/missing_id.coffee @@ -0,0 +1,34 @@ + +#> missing id +path: '/v1/place' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.exist json.geocoding.errors +json.geocoding.errors.should.eql [ 'invalid param \'id\': text length, must be >0' ] + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['ids'].should.eql [] +should.not.exist json.geocoding.query['size'] \ No newline at end of file diff --git a/test/ciao/place/msuccess.coffee b/test/ciao/place/msuccess.coffee deleted file mode 100644 index 8f1b0056..00000000 --- a/test/ciao/place/msuccess.coffee +++ /dev/null @@ -1,16 +0,0 @@ - -#> valid place query -path: '/v1/place?id=geoname:4221195&id=geoname:4193595' - -#? 200 ok -response.statusCode.should.equal 200 - -#? valid response -now = new Date().getTime() -should.exist json -should.not.exist json.error -json.date.should.be.within now-5000, now+5000 - -#? valid geojson -json.type.should.equal 'FeatureCollection' -json.features.should.be.instanceof Array diff --git a/test/ciao/place/success.coffee b/test/ciao/place/success.coffee deleted file mode 100644 index 323046a0..00000000 --- a/test/ciao/place/success.coffee +++ /dev/null @@ -1,16 +0,0 @@ - -#> valid place query -path: '/v1/place?id=geoname:4221195' - -#? 200 ok -response.statusCode.should.equal 200 - -#? valid response -now = new Date().getTime() -should.exist json -should.not.exist json.error -json.date.should.be.within now-5000, now+5000 - -#? valid geojson -json.type.should.equal 'FeatureCollection' -json.features.should.be.instanceof Array diff --git a/test/ciao/reverse/basic_reverse.coffee b/test/ciao/reverse/basic_reverse.coffee new file mode 100644 index 00000000..d4d708ed --- /dev/null +++ b/test/ciao/reverse/basic_reverse.coffee @@ -0,0 +1,34 @@ + +#> basic reverse +path: '/v1/reverse?point.lat=1&point.lon=2' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['lat'].should.eql 1 +json.geocoding.query['lon'].should.eql 2 +json.geocoding.query['size'].should.eql 10 \ No newline at end of file diff --git a/test/ciao/reverse/null_island.coffee b/test/ciao/reverse/null_island.coffee new file mode 100644 index 00000000..5189a152 --- /dev/null +++ b/test/ciao/reverse/null_island.coffee @@ -0,0 +1,34 @@ + +#> null island +path: '/v1/reverse?point.lat=0&point.lon=0' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['lat'].should.eql 0 +json.geocoding.query['lon'].should.eql 0 +json.geocoding.query['size'].should.eql 10 \ No newline at end of file diff --git a/test/ciao/reverse/success.coffee b/test/ciao/reverse/success.coffee deleted file mode 100644 index 78c73832..00000000 --- a/test/ciao/reverse/success.coffee +++ /dev/null @@ -1,15 +0,0 @@ -#> valid reverse query -path: '/v1/reverse?lat=29.49136&lon=-82.50622' - -#? 200 ok -response.statusCode.should.equal 200 - -#? valid response -now = new Date().getTime() -should.exist json -should.not.exist json.error -json.date.should.be.within now-5000, now+5000 - -#? valid geojson -json.type.should.equal 'FeatureCollection' -json.features.should.be.instanceof Array diff --git a/test/ciao/search/address_parsing.coffee b/test/ciao/search/address_parsing.coffee new file mode 100644 index 00000000..83d3e1bc --- /dev/null +++ b/test/ciao/search/address_parsing.coffee @@ -0,0 +1,41 @@ + +#> address parsing +path: '/v1/search?text=30%20w%2026th%20st%2C%20ny' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['text'].should.eql '30 w 26th st, ny' +json.geocoding.query['size'].should.eql 10 + +#? address parsing +json.geocoding.query.parsed_text['name'].should.eql '30 w 26th st' +json.geocoding.query.parsed_text['number'].should.eql 30 +json.geocoding.query.parsed_text['street'].should.eql 'w 26th st' +json.geocoding.query.parsed_text['state'].should.eql 'NY' +json.geocoding.query.parsed_text['regions'].should.eql [] +json.geocoding.query.parsed_text['admin_parts'].should.eql "ny" \ No newline at end of file diff --git a/test/ciao/search/basic_search.coffee b/test/ciao/search/basic_search.coffee new file mode 100644 index 00000000..039fe8f8 --- /dev/null +++ b/test/ciao/search/basic_search.coffee @@ -0,0 +1,33 @@ + +#> basic search +path: '/v1/search?text=a' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['text'].should.eql 'a' +json.geocoding.query['size'].should.eql 10 \ No newline at end of file diff --git a/test/ciao/search/no_params.coffee b/test/ciao/search/no_params.coffee new file mode 100644 index 00000000..e374c6aa --- /dev/null +++ b/test/ciao/search/no_params.coffee @@ -0,0 +1,33 @@ + +#> no params specified +path: '/v1/search' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.exist json.geocoding.errors +json.geocoding.errors.should.eql [ 'invalid param \'text\': text length, must be >0' ] + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['size'].should.eql 10 \ No newline at end of file diff --git a/test/ciao/search/null_island.coffee b/test/ciao/search/null_island.coffee new file mode 100644 index 00000000..e620dc80 --- /dev/null +++ b/test/ciao/search/null_island.coffee @@ -0,0 +1,35 @@ + +#> null island +path: '/v1/search?text=a&focus.point.lat=0&focus.point.lon=0' + +#? 200 ok +response.statusCode.should.be.equal 200 +response.should.have.header 'charset', 'utf8' +response.should.have.header 'content-type', 'application/json; charset=utf-8' + +#? valid geocoding block +should.exist json.geocoding +should.exist json.geocoding.version +should.exist json.geocoding.attribution +should.exist json.geocoding.query +should.exist json.geocoding.engine +should.exist json.geocoding.engine.name +should.exist json.geocoding.engine.author +should.exist json.geocoding.engine.version +should.exist json.geocoding.timestamp + +#? valid geojson +json.type.should.be.equal 'FeatureCollection' +json.features.should.be.instanceof Array + +#? expected errors +should.not.exist json.geocoding.errors + +#? expected warnings +should.not.exist json.geocoding.warnings + +#? inputs +json.geocoding.query['text'].should.eql 'a' +json.geocoding.query['size'].should.eql 10 +json.geocoding.query['lat'].should.eql 0 +json.geocoding.query['lon'].should.eql 0 \ No newline at end of file diff --git a/test/ciao/search/success.coffee b/test/ciao/search/success.coffee deleted file mode 100644 index 9e37a5f4..00000000 --- a/test/ciao/search/success.coffee +++ /dev/null @@ -1,15 +0,0 @@ -#> valid search query -path: '/v1/search?text=lake&lat=29.49136&lon=-82.50622' - -#? 200 ok -response.statusCode.should.equal 200 - -#? valid response -now = new Date().getTime() -should.exist json -should.not.exist json.error -json.date.should.be.within now-5000, now+5000 - -#? valid geojson -json.type.should.equal 'FeatureCollection' -json.features.should.be.instanceof Array diff --git a/test/ciao/suggest/nearby_nobias.coffee b/test/ciao/suggest/nearby_nobias.coffee deleted file mode 100644 index 295f0740..00000000 --- a/test/ciao/suggest/nearby_nobias.coffee +++ /dev/null @@ -1,6 +0,0 @@ - -#> suggest without geo bias -path: '/v1/suggest/nearby?text=a' - -#? 400 bad request -response.statusCode.should.equal 400 diff --git a/test/ciao/suggest/success.coffee b/test/ciao/suggest/success.coffee deleted file mode 100644 index e33b3f56..00000000 --- a/test/ciao/suggest/success.coffee +++ /dev/null @@ -1,16 +0,0 @@ - -#> valid suggest query -path: '/v1/suggest?text=a&lat=0&lon=0' - -#? 200 ok -response.statusCode.should.equal 200 - -#? valid response -now = new Date().getTime() -should.exist json -should.not.exist json.error -json.date.should.be.within now-5000, now+5000 - -#? valid geojson -json.type.should.equal 'FeatureCollection' -json.features.should.be.instanceof Array diff --git a/test/ciao/suggest/success_nearby.coffee b/test/ciao/suggest/success_nearby.coffee deleted file mode 100644 index 35282a47..00000000 --- a/test/ciao/suggest/success_nearby.coffee +++ /dev/null @@ -1,16 +0,0 @@ - -#> valid suggest query -path: '/v1/suggest/nearby?text=a&lat=29.49136&lon=-82.50622' - -#? 200 ok -response.statusCode.should.equal 200 - -#? valid response -now = new Date().getTime() -should.exist json -should.not.exist json.error -json.date.should.be.within now-5000, now+5000 - -#? valid geojson -json.type.should.equal 'FeatureCollection' -json.features.should.be.instanceof Array diff --git a/test/unit/helper/geojsonify.js b/test/unit/helper/geojsonify.js index 838dc526..9ed4ed0a 100644 --- a/test/unit/helper/geojsonify.js +++ b/test/unit/helper/geojsonify.js @@ -153,7 +153,6 @@ module.exports.tests.search = function(test, common) { 'localadmin': 'test1', 'locality': 'test2', 'neighbourhood': 'test3', - 'category': ['food', 'nightlife'], 'housenumber': '13', 'street': 'Liverpool Road', 'postalcode': 'N1 0RW' @@ -206,8 +205,7 @@ module.exports.tests.search = function(test, common) { 'county': 'New York', 'localadmin': 'Manhattan', 'locality': 'New York', - 'neighbourhood': 'Koreatown', - 'category': ['tourism', 'transport'] + 'neighbourhood': 'Koreatown' } } ] diff --git a/test/unit/middleware/distance.js b/test/unit/middleware/distance.js index fd4c1490..81cf6871 100644 --- a/test/unit/middleware/distance.js +++ b/test/unit/middleware/distance.js @@ -8,22 +8,22 @@ module.exports.tests.computeDistance = function(test, common) { clean: { lat: 45, lon: -77 - }, - results: { - data: [ - { - center_point: { - lat: 40, - lon: -71 - } - } - ] } }; + var res = { + data: [ + { + center_point: { + lat: 40, + lon: -71 + } + } + ] + }; var expected = 742.348; - distance(req, null, function () { - t.equal(req.results.data[0].distance, expected, 'correct distance computed'); + distance(req, res, function () { + t.equal(res.data[0].distance, expected, 'correct distance computed'); t.end(); }); }); diff --git a/test/unit/run.js b/test/unit/run.js index f54b77f8..7ce2d23d 100644 --- a/test/unit/run.js +++ b/test/unit/run.js @@ -28,6 +28,7 @@ var tests = [ require('./sanitiser/_geo_common'), require('./middleware/distance'), require('./middleware/confidenceScoreReverse'), + require('./sanitiser/_size'), ]; tests.map(function(t) { diff --git a/test/unit/sanitiser/_size.js b/test/unit/sanitiser/_size.js new file mode 100644 index 00000000..e4e61ac9 --- /dev/null +++ b/test/unit/sanitiser/_size.js @@ -0,0 +1,61 @@ +var sanitize = require('../../../sanitiser/_size'); + +module.exports.tests = {}; + +module.exports.tests.sanitize_size = function(test, common) { + test('size=0', function(t) { + var raw = { size: 0 }; + var clean = {}; + var res = sanitize(raw, clean); + t.equal(res.errors.length, 0, 'should return no errors'); + t.equal(res.warnings.length, 1, 'should return warning'); + t.equal(res.warnings[0], 'out-of-range integer \'size\', using MIN_SIZE', 'check warning text'); + t.equal(clean.size, 1, 'default to 1'); + t.end(); + }); + + test('size=10000', function(t) { + var raw = { size: 10000 }; + var clean = {}; + var res = sanitize(raw, clean); + t.equal(res.errors.length, 0, 'should return no errors'); + t.equal(res.warnings.length, 1, 'should return warning'); + t.equal(res.warnings[0], 'out-of-range integer \'size\', using MAX_SIZE', 'check warning text'); + t.equal(clean.size, 40, 'default to 40'); + t.end(); + }); + + test('size not set', function(t) { + var raw = {}; + var clean = {}; + var res = sanitize(raw, clean); + t.equal(res.errors.length, 0, 'should return no errors'); + t.equal(res.warnings.length, 0, 'should return no warning'); + t.equal(clean.size, 10, 'default to 10'); + t.end(); + }); + + + var valid_sizes = [5, '5', 5.5, '5.5']; + valid_sizes.forEach(function (size) { + test('size=' + size, function (t) { + var raw = {size: size}; + var clean = {}; + var res = sanitize(raw, clean); + t.equal(res.errors.length, 0, 'should return no errors'); + t.equal(res.warnings.length, 0, 'should return warning'); + t.equal(clean.size, 5, 'set to correct integer'); + t.end(); + }); + }); +}; + +module.exports.all = function (tape, common) { + function test(name, testFunction) { + return tape('SANTIZE _size ' + name, testFunction); + } + + for( var testCase in module.exports.tests ){ + module.exports.tests[testCase](test, common); + } +}; diff --git a/test/unit/sanitiser/place.js b/test/unit/sanitiser/place.js index 812744a8..75fb87ff 100644 --- a/test/unit/sanitiser/place.js +++ b/test/unit/sanitiser/place.js @@ -1,6 +1,8 @@ +// @todo: refactor this test, it's pretty messy, brittle and hard to follow + var place = require('../../../sanitiser/place'), - _sanitize = place.sanitize, + sanitize = place.sanitize, middleware = place.middleware, types = require('../../../query/types'), delimiter = ':', @@ -11,12 +13,14 @@ var place = require('../../../sanitiser/place'), var type = input.split(delimiter)[0]; return type + ' is invalid. It must be one of these values - [' + types.join(', ') + ']'; }, defaultClean = { ids: [ { id: '123', type: 'geoname' } ], private: false }, - sanitize = function(query, cb) { _sanitize({'query':query}, cb); }, inputs = { valid: [ 'geoname:1', 'osmnode:2', 'admin0:53', 'osmway:44', 'geoname:5' ], invalid: [ ':', '', '::', 'geoname:', ':234', 'gibberish:23' ] }; +// these are the default values you would expect when no input params are specified. +var emptyClean = { ids: [], private: false }; + module.exports.tests = {}; module.exports.tests.interface = function(test, common) { @@ -33,33 +37,35 @@ module.exports.tests.interface = function(test, common) { }; module.exports.tests.sanitize_id = function(test, common) { - test('invalid input', function(t) { + test('id: invalid input', function(t) { inputs.invalid.forEach( function( input ){ - sanitize({ id: input }, function( err, clean ){ - switch (err) { + var req = { query: { id: input } }; + sanitize(req, function( ){ + switch (req.errors[0]) { case defaultError: - t.equal(err, defaultError, input + ' is invalid input'); break; + t.equal(req.errors[0], defaultError, input + ' is invalid input'); break; case defaultLengthError(input): - t.equal(err, defaultLengthError(input), input + ' is invalid (missing id/type)'); break; + t.equal(req.errors[0], defaultLengthError(input), input + ' is invalid (missing id/type)'); break; case defaultFormatError: - t.equal(err, defaultFormatError, input + ' is invalid (invalid format)'); break; + t.equal(req.errors[0], defaultFormatError, input + ' is invalid (invalid format)'); break; case defaultMissingTypeError(input): - t.equal(err, defaultMissingTypeError(input), input + ' is an unknown type'); break; + t.equal(req.errors[0], defaultMissingTypeError(input), input + ' is an unknown type'); break; default: break; } - t.equal(clean, undefined, 'clean not set'); + t.deepEqual(req.clean, emptyClean, 'clean only has default values set'); }); }); t.end(); }); - test('valid input', function(t) { + test('id: valid input', function(t) { inputs.valid.forEach( function( input ){ var input_parts = input.split(delimiter); var expected = { ids: [ { id: input_parts[1], type: input_parts[0] } ], private: false }; - sanitize({ id: input }, function( err, clean ){ - t.equal(err, undefined, 'no error (' + input + ')' ); - t.deepEqual(clean, expected, 'clean set correctly (' + input + ')'); + var req = { query: { id: input } }; + sanitize(req, function(){ + t.deepEqual( req.errors, [], 'no error (' + input + ')' ); + t.deepEqual( req.clean, expected, 'clean set correctly (' + input + ')'); }); }); t.end(); @@ -68,26 +74,26 @@ module.exports.tests.sanitize_id = function(test, common) { module.exports.tests.sanitize_ids = function(test, common) { - test('invalid input', function(t) { - sanitize({ id: inputs.invalid }, function( err, clean ){ - var input = inputs.invalid[0]; // since it breaks on the first invalid element - switch (err) { - case defaultError: - t.equal(err, defaultError, input + ' is invalid input'); break; - case defaultLengthError(input): - t.equal(err, defaultLengthError(input), input + ' is invalid (missing id/type)'); break; - case defaultFormatError: - t.equal(err, defaultFormatError, input + ' is invalid (invalid format)'); break; - case defaultMissingTypeError(input): - t.equal(err, defaultMissingTypeError(input), input + ' is an unknown type'); break; - default: break; - } - t.equal(clean, undefined, 'clean not set'); + test('ids: invalid input', function(t) { + var req = { query: { id: inputs.invalid } }; + var expected = [ + 'invalid param \'id\': text length, must be >0', + 'invalid param \':\': text length, must be >0', + 'invalid param \'::\': text length, must be >0', + 'invalid param \'geoname:\': text length, must be >0', + 'invalid param \':234\': text length, must be >0', + 'gibberish is invalid. It must be one of these values - ' + + '[geoname, osmnode, osmway, admin0, admin1, admin2, neighborhood, ' + + 'locality, local_admin, osmaddress, openaddresses]' + ]; + sanitize(req, function(){ + t.deepEqual(req.errors, expected); + t.deepEqual(req.clean, emptyClean, 'clean only has default values set'); }); t.end(); }); - test('valid input', function(t) { + test('ids: valid input', function(t) { var expected={}; expected.ids = []; inputs.valid.forEach( function( input ){ @@ -95,9 +101,10 @@ module.exports.tests.sanitize_ids = function(test, common) { expected.ids.push({ id: input_parts[1], type: input_parts[0] }); }); expected.private = false; - sanitize({ id: inputs.valid }, function( err, clean ){ - t.equal(err, undefined, 'no error' ); - t.deepEqual(clean, expected, 'clean set correctly'); + var req = { query: { id: inputs.valid } }; + sanitize(req, function(){ + t.deepEqual( req.errors, [], 'no errors' ); + t.deepEqual( req.clean, expected, 'clean set correctly' ); }); t.end(); }); @@ -107,8 +114,11 @@ module.exports.tests.sanitize_private = function(test, common) { var invalid_values = [null, -1, 123, NaN, 'abc']; invalid_values.forEach(function(value) { test('invalid private param ' + value, function(t) { - sanitize({ id:'geoname:123', 'private': value}, function( err, clean ){ - t.equal(clean.private, false, 'default private set (to false)'); + var req = { query: { id:'geoname:123', 'private': value } }; + sanitize(req, function(){ + t.deepEqual( req.errors, [], 'no errors' ); + t.deepEqual( req.warnings, [], 'no warnings' ); + t.equal(req.clean.private, false, 'default private set (to false)'); t.end(); }); }); @@ -117,8 +127,11 @@ module.exports.tests.sanitize_private = function(test, common) { var valid_values = ['true', true, 1]; valid_values.forEach(function(value) { test('valid private param ' + value, function(t) { - sanitize({ id:'geoname:123', 'private': value }, function( err, clean ){ - t.equal(clean.private, true, 'private set to true'); + var req = { query: { id:'geoname:123', 'private': value } }; + sanitize(req, function(){ + t.deepEqual( req.errors, [], 'no errors' ); + t.deepEqual( req.warnings, [], 'no warnings' ); + t.equal(req.clean.private, true, 'private set to true'); t.end(); }); }); @@ -127,16 +140,22 @@ module.exports.tests.sanitize_private = function(test, common) { var valid_false_values = ['false', false, 0]; valid_false_values.forEach(function(value) { test('test setting false explicitly ' + value, function(t) { - sanitize({ id:'geoname:123', 'private': value }, function( err, clean ){ - t.equal(clean.private, false, 'private set to false'); + var req = { query: { id:'geoname:123', 'private': value } }; + sanitize(req, function(){ + t.deepEqual( req.errors, [], 'no errors' ); + t.deepEqual( req.warnings, [], 'no warnings' ); + t.equal(req.clean.private, false, 'private set to false'); t.end(); }); }); }); test('test default behavior', function(t) { - sanitize({ id:'geoname:123' }, function( err, clean ){ - t.equal(clean.private, false, 'private set to false'); + var req = { query: { id:'geoname:123' } }; + sanitize(req, function(){ + t.deepEqual( req.errors, [], 'no errors' ); + t.deepEqual( req.warnings, [], 'no warnings' ); + t.equal(req.clean.private, false, 'private set to false'); t.end(); }); }); @@ -144,10 +163,12 @@ module.exports.tests.sanitize_private = function(test, common) { module.exports.tests.de_dupe = function(test, common) { var expected = { ids: [ { id: '1', type: 'geoname' }, { id: '2', type: 'osmnode' } ], private: false }; + var req = { query: { id: ['geoname:1', 'osmnode:2', 'geoname:1'] } }; test('duplicate ids', function(t) { - sanitize( { id: ['geoname:1', 'osmnode:2', 'geoname:1'] }, function( err, clean ){ - t.equal(err, undefined, 'no error' ); - t.deepEqual(clean, expected, 'clean set correctly'); + sanitize( req, function(){ + t.deepEqual( req.errors, [], 'no errors' ); + t.deepEqual( req.warnings, [], 'no warnings' ); + t.deepEqual(req.clean, expected, 'clean set correctly'); t.end(); }); }); @@ -155,31 +176,21 @@ module.exports.tests.de_dupe = function(test, common) { module.exports.tests.invalid_params = function(test, common) { test('invalid params', function(t) { - sanitize( undefined, function( err, clean ){ - t.equal(err, defaultError, 'handle invalid params gracefully'); + var req = { query: {} }; + sanitize( req, function(){ + t.equal( req.errors[0], defaultError, 'handle invalid params gracefully'); + t.deepEqual( req.warnings, [], 'no warnings' ); t.end(); }); }); }; -module.exports.tests.middleware_failure = function(test, common) { - test('middleware failure', function(t) { - var res = { status: function( code ){ - t.equal(code, 400, 'status set'); - }}; - var next = function( message ){ - t.equal(message, defaultError); - t.end(); - }; - middleware( {}, res, next ); - }); -}; - module.exports.tests.middleware_success = function(test, common) { test('middleware success', function(t) { - var req = { query: { id: 'geoname' + delimiter + '123' }}; - var next = function( message ){ - t.equal(message, undefined, 'no error message set'); + var req = { query: { id: 'geoname:123' }}; + var next = function(){ + t.deepEqual( req.errors, [], 'no errors' ); + t.deepEqual( req.warnings, [], 'no warnings' ); t.deepEqual(req.clean, defaultClean); t.end(); }; diff --git a/test/unit/sanitiser/reverse.js b/test/unit/sanitiser/reverse.js index cd72eecd..1465a949 100644 --- a/test/unit/sanitiser/reverse.js +++ b/test/unit/sanitiser/reverse.js @@ -1,6 +1,8 @@ +// @todo: refactor this test, it's pretty messy, brittle and hard to follow + var reverse = require('../../../sanitiser/reverse'), - _sanitize = reverse.sanitize, + sanitize = reverse.sanitize, middleware = reverse.middleware, defaultError = 'missing param \'lat\'', defaultClean = { lat:0, @@ -9,10 +11,12 @@ var reverse = require('../../../sanitiser/reverse'), lon: 0, size: 10, private: false, - categories: [], boundary: { } - }, - sanitize = function(query, cb) { _sanitize({'query':query}, cb); }; + }; + +// these are the default values you would expect when no input params are specified. +// @todo: why is this different from $defaultClean? +var emptyClean = { boundary: {}, private: false, size: 10, types: {} }; module.exports.tests = {}; @@ -31,7 +35,7 @@ module.exports.tests.interface = function(test, common) { module.exports.tests.sanitisers = function(test, common) { test('check sanitiser list', function (t) { - var expected = ['layers', 'sources', 'size', 'private', 'geo_reverse', 'categories', 'boundary_country']; + var expected = ['layers', 'sources', 'size', 'private', 'geo_reverse', 'boundary_country']; t.deepEqual(Object.keys(reverse.sanitiser_list), expected); t.end(); }); @@ -45,29 +49,32 @@ module.exports.tests.sanitize_lat = function(test, common) { }; test('invalid lat', function(t) { lats.invalid.forEach( function( lat ){ - sanitize({ 'point.lat': lat, 'point.lon': 0 }, function( err, clean ){ - t.equal(err, 'invalid param \'lat\': must be >-90 and <90', lat + ' is an invalid latitude'); - t.equal(clean, undefined, 'clean not set'); + var req = { query: { 'point.lat': lat, 'point.lon': 0 } }; + sanitize(req, function(){ + t.equal(req.errors[0], 'invalid param \'lat\': must be >-90 and <90', lat + ' is an invalid latitude'); + t.deepEqual(req.clean, emptyClean, 'clean only has default values set'); }); }); t.end(); }); test('valid lat', function(t) { lats.valid.forEach( function( lat ){ - sanitize({ 'point.lat': lat, 'point.lon': 0 }, function( err, clean ){ + var req = { query: { 'point.lat': lat, 'point.lon': 0 } }; + sanitize(req, function(){ var expected = JSON.parse(JSON.stringify( defaultClean )); expected.lat = parseFloat( lat ); - t.equal(err, undefined, 'no error'); - t.equal(clean.lat, parseFloat(lat), 'clean set correctly (' + lat + ')'); + t.deepEqual(req.errors, [], 'no errors'); + t.equal(req.clean.lat, parseFloat(lat), 'clean set correctly (' + lat + ')'); }); }); t.end(); }); test('missing lat', function(t) { lats.missing.forEach( function( lat ){ - sanitize({ 'point.lat': lat, 'point.lon': 0 }, function( err, clean ){ - t.equal(err, 'missing param \'lat\'', 'latitude is a required field'); - t.equal(clean, undefined, 'clean not set'); + var req = { query: { 'point.lat': lat, 'point.lon': 0 } }; + sanitize(req, function(){ + t.equal(req.errors[0], 'missing param \'lat\'', 'latitude is a required field'); + t.deepEqual(req.clean, emptyClean, 'clean only has default values set'); }); }); t.end(); @@ -81,43 +88,50 @@ module.exports.tests.sanitize_lon = function(test, common) { }; test('valid lon', function(t) { lons.valid.forEach( function( lon ){ - sanitize({ 'point.lat': 0, 'point.lon': lon }, function( err, clean ){ + var req = { query: { 'point.lat': 0, 'point.lon': lon } }; + sanitize(req, function(){ var expected = JSON.parse(JSON.stringify( defaultClean )); expected.lon = parseFloat( lon ); - t.equal(err, undefined, 'no error'); - t.equal(clean.lon, parseFloat(lon), 'clean set correctly (' + lon + ')'); + t.deepEqual(req.errors, [], 'no errors'); + t.equal(req.clean.lon, parseFloat(lon), 'clean set correctly (' + lon + ')'); }); }); t.end(); }); test('missing lon', function(t) { lons.missing.forEach( function( lon ){ - sanitize({ 'point.lat': 0, 'point.lon': lon }, function( err, clean ){ - t.equal(err, 'missing param \'lon\'', 'longitude is a required field'); - t.equal(clean, undefined, 'clean not set'); + var req = { query: { 'point.lat': 0, 'point.lon': lon } }; + + // @todo: why is lat set? + var expected = { boundary: {}, lat: 0, private: false, size: 10, types: {} }; + sanitize(req, function(){ + t.equal(req.errors[0], 'missing param \'lon\'', 'longitude is a required field'); + t.deepEqual(req.clean, expected, 'clean only has default values set'); }); }); t.end(); }); }; - module.exports.tests.sanitize_size = function(test, common) { test('invalid size value', function(t) { - sanitize({ size: 'a', 'point.lat': 0, 'point.lon': 0 }, function( err, clean ){ - t.equal(clean.size, 10, 'default size set'); + var req = { query: { size: 'a', 'point.lat': 0, 'point.lon': 0 } }; + sanitize(req, function(){ + t.equal(req.clean.size, 10, 'default size set'); t.end(); }); }); test('below min size value', function(t) { - sanitize({ size: -100, 'point.lat': 0, 'point.lon': 0 }, function( err, clean ){ - t.equal(clean.size, 1, 'min size set'); + var req = { query: { size: -100, 'point.lat': 0, 'point.lon': 0 } }; + sanitize(req, function(){ + t.equal(req.clean.size, 1, 'min size set'); t.end(); }); }); test('above max size value', function(t) { - sanitize({ size: 9999, 'point.lat': 0, 'point.lon': 0 }, function( err, clean ){ - t.equal(clean.size, 40, 'max size set'); + var req = { query: { size: 9999, 'point.lat': 0, 'point.lon': 0 } }; + sanitize(req, function(){ + t.equal(req.clean.size, 40, 'max size set'); t.end(); }); }); @@ -127,8 +141,9 @@ module.exports.tests.sanitize_private = function(test, common) { var invalid_values = [null, -1, 123, NaN, 'abc']; invalid_values.forEach(function(value) { test('invalid private param ' + value, function(t) { - sanitize({ 'point.lat': 0, 'point.lon': 0, 'private': value}, function( err, clean ){ - t.equal(clean.private, false, 'default private set (to false)'); + var req = { query: { 'point.lat': 0, 'point.lon': 0, 'private': value } }; + sanitize(req, function(){ + t.equal(req.clean.private, false, 'default private set (to false)'); t.end(); }); }); @@ -137,8 +152,9 @@ module.exports.tests.sanitize_private = function(test, common) { var valid_values = ['true', true, 1, '1']; valid_values.forEach(function(value) { test('valid private param ' + value, function(t) { - sanitize({ 'point.lat': 0, 'point.lon': 0, 'private': value }, function( err, clean ){ - t.equal(clean.private, true, 'private set to true'); + var req = { query: { 'point.lat': 0, 'point.lon': 0, 'private': value } }; + sanitize(req, function(){ + t.equal(req.clean.private, true, 'private set to true'); t.end(); }); }); @@ -147,78 +163,28 @@ module.exports.tests.sanitize_private = function(test, common) { var valid_false_values = ['false', false, 0]; valid_false_values.forEach(function(value) { test('test setting false explicitly ' + value, function(t) { - sanitize({ 'point.lat': 0, 'point.lon': 0, 'private': value }, function( err, clean ){ - t.equal(clean.private, false, 'private set to false'); + var req = { query: { 'point.lat': 0, 'point.lon': 0, 'private': value } }; + sanitize(req, function(){ + t.equal(req.clean.private, false, 'private set to false'); t.end(); }); }); }); test('test default behavior', function(t) { - sanitize({ 'point.lat': 0, 'point.lon': 0 }, function( err, clean ){ - t.equal(clean.private, false, 'private set to false'); - t.end(); - }); - }); -}; - -module.exports.tests.sanitize_categories = function(test, common) { - var queryParams = { 'point.lat': 0, 'point.lon': 0 }; - test('unspecified', function(t) { - queryParams.categories = undefined; - sanitize(queryParams, function( err, clean ){ - t.deepEqual(clean.categories, defaultClean.categories, 'default to empty categories array'); - t.end(); - }); - }); - test('single category', function(t) { - queryParams.categories = 'food'; - sanitize(queryParams, function( err, clean ){ - t.deepEqual(clean.categories, ['food'], 'category set'); - t.end(); - }); - }); - test('multiple categories', function(t) { - queryParams.categories = 'food,education,nightlife'; - sanitize(queryParams, function( err, clean ){ - t.deepEqual(clean.categories, ['food', 'education', 'nightlife'], 'categories set'); + var req = { query: { 'point.lat': 0, 'point.lon': 0 } }; + sanitize(req, function(){ + t.equal(req.clean.private, false, 'private set to false'); t.end(); }); }); - test('whitespace and empty strings', function(t) { - queryParams.categories = 'food, , nightlife ,'; - sanitize(queryParams, function( err, clean ){ - t.deepEqual(clean.categories, ['food', 'nightlife'], 'categories set'); - t.end(); - }); - }); - test('all empty strings', function(t) { - queryParams.categories = ', , ,'; - sanitize(queryParams, function( err, clean ){ - t.deepEqual(clean.categories, defaultClean.categories, 'empty strings filtered out'); - t.end(); - }); - }); -}; - -module.exports.tests.middleware_failure = function(test, common) { - test('middleware failure', function(t) { - var res = { status: function( code ){ - t.equal(code, 400, 'status set'); - }}; - var next = function( message ){ - t.equals(message, defaultError); - t.end(); - }; - middleware( {}, res, next ); - }); }; module.exports.tests.middleware_success = function(test, common) { test('middleware success', function(t) { var req = { query: { 'point.lat': 0, 'point.lon': 0 }}; - var next = function( message ){ - t.equal(message, undefined, 'no error message set'); + var next = function(){ + t.deepEqual(req.errors, [], 'no error message set'); t.deepEqual(req.clean, defaultClean); t.end(); }; diff --git a/test/unit/sanitiser/search.js b/test/unit/sanitiser/search.js index 3a085a55..5992f94e 100644 --- a/test/unit/sanitiser/search.js +++ b/test/unit/sanitiser/search.js @@ -3,7 +3,7 @@ var search = require('../../../sanitiser/search'), _text = require('../sanitiser/_text'), parser = require('../../../helper/query_parser'), defaultParsed = _text.defaultParsed, - _sanitize = search.sanitize, + sanitize = search.sanitize, middleware = search.middleware, defaultError = 'invalid param \'text\': text length, must be >0', defaultClean = { text: 'test', @@ -11,8 +11,11 @@ var search = require('../../../sanitiser/search'), }, size: 10, parsed_text: defaultParsed, - }, - sanitize = function(query, cb) { _sanitize({'query':query}, cb); }; + }; + +// these are the default values you would expect when no input params are specified. +// @todo: why is this different from $defaultClean? +var emptyClean = { boundary: {}, private: false, size: 10, types: {} }; module.exports.tests = {}; @@ -31,7 +34,7 @@ module.exports.tests.interface = function(test, common) { module.exports.tests.sanitisers = function(test, common) { test('check sanitiser list', function (t) { - var expected = ['text', 'size', 'layers', 'sources', 'private', 'geo_search', 'categories', 'boundary_country' ]; + var expected = ['text', 'size', 'layers', 'sources', 'private', 'geo_search', 'boundary_country' ]; t.deepEqual(Object.keys(search.sanitiser_list), expected); t.end(); }); @@ -41,9 +44,10 @@ module.exports.tests.sanitize_invalid_text = function(test, common) { test('invalid text', function(t) { var invalid = [ '', 100, null, undefined, new Date() ]; invalid.forEach( function( text ){ - sanitize({ text: text }, function( err, clean ){ - t.equal(err, 'invalid param \'text\': text length, must be >0', text + ' is an invalid text'); - t.equal(clean, undefined, 'clean not set'); + var req = { query: { text: text } }; + sanitize(req, function(){ + t.equal(req.errors[0], 'invalid param \'text\': text length, must be >0', text + ' is an invalid text'); + t.deepEqual(req.clean, emptyClean, 'clean only has default values set'); }); }); t.end(); @@ -52,22 +56,25 @@ module.exports.tests.sanitize_invalid_text = function(test, common) { module.exports.tests.sanitise_valid_text = function(test, common) { test('valid short text', function(t) { - sanitize({ text: 'a' }, function( err, clean ){ - t.equal(err, undefined, 'no error'); + var req = { query: { text: 'a' } }; + sanitize(req, function(){ + t.equal(req.errors[0], undefined, 'no error'); }); t.end(); }); test('valid not-quite-as-short text', function(t) { - sanitize({ text: 'aa' }, function( err, clean ){ - t.equal(err, undefined, 'no error'); + var req = { query: { text: 'aa' } }; + sanitize(req, function(){ + t.equal(req.errors[0], undefined, 'no error'); }); t.end(); }); test('valid longer text', function(t) { - sanitize({ text: 'aaaaaaaa' }, function( err, clean ){ - t.equal(err, undefined, 'no error'); + var req = { query: { text: 'aaaaaaaa' } }; + sanitize(req, function(){ + t.equal(req.errors[0], undefined, 'no error'); }); t.end(); }); @@ -78,13 +85,14 @@ module.exports.tests.sanitize_text_with_delim = function(test, common) { test('valid texts with a comma', function(t) { texts.forEach( function( text ){ - sanitize({ text: text }, function( err, clean ){ + var req = { query: { text: text } }; + sanitize(req, function(){ var expected = JSON.parse(JSON.stringify( defaultClean )); expected.text = text; expected.parsed_text = parser.get_parsed_address(text); - t.equal(err, undefined, 'no error'); - t.equal(clean.parsed_text.name, expected.parsed_text.name, 'clean name set correctly'); + t.equal(req.errors[0], undefined, 'no error'); + t.equal(req.clean.parsed_text.name, expected.parsed_text.name, 'clean name set correctly'); }); }); @@ -94,8 +102,9 @@ module.exports.tests.sanitize_text_with_delim = function(test, common) { module.exports.tests.sanitize_private_no_value = function(test, common) { test('default private should be set to true', function(t) { - sanitize({ text: 'test' }, function( err, clean ){ - t.equal(clean.private, false, 'private set to false'); + var req = { query: { text: 'test' } }; + sanitize(req, function(){ + t.equal(req.clean.private, false, 'private set to false'); }); t.end(); }); @@ -103,8 +112,9 @@ module.exports.tests.sanitize_private_no_value = function(test, common) { module.exports.tests.sanitize_private_explicit_true_value = function(test, common) { test('explicit private should be set to true', function(t) { - sanitize({ text: 'test', private: true }, function( err, clean ){ - t.equal(clean.private, true, 'private set to true'); + var req = { query: { text: 'test', private: true } }; + sanitize(req, function(){ + t.equal(req.clean.private, true, 'private set to true'); }); t.end(); }); @@ -112,8 +122,9 @@ module.exports.tests.sanitize_private_explicit_true_value = function(test, commo module.exports.tests.sanitize_private_explicit_false_value = function(test, common) { test('explicit private should be set to false', function(t) { - sanitize({ text: 'test', private: false }, function( err, clean ){ - t.equal(clean.private, false, 'private set to false'); + var req = { query: { text: 'test', private: false } }; + sanitize(req, function(){ + t.equal(req.clean.private, false, 'private set to false'); }); t.end(); }); @@ -126,19 +137,21 @@ module.exports.tests.sanitize_lat = function(test, common) { }; test('invalid lat', function(t) { lats.invalid.forEach( function( lat ){ - sanitize({ text: 'test', 'focus.point.lat': lat, 'focus.point.lon': 0 }, function( err, clean ){ - t.equal(err, 'invalid param \'lat\': must be >-90 and <90', lat + ' is an invalid latitude'); - t.equal(clean, undefined, 'clean not set'); + var req = { query: { text: 'test', 'focus.point.lat': lat, 'focus.point.lon': 0 } }; + sanitize(req, function(){ + t.equal(req.errors[0], 'invalid param \'lat\': must be >-90 and <90', lat + ' is an invalid latitude'); + t.deepEqual(req.clean, emptyClean, 'clean only has default values set'); }); }); t.end(); }); test('valid lat', function(t) { lats.valid.forEach( function( lat ){ - sanitize({ text: 'test', 'focus.point.lat': lat, 'focus.point.lon': 0 }, function( err, clean ){ + var req = { query: { text: 'test', 'focus.point.lat': lat, 'focus.point.lon': 0 } }; + sanitize(req, function(){ var expected_lat = parseFloat( lat ); - t.equal(err, undefined, 'no error'); - t.deepEqual(clean.lat, expected_lat, 'clean lat set correctly (' + lat + ')'); + t.equal(req.errors[0], undefined, 'no error'); + t.equal(req.clean.lat, expected_lat, 'clean lat set correctly (' + lat + ')'); }); }); t.end(); @@ -151,11 +164,12 @@ module.exports.tests.sanitize_lon = function(test, common) { }; test('valid lon', function(t) { lons.valid.forEach( function( lon ){ - sanitize({ text: 'test', 'focus.point.lat': 0, 'focus.point.lon': lon }, function( err, clean ){ + var req = { query: { text: 'test', 'focus.point.lat': 0, 'focus.point.lon': lon } }; + sanitize(req, function(){ var expected = JSON.parse(JSON.stringify( defaultClean )); expected.lon = parseFloat( lon ); - t.equal(err, undefined, 'no error'); - t.deepEqual(clean.lon, expected.lon, 'clean set correctly (' + lon + ')'); + t.equal(req.errors[0], undefined, 'no error'); + t.equal(req.clean.lon, expected.lon, 'clean set correctly (' + lon + ')'); }); }); t.end(); @@ -164,26 +178,29 @@ module.exports.tests.sanitize_lon = function(test, common) { module.exports.tests.sanitize_optional_geo = function(test, common) { test('no lat/lon', function(t) { - sanitize({ text: 'test' }, function( err, clean ){ - t.equal(err, undefined, 'no error'); - t.equal(clean.lat, undefined, 'clean set without lat'); - t.equal(clean.lon, undefined, 'clean set without lon'); + var req = { query: { text: 'test' } }; + sanitize(req, function(){ + t.equal(req.errors[0], undefined, 'no error'); + t.equal(req.clean.lat, undefined, 'clean set without lat'); + t.equal(req.clean.lon, undefined, 'clean set without lon'); }); t.end(); }); test('no lat', function(t) { - sanitize({ text: 'test', 'focus.point.lon': 0 }, function( err, clean ){ + var req = { query: { text: 'test', 'focus.point.lon': 0 } }; + sanitize(req, function(){ var expected_lon = 0; - t.equal(err, undefined, 'no error'); - t.deepEqual(clean.lon, expected_lon, 'clean set correctly (without any lat)'); + t.equal(req.errors[0], undefined, 'no error'); + t.deepEqual(req.clean.lon, expected_lon, 'clean set correctly (without any lat)'); }); t.end(); }); test('no lon', function(t) { - sanitize({ text: 'test', 'focus.point.lat': 0 }, function( err, clean ){ + var req = { query: { text: 'test', 'focus.point.lat': 0 } }; + sanitize(req, function(){ var expected_lat = 0; - t.equal(err, undefined, 'no error'); - t.deepEqual(clean.lat, expected_lat, 'clean set correctly (without any lon)'); + t.equal(req.errors[0], undefined, 'no error'); + t.deepEqual(req.clean.lat, expected_lat, 'clean set correctly (without any lon)'); }); t.end(); }); @@ -219,18 +236,20 @@ module.exports.tests.sanitize_bbox = function(test, common) { }; test('invalid bbox', function(t) { bboxes.invalid.forEach( function( bbox ){ - sanitize({ text: 'test', bbox: bbox }, function( err, clean ){ - t.equal(err, undefined, 'no error'); - t.equal(clean.bbox, undefined, 'falling back on 50km distance from centroid'); + var req = { query: { text: 'test', bbox: bbox } }; + sanitize(req, function(){ + t.equal(req.errors[0], undefined, 'no error'); + t.equal(req.clean.bbox, undefined, 'falling back on 50km distance from centroid'); }); }); t.end(); }); test('valid bbox', function(t) { bboxes.valid.forEach( function( bbox ){ - sanitize({ text: 'test', bbox: bbox }, function( err, clean ){ + var req = { query: { text: 'test', bbox: bbox } }; + sanitize(req, function(){ var bboxArray = bbox.split(',').map(function(i) { - return parseInt(i); + return parseInt(i, 10); }); var expected_bbox = { right: Math.max(bboxArray[0], bboxArray[2]), @@ -238,8 +257,8 @@ module.exports.tests.sanitize_bbox = function(test, common) { left: Math.min(bboxArray[0], bboxArray[2]), bottom: Math.min(bboxArray[1], bboxArray[3]) }; - t.equal(err, undefined, 'no error'); - t.deepEqual(clean.bbox, expected_bbox, 'clean set correctly (' + bbox + ')'); + t.equal(req.errors[0], undefined, 'no error'); + t.deepEqual(req.clean.bbox, expected_bbox, 'clean set correctly (' + bbox + ')'); }); }); t.end(); @@ -248,20 +267,23 @@ module.exports.tests.sanitize_bbox = function(test, common) { module.exports.tests.sanitize_size = function(test, common) { test('invalid size value', function(t) { - sanitize({ size: 'a', text: 'test', lat: 0, lon: 0 }, function( err, clean ){ - t.equal(clean.size, 10, 'default size set'); + var req = { query: { size: 'a', text: 'test', lat: 0, lon: 0 } }; + sanitize(req, function(){ + t.equal(req.clean.size, 10, 'default size set'); t.end(); }); }); test('below min size value', function(t) { - sanitize({ size: -100, text: 'test', lat: 0, lon: 0 }, function( err, clean ){ - t.equal(clean.size, 1, 'min size set'); + var req = { query: { size: -100, text: 'test', lat: 0, lon: 0 } }; + sanitize(req, function(){ + t.equal(req.clean.size, 1, 'min size set'); t.end(); }); }); test('above max size value', function(t) { - sanitize({ size: 9999, text: 'test', lat: 0, lon: 0 }, function( err, clean ){ - t.equal(clean.size, 40, 'max size set'); + var req = { query: { size: 9999, text: 'test', lat: 0, lon: 0 } }; + sanitize(req, function(){ + t.equal(req.clean.size, 40, 'max size set'); t.end(); }); }); @@ -271,8 +293,9 @@ module.exports.tests.sanitize_private = function(test, common) { var invalid_values = [null, -1, 123, NaN, 'abc']; invalid_values.forEach(function(value) { test('invalid private param ' + value, function(t) { - sanitize({ text: 'test', lat: 0, lon: 0, 'private': value }, function( err, clean ){ - t.equal(clean.private, false, 'default private set (to false)'); + var req = { query: { text: 'test', lat: 0, lon: 0, 'private': value } }; + sanitize(req, function(){ + t.equal(req.clean.private, false, 'default private set (to false)'); t.end(); }); }); @@ -281,8 +304,9 @@ module.exports.tests.sanitize_private = function(test, common) { var valid_values = ['true', true, 1, '1']; valid_values.forEach(function(value) { test('valid private ' + value, function(t) { - sanitize({ text: 'test', 'private': value}, function( err, clean ){ - t.equal(clean.private, true, 'private set to true'); + var req = { query: { text: 'test', 'private': value } }; + sanitize(req, function(){ + t.equal(req.clean.private, true, 'private set to true'); t.end(); }); }); @@ -291,16 +315,18 @@ module.exports.tests.sanitize_private = function(test, common) { var valid_false_values = ['false', false, 0, '0']; valid_false_values.forEach(function(value) { test('test setting false explicitly ' + value, function(t) { - sanitize({ text: 'test', 'private': value }, function( err, clean ){ - t.equal(clean.private, false, 'private set to false'); + var req = { query: { text: 'test', 'private': value } }; + sanitize(req, function(){ + t.equal(req.clean.private, false, 'private set to false'); t.end(); }); }); }); test('test default behavior', function(t) { - sanitize({ text: 'test' }, function( err, clean ){ - t.equal(clean.private, false, 'private set to false'); + var req = { query: { text: 'test' } }; + sanitize(req, function(){ + t.equal(req.clean.private, false, 'private set to false'); t.end(); }); }); @@ -308,31 +334,19 @@ module.exports.tests.sanitize_private = function(test, common) { module.exports.tests.invalid_params = function(test, common) { test('invalid text params', function(t) { - sanitize( undefined, function( err, clean ){ - t.equal(err, defaultError, 'handle invalid params gracefully'); + var req = { query: {} }; + sanitize( req, function(){ + t.equal(req.errors[0], defaultError, 'handle invalid params gracefully'); t.end(); }); }); }; -module.exports.tests.middleware_failure = function(test, common) { - test('middleware failure', function(t) { - var res = { status: function( code ){ - t.equal(code, 400, 'status set'); - }}; - var next = function( message ){ - t.equal(message, defaultError); - t.end(); - }; - middleware( {}, res, next ); - }); -}; - module.exports.tests.middleware_success = function(test, common) { test('middleware success', function(t) { var req = { query: { text: 'test' }}; var next = function( message ){ - t.equal(message, undefined, 'no error message set'); + t.deepEqual(req.errors, [], 'no error messages set'); t.end(); }; middleware( req, undefined, next );