diff --git a/controller/place.js b/controller/place.js index 1aa22aeb..29acfe8b 100644 --- a/controller/place.js +++ b/controller/place.js @@ -14,35 +14,10 @@ function setup( backend ){ return next(); } - /* req.clean.ids contains an array of objects with id and types properties. - * types is an array of one or more types, since it can't always be known which single - * type a gid might belong to (osmnode and osmway both have source osm and layer venue). - * - * However, the mget Elasticsearch query only accepts a single type at a - * time. - * - * So, first create a new array that, has an entry - * with each type and id combination. This requires creating a new array with more entries - * than req.clean.ids in the case where entries have multiple types. - */ - - var recordsToReturn = req.clean.ids.reduce(function (acc, ids_element) { - ids_element.types.forEach(function(type) { - acc.push({ - id: ids_element.id, - type: type - }); - }); - return acc; - }, []); - - /* - * Next, map the list of records to an Elasticsearch mget query - */ - var query = recordsToReturn.map( function(id) { + var query = req.clean.ids.map( function(id) { return { _index: 'pelias', - _type: id.type, + _type: id.layers, _id: id.id }; }); @@ -50,6 +25,7 @@ function setup( backend ){ logger.debug( '[ES req]', JSON.stringify(query) ); service.mget( backend, query, function( err, docs ) { + console.log('err:' + err); // error handler if( err ){ diff --git a/controller/search.js b/controller/search.js index 9fafdd20..4adaf31d 100644 --- a/controller/search.js +++ b/controller/search.js @@ -8,10 +8,10 @@ function setup( backend, query ){ query = query || require('../query/search'); function controller( req, res, next ){ - // do not run controller when a request // validation error has occurred. if( req.errors && req.errors.length ){ + delete req.clean.type; //to declutter output return next(); } @@ -25,9 +25,12 @@ function setup( backend, query ){ body: query( req.clean ) }; - // ? + // set the Elasticsearch types to filter by, + // and remove the property from clean so the API + // response output is cleaner if( req.clean.hasOwnProperty('type') ){ cmd.type = req.clean.type; + delete req.clean.type; } logger.debug( '[ES req]', JSON.stringify(cmd) ); diff --git a/helper/text_parser.js b/helper/text_parser.js index a561aef9..7b9ffcfe 100644 --- a/helper/text_parser.js +++ b/helper/text_parser.js @@ -15,7 +15,7 @@ module.exports = {}; module.exports.get_layers = function get_layers(query) { if (query.length <= 3 ) { // no address parsing required - return type_mapping.layer_with_aliases_to_type.coarse; + return type_mapping.layer_mapping.coarse; } }; diff --git a/helper/type_mapping.js b/helper/type_mapping.js index 06a8ec2d..ced5f1f3 100644 --- a/helper/type_mapping.js +++ b/helper/type_mapping.js @@ -1,87 +1,85 @@ var extend = require('extend'), _ = require('lodash'); -var TYPE_TO_SOURCE = { - 'geoname': 'gn', - 'osmnode': 'osm', - 'osmway': 'osm', - 'admin0': 'qs', - 'admin1': 'qs', - 'admin2': 'qs', - 'neighborhood': 'qs', - 'locality': 'qs', - 'local_admin': 'qs', - 'osmaddress': 'osm', - 'openaddresses': 'oa' -}; +function addStandardTargetsToAliases(standard, aliases) { + var combined = _.extend({}, aliases); + standard.forEach(function(target) { + if (combined[target] === undefined) { + combined[target] = [target]; + } + }); + + return combined; +} /* - * This doesn't include alias layers such as coarse + * Sources */ -var TYPE_TO_LAYER = { - 'geoname': 'venue', - 'osmnode': 'venue', - 'osmway': 'venue', - 'admin0': 'country', - 'admin1': 'region', - 'admin2': 'county', - 'neighborhood': 'neighbourhood', - 'locality': 'locality', - 'local_admin': 'localadmin', - 'osmaddress': 'address', - 'openaddresses': 'address' -}; -var SOURCE_TO_TYPE = { - 'gn' : ['geoname'], - 'geonames' : ['geoname'], - 'oa' : ['openaddresses'], - 'openaddresses' : ['openaddresses'], - 'qs' : ['admin0', 'admin1', 'admin2', 'neighborhood', 'locality', 'local_admin'], - 'quattroshapes' : ['admin0', 'admin1', 'admin2', 'neighborhood', 'locality', 'local_admin'], - 'osm' : ['osmaddress', 'osmnode', 'osmway'], - 'openstreetmap' : ['osmaddress', 'osmnode', 'osmway'] +// a list of all sources +var SOURCES = ['openstreetmap', 'openaddresses', 'geonames', 'quattroshapes', 'whosonfirst']; + +/* + * A list of alternate names for sources, mostly used to save typing + */ +var SOURCE_ALIASES = { + 'osm': ['openstreetmap'], + 'oa': ['openaddresses'], + 'gn': ['geonames'], + 'qs': ['quattroshapes'], + 'wof': ['whosonfirst'] }; -/** - * This does not included alias layers, those are built separately +/* + * Create an object that contains all sources or aliases. The key is the source or alias, + * the value is either that source, or the canonical name for that alias if it's an alias. */ -var LAYER_TO_TYPE = { - 'venue': ['geoname','osmnode','osmway'], - 'address': ['osmaddress','openaddresses'], - 'country': ['admin0'], - 'region': ['admin1'], - 'county': ['admin2'], - 'locality': ['locality'], - 'localadmin': ['local_admin'], - 'neighbourhood': ['neighborhood'] +var SOURCE_MAPPING = addStandardTargetsToAliases(SOURCES, SOURCE_ALIASES); + +/* + * Layers + */ + +/* + * A list of all layers in each source. This is used for convenience elswhere + * and to determine when a combination of source and layer parameters is + * not going to match any records and will return no results. + */ +var LAYERS_BY_SOURCE = { + openstreetmap: [ 'address', 'venue' ], + openaddresses: [ 'address' ], + geonames: [ 'country', 'region', 'county', 'locality', 'venue' ], + quattroshapes: ['admin0', 'admin1', 'admin2', 'neighborhood', 'locality', 'local_admin'], + whosonfirst: [ 'continent', 'macrocountry', 'country', 'dependency', 'region', + 'locality', 'localadmin', 'county', 'macrohood', 'neighbourhood', 'microhood', 'disputed'] }; +/* + * A list of layer aliases that can be used to support specific use cases + * (like coarse geocoding) * or work around the fact that different sources + * may have layers that mean the same thing but have a different name + */ var LAYER_ALIASES = { - 'coarse': ['admin0','admin1','admin2','neighborhood','locality','local_admin'] + 'coarse': LAYERS_BY_SOURCE.whosonfirst.concat(LAYERS_BY_SOURCE.quattroshapes), + 'country': ['country', 'admin0'], // Include both QS and WOF layers for various types of places + 'region': ['region', 'admin1'] // Alias layers that include themselves look weird, but do work }; -var LAYER_WITH_ALIASES_TO_TYPE = extend({}, LAYER_ALIASES, LAYER_TO_TYPE); +// create a list of all layers by combining each entry from LAYERS_BY_SOURCE +var LAYERS = _.uniq(Object.keys(LAYERS_BY_SOURCE).reduce(function(acc, key) { + return acc.concat(LAYERS_BY_SOURCE[key]); +}, [])); /* - * derive the list of types, sources, and layers from above mappings + * Create the an object that has a key for each possible layer or alias, + * and returns either that layer, or all the layers in the alias */ -var TYPES = Object.keys(TYPE_TO_SOURCE); -var SOURCES = Object.keys(SOURCE_TO_TYPE); -var LAYERS = Object.keys(LAYER_TO_TYPE); - -var sourceAndLayerToType = function sourceAndLayerToType(source, layer) { - return _.intersection(SOURCE_TO_TYPE[source], LAYER_WITH_ALIASES_TO_TYPE[layer]); -}; +var LAYER_MAPPING = addStandardTargetsToAliases(LAYERS, LAYER_ALIASES); module.exports = { - types: TYPES, sources: SOURCES, layers: LAYERS, - type_to_source: TYPE_TO_SOURCE, - type_to_layer: TYPE_TO_LAYER, - source_to_type: SOURCE_TO_TYPE, - layer_to_type: LAYER_TO_TYPE, - layer_with_aliases_to_type: LAYER_WITH_ALIASES_TO_TYPE, - source_and_layer_to_type: sourceAndLayerToType + source_mapping: SOURCE_MAPPING, + layer_mapping: LAYER_MAPPING, + layers_by_source: LAYERS_BY_SOURCE }; diff --git a/helper/types.js b/helper/types.js deleted file mode 100644 index d9dec59d..00000000 --- a/helper/types.js +++ /dev/null @@ -1,43 +0,0 @@ -var type_mapping = require( '../helper/type_mapping' ); -var _ = require('lodash'); - -/** - * Different parts of the code express "preferences" for which Elasticsearch types are going to be searched - * This method decides how to combine all the preferences. - * - * @param {Array} clean_types - * @returns {Array} - */ -module.exports = function calculate_types(clean_types) { - //Check that at least one preference of types is defined - if (!clean_types || !(clean_types.from_layers || clean_types.from_sources || clean_types.from_text_parser)) { - throw new Error('clean_types should not be null or undefined'); - } - - /* the layers and source parameters are cumulative: - * perform a set intersection of their specified types - */ - if (clean_types.from_layers || clean_types.from_sources) { - var types = type_mapping.types; - - if (clean_types.from_layers) { - types = _.intersection(types, clean_types.from_layers); - } - - if (clean_types.from_sources) { - types = _.intersection(types, clean_types.from_sources); - } - - return types; - } - - /* - * Type restrictions requested by the address parser should only be used - * if both the source and layers parameters are empty, so do this last - */ - if (clean_types.from_text_parser) { - return clean_types.from_text_parser; - } - - throw new Error('no types specified'); -}; diff --git a/middleware/_types.js b/middleware/_types.js index 3e604f7b..ccabdab9 100644 --- a/middleware/_types.js +++ b/middleware/_types.js @@ -1,43 +1,15 @@ -var types_helper = require( '../helper/types' ); - /** - * Validate the types specified to be searched. + * Take the layers specified by the layers parameter and use them to set the + * list of Elasticsearch types to filter. * - * Elasticsearch interprets an empty array of types as "search anything" rather - * than "search nothing", so in the case of an empty array, return an error - * message instead of searching at all. + * This has to be done outside the layers sanitizer since it doesn't know that + * the layers property is eventualy used to choose the _type. */ function middleware(req, res, next) { req.clean = req.clean || {}; - if (req.clean.hasOwnProperty('types')) { - - try { - var types = types_helper(req.clean.types); - - if ((types instanceof Array) && types.length === 0) { - var err = 'You have specified both the `sources` and `layers` ' + - 'parameters in a combination that will return no results.'; - req.errors.push( err ); - } - - else { - req.clean.type = types; - } - - } - - // @todo: refactor this flow, it is confusing as `types_helper()` can throw - // with an error "clean_types should not be null or undefined" which is - // not returned to the user yet the return value CAN trigger a user error. - // I would have liked to throw for BOTH cases and then handle the users errors - // inside the 'catch' but this is not possible. - // also: why are we deleting things from $clean? - catch (err) { - // this means there were no types specified - delete req.clean.types; - } - + if (req.clean.hasOwnProperty('layers')) { + req.clean.type = req.clean.layers; } next(); diff --git a/query/search.js b/query/search.js index 764cf999..804cb88b 100644 --- a/query/search.js +++ b/query/search.js @@ -37,7 +37,7 @@ query.score( peliasQuery.view.admin('neighborhood') ); // non-scoring hard filters query.filter( peliasQuery.view.boundary_circle ); query.filter( peliasQuery.view.boundary_rect ); - +query.filter( peliasQuery.view.sources ); // -------------------------------- /** @@ -51,6 +51,8 @@ function generateQuery( clean ){ // input text vs.var( 'input:name', clean.text ); + vs.var( 'sources', clean.sources); + // size if( clean.querySize ) { vs.var( 'size', clean.querySize ); diff --git a/sanitiser/_ids.js b/sanitiser/_ids.js index a4d135a0..4a1844be 100644 --- a/sanitiser/_ids.js +++ b/sanitiser/_ids.js @@ -45,17 +45,10 @@ function sanitizeId(rawId, messages) { return; } - //TODO: remove this once we have a better set of layers for Geonames - var types; - if (source === 'gn' || source === 'geonames') { - types = ['geoname']; - } else { - types = type_mapping.source_and_layer_to_type(source, layer); - } - return { + source: source, + layer: layer, id: id, - types: types }; } diff --git a/sanitiser/_targets.js b/sanitiser/_targets.js index 550d3a0c..a8a50400 100644 --- a/sanitiser/_targets.js +++ b/sanitiser/_targets.js @@ -1,25 +1,24 @@ - var _ = require('lodash'), check = require('check-types'); +function getValidKeys(mapping) { + return _.uniq(Object.keys(mapping)).join(','); +} + function setup( paramName, targetMap ) { return function( raw, clean ){ return sanitize( raw, clean, { paramName: paramName, targetMap: targetMap, - targetMapKeysString: Object.keys(targetMap).join(',') + targetMapKeysString: getValidKeys(targetMap) }); }; } function sanitize( raw, clean, opts ) { - // error & warning messages var messages = { errors: [], warnings: [] }; - // init clean.types - clean.types = clean.types || {}; - // the string of targets (comma delimeted) var targetsString = raw[opts.paramName]; @@ -51,19 +50,13 @@ function sanitize( raw, clean, opts ) { // only set types value when no error occured if( !messages.errors.length ){ - - // store the values under a new key as 'clean.types.from_*' - var typesKey = 'from_' + opts.paramName; - - // ? - clean.types[typesKey] = targets.reduce(function(acc, target) { + clean[opts.paramName] = targets.reduce(function(acc, target) { return acc.concat(opts.targetMap[target]); }, []); // dedupe in case aliases expanded to common things or user typed in duplicates - clean.types[typesKey] = _.uniq(clean.types[typesKey]); + clean[opts.paramName] = _.uniq(clean[opts.paramName]); } - } } diff --git a/sanitiser/_text.js b/sanitiser/_text.js index 53898ff0..e6897a5e 100644 --- a/sanitiser/_text.js +++ b/sanitiser/_text.js @@ -23,10 +23,6 @@ function sanitize( raw, clean ){ if (check.assigned(parsed_text)) { clean.parsed_text = parsed_text; } - - // try to set layers from query parser results - clean.types = clean.layers || {}; - clean.types.from_text_parser = text_parser.get_layers(clean.text); } return messages; diff --git a/sanitiser/reverse.js b/sanitiser/reverse.js index 154fd3d9..045ff3a1 100644 --- a/sanitiser/reverse.js +++ b/sanitiser/reverse.js @@ -3,8 +3,8 @@ var type_mapping = require('../helper/type_mapping'); var sanitizeAll = require('../sanitiser/sanitizeAll'), sanitizers = { singleScalarParameters: require('../sanitiser/_single_scalar_parameters'), - layers: require('../sanitiser/_targets')('layers', type_mapping.layer_with_aliases_to_type), - sources: require('../sanitiser/_targets')('sources', type_mapping.source_to_type), + layers: require('../sanitiser/_targets')('layers', type_mapping.layer_mapping), + sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping), size: require('../sanitiser/_size'), private: require('../sanitiser/_flag_bool')('private', false), geo_reverse: require('../sanitiser/_geo_reverse'), diff --git a/sanitiser/search.js b/sanitiser/search.js index cc596b26..1be32b02 100644 --- a/sanitiser/search.js +++ b/sanitiser/search.js @@ -5,8 +5,8 @@ var sanitizeAll = require('../sanitiser/sanitizeAll'), singleScalarParameters: require('../sanitiser/_single_scalar_parameters'), text: require('../sanitiser/_text'), size: require('../sanitiser/_size'), - layers: require('../sanitiser/_targets')('layers', type_mapping.layer_with_aliases_to_type), - sources: require('../sanitiser/_targets')('sources', type_mapping.source_to_type), + layers: require('../sanitiser/_targets')('layers', type_mapping.layer_mapping), + sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping), private: require('../sanitiser/_flag_bool')('private', false), geo_search: require('../sanitiser/_geo_search'), boundary_country: require('../sanitiser/_boundary_country'), diff --git a/service/mget.js b/service/mget.js index a878e8cb..529d0fc1 100644 --- a/service/mget.js +++ b/service/mget.js @@ -24,7 +24,6 @@ function service( backend, query, cb ){ // query new backend backend().client.mget( cmd, function( err, data ){ - // handle backend errors if( err ){ return cb( err ); } diff --git a/test/unit/controller/place.js b/test/unit/controller/place.js index e8a8c333..977be59e 100644 --- a/test/unit/controller/place.js +++ b/test/unit/controller/place.js @@ -41,7 +41,7 @@ module.exports.tests.functional_success = function(test, common) { test('functional success', function(t) { var backend = mockBackend( 'client/mget/ok/1', function( cmd ){ - t.deepEqual(cmd, { body: { docs: [ { _id: 123, _index: 'pelias', _type: 'a' } ] } }, 'correct backend command'); + t.deepEqual(cmd, { body: { docs: [ { _id: 123, _index: 'pelias', _type: [ 'a' ] } ] } }, 'correct backend command'); }); var controller = setup( backend ); var res = { @@ -57,7 +57,7 @@ module.exports.tests.functional_success = function(test, common) { t.deepEqual(json.features, expected, 'values correctly mapped'); } }; - var req = { clean: { ids: [ {'id' : 123, types: [ 'a' ] } ] }, errors: [], warnings: [] }; + var req = { clean: { ids: [ {'id' : 123, layers: [ 'a' ] } ] }, errors: [], warnings: [] }; var next = function next() { t.equal(req.errors.length, 0, 'next was called without error'); t.end(); @@ -70,10 +70,10 @@ module.exports.tests.functional_success = function(test, common) { module.exports.tests.functional_failure = function(test, common) { test('functional failure', function(t) { var backend = mockBackend( 'client/mget/fail/1', function( cmd ){ - t.deepEqual(cmd, { body: { docs: [ { _id: 123, _index: 'pelias', _type: 'b' } ] } }, 'correct backend command'); + t.deepEqual(cmd, { body: { docs: [ { _id: 123, _index: 'pelias', _type: ['b'] } ] } }, 'correct backend command'); }); var controller = setup( backend ); - var req = { clean: { ids: [ {'id' : 123, types: [ 'b' ] } ] }, errors: [], warnings: [] }; + var req = { clean: { ids: [ {'id' : 123, layers: [ 'b' ] } ] }, errors: [], warnings: [] }; var next = function( message ){ t.equal(req.errors[0],'a backend error occurred','error passed to errorHandler'); t.end(); diff --git a/test/unit/helper/text_parser.js b/test/unit/helper/text_parser.js index 3accf7b9..90b8fa52 100644 --- a/test/unit/helper/text_parser.js +++ b/test/unit/helper/text_parser.js @@ -1,7 +1,7 @@ var parser = require('../../../helper/text_parser'); var type_mapping = require('../../../helper/type_mapping'); -var layers_map = type_mapping.layer_with_aliases_to_type; +var layers_map = type_mapping.layer_mapping; module.exports.tests = {}; diff --git a/test/unit/helper/type_mapping.js b/test/unit/helper/type_mapping.js index b44b00a9..39d9c564 100644 --- a/test/unit/helper/type_mapping.js +++ b/test/unit/helper/type_mapping.js @@ -4,132 +4,32 @@ var type_mapping = require('../../../helper/type_mapping'); module.exports.tests = {}; module.exports.tests.interfaces = function(test, common) { - test('types list', function(t) { - t.ok(check.array(type_mapping.types), 'is array'); - t.ok(check.hasLength(type_mapping.types, 11), 'has correct number of elements'); + test('basic layer mapping', function(t) { + t.deepEquals(type_mapping.layer_mapping.venue, ['venue']); + t.deepEquals(type_mapping.layer_mapping.address, ['address']); t.end(); }); - test('type to source mapping', function(t) { - t.ok(check.object(type_mapping.type_to_source), 'is object'); - t.ok(check.hasLength(Object.keys(type_mapping.type_to_source), 11), 'has correct number of elements'); + test('alias layer mapping', function(t) { + t.deepEquals(type_mapping.layer_mapping.coarse, + [ 'continent', 'macrocountry', 'country', 'dependency', + 'region', 'locality', 'localadmin', 'county', 'macrohood', + 'neighbourhood', 'microhood', 'disputed', 'admin0', + 'admin1', 'admin2', 'neighborhood', 'locality', 'local_admin']); t.end(); }); - test('type to layer mapping', function(t) { - t.ok(check.object(type_mapping.type_to_layer), 'is object'); - t.ok(check.hasLength(Object.keys(type_mapping.type_to_layer), 11), 'has correct number of elements'); + test('basic source mapping', function(t) { + t.deepEquals(type_mapping.source_mapping.osm, ['openstreetmap']); + t.deepEquals(type_mapping.source_mapping.openaddresses, ['openaddresses']); t.end(); }); - test('source to type mapping', function(t) { - t.ok(check.object(type_mapping.source_to_type), 'is object'); - t.ok(check.hasLength(Object.keys(type_mapping.source_to_type), 8), 'has correct number of elements'); + test('alias source mapping', function(t) { + t.deepEquals(type_mapping.source_mapping.openstreetmap, ['openstreetmap']); + t.deepEquals(type_mapping.source_mapping.wof, ['whosonfirst']); t.end(); }); - - test('layer to type mapping', function(t) { - t.ok(check.object(type_mapping.layer_to_type), 'is object'); - t.equal(Object.keys(type_mapping.layer_to_type).length, 8, 'has correct number of elements'); - t.end(); - }); - - test('layer to type mapping (with aliases)', function(t) { - t.ok(check.object(type_mapping.layer_with_aliases_to_type), 'is object'); - t.ok(check.hasLength(Object.keys(type_mapping.layer_with_aliases_to_type), 9), 'has correct number of elements'); - t.end(); - }); - - test('\'osm\' and \'openstreetmap\' sources should only successfully map to \'venue\' and \'address\' layers', function(t) { - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'venue'), ['osmnode', 'osmway']); - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'address'), ['osmaddress']); - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'country'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'region'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'county'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'locality'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'localadmin'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'neighbourhood'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('osm', 'coarse'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'venue'), ['osmnode', 'osmway']); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'address'), ['osmaddress']); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'country'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'region'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'county'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'locality'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'localadmin'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'neighbourhood'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openstreetmap', 'coarse'), []); - t.end(); - }); - - test('\'gn\' and \'geonames\' sources should only successfully map to \'venue\' layers', function(t) { - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'venue'), ['geoname']); - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'address'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'country'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'region'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'county'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'locality'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'localadmin'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'neighbourhood'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('gn', 'coarse'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'venue'), ['geoname']); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'address'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'country'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'region'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'county'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'locality'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'localadmin'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'neighbourhood'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('geonames', 'coarse'), []); - t.end(); - }); - - test('\'oa\' and \'openaddresses\' sources should only successfully map to \'address\' layer', function(t) { - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'venue'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'address'), ['openaddresses']); - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'country'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'region'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'county'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'locality'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'localadmin'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'neighbourhood'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('oa', 'coarse'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'venue'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'address'), ['openaddresses']); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'country'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'region'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'county'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'locality'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'localadmin'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'neighbourhood'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('openaddresses', 'coarse'), []); - t.end(); - }); - - test('\'qs\' and \'quattroshapes\' sources should only successfully map to \'address\' layer', function(t) { - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'venue'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'address'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'country'), ['admin0']); - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'region'), ['admin1']); - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'county'), ['admin2']); - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'locality'), ['locality']); - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'localadmin'), ['local_admin']); - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'neighbourhood'), ['neighborhood']); - t.deepEquals(type_mapping.source_and_layer_to_type('qs', 'coarse'), - ['admin0','admin1','admin2','neighborhood','locality','local_admin']); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'venue'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'address'), []); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'country'), ['admin0']); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'region'), ['admin1']); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'county'), ['admin2']); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'locality'), ['locality']); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'localadmin'), ['local_admin']); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'neighbourhood'), ['neighborhood']); - t.deepEquals(type_mapping.source_and_layer_to_type('quattroshapes', 'coarse'), - ['admin0','admin1','admin2','neighborhood','locality','local_admin']); - t.end(); - }); - }; module.exports.all = function (tape, common) { diff --git a/test/unit/helper/types.js b/test/unit/helper/types.js deleted file mode 100644 index 4c68b46c..00000000 --- a/test/unit/helper/types.js +++ /dev/null @@ -1,92 +0,0 @@ -var types = require('../../../helper/types'); - -module.exports.tests = {}; - -module.exports.tests.no_cleaned_types = function(test, common) { - test('no cleaned types', function(t) { - function testIt() { - types({}); - } - - t.throws(testIt, /clean_types should not be null or undefined/, 'no input should result in exception'); - - t.end(); - }); -}; - -module.exports.tests.address_parser = function(test, common) { - test('address parser specifies only admin layers', function(t) { - var cleaned_types = { - from_text_parser: ['admin0'] // simplified return value from address parser - }; - var actual = types(cleaned_types); - var expected = ['admin0']; // simplified expected value for all admin layers - t.deepEqual(actual, expected, 'only layers specified by address parser returned'); - t.end(); - }); -}; - -module.exports.tests.layers_parameter = function(test, common) { - test('layers parameter specifies only some layers', function(t) { - var cleaned_types = { - from_layers: ['geoname'] - }; - var actual = types(cleaned_types); - var expected = ['geoname']; - t.deepEqual(actual, expected, 'only types specified by layers parameter returned'); - t.end(); - }); -}; - -module.exports.tests.layers_parameter_and_address_parser = function(test, common) { - test('layers parameter and address parser present', function(t) { - var cleaned_types = { - from_layers: ['geoname'], - from_text_parser: ['admin0'] // simplified return value from address parse - }; - var actual = types(cleaned_types); - var expected = ['geoname']; - t.deepEqual(actual, expected, 'layers parameter overrides address parser completely'); - t.end(); - }); -}; - -module.exports.tests.source_parameter = function(test, common) { - test('source parameter specified', function(t) { - var cleaned_types = { - from_sources: ['openaddresses'] - }; - - var actual = types(cleaned_types); - - var expected = ['openaddresses']; - t.deepEqual(actual, expected, 'type parameter set to types specified by source'); - t.end(); - }); -}; - -module.exports.tests.source_and_layers_parameters = function(test, common) { - test('source and layers parameter both specified', function(t) { - var cleaned_types = { - from_sources: ['openaddresses'], - from_layers: ['osmaddress', 'openaddresses'] - }; - - var actual = types(cleaned_types); - - var expected = ['openaddresses']; - t.deepEqual(actual, expected, 'type set to intersection of source and layer types'); - t.end(); - }); -}; - -module.exports.all = function (tape, common) { - - function test(name, testFunction) { - return tape('types: ' + name, testFunction); - } - - for( var testCase in module.exports.tests ){ - module.exports.tests[testCase](test, common); - } -}; diff --git a/test/unit/run.js b/test/unit/run.js index c600e0ad..89b7c31b 100644 --- a/test/unit/run.js +++ b/test/unit/run.js @@ -15,7 +15,6 @@ var tests = [ require('./helper/labelSchema'), require('./helper/text_parser'), require('./helper/type_mapping'), - require('./helper/types'), require('./helper/sizeCalculator'), require('./middleware/confidenceScore'), require('./middleware/confidenceScoreReverse'), diff --git a/test/unit/sanitiser/_ids.js b/test/unit/sanitiser/_ids.js index 98237180..64e749b0 100644 --- a/test/unit/sanitiser/_ids.js +++ b/test/unit/sanitiser/_ids.js @@ -94,60 +94,79 @@ module.exports.tests.valid_ids = function(test, common) { test('ids: valid input (openaddresses)', function(t) { var raw = { ids: 'openaddresses:address:20' }; var clean = {}; - var expected_ids = [{ - id: '20', - types: [ 'openaddresses' ] - }]; var messages = sanitize( raw, clean ); + var expected_ids = [{ + source: 'openaddresses', + layer: 'address', + id: '20', + }]; t.deepEqual( messages.errors, [], ' no errors'); t.deepEqual( clean.ids, expected_ids, 'single type value returned'); t.end(); }); test('ids: valid input (osm)', function(t) { - var raw = { ids: 'osm:venue:500' }; + var raw = { ids: 'openstreetmap:venue:node:500' }; var clean = {}; var expected_ids = [{ - id: '500', - types: [ 'osmnode', 'osmway' ] + source: 'openstreetmap', + layer: 'venue', + id: 'node:500', }]; var messages = sanitize( raw, clean ); t.deepEqual( messages.errors, [], ' no errors'); - t.deepEqual( clean.ids, expected_ids, 'osm could be two types, but that\'s ok'); + t.deepEqual( clean.ids, expected_ids, 'osm has node: or way: in id field'); t.end(); }); }; module.exports.tests.multiple_ids = function(test, common) { test('multiple ids', function(t) { - var raw = { ids: 'geonames:venue:1,osm:venue:2' }; + var raw = { ids: 'geonames:venue:1,openstreetmap:address:way:2' }; var clean = {}; - var expected_clean = { ids: [ { id: '1', types: [ 'geoname' ] }, { id: '2', types: [ 'osmnode', 'osmway' ] } ] }; var messages = sanitize( raw, clean); + var expected_ids = [ { + source: 'geonames', + layer: 'venue', + id: '1' + }, { + source: 'openstreetmap', + layer: 'address', + id: 'way:2' + } ]; + t.deepEqual( messages.errors, [], 'no errors' ); t.deepEqual( messages.warnings, [], 'no warnings' ); - t.deepEqual(clean, expected_clean, 'clean set correctly'); + t.deepEqual(clean.ids, expected_ids, 'clean set correctly'); t.end(); }); }; module.exports.tests.de_dupe = function(test, common) { test('duplicate ids', function(t) { - var expected_clean = { ids: [ { id: '1', types: [ 'geoname' ] }, { id: '2', types: [ 'osmnode', 'osmway' ] } ] }; - var raw = { ids: 'geonames:venue:1,osm:venue:2,geonames:venue:1' }; + var raw = { ids: 'geonames:venue:1,openstreetmap:venue:node:2,geonames:venue:1' }; var clean = {}; var messages = sanitize( raw, clean ); + var expected_ids = [ { + source: 'geonames', + layer: 'venue', + id: '1' + }, { + source: 'openstreetmap', + layer: 'venue', + id: 'node:2' + } ]; t.deepEqual( messages.errors, [], 'no errors' ); t.deepEqual( messages.warnings, [], 'no warnings' ); - t.deepEqual(clean, expected_clean, 'clean set correctly'); + t.deepEqual(clean.ids, expected_ids, 'clean set correctly'); t.end(); }); }; diff --git a/test/unit/sanitiser/_layers.js b/test/unit/sanitiser/_layers.js index 16ca19ae..bd499fc7 100644 --- a/test/unit/sanitiser/_layers.js +++ b/test/unit/sanitiser/_layers.js @@ -1,6 +1,6 @@ var type_mapping = require('../../../helper/type_mapping'); -var sanitize = require('../../../sanitiser/_targets')('layers', type_mapping.layer_with_aliases_to_type); +var sanitize = require('../../../sanitiser/_targets')('layers', type_mapping.layer_mapping); module.exports.tests = {}; @@ -25,95 +25,101 @@ module.exports.tests.sanitize_layers = function(test, common) { }); test('venue (alias) layer', function(t) { - var venue_layers = ['geoname','osmnode','osmway']; var raw = { layers: 'venue' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, venue_layers, 'venue layers set'); + var venue_layers = ['venue']; + t.deepEqual(clean.layers, venue_layers, 'venue layers set'); t.end(); }); test('coarse (alias) layer', function(t) { - var admin_layers = ['admin0','admin1','admin2','neighborhood','locality','local_admin']; var raw = { layers: 'coarse' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, admin_layers, 'coarse layers set'); + var admin_layers = [ 'continent', 'macrocountry', 'country', 'dependency', + 'region', 'locality', 'localadmin', 'county', 'macrohood', 'neighbourhood', + 'microhood', 'disputed', 'admin0', 'admin1', 'admin2', 'neighborhood', 'local_admin' ]; + + t.deepEqual(clean.layers, admin_layers, 'coarse layers set'); t.end(); }); - test('address (alias) layer', function(t) { - var address_layers = ['osmaddress','openaddresses']; + test('address layer', function(t) { var raw = { layers: 'address' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, address_layers, 'address layers set'); + t.deepEqual(clean.layers, ['address'], 'address layer set'); t.end(); }); test('venue alias layer plus regular layers', function(t) { - var venue_layers = ['geoname','osmnode','osmway']; - var reg_layers = ['admin0', 'admin1']; var raw = { layers: 'venue,country,region' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, venue_layers.concat(reg_layers), 'venue + regular layers'); + var expected_layers = ['venue', 'country', 'admin0', 'region', 'admin1']; + t.deepEqual(clean.layers, expected_layers, 'venue + regular layers'); t.end(); }); test('coarse alias layer plus regular layers', function(t) { - var admin_layers = ['admin0','admin1','admin2','neighborhood','locality','local_admin']; - var reg_layers = ['geoname', 'osmnode', 'osmway']; - - var raw = { layers: 'coarse,venue,country' }; + var raw = { layers: 'coarse,country' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, admin_layers.concat(reg_layers), 'coarse + regular layers set'); + var expected_layers = [ 'continent', 'macrocountry', 'country', 'dependency', + 'region', 'locality', 'localadmin', 'county', 'macrohood', 'neighbourhood', + 'microhood', 'disputed', 'admin0', 'admin1', 'admin2', 'neighborhood', 'local_admin' ]; + + t.deepEqual(clean.layers, expected_layers, 'coarse + regular layers set'); t.end(); }); test('address alias layer plus regular layers', function(t) { - var address_layers = ['osmaddress','openaddresses']; - var reg_layers = ['admin0', 'locality']; - var raw = { layers: 'address,country,locality' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, address_layers.concat(reg_layers), 'address + regular layers set'); + var expected_layers = ['address', 'country', 'admin0', 'locality' ]; + t.deepEqual(clean.layers, expected_layers, 'address + regular layers set'); t.end(); }); test('alias layer plus regular layers (no duplicates)', function(t) { - var venue_layers = ['geoname','osmnode','osmway','admin0']; var raw = { layers: 'venue,country' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, venue_layers, 'venue layers found (no duplicates)'); + var expected_layers = ['venue', 'country', 'admin0']; + t.deepEqual(clean.layers, expected_layers, 'venue layers found (no duplicates)'); t.end(); }); test('multiple alias layers (no duplicates)', function(t) { - var alias_layers = ['geoname','osmnode','osmway','admin0','admin1','admin2','neighborhood','locality','local_admin']; var raw = { layers: 'venue,coarse' }; var clean = {}; sanitize(raw, clean); - t.deepEqual(clean.types.from_layers, alias_layers, 'all layers found (no duplicates)'); + var coarse_layers = [ 'continent', 'macrocountry', + 'country', 'dependency', 'region', 'locality', 'localadmin', + 'county', 'macrohood', 'neighbourhood', 'microhood', + 'disputed', 'admin0', 'admin1', 'admin2', 'neighborhood', 'local_admin']; + + var venue_layers = [ 'venue' ]; + var expected_layers = venue_layers.concat(coarse_layers); + t.deepEqual(clean.layers, expected_layers, 'all layers found (no duplicates)'); t.end(); }); }; diff --git a/test/unit/sanitiser/_sources.js b/test/unit/sanitiser/_sources.js index d2ef405f..61b2be90 100644 --- a/test/unit/sanitiser/_sources.js +++ b/test/unit/sanitiser/_sources.js @@ -1,5 +1,5 @@ var type_mapping = require('../../../helper/type_mapping'); -var sanitize = require( '../../../sanitiser/_targets' )('sources', type_mapping.source_to_type); +var sanitize = require( '../../../sanitiser/_targets' )('sources', type_mapping.source_mapping); var success_messages = { error: false }; @@ -14,7 +14,7 @@ module.exports.tests.no_sources = function(test, common) { var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, {}, 'clean.types should be empty object'); + t.equal(req.clean.sources, undefined, 'no sources should be defined'); t.deepEqual(messages.errors, [], 'no error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); t.end(); @@ -29,11 +29,11 @@ module.exports.tests.no_sources = function(test, common) { }; var expected_error = 'sources parameter cannot be an empty string. ' + - 'Valid options: gn,geonames,oa,openaddresses,qs,quattroshapes,osm,openstreetmap'; + 'Valid options: osm,oa,gn,qs,wof,openstreetmap,openaddresses,geonames,quattroshapes,whosonfirst'; var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, {}, 'clean.types should be empty object'); + t.equal(req.clean.sources, undefined, 'no sources should be defined'); t.deepEqual(messages.errors.length, 1, 'error returned'); t.deepEqual(messages.errors[0], expected_error, 'error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); @@ -52,27 +52,24 @@ module.exports.tests.valid_sources = function(test, common) { var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, { from_sources: ['geoname'] }, 'clean.types should contain from_source entry with geonames'); + t.deepEqual(req.clean.sources, ['geonames'], 'sources should contain geonames'); t.deepEqual(messages.errors, [], 'no error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); t.end(); }); - test('openstreetmap source', function(t) { + test('openstreetmap (abbreviated) source', function(t) { var req = { query: { - sources: 'openstreetmap' + sources: 'osm' }, clean: { } }; - var expected_types = { - from_sources: ['osmaddress', 'osmnode', 'osmway'] - }; var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, expected_types, 'clean.types should contain from_source entry with multiple entries for openstreetmap'); + t.deepEqual(req.clean.sources, ['openstreetmap'], 'abbreviation is expanded to full version'); t.deepEqual(messages.errors, [], 'no error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); t.end(); @@ -85,14 +82,11 @@ module.exports.tests.valid_sources = function(test, common) { }, clean: { } }; - var expected_types = { - from_sources: ['osmaddress', 'osmnode', 'osmway', 'openaddresses'] - }; var messages = sanitize(req.query, req.clean); - t.deepEqual(req.clean.types, expected_types, - 'clean.types should contain from_source entry with multiple entries for openstreetmap and openadresses'); + t.deepEqual(req.clean.sources, ['openstreetmap', 'openaddresses'], + 'clean.sources should contain openstreetmap and openadresses'); t.deepEqual(messages.errors, [], 'no error returned'); t.deepEqual(messages.warnings, [], 'no warnings returned'); t.end(); @@ -109,7 +103,8 @@ module.exports.tests.invalid_sources = function(test, common) { }; var expected_messages = { errors: [ - '\'notasource\' is an invalid sources parameter. Valid options: gn,geonames,oa,openaddresses,qs,quattroshapes,osm,openstreetmap' + '\'notasource\' is an invalid sources parameter. ' + + 'Valid options: osm,oa,gn,qs,wof,openstreetmap,openaddresses,geonames,quattroshapes,whosonfirst' ], warnings: [] }; @@ -117,7 +112,7 @@ module.exports.tests.invalid_sources = function(test, common) { var messages = sanitize(req.query, req.clean); t.deepEqual(messages, expected_messages, 'error with message returned'); - t.deepEqual(req.clean.types, { }, 'clean.types should remain empty'); + t.equal(req.clean.sources, undefined, 'clean.sources should remain empty'); t.end(); }); }; diff --git a/test/unit/sanitiser/_text.js b/test/unit/sanitiser/_text.js index 8bc0710f..3868c86c 100644 --- a/test/unit/sanitiser/_text.js +++ b/test/unit/sanitiser/_text.js @@ -15,7 +15,6 @@ module.exports.tests.text_parser = function(test, common) { t.deepEquals(messages.errors, [], 'no errors'); t.deepEquals(messages.warnings, [], 'no warnings'); - t.equal(clean.types.from_text_parser, type_mapping.layer_with_aliases_to_type.coarse, 'coarse layers preferred'); t.end(); }); diff --git a/test/unit/sanitiser/place.js b/test/unit/sanitiser/place.js index f7cda1e9..dd9d1645 100644 --- a/test/unit/sanitiser/place.js +++ b/test/unit/sanitiser/place.js @@ -1,7 +1,7 @@ var place = require('../../../sanitiser/place'), sanitize = place.sanitize, middleware = place.middleware, - defaultClean = { ids: [ { id: '123', types: [ 'geoname' ] } ], private: false }; + defaultClean = { ids: [ { source: 'geonames', layer: 'venue', id: '123' } ], private: false }; // these are the default values you would expect when no input params are specified. module.exports.tests = {}; diff --git a/test/unit/sanitiser/reverse.js b/test/unit/sanitiser/reverse.js index 4e727b5b..a55568e8 100644 --- a/test/unit/sanitiser/reverse.js +++ b/test/unit/sanitiser/reverse.js @@ -11,15 +11,13 @@ var reverse = require('../../../sanitiser/reverse'), 'boundary.circle.lat': 0, 'boundary.circle.lon': 0, 'boundary.circle.radius': parseFloat(defaults['boundary:circle:radius']), - types: { - }, size: 10, private: false }; // these are the default values you would expect when no input params are specified. // @todo: why is this different from $defaultClean? -var emptyClean = { private: false, size: 10, types: {} }; +var emptyClean = { private: false, size: 10 }; module.exports.tests = {}; @@ -38,7 +36,8 @@ module.exports.tests.interface = function(test, common) { module.exports.tests.sanitisers = function(test, common) { test('check sanitiser list', function (t) { - var expected = ['singleScalarParameters', 'layers', 'sources', 'size', 'private', 'geo_reverse', 'boundary_country']; + var expected = ['singleScalarParameters', 'layers', 'sources', + 'sources_and_layers', 'size', 'private', 'geo_reverse', 'boundary_country']; t.deepEqual(Object.keys(reverse.sanitiser_list), expected); t.end(); }); @@ -104,7 +103,7 @@ module.exports.tests.sanitize_lon = function(test, common) { var req = { query: { 'point.lat': 0, 'point.lon': lon } }; // @todo: why is lat set? - var expected = { 'point.lat': 0, private: false, size: 10, types: {} }; + var expected = { 'point.lat': 0, private: false, size: 10 }; sanitize(req, function(){ t.equal(req.errors[0], 'missing param \'point.lon\'', 'longitude is a required field'); t.deepEqual(req.clean, expected, 'clean only has default values set'); diff --git a/test/unit/sanitiser/search.js b/test/unit/sanitiser/search.js index b00160dd..d42d87f4 100644 --- a/test/unit/sanitiser/search.js +++ b/test/unit/sanitiser/search.js @@ -5,7 +5,7 @@ var extend = require('extend'), middleware = search.middleware, defaultError = 'invalid param \'text\': text length, must be >0'; // these are the default values you would expect when no input params are specified. -var emptyClean = { private: false, size: 10, types: {} }; +var emptyClean = { private: false, size: 10 }; module.exports.tests = {}; @@ -24,7 +24,8 @@ module.exports.tests.interface = function(test, common) { module.exports.tests.sanitisers = function(test, common) { test('check sanitiser list', function (t) { - var expected = ['singleScalarParameters', 'text', 'size', 'layers', 'sources', 'private', 'geo_search', 'boundary_country' ]; + var expected = ['singleScalarParameters', 'text', 'size', 'layers', 'sources', + 'sources_and_layers', 'private', 'geo_search', 'boundary_country' ]; t.deepEqual(Object.keys(search.sanitiser_list), expected); t.end(); });