Browse Source

Merge pull request #1011 from pelias/staging

Merge staging into production
production
Julian Simioni 7 years ago committed by GitHub
parent
commit
12a395778b
  1. 11
      .travis.yml
  2. 17
      circle.yml
  3. 8
      controller/coarse_reverse.js
  4. 111
      helper/geojsonify.js
  5. 42
      helper/geojsonify_meta_data.js
  6. 66
      helper/geojsonify_place_details.js
  7. 4
      helper/placeTypes.js
  8. 12
      helper/type_mapping.js
  9. 3
      middleware/cors.js
  10. 18
      package.json
  11. 10
      service/configurations/PointInPolygon.js
  12. 1
      test/ciao/CORS/headers_GET.coffee
  13. 1
      test/ciao/CORS/headers_OPTIONS.coffee
  14. 106
      test/unit/controller/coarse_reverse.js
  15. 4
      test/unit/controller/predicates/is_coarse_reverse.js
  16. 2
      test/unit/controller/search_with_ids.js
  17. 910
      test/unit/helper/geojsonify.js
  18. 544
      test/unit/helper/geojsonify_place_details.js
  19. 5
      test/unit/helper/type_mapping.js
  20. 1
      test/unit/run.js
  21. 16
      test/unit/sanitizer/_layers.js
  22. 56
      test/unit/service/configurations/PointInPolygon.js

11
.travis.yml

@ -9,20 +9,15 @@ matrix:
fast_finish: true fast_finish: true
env: env:
global: global:
- CXX=g++-4.8 - BUILD_LEADER_ID=2
script: npm run travis script: npm run travis
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
before_install: before_install:
- npm i -g npm@^3.0.0 - npm i -g npm@^3.0.0
before_script: before_script:
- npm prune - npm prune
after_success: after_success:
- npm run semantic-release - npm install -g npx
- npx -p node@8 npm run semantic-release
branches: branches:
except: except:
- /^v\d+\.\d+\.\d+$/ - /^v\d+\.\d+\.\d+$/

17
circle.yml

@ -1,17 +0,0 @@
machine:
ruby:
version: 2.1.2
node:
version: 0.12.2
deployment:
dev:
branch: master
commands:
- git clone git@github.com:mapzen/pelias-deploy.git && cd pelias-deploy && bundle install
- cd pelias-deploy && bundle exec rake deploy:api dev
prod_build:
branch: staging
commands:
- git clone git@github.com:mapzen/pelias-deploy.git && cd pelias-deploy && bundle install
- cd pelias-deploy && bundle exec rake deploy:api prod_build

8
controller/coarse_reverse.js

@ -15,7 +15,11 @@ const coarse_granularities = [
'region', 'region',
'macroregion', 'macroregion',
'dependency', 'dependency',
'country' 'country',
'empire',
'continent',
'ocean',
'marinearea'
]; ];
// remove non-coarse layers and return what's left (or all if empty) // remove non-coarse layers and return what's left (or all if empty)
@ -68,7 +72,7 @@ function synthesizeDoc(results) {
// assign the administrative hierarchy // assign the administrative hierarchy
_.keys(results).forEach((layer) => { _.keys(results).forEach((layer) => {
doc.addParent(layer, results[layer][0].name, results[layer][0].id.toString(), results[layer][0].abbr); doc.addParent(layer, results[layer][0].name, results[layer][0].id.toString(), results[layer][0].abbr || undefined);
}); });
// set centroid if available // set centroid if available

111
helper/geojsonify.js

@ -1,28 +1,32 @@
var GeoJSON = require('geojson'); const GeoJSON = require('geojson');
var extent = require('@mapbox/geojson-extent'); const extent = require('@mapbox/geojson-extent');
var logger = require('pelias-logger').get('api'); const logger = require('pelias-logger').get('geojsonify');
var type_mapping = require('./type_mapping'); const collectDetails = require('./geojsonify_place_details');
var _ = require('lodash'); const _ = require('lodash');
var addDetails = require('./geojsonify_place_details'); const Document = require('pelias-model').Document;
var addMetaData = require('./geojsonify_meta_data');
function geojsonifyPlaces( params, docs ){ function geojsonifyPlaces( params, docs ){
// flatten & expand data for geojson conversion // flatten & expand data for geojson conversion
var geodata = docs const geodata = docs
.map(geojsonifyPlace.bind(null, params)) .filter(doc => {
.filter( function( doc ){ if (!_.has(doc, 'center_point')) {
return !!doc; logger.warn('No doc or center_point property');
}); return false;
} else {
return true;
}
})
.map(geojsonifyPlace.bind(null, params));
// get all the bounding_box corners as well as single points // get all the bounding_box corners as well as single points
// to be used for computing the overall bounding_box for the FeatureCollection // to be used for computing the overall bounding_box for the FeatureCollection
var extentPoints = extractExtentPoints(geodata); const extentPoints = extractExtentPoints(geodata);
// convert to geojson // convert to geojson
var geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] }); const geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] });
var geojsonExtentPoints = GeoJSON.parse( extentPoints, { Point: ['lat', 'lng'] }); const geojsonExtentPoints = GeoJSON.parse( extentPoints, { Point: ['lat', 'lng'] });
// to insert the bbox property at the top level of each feature, it must be done separately after // to insert the bbox property at the top level of each feature, it must be done separately after
// initial geojson construction is finished // initial geojson construction is finished
@ -35,36 +39,29 @@ function geojsonifyPlaces( params, docs ){
} }
function geojsonifyPlace(params, place) { function geojsonifyPlace(params, place) {
// setup the base doc
// something went very wrong const doc = {
if( !place || !place.hasOwnProperty( 'center_point' ) ) { id: place._id,
return warning('No doc or center_point property'); gid: new Document(place.source, place.layer, place._id).getGid(),
layer: place.layer,
source: place.source,
source_id: place.source_id,
bounding_box: place.bounding_box,
lat: parseFloat(place.center_point.lat),
lng: parseFloat(place.center_point.lon)
};
// assign name, logging a warning if it doesn't exist
if (_.has(place, 'name.default')) {
doc.name = place.name.default;
} else {
logger.warn(`doc ${doc.gid} does not contain name.default`);
} }
var output = {}; // assign all the details info into the doc
Object.assign(doc, collectDetails(params, place));
addMetaData(place, output);
addName(place, output);
addDetails(params, place, output);
// map center_point for GeoJSON to work properly
// these should not show up in the final feature properties
output.lat = parseFloat(place.center_point.lat);
output.lng = parseFloat(place.center_point.lon);
return output; return doc;
}
/**
* Validate and add name property
*
* @param {object} src
* @param {object} dst
*/
function addName(src, dst) {
// map name
if( !src.name || !src.name.default ) { return warning(src); }
dst.name = src.name.default;
} }
/** /**
@ -73,12 +70,7 @@ function addName(src, dst) {
* @param {object} geojson * @param {object} geojson
*/ */
function addBBoxPerFeature(geojson) { function addBBoxPerFeature(geojson) {
geojson.features.forEach(function (feature) { geojson.features.forEach(feature => {
if (!feature.properties.hasOwnProperty('bounding_box')) {
return;
}
if (feature.properties.bounding_box) { if (feature.properties.bounding_box) {
feature.bbox = [ feature.bbox = [
feature.properties.bounding_box.min_lon, feature.properties.bounding_box.min_lon,
@ -101,8 +93,8 @@ function addBBoxPerFeature(geojson) {
* @returns {Array} * @returns {Array}
*/ */
function extractExtentPoints(geodata) { function extractExtentPoints(geodata) {
var extentPoints = []; return geodata.reduce((extentPoints, place) => {
geodata.forEach(function (place) { // if there's a bounding_box, use the LL/UR for the extent
if (place.bounding_box) { if (place.bounding_box) {
extentPoints.push({ extentPoints.push({
lng: place.bounding_box.min_lon, lng: place.bounding_box.min_lon,
@ -112,16 +104,20 @@ function extractExtentPoints(geodata) {
lng: place.bounding_box.max_lon, lng: place.bounding_box.max_lon,
lat: place.bounding_box.max_lat lat: place.bounding_box.max_lat
}); });
} }
else { else {
// otherwise, use the point for the extent
extentPoints.push({ extentPoints.push({
lng: place.lng, lng: place.lng,
lat: place.lat lat: place.lat
}); });
} }
}); return extentPoints;
}, []);
return extentPoints;
} }
/** /**
@ -144,15 +140,4 @@ function computeBBox(geojson, geojsonExtentPoints) {
} }
} }
/**
* emit a warning if the doc format is invalid
*
* @note: if you see this error, fix it ASAP!
*/
function warning( doc ) {
console.error( 'error: invalid doc', __filename, doc);
return false; // remove offending doc from results
}
module.exports = geojsonifyPlaces; module.exports = geojsonifyPlaces;

42
helper/geojsonify_meta_data.js

@ -1,42 +0,0 @@
var Document = require('pelias-model').Document;
/**
* Determine and set place id, type, and source
*
* @param {object} src
* @param {object} dst
*/
function addMetaData(src, dst) {
dst.id = src._id;
dst.gid = makeGid(src);
dst.layer = lookupLayer(src);
dst.source = lookupSource(src);
dst.source_id = lookupSourceId(src);
if (src.hasOwnProperty('bounding_box')) {
dst.bounding_box = src.bounding_box;
}
}
/**
* Create a gid from a document
*
* @param {object} src
*/
function makeGid(src) {
var doc = new Document(lookupSource(src), lookupLayer(src), src._id);
return doc.getGid();
}
function lookupSource(src) {
return src.source;
}
function lookupSourceId(src) {
return src.source_id;
}
function lookupLayer(src) {
return src.layer;
}
module.exports = addMetaData;

66
helper/geojsonify_place_details.js

@ -1,9 +1,11 @@
var _ = require('lodash'); 'use strict';
const _ = require('lodash');
// Properties to be copied // Properties to be copied
// If a property is identified as a single string, assume it should be presented as a string in response // If a property is identified as a single string, assume it should be presented as a string in response
// If something other than string is desired, use the following structure: { name: 'category', type: 'array' } // If something other than string is desired, use the following structure: { name: 'category', type: 'array' }
var DETAILS_PROPS = [ const DETAILS_PROPS = [
{ name: 'housenumber', type: 'string' }, { name: 'housenumber', type: 'string' },
{ name: 'street', type: 'string' }, { name: 'street', type: 'string' },
{ name: 'postalcode', type: 'string' }, { name: 'postalcode', type: 'string' },
@ -41,6 +43,15 @@ var DETAILS_PROPS = [
{ name: 'borough_a', type: 'string' }, { name: 'borough_a', type: 'string' },
{ name: 'neighbourhood', type: 'string' }, { name: 'neighbourhood', type: 'string' },
{ name: 'neighbourhood_gid', type: 'string' }, { name: 'neighbourhood_gid', type: 'string' },
{ name: 'continent', type: 'string' },
{ name: 'continent_gid', type: 'string' },
{ name: 'continent_a', type: 'string' },
{ name: 'ocean', type: 'string' },
{ name: 'ocean_gid', type: 'string' },
{ name: 'ocean_a', type: 'string' },
{ name: 'marinearea', type: 'string' },
{ name: 'marinearea_gid', type: 'string' },
{ name: 'marinearea_a', type: 'string' },
{ name: 'bounding_box', type: 'default' }, { name: 'bounding_box', type: 'default' },
{ name: 'label', type: 'string' }, { name: 'label', type: 'string' },
{ name: 'category', type: 'array', condition: checkCategoryParam } { name: 'category', type: 'array', condition: checkCategoryParam }
@ -51,58 +62,44 @@ function checkCategoryParam(params) {
} }
/** /**
* Add details properties * Collect the specified properties from source into an object and return it
*
* @param {object} params clean query params
* @param {object} src
* @param {object} dst
*/
function addDetails(params, src, dst) {
copyProperties(params, src, DETAILS_PROPS, dst);
}
/**
* Copy specified properties from source to dest.
* Ignore missing properties. * Ignore missing properties.
* *
* @param {object} params clean query params * @param {object} params clean query params
* @param {object} source * @param {object} source
* @param {[]} props
* @param {object} dst * @param {object} dst
*/ */
function copyProperties( params, source, props, dst ) { function collectProperties( params, source ) {
props.forEach( function ( prop ) { return DETAILS_PROPS.reduce((result, prop) => {
// if condition isn't met, don't set the property
// if condition isn't met, just return without setting the property
if (_.isFunction(prop.condition) && !prop.condition(params)) { if (_.isFunction(prop.condition) && !prop.condition(params)) {
return; return result;
} }
var property = { if ( source.hasOwnProperty( prop.name ) ) {
name: prop.name || prop, let value = null;
type: prop.type || 'default'
};
var value = null; switch (prop.type) {
if ( source.hasOwnProperty( property.name ) ) {
switch (property.type) {
case 'string': case 'string':
value = getStringValue(source[property.name]); value = getStringValue(source[prop.name]);
break; break;
case 'array': case 'array':
value = getArrayValue(source[property.name]); value = getArrayValue(source[prop.name]);
break; break;
// default behavior is to copy property exactly as is // default behavior is to copy property exactly as is
default: default:
value = source[property.name]; value = source[prop.name];
} }
if (_.isNumber(value) || (value && !_.isEmpty(value))) { if (_.isNumber(value) || (value && !_.isEmpty(value))) {
dst[property.name] = value; result[prop.name] = value;
} }
} }
});
return result;
}, {});
} }
function getStringValue(property) { function getStringValue(property) {
@ -123,7 +120,6 @@ function getStringValue(property) {
return _.toString(property); return _.toString(property);
} }
function getArrayValue(property) { function getArrayValue(property) {
// isEmpty check works for all types of values: strings, arrays, objects // isEmpty check works for all types of values: strings, arrays, objects
if (_.isEmpty(property)) { if (_.isEmpty(property)) {
@ -137,4 +133,4 @@ function getArrayValue(property) {
return [property]; return [property];
} }
module.exports = addDetails; module.exports = collectProperties;

4
helper/placeTypes.js

@ -1,4 +1,8 @@
module.exports = [ module.exports = [
'ocean',
'marinearea',
'continent',
'empire',
'country', 'country',
'dependency', 'dependency',
'macroregion', 'macroregion',

12
helper/type_mapping.js

@ -49,9 +49,10 @@ var LAYERS_BY_SOURCE = {
openaddresses: [ 'address' ], openaddresses: [ 'address' ],
geonames: [ 'country','macroregion', 'region', 'county','localadmin', geonames: [ 'country','macroregion', 'region', 'county','localadmin',
'locality','borough', 'neighbourhood', 'venue' ], 'locality','borough', 'neighbourhood', 'venue' ],
whosonfirst: [ 'continent', 'country', 'dependency', 'macroregion', 'region', whosonfirst: [ 'continent', 'empire', 'country', 'dependency', 'macroregion', 'region',
'locality', 'localadmin', 'macrocounty', 'county', 'macrohood', 'borough', 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood', 'borough',
'neighbourhood', 'microhood', 'disputed', 'venue', 'postalcode'] 'neighbourhood', 'microhood', 'disputed', 'venue', 'postalcode',
'continent', 'ocean', 'marinearea']
}; };
/* /*
@ -60,9 +61,10 @@ var LAYERS_BY_SOURCE = {
* may have layers that mean the same thing but have a different name * may have layers that mean the same thing but have a different name
*/ */
var LAYER_ALIASES = { var LAYER_ALIASES = {
'coarse': [ 'continent', 'country', 'dependency', 'macroregion', 'region', 'coarse': [ 'continent', 'empire', 'country', 'dependency', 'macroregion',
'locality', 'localadmin', 'macrocounty', 'county', 'macrohood', 'borough', 'region', 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood',
'neighbourhood', 'microhood', 'disputed', 'postalcode' ] 'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode',
'continent', 'ocean', 'marinearea']
}; };
// create a list of all layers by combining each entry from LAYERS_BY_SOURCE // create a list of all layers by combining each entry from LAYERS_BY_SOURCE

3
middleware/cors.js

@ -3,8 +3,7 @@ function middleware(req, res, next){
res.header('Access-Control-Allow-Origin', '*'); res.header('Access-Control-Allow-Origin', '*');
res.header('Access-Control-Allow-Methods', 'GET, OPTIONS'); res.header('Access-Control-Allow-Methods', 'GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type'); res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.header('Access-Control-Allow-Credentials', true);
next(); next();
} }
module.exports = middleware; module.exports = middleware;

18
package.json

@ -19,7 +19,8 @@
"validate": "npm ls", "validate": "npm ls",
"semantic-release": "semantic-release pre && npm publish && semantic-release post", "semantic-release": "semantic-release pre && npm publish && semantic-release post",
"config": "node -e \"console.log(JSON.stringify(require( 'pelias-config' ).generate(require('./schema')), null, 2))\"", "config": "node -e \"console.log(JSON.stringify(require( 'pelias-config' ).generate(require('./schema')), null, 2))\"",
"check-dependencies": "node_modules/.bin/npm-check --production --ignore pelias-interpolation" "check-dependencies": "node_modules/.bin/npm-check --production --ignore pelias-interpolation",
"prune": "npm prune"
}, },
"repository": { "repository": {
"type": "git", "type": "git",
@ -55,14 +56,14 @@
"markdown": "0.5.0", "markdown": "0.5.0",
"morgan": "^1.8.2", "morgan": "^1.8.2",
"pelias-categories": "1.2.0", "pelias-categories": "1.2.0",
"pelias-config": "2.12.0", "pelias-config": "2.12.1",
"pelias-labels": "1.6.0", "pelias-labels": "1.6.0",
"pelias-logger": "0.2.0", "pelias-logger": "0.2.0",
"pelias-microservice-wrapper": "1.2.0", "pelias-microservice-wrapper": "1.2.1",
"pelias-model": "5.0.1", "pelias-model": "5.1.0",
"pelias-query": "9.1.0", "pelias-query": "9.1.0",
"pelias-sorting": "1.0.1", "pelias-sorting": "1.0.1",
"pelias-text-analyzer": "1.9.1", "pelias-text-analyzer": "1.9.2",
"predicates": "^1.0.1", "predicates": "^1.0.1",
"retry": "^0.10.1", "retry": "^0.10.1",
"stats-lite": "^2.0.4", "stats-lite": "^2.0.4",
@ -75,11 +76,11 @@
"jshint": "^2.5.6", "jshint": "^2.5.6",
"npm-check": "git://github.com/orangejulius/npm-check.git#disable-update-check", "npm-check": "git://github.com/orangejulius/npm-check.git#disable-update-check",
"nsp": "^2.2.0", "nsp": "^2.2.0",
"pelias-mock-logger": "1.1.1", "pelias-mock-logger": "1.2.0",
"precommit-hook": "^3.0.0", "precommit-hook": "^3.0.0",
"proxyquire": "^1.7.10", "proxyquire": "^1.7.10",
"semantic-release": "^7.0.1", "semantic-release": "^8.0.0",
"source-map": "^0.5.6", "source-map": "^0.6.0",
"tap-dot": "1.0.5", "tap-dot": "1.0.5",
"tape": "^4.5.1", "tape": "^4.5.1",
"tmp": "0.0.33", "tmp": "0.0.33",
@ -87,6 +88,7 @@
}, },
"pre-commit": [ "pre-commit": [
"lint", "lint",
"prune",
"validate", "validate",
"test", "test",
"check-dependencies" "check-dependencies"

10
service/configurations/PointInPolygon.js

@ -11,6 +11,16 @@ class PointInPolygon extends ServiceConfiguration {
super('pip', o); super('pip', o);
} }
getParameters(req) {
if (_.has(req, 'clean.layers')) {
return {
layers: _.join(req.clean.layers, ',')
};
}
return {};
}
getUrl(req) { getUrl(req) {
// use resolve to eliminate possibility of duplicate /'s in URL // use resolve to eliminate possibility of duplicate /'s in URL
return url.resolve(this.baseUrl, `${req.clean['point.lon']}/${req.clean['point.lat']}`); return url.resolve(this.baseUrl, `${req.clean['point.lon']}/${req.clean['point.lat']}`);

1
test/ciao/CORS/headers_GET.coffee

@ -6,4 +6,3 @@ path: '/'
response.should.have.header 'Access-Control-Allow-Origin','*' 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-Methods','GET, OPTIONS'
response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type' response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type'
response.should.have.header 'Access-Control-Allow-Credentials','true'

1
test/ciao/CORS/headers_OPTIONS.coffee

@ -7,4 +7,3 @@ method: 'OPTIONS'
response.should.have.header 'Access-Control-Allow-Origin','*' 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-Methods','GET, OPTIONS'
response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type' response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type'
response.should.have.header 'Access-Control-Allow-Credentials','true'

106
test/unit/controller/coarse_reverse.js

@ -210,6 +210,22 @@ module.exports.tests.success_conditions = (test, common) => {
country: [ country: [
{ id: 100, name: 'country name', abbr: 'xyz'}, { id: 100, name: 'country name', abbr: 'xyz'},
{ id: 101, name: 'country name 2'} { id: 101, name: 'country name 2'}
],
empire: [
{ id: 110, name: 'empire name', abbr: 'empire abbr'},
{ id: 111, name: 'empire name 2'}
],
continent: [
{ id: 120, name: 'continent name', abbr: 'continent abbr'},
{ id: 121, name: 'continent name 2'}
],
ocean: [
{ id: 130, name: 'ocean name', abbr: 'ocean abbr'},
{ id: 131, name: 'ocean name 2'}
],
marinearea: [
{ id: 140, name: 'marinearea name', abbr: 'marinearea abbr'},
{ id: 141, name: 'marinearea name 2'}
] ]
}; };
@ -282,7 +298,19 @@ module.exports.tests.success_conditions = (test, common) => {
dependency_a: ['dependency abbr'], dependency_a: ['dependency abbr'],
country: ['country name'], country: ['country name'],
country_id: ['100'], country_id: ['100'],
country_a: ['xyz'] country_a: ['xyz'],
empire: ['empire name'],
empire_id: ['110'],
empire_a: ['empire abbr'],
continent: ['continent name'],
continent_id: ['120'],
continent_a: ['continent abbr'],
ocean: ['ocean name'],
ocean_id: ['130'],
ocean_a: ['ocean abbr'],
marinearea: ['marinearea name'],
marinearea_id: ['140'],
marinearea_a: ['marinearea abbr'],
}, },
center_point: { center_point: {
lat: 12.121212, lat: 12.121212,
@ -822,6 +850,22 @@ module.exports.tests.failure_conditions = (test, common) => {
country: [ country: [
{ id: 100, name: 'country name', abbr: 'xyz'}, { id: 100, name: 'country name', abbr: 'xyz'},
{ id: 101, name: 'country name 2'} { id: 101, name: 'country name 2'}
],
empire: [
{ id: 110, name: 'empire name', abbr: 'empire abbr'},
{ id: 111, name: 'empire name 2'}
],
continent: [
{ id: 120, name: 'continent name', abbr: 'continent abbr'},
{ id: 121, name: 'continent name 2'}
],
ocean: [
{ id: 130, name: 'ocean name', abbr: 'ocean abbr'},
{ id: 131, name: 'ocean name 2'}
],
marinearea: [
{ id: 140, name: 'marinearea name', abbr: 'marinearea abbr'},
{ id: 141, name: 'marinearea name 2'}
] ]
}; };
@ -911,6 +955,66 @@ module.exports.tests.failure_conditions = (test, common) => {
t.end(); t.end();
}); });
test('service returns 0 length abbr', (t) => {
t.plan(4);
const service = (req, callback) => {
t.deepEquals(req, { clean: { layers: ['neighbourhood'] } } );
const results = {
neighbourhood: [
{ id: 20, name: 'Example', abbr: '' }
]
};
callback(undefined, results);
};
const logger = require('pelias-mock-logger')();
const controller = proxyquire('../../../controller/coarse_reverse', {
'pelias-logger': logger
})(service, _.constant(true));
const req = {
clean: {
layers: ['neighbourhood']
}
};
const res = { };
// verify that next was called
const next = () => {
t.pass('next() was called');
};
controller(req, res, next);
const expected = {
meta: {},
data: [{
name: { default: 'Example' },
phrase: { default: 'Example' },
parent: {
neighbourhood: [ 'Example' ],
neighbourhood_id: [ '20' ],
neighbourhood_a: [ null ]
},
source: 'whosonfirst',
layer: 'neighbourhood',
source_id: '20',
_id: '20',
_type: 'neighbourhood'
}]
};
t.deepEquals(res, expected);
t.notOk(logger.hasErrorMessages());
t.end();
});
}; };
module.exports.all = (tape, common) => { module.exports.all = (tape, common) => {

4
test/unit/controller/predicates/is_coarse_reverse.js

@ -17,7 +17,9 @@ const coarse_layers = [
'borough', 'borough',
'neighbourhood', 'neighbourhood',
'microhood', 'microhood',
'disputed' 'disputed',
'ocean',
'marinearea'
]; ];
module.exports.tests = {}; module.exports.tests = {};

2
test/unit/controller/search_with_ids.js

@ -388,7 +388,7 @@ module.exports.tests.service_errors = (test, common) => {
const next = () => { const next = () => {
t.deepEqual(logger.getInfoMessages(), [ t.deepEqual(logger.getInfoMessages(), [
'[req]', '[req] endpoint=undefined {}',
'request timed out on attempt 1, retrying', 'request timed out on attempt 1, retrying',
'request timed out on attempt 2, retrying', 'request timed out on attempt 2, retrying',
'request timed out on attempt 3, retrying' 'request timed out on attempt 3, retrying'

910
test/unit/helper/geojsonify.js

@ -1,5 +1,6 @@
const geojsonify = require('../../../helper/geojsonify');
var geojsonify = require('../../../helper/geojsonify'); const proxyquire = require('proxyquire').noCallThru();
module.exports.tests = {}; module.exports.tests = {};
@ -36,433 +37,610 @@ module.exports.tests.earth = function(test, common) {
}; };
module.exports.tests.geojsonify = function(test, common) { module.exports.tests.all = (test, common) => {
test('bounding_box should be calculated using points when avaiable', t => {
const input = [
{
_id: 'id 1',
source: 'source 1',
source_id: 'source_id 1',
layer: 'layer 1',
name: {
default: 'name 1',
},
center_point: {
lat: 12.121212,
lon: 21.212121
}
},
{
_id: 'id 2',
source: 'source 2',
source_id: 'source_id 2',
layer: 'layer 2',
name: {
default: 'name 2',
},
center_point: {
lat: 13.131313,
lon: 31.313131
}
}
];
const geojsonify = proxyquire('../../../helper/geojsonify', {
'./geojsonify_place_details': (params, source, dst) => {
if (source._id === 'id 1') {
return {
property1: 'property 1',
property2: 'property 2'
};
} else if (source._id === 'id 2') {
return {
property3: 'property 3',
property4: 'property 4'
};
}
}
});
const actual = geojsonify({}, input);
const expected = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 21.212121, 12.121212 ]
},
properties: {
id: 'id 1',
gid: 'source 1:layer 1:id 1',
layer: 'layer 1',
source: 'source 1',
source_id: 'source_id 1',
name: 'name 1',
property1: 'property 1',
property2: 'property 2'
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 31.313131, 13.131313 ]
},
properties: {
id: 'id 2',
gid: 'source 2:layer 2:id 2',
layer: 'layer 2',
source: 'source 2',
source_id: 'source_id 2',
name: 'name 2',
property3: 'property 3',
property4: 'property 4'
}
}
],
bbox: [21.212121, 12.121212, 31.313131, 13.131313]
};
t.deepEquals(actual, expected);
t.end();
test('geojsonify(doc)', function(t) { });
var input = [
test('bounding_box should be calculated using polygons when avaiable', t => {
const input = [
{ {
'_id': 'id1', _id: 'id 1',
'_type': 'layer1', source: 'source 1',
'source': 'source1', source_id: 'source_id 1',
'source_id': 'source_id_1', layer: 'layer 1',
'layer': 'layer1', name: {
'center_point': { default: 'name 1',
'lat': 51.5337144,
'lon': -0.1069716
}, },
'name': { bounding_box: {
'default': '\'Round Midnight Jazz and Blues Bar' min_lon: 1,
min_lat: 1,
max_lon: 2,
max_lat: 2
}, },
'housenumber': '13', center_point: {
'street': 'Liverpool Road', lat: 12.121212,
'postalcode': 'N1 0RW', lon: 21.212121
'country_a': 'GBR', }
'country': 'United Kingdom',
'dependency': 'dependency name',
'region': 'Islington',
'region_a': 'ISL',
'macroregion': 'England',
'county': 'Angel',
'localadmin': 'test1',
'locality': 'test2',
'neighbourhood': 'test3',
'category': [
'food',
'nightlife'
],
'label': 'label for id id1'
}, },
{ {
'_id': 'id2', _id: 'id 2',
'_type': 'layer2', source: 'source 2',
'source': 'source2', source_id: 'source_id 2',
'source_id': 'source_id_2', layer: 'layer 2',
'layer': 'layer2', name: {
'name': { default: 'name 2',
'default': 'Blues Cafe'
}, },
'center_point': { bounding_box: {
'lat': '51.517806', min_lon: -3,
'lon': '-0.101795' min_lat: -3,
max_lon: -1,
max_lat: -1
}, },
'country_a': 'GBR', center_point: {
'country': 'United Kingdom', lat: 13.131313,
'dependency': 'dependency name', lon: 31.313131
'region': 'City And County Of The City Of London', }
'region_a': 'COL', }
'macroregion': 'England', ];
'county': 'Smithfield',
'localadmin': 'test1', const geojsonify = proxyquire('../../../helper/geojsonify', {
'locality': 'test2', './geojsonify_place_details': (params, source, dst) => {
'neighbourhood': 'test3', if (source._id === 'id 1') {
'label': 'label for id id2' return {
property1: 'property 1',
property2: 'property 2'
};
} else if (source._id === 'id 2') {
return {
property3: 'property 3',
property4: 'property 4'
};
}
}
});
const actual = geojsonify({}, input);
const expected = {
type: 'FeatureCollection',
features: [
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 21.212121, 12.121212 ]
},
properties: {
id: 'id 1',
gid: 'source 1:layer 1:id 1',
layer: 'layer 1',
source: 'source 1',
source_id: 'source_id 1',
name: 'name 1',
property1: 'property 1',
property2: 'property 2'
},
bbox: [ 1, 1, 2, 2 ]
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 31.313131, 13.131313 ]
},
properties: {
id: 'id 2',
gid: 'source 2:layer 2:id 2',
layer: 'layer 2',
source: 'source 2',
source_id: 'source_id 2',
name: 'name 2',
property3: 'property 3',
property4: 'property 4'
},
bbox: [ -3, -3, -1, -1 ]
}
],
bbox: [ -3, -3, 2, 2 ]
};
t.deepEquals(actual, expected);
t.end();
});
test('bounding_box should be calculated using polygons AND points when avaiable', t => {
const input = [
{
_id: 'id 1',
source: 'source 1',
source_id: 'source_id 1',
layer: 'layer 1',
name: {
default: 'name 1',
},
center_point: {
lat: 12.121212,
lon: 21.212121
}
}, },
{ {
'_id': 'node:34633854', _id: 'id 2',
'_type': 'venue', source: 'source 2',
'source': 'openstreetmap', source_id: 'source_id 2',
'source_id': 'source_id_3', layer: 'layer 2',
'layer': 'venue', name: {
'name': { default: 'name 2',
'default': 'Empire State Building'
}, },
'center_point': { bounding_box: {
'lat': '40.748432', min_lon: -3,
'lon': '-73.985656' min_lat: -3,
max_lon: -1,
max_lat: -1
}, },
'country_a': 'USA', center_point: {
'country': 'United States', lat: 13.131313,
'dependency': 'dependency name', lon: 31.313131
'region': 'New York', }
'region_a': 'NY',
'county': 'New York',
'borough': 'Manhattan',
'locality': 'New York',
'neighbourhood': 'Koreatown',
'category': [
'tourism',
'transport'
],
'label': 'label for id node:34633854'
} }
]; ];
var expected = { const geojsonify = proxyquire('../../../helper/geojsonify', {
'type': 'FeatureCollection', './geojsonify_place_details': (params, source, dst) => {
'bbox': [ -73.985656, 40.748432, -0.101795, 51.5337144 ], if (source._id === 'id 1') {
'features': [ return {
property1: 'property 1',
property2: 'property 2'
};
} else if (source._id === 'id 2') {
return {
property3: 'property 3',
property4: 'property 4'
};
}
}
});
const actual = geojsonify({}, input);
const expected = {
type: 'FeatureCollection',
features: [
{ {
'type': 'Feature', type: 'Feature',
'geometry': { geometry: {
'type': 'Point', type: 'Point',
'coordinates': [ coordinates: [ 21.212121, 12.121212 ]
-0.1069716,
51.5337144
]
}, },
'properties': { properties: {
'id': 'id1', id: 'id 1',
'gid': 'source1:layer1:id1', gid: 'source 1:layer 1:id 1',
'layer': 'layer1', layer: 'layer 1',
'source': 'source1', source: 'source 1',
'source_id': 'source_id_1', source_id: 'source_id 1',
'name': '\'Round Midnight Jazz and Blues Bar', name: 'name 1',
'country_a': 'GBR', property1: 'property 1',
'country': 'United Kingdom', property2: 'property 2'
'dependency': 'dependency name',
'macroregion': 'England',
'region': 'Islington',
'region_a': 'ISL',
'county': 'Angel',
'localadmin': 'test1',
'locality': 'test2',
'neighbourhood': 'test3',
'housenumber': '13',
'street': 'Liverpool Road',
'postalcode': 'N1 0RW',
'category': [
'food',
'nightlife'
],
'label': 'label for id id1'
} }
}, },
{ {
'type': 'Feature', type: 'Feature',
'geometry': { geometry: {
'type': 'Point', type: 'Point',
'coordinates': [ coordinates: [ 31.313131, 13.131313 ]
-0.101795,
51.517806
]
}, },
'properties': { properties: {
'id': 'id2', id: 'id 2',
'gid': 'source2:layer2:id2', gid: 'source 2:layer 2:id 2',
'layer': 'layer2', layer: 'layer 2',
'source': 'source2', source: 'source 2',
'source_id': 'source_id_2', source_id: 'source_id 2',
'name': 'Blues Cafe', name: 'name 2',
'country_a': 'GBR', property3: 'property 3',
'country': 'United Kingdom', property4: 'property 4'
'dependency': 'dependency name', },
'macroregion': 'England', bbox: [ -3, -3, -1, -1 ]
'region': 'City And County Of The City Of London', }
'region_a': 'COL', ],
'county': 'Smithfield', bbox: [ -3, -3, 21.212121, 12.121212 ]
'localadmin': 'test1', };
'locality': 'test2',
'neighbourhood': 'test3', t.deepEquals(actual, expected);
'label': 'label for id id2' t.end();
}
});
};
module.exports.tests.non_optimal_conditions = (test, common) => {
test('null/undefined places should log warnings and be ignored', t => {
const logger = require('pelias-mock-logger')();
const input = [
null,
undefined,
{
_id: 'id 1',
source: 'source 1',
source_id: 'source_id 1',
layer: 'layer 1',
name: {
default: 'name 1',
}, },
center_point: {
lat: 12.121212,
lon: 21.212121
}
}
];
const geojsonify = proxyquire('../../../helper/geojsonify', {
'./geojsonify_place_details': (params, source, dst) => {
if (source._id === 'id 1') {
return {
property1: 'property 1',
property2: 'property 2'
};
}
},
'pelias-logger': logger
});
const actual = geojsonify({}, input);
const expected = {
type: 'FeatureCollection',
features: [
{ {
'type': 'Feature', type: 'Feature',
'geometry': { geometry: {
'type': 'Point', type: 'Point',
'coordinates': [ coordinates: [ 21.212121, 12.121212 ]
-73.985656,
40.748432
]
}, },
'properties': { properties: {
'id': 'node:34633854', id: 'id 1',
'gid': 'openstreetmap:venue:node:34633854', gid: 'source 1:layer 1:id 1',
'layer': 'venue', layer: 'layer 1',
'source': 'openstreetmap', source: 'source 1',
'source_id': 'source_id_3', source_id: 'source_id 1',
'name': 'Empire State Building', name: 'name 1',
'country_a': 'USA', property1: 'property 1',
'country': 'United States', property2: 'property 2'
'dependency': 'dependency name',
'region': 'New York',
'region_a': 'NY',
'county': 'New York',
'borough': 'Manhattan',
'locality': 'New York',
'neighbourhood': 'Koreatown',
'category': [
'tourism',
'transport'
],
'label': 'label for id node:34633854'
} }
} }
] ],
bbox: [21.212121, 12.121212, 21.212121, 12.121212]
}; };
var json = geojsonify( {categories: 'foo'}, input ); t.deepEquals(actual, expected);
t.ok(logger.isWarnMessage('No doc or center_point property'));
t.deepEqual(json, expected, 'all docs mapped');
t.end(); t.end();
}); });
test('filtering out empty items', function (t) { test('places w/o center_point should log warnings and be ignored', t => {
var input = [ const logger = require('pelias-mock-logger')();
const input = [
{ {
'bounding_box': { _id: 'id 1',
'min_lat': 40.6514712164, source: 'source 1',
'max_lat': 40.6737320588, source_id: 'source_id 1',
'min_lon': -73.8967895508, layer: 'layer 1',
'max_lon': -73.8665771484 name: {
}, default: 'name 1',
'locality': [ }
'New York' },
], {
'source': 'whosonfirst', _id: 'id 2',
'layer': 'neighbourhood', source: 'source 2',
'population': 173198, source_id: 'source_id 2',
'popularity': 495, layer: 'layer 2',
'center_point': { name: {
'lon': -73.881319, default: 'name 2',
'lat': 40.663303
},
'name': {
'default': 'East New York'
}, },
'source_id': '85816607', center_point: {
'category': ['government'], lat: 13.131313,
'_id': '85816607', lon: 31.313131
'_type': 'neighbourhood', }
'_score': 21.434,
'confidence': 0.888,
'country': [
'United States'
],
'country_gid': [
'85633793'
],
'country_a': [
'USA'
],
'dependency': [
'dependency name'
],
'dependency_gid': [
'dependency id'
],
'dependency_a': [
'dependency abbrevation'
],
'macroregion': [
'MacroRegion Name'
],
'macroregion_gid': [
'MacroRegion Id'
],
'macroregion_a': [
'MacroRegion Abbreviation'
],
'region': [
'New York'
],
'region_gid': [
'85688543'
],
'region_a': [
'NY'
],
'macrocounty': [
'MacroCounty Name'
],
'macrocounty_gid': [
'MacroCounty Id'
],
'macrocounty_a': [
'MacroCounty Abbreviation'
],
'county': [
'Kings County'
],
'county_gid': [
'102082361'
],
'county_a': [
null
],
'borough': [
'Brooklyn'
],
'localadmin_gid': [
'404521211'
],
'borough_a': [
null
],
'locality_gid': [
'85977539'
],
'locality_a': [
null
],
'neighbourhood': [],
'neighbourhood_gid': [],
'label': 'label for id 85816607'
} }
]; ];
var expected = { const geojsonify = proxyquire('../../../helper/geojsonify', {
'type': 'FeatureCollection', './geojsonify_place_details': (params, source, dst) => {
'bbox': [-73.8967895508, 40.6514712164, -73.8665771484, 40.6737320588], if (source._id === 'id 2') {
'features': [ return {
property3: 'property 3',
property4: 'property 4'
};
}
},
'pelias-logger': logger
});
const actual = geojsonify({}, input);
const expected = {
type: 'FeatureCollection',
features: [
{ {
'type': 'Feature', type: 'Feature',
'properties': { geometry: {
'id': '85816607', type: 'Point',
'gid': 'whosonfirst:neighbourhood:85816607', coordinates: [ 31.313131, 13.131313 ]
'layer': 'neighbourhood',
'source': 'whosonfirst',
'source_id': '85816607',
'name': 'East New York',
'category': ['government'],
'confidence': 0.888,
'country': 'United States',
'country_gid': '85633793',
'country_a': 'USA',
'dependency': 'dependency name',
'dependency_gid': 'dependency id',
'dependency_a': 'dependency abbrevation',
'macroregion': 'MacroRegion Name',
'macroregion_gid': 'MacroRegion Id',
'macroregion_a': 'MacroRegion Abbreviation',
'region': 'New York',
'region_gid': '85688543',
'region_a': 'NY',
'macrocounty': 'MacroCounty Name',
'macrocounty_gid': 'MacroCounty Id',
'macrocounty_a': 'MacroCounty Abbreviation',
'county': 'Kings County',
'borough': 'Brooklyn',
'county_gid': '102082361',
'localadmin_gid': '404521211',
'locality': 'New York',
'locality_gid': '85977539',
'label': 'label for id 85816607'
}, },
'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588], properties: {
'geometry': { id: 'id 2',
'type': 'Point', gid: 'source 2:layer 2:id 2',
'coordinates': [ layer: 'layer 2',
-73.881319, source: 'source 2',
40.663303 source_id: 'source_id 2',
] name: 'name 2',
property3: 'property 3',
property4: 'property 4'
} }
} }
] ],
bbox: [31.313131, 13.131313, 31.313131, 13.131313]
}; };
var json = geojsonify( {categories: 'foo'}, input ); t.deepEquals(actual, expected);
t.ok(logger.isWarnMessage('No doc or center_point property'));
t.deepEqual(json, expected, 'all wanted properties exposed');
t.end(); t.end();
}); });
};
module.exports.tests.categories = function (test, common) { test('places w/o names should log warnings and be ignored', t => {
test('only set category if categories filter was used', function (t) { const logger = require('pelias-mock-logger')();
var input = [
const input = [
{ {
'_id': '85816607', _id: 'id 1',
'bounding_box': { source: 'source 1',
'min_lat': 40.6514712164, source_id: 'source_id 1',
'max_lat': 40.6737320588, layer: 'layer 1',
'min_lon': -73.8967895508, center_point: {
'max_lon': -73.8665771484 lat: 12.121212,
}, lon: 21.212121
'source': 'whosonfirst', }
'layer': 'neighbourhood', },
'center_point': { {
'lon': -73.881319, _id: 'id 2',
'lat': 40.663303 source: 'source 2',
}, source_id: 'source_id 2',
'name': { layer: 'layer 2',
'default': 'East New York' name: {},
center_point: {
lat: 13.131313,
lon: 31.313131
}
},
{
_id: 'id 3',
source: 'source 3',
source_id: 'source_id 3',
layer: 'layer 3',
name: {
default: 'name 3',
}, },
'source_id': '85816607', center_point: {
'category': ['government'], lat: 14.141414,
'label': 'label for id 85816607' lon: 41.414141
}
} }
]; ];
var expected = { const geojsonify = proxyquire('../../../helper/geojsonify', {
'type': 'FeatureCollection', './geojsonify_place_details': (params, source, dst) => {
'bbox': [-73.8967895508, 40.6514712164, -73.8665771484, 40.6737320588], if (source._id === 'id 1') {
'features': [ return {
property1: 'property 1',
property2: 'property 2'
};
} else if (source._id === 'id 2') {
return {
property3: 'property 3',
property4: 'property 4'
};
} else if (source._id === 'id 3') {
return {
property5: 'property 5',
property6: 'property 6'
};
}
},
'pelias-logger': logger
});
const actual = geojsonify({}, input);
const expected = {
type: 'FeatureCollection',
features: [
{ {
'type': 'Feature', type: 'Feature',
'properties': { geometry: {
'id': '85816607', type: 'Point',
'gid': 'whosonfirst:neighbourhood:85816607', coordinates: [ 21.212121, 12.121212 ]
'layer': 'neighbourhood',
'source': 'whosonfirst',
'source_id': '85816607',
'name': 'East New York',
'category': ['government'],
'label': 'label for id 85816607'
}, },
'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588], properties: {
'geometry': { id: 'id 1',
'type': 'Point', gid: 'source 1:layer 1:id 1',
'coordinates': [ layer: 'layer 1',
-73.881319, source: 'source 1',
40.663303 source_id: 'source_id 1',
] property1: 'property 1',
property2: 'property 2'
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 31.313131, 13.131313 ]
},
properties: {
id: 'id 2',
gid: 'source 2:layer 2:id 2',
layer: 'layer 2',
source: 'source 2',
source_id: 'source_id 2',
property3: 'property 3',
property4: 'property 4'
}
},
{
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 41.414141, 14.141414 ]
},
properties: {
id: 'id 3',
gid: 'source 3:layer 3:id 3',
layer: 'layer 3',
source: 'source 3',
source_id: 'source_id 3',
name: 'name 3',
property5: 'property 5',
property6: 'property 6'
} }
} }
] ],
bbox: [21.212121, 12.121212, 41.414141, 14.141414]
}; };
var json = geojsonify( {categories: 'foo'}, input ); t.deepEquals(actual, expected);
t.ok(logger.isWarnMessage('doc source 1:layer 1:id 1 does not contain name.default'));
t.ok(logger.isWarnMessage('doc source 2:layer 2:id 2 does not contain name.default'));
t.end();
t.deepEqual(json, expected, 'all wanted properties exposed'); });
test('no points', t => {
const logger = require('pelias-mock-logger')();
const input = [];
const geojsonify = proxyquire('../../../helper/geojsonify', {
'./geojsonify_place_details': (params, source, dst) => {
t.fail('should not have bee called');
},
'pelias-logger': logger
});
const actual = geojsonify({}, input);
const expected = {
type: 'FeatureCollection',
features: []
};
t.deepEquals(actual, expected);
t.end(); t.end();
}); });
};
module.exports.all = function (tape, common) { };
module.exports.all = (tape, common) => {
function test(name, testFunction) { function test(name, testFunction) {
return tape('geojsonify: ' + name, testFunction); return tape(`geojsonify: ${name}`, testFunction);
} }
for( var testCase in module.exports.tests ){ for( var testCase in module.exports.tests ){

544
test/unit/helper/geojsonify_place_details.js

@ -0,0 +1,544 @@
const geojsonify = require('../../../helper/geojsonify_place_details');
module.exports.tests = {};
module.exports.tests.geojsonify_place_details = (test, common) => {
test('plain old string values should be copied verbatim, replacing old values', t => {
const source = {
housenumber: 'housenumber value',
street: 'street value',
postalcode: 'postalcode value',
postalcode_gid: 'postalcode_gid value',
match_type: 'match_type value',
accuracy: 'accuracy value',
country: 'country value',
country_gid: 'country_gid value',
country_a: 'country_a value',
dependency: 'dependency value',
dependency_gid: 'dependency_gid value',
dependency_a: 'dependency_a value',
macroregion: 'macroregion value',
macroregion_gid: 'macroregion_gid value',
macroregion_a: 'macroregion_a value',
region: 'region value',
region_gid: 'region_gid value',
region_a: 'region_a value',
macrocounty: 'macrocounty value',
macrocounty_gid: 'macrocounty_gid value',
macrocounty_a: 'macrocounty_a value',
county: 'county value',
county_gid: 'county_gid value',
county_a: 'county_a value',
localadmin: 'localadmin value',
localadmin_gid: 'localadmin_gid value',
localadmin_a: 'localadmin_a value',
locality: 'locality value',
locality_gid: 'locality_gid value',
locality_a: 'locality_a value',
borough: 'borough value',
borough_gid: 'borough_gid value',
borough_a: 'borough_a value',
neighbourhood: 'neighbourhood value',
neighbourhood_gid: 'neighbourhood_gid value',
continent: 'continent value',
continent_gid: 'continent_gid value',
continent_a: 'continent_a value',
ocean: 'ocean value',
ocean_gid: 'ocean_gid value',
ocean_a: 'ocean_a value',
marinearea: 'marinearea value',
marinearea_gid: 'marinearea_gid value',
marinearea_a: 'marinearea_a value',
label: 'label value'
};
const expected = {
housenumber: 'housenumber value',
street: 'street value',
postalcode: 'postalcode value',
postalcode_gid: 'postalcode_gid value',
match_type: 'match_type value',
accuracy: 'accuracy value',
country: 'country value',
country_gid: 'country_gid value',
country_a: 'country_a value',
dependency: 'dependency value',
dependency_gid: 'dependency_gid value',
dependency_a: 'dependency_a value',
macroregion: 'macroregion value',
macroregion_gid: 'macroregion_gid value',
macroregion_a: 'macroregion_a value',
region: 'region value',
region_gid: 'region_gid value',
region_a: 'region_a value',
macrocounty: 'macrocounty value',
macrocounty_gid: 'macrocounty_gid value',
macrocounty_a: 'macrocounty_a value',
county: 'county value',
county_gid: 'county_gid value',
county_a: 'county_a value',
localadmin: 'localadmin value',
localadmin_gid: 'localadmin_gid value',
localadmin_a: 'localadmin_a value',
locality: 'locality value',
locality_gid: 'locality_gid value',
locality_a: 'locality_a value',
borough: 'borough value',
borough_gid: 'borough_gid value',
borough_a: 'borough_a value',
neighbourhood: 'neighbourhood value',
neighbourhood_gid: 'neighbourhood_gid value',
continent: 'continent value',
continent_gid: 'continent_gid value',
continent_a: 'continent_a value',
ocean: 'ocean value',
ocean_gid: 'ocean_gid value',
ocean_a: 'ocean_a value',
marinearea: 'marinearea value',
marinearea_gid: 'marinearea_gid value',
marinearea_a: 'marinearea_a value',
label: 'label value'
};
const actual = geojsonify({}, source);
t.deepEqual(actual, expected);
t.end();
});
test('\'empty\' string-type values should be output as \'\'', t => {
[ [], {}, '', 17, true, null, undefined ].forEach(empty_value => {
const source = {
housenumber: empty_value,
street: empty_value,
postalcode: empty_value,
postalcode_gid: empty_value,
match_type: empty_value,
accuracy: empty_value,
country: empty_value,
country_gid: empty_value,
country_a: empty_value,
dependency: empty_value,
dependency_gid: empty_value,
dependency_a: empty_value,
macroregion: empty_value,
macroregion_gid: empty_value,
macroregion_a: empty_value,
region: empty_value,
region_gid: empty_value,
region_a: empty_value,
macrocounty: empty_value,
macrocounty_gid: empty_value,
macrocounty_a: empty_value,
county: empty_value,
county_gid: empty_value,
county_a: empty_value,
localadmin: empty_value,
localadmin_gid: empty_value,
localadmin_a: empty_value,
locality: empty_value,
locality_gid: empty_value,
locality_a: empty_value,
borough: empty_value,
borough_gid: empty_value,
borough_a: empty_value,
neighbourhood: empty_value,
neighbourhood_gid: empty_value,
continent: empty_value,
continent_gid: empty_value,
continent_a: empty_value,
ocean: empty_value,
ocean_gid: empty_value,
ocean_a: empty_value,
marinearea: empty_value,
marinearea_gid: empty_value,
marinearea_a: empty_value,
label: empty_value
};
const expected = {};
const actual = geojsonify({}, source);
t.deepEqual(actual, expected);
});
t.end();
});
test('source arrays should be copy first value', t => {
const source = {
housenumber: ['housenumber value 1', 'housenumber value 2'],
street: ['street value 1', 'street value 2'],
postalcode: ['postalcode value 1', 'postalcode value 2'],
postalcode_gid: ['postalcode_gid value 1', 'postalcode_gid value 2'],
match_type: ['match_type value 1', 'match_type value 2'],
accuracy: ['accuracy value 1', 'accuracy value 2'],
country: ['country value 1', 'country value 2'],
country_gid: ['country_gid value 1', 'country_gid value 2'],
country_a: ['country_a value 1', 'country_a value 2'],
dependency: ['dependency value 1', 'dependency value 2'],
dependency_gid: ['dependency_gid value 1', 'dependency_gid value 2'],
dependency_a: ['dependency_a value 1', 'dependency_a value 2'],
macroregion: ['macroregion value 1', 'macroregion value 2'],
macroregion_gid: ['macroregion_gid value 1', 'macroregion_gid value 2'],
macroregion_a: ['macroregion_a value 1', 'macroregion_a value 2'],
region: ['region value 1', 'region value 2'],
region_gid: ['region_gid value 1', 'region_gid value 2'],
region_a: ['region_a value 1', 'region_a value 2'],
macrocounty: ['macrocounty value 1', 'macrocounty value 2'],
macrocounty_gid: ['macrocounty_gid value 1', 'macrocounty_gid value 2'],
macrocounty_a: ['macrocounty_a value 1', 'macrocounty_a value 2'],
county: ['county value 1', 'county value 2'],
county_gid: ['county_gid value 1', 'county_gid value 2'],
county_a: ['county_a value 1', 'county_a value 2'],
localadmin: ['localadmin value 1', 'localadmin value 2'],
localadmin_gid: ['localadmin_gid value 1', 'localadmin_gid value 2'],
localadmin_a: ['localadmin_a value 1', 'localadmin_a value 2'],
locality: ['locality value 1', 'locality value 2'],
locality_gid: ['locality_gid value 1', 'locality_gid value 2'],
locality_a: ['locality_a value 1', 'locality_a value 2'],
borough: ['borough value 1', 'borough value 2'],
borough_gid: ['borough_gid value 1', 'borough_gid value 2'],
borough_a: ['borough_a value 1', 'borough_a value 2'],
neighbourhood: ['neighbourhood value 1', 'neighbourhood value 2'],
neighbourhood_gid: ['neighbourhood_gid value 1', 'neighbourhood_gid value 2'],
continent: ['continent value 1', 'continent value 2'],
continent_gid: ['continent_gid value 1', 'continent_gid value 2'],
continent_a: ['continent_a value 1', 'continent_a value 2'],
ocean: ['ocean value 1', 'ocean value 2'],
ocean_gid: ['ocean_gid value 1', 'ocean_gid value 2'],
ocean_a: ['ocean_a value 1', 'ocean_a value 2'],
marinearea: ['marinearea value 1', 'marinearea value 2'],
marinearea_gid: ['marinearea_gid value 1', 'marinearea_gid value 2'],
marinearea_a: ['marinearea_a value 1','marinearea_a value 2'],
label: ['label value 1', 'label value 2']
};
const expected = {
housenumber: 'housenumber value 1',
street: 'street value 1',
postalcode: 'postalcode value 1',
postalcode_gid: 'postalcode_gid value 1',
match_type: 'match_type value 1',
accuracy: 'accuracy value 1',
country: 'country value 1',
country_gid: 'country_gid value 1',
country_a: 'country_a value 1',
dependency: 'dependency value 1',
dependency_gid: 'dependency_gid value 1',
dependency_a: 'dependency_a value 1',
macroregion: 'macroregion value 1',
macroregion_gid: 'macroregion_gid value 1',
macroregion_a: 'macroregion_a value 1',
region: 'region value 1',
region_gid: 'region_gid value 1',
region_a: 'region_a value 1',
macrocounty: 'macrocounty value 1',
macrocounty_gid: 'macrocounty_gid value 1',
macrocounty_a: 'macrocounty_a value 1',
county: 'county value 1',
county_gid: 'county_gid value 1',
county_a: 'county_a value 1',
localadmin: 'localadmin value 1',
localadmin_gid: 'localadmin_gid value 1',
localadmin_a: 'localadmin_a value 1',
locality: 'locality value 1',
locality_gid: 'locality_gid value 1',
locality_a: 'locality_a value 1',
borough: 'borough value 1',
borough_gid: 'borough_gid value 1',
borough_a: 'borough_a value 1',
neighbourhood: 'neighbourhood value 1',
neighbourhood_gid: 'neighbourhood_gid value 1',
continent: 'continent value 1',
continent_gid: 'continent_gid value 1',
continent_a: 'continent_a value 1',
ocean: 'ocean value 1',
ocean_gid: 'ocean_gid value 1',
ocean_a: 'ocean_a value 1',
marinearea: 'marinearea value 1',
marinearea_gid: 'marinearea_gid value 1',
marinearea_a: 'marinearea_a value 1',
label: 'label value 1'
};
const actual = geojsonify({}, source);
t.deepEqual(actual, expected);
t.end();
});
test('non-empty objects should be converted to strings', t => {
// THIS TEST SHOWS THAT THE CODE DOES NOT DO WHAT IT EXPECTED
const source = {
housenumber: { housenumber: 'housenumber value'},
street: { street: 'street value'},
postalcode: { postalcode: 'postalcode value'},
postalcode_gid: { postalcode_gid: 'postalcode_gid value'},
match_type: { match_type: 'match_type value'},
accuracy: { accuracy: 'accuracy value'},
country: { country: 'country value'},
country_gid: { country_gid: 'country_gid value'},
country_a: { country_a: 'country_a value'},
dependency: { dependency: 'dependency value'},
dependency_gid: { dependency_gid: 'dependency_gid value'},
dependency_a: { dependency_a: 'dependency_a value'},
macroregion: { macroregion: 'macroregion value'},
macroregion_gid: { macroregion_gid: 'macroregion_gid value'},
macroregion_a: { macroregion_a: 'macroregion_a value'},
region: { region: 'region value'},
region_gid: { region_gid: 'region_gid value'},
region_a: { region_a: 'region_a value'},
macrocounty: { macrocounty: 'macrocounty value'},
macrocounty_gid: { macrocounty_gid: 'macrocounty_gid value'},
macrocounty_a: { macrocounty_a: 'macrocounty_a value'},
county: { county: 'county value'},
county_gid: { county_gid: 'county_gid value'},
county_a: { county_a: 'county_a value'},
localadmin: { localadmin: 'localadmin value'},
localadmin_gid: { localadmin_gid: 'localadmin_gid value'},
localadmin_a: { localadmin_a: 'localadmin_a value'},
locality: { locality: 'locality value'},
locality_gid: { locality_gid: 'locality_gid value'},
locality_a: { locality_a: 'locality_a value'},
borough: { borough: 'borough value'},
borough_gid: { borough_gid: 'borough_gid value'},
borough_a: { borough_a: 'borough_a value'},
neighbourhood: { neighbourhood: 'neighbourhood value'},
neighbourhood_gid: { neighbourhood_gid: 'neighbourhood_gid value'},
continent: { continent: 'continent value'} ,
continent_gid: { continent: 'continent_gid value'},
continent_a: { continent: 'continent_a value'},
ocean: { ocean: 'ocean value'},
ocean_gid: { ocean_gid: 'ocean_gid value'},
ocean_a: { ocean_a: 'ocean_a value'},
marinearea: { marinearea: 'marinearea value'},
marinearea_gid: { marinearea_gid: 'marinearea_gid value'},
marinearea_a: { marinearea_a: 'marinearea_a value'},
label: { label: 'label value'}
};
const expected = {
housenumber: '[object Object]',
street: '[object Object]',
postalcode: '[object Object]',
postalcode_gid: '[object Object]',
match_type: '[object Object]',
accuracy: '[object Object]',
country: '[object Object]',
country_gid: '[object Object]',
country_a: '[object Object]',
dependency: '[object Object]',
dependency_gid: '[object Object]',
dependency_a: '[object Object]',
macroregion: '[object Object]',
macroregion_gid: '[object Object]',
macroregion_a: '[object Object]',
region: '[object Object]',
region_gid: '[object Object]',
region_a: '[object Object]',
macrocounty: '[object Object]',
macrocounty_gid: '[object Object]',
macrocounty_a: '[object Object]',
county: '[object Object]',
county_gid: '[object Object]',
county_a: '[object Object]',
localadmin: '[object Object]',
localadmin_gid: '[object Object]',
localadmin_a: '[object Object]',
locality: '[object Object]',
locality_gid: '[object Object]',
locality_a: '[object Object]',
borough: '[object Object]',
borough_gid: '[object Object]',
borough_a: '[object Object]',
neighbourhood: '[object Object]',
neighbourhood_gid: '[object Object]',
continent: '[object Object]',
continent_gid: '[object Object]',
continent_a: '[object Object]',
ocean: '[object Object]',
ocean_gid: '[object Object]',
ocean_a: '[object Object]',
marinearea: '[object Object]',
marinearea_gid: '[object Object]',
marinearea_a: '[object Object]',
label: '[object Object]'
};
const actual = geojsonify({}, source);
t.deepEqual(actual, expected);
t.end();
});
test('\'default\'-type properties should be copied without type conversion and overwrite old values', t => {
[ 'this is a string', 17.3, { a: 1 }, [1, 2, 3] ].forEach(value => {
const source = {
confidence: value,
distance: value,
bounding_box: value
};
const expected = {
confidence: value,
distance: value,
bounding_box: value
};
const actual = geojsonify({}, source);
t.deepEqual(actual, expected);
});
t.end();
});
test('\'default\'-type properties that are numbers should be output as numbers', t => {
[ 17, 17.3 ].forEach(value => {
const source = {
confidence: value,
distance: value,
bounding_box: value
};
const expected = {
confidence: value,
distance: value,
bounding_box: value
};
const actual = geojsonify({}, source);
t.deepEqual(actual, expected);
});
t.end();
});
test('\'empty\' values for default-type properties should not be output', t => {
[ undefined, null, true, {}, [] ].forEach(value => {
const source = {
confidence: value,
distance: value,
bounding_box: value
};
const expected = {};
const actual = geojsonify({}, source);
t.deepEqual(actual, expected);
});
t.end();
});
test('array-type properties should not be output when empty', t => {
const source = {
category: []
};
const expected = {};
const actual = geojsonify({}, source);
t.deepEqual(actual, expected);
t.end();
});
test('array-type properties with array values should be output as arrays', t => {
const source = {
category: [ 1, 2 ]
};
const expected = {
category: [ 1, 2 ]
};
const clean = {
categories: true
};
const actual = geojsonify(clean, source);
t.deepEqual(actual, expected);
t.end();
});
test('category property should be output when params contains \'category\' property', t => {
[ {a: 1}, 'this is a string'].forEach(value => {
const source = {
category: value
};
const expected = {
category: [ value ]
};
const clean = {
categories: true
};
const actual = geojsonify(clean, source);
t.deepEqual(actual, expected);
});
t.end();
});
test('category property should not be output when params does not contain \'category\' property', t => {
const source = {
category: [ 1, 2 ]
};
const expected = {
};
const clean = {};
const actual = geojsonify(clean, source);
t.deepEqual(actual, expected);
t.end();
});
test('category property should not be output when params is not an object', t => {
const source = {
category: [ 1, 2 ]
};
const expected = {
};
const clean = 'this is not an object';
const actual = geojsonify(clean, source);
t.deepEqual(actual, expected);
t.end();
});
};
module.exports.all = (tape, common) => {
function test(name, testFunction) {
return tape(`geojsonify: ${name}`, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

5
test/unit/helper/type_mapping.js

@ -12,9 +12,10 @@ module.exports.tests.interfaces = function(test, common) {
test('alias layer mapping', function(t) { test('alias layer mapping', function(t) {
t.deepEquals(type_mapping.layer_mapping.coarse, t.deepEquals(type_mapping.layer_mapping.coarse,
[ 'continent', 'country', 'dependency', 'macroregion', [ 'continent', 'empire', 'country', 'dependency', 'macroregion',
'region', 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood', 'region', 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood',
'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode' ]); 'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode',
'continent', 'ocean', 'marinearea']);
t.end(); t.end();
}); });

1
test/unit/run.js

@ -31,6 +31,7 @@ var tests = [
require('./controller/predicates/is_request_sources_only_whosonfirst'), require('./controller/predicates/is_request_sources_only_whosonfirst'),
require('./helper/debug'), require('./helper/debug'),
require('./helper/diffPlaces'), require('./helper/diffPlaces'),
require('./helper/geojsonify_place_details'),
require('./helper/geojsonify'), require('./helper/geojsonify'),
require('./helper/logging'), require('./helper/logging'),
require('./helper/type_mapping'), require('./helper/type_mapping'),

16
test/unit/sanitizer/_layers.js

@ -41,9 +41,10 @@ module.exports.tests.sanitize_layers = function(test, common) {
sanitizer.sanitize(raw, clean); sanitizer.sanitize(raw, clean);
var admin_layers = [ 'continent', 'country', 'dependency', var admin_layers = [ 'continent', 'empire', 'country', 'dependency', 'macroregion',
'macroregion', 'region', 'locality', 'localadmin', 'macrocounty', 'county', 'region', 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood',
'macrohood', 'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode' ]; 'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode', 'ocean',
'marinearea' ];
t.deepEqual(clean.layers, admin_layers, 'coarse layers set'); t.deepEqual(clean.layers, admin_layers, 'coarse layers set');
t.end(); t.end();
@ -76,9 +77,10 @@ module.exports.tests.sanitize_layers = function(test, common) {
sanitizer.sanitize(raw, clean); sanitizer.sanitize(raw, clean);
var expected_layers = [ 'continent', 'country', 'dependency', var expected_layers = [ 'continent', 'empire', 'country', 'dependency',
'macroregion', 'region', 'locality', 'localadmin', 'macrocounty', 'county', 'macroregion', 'region', 'locality', 'localadmin', 'macrocounty', 'county',
'macrohood', 'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode' ]; 'macrohood', 'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode',
'ocean', 'marinearea'];
t.deepEqual(clean.layers, expected_layers, 'coarse + regular layers set'); t.deepEqual(clean.layers, expected_layers, 'coarse + regular layers set');
t.end(); t.end();
@ -112,10 +114,10 @@ module.exports.tests.sanitize_layers = function(test, common) {
sanitizer.sanitize(raw, clean); sanitizer.sanitize(raw, clean);
var coarse_layers = [ 'continent', var coarse_layers = [ 'continent', 'empire',
'country', 'dependency', 'macroregion', 'region', 'locality', 'localadmin', 'country', 'dependency', 'macroregion', 'region', 'locality', 'localadmin',
'macrocounty', 'county', 'macrohood', 'borough', 'neighbourhood', 'microhood', 'macrocounty', 'county', 'macrohood', 'borough', 'neighbourhood', 'microhood',
'disputed', 'postalcode' ]; 'disputed', 'postalcode', 'ocean', 'marinearea' ];
var venue_layers = [ 'venue' ]; var venue_layers = [ 'venue' ];
var expected_layers = venue_layers.concat(coarse_layers); var expected_layers = venue_layers.concat(coarse_layers);

56
test/unit/service/configurations/PointInPolygon.js

@ -53,16 +53,62 @@ module.exports.tests.all = (test, common) => {
}); });
test('getParameters should return an empty object', (t) => { test('getParameters should return an empty object when req is undefined', (t) => {
const configBlob = { const configBlob = {
url: 'http://localhost:1234', url: 'http://localhost:1234'
timeout: 17,
retries: 19
}; };
const pointInPolygon = new PointInPolygon(configBlob); const pointInPolygon = new PointInPolygon(configBlob);
t.deepEquals(pointInPolygon.getParameters(), {}); t.deepEquals(pointInPolygon.getParameters(undefined), {});
t.end();
});
test('getParameters should return an empty object when req.clean is undefined', (t) => {
const configBlob = {
url: 'http://localhost:1234'
};
const pointInPolygon = new PointInPolygon(configBlob);
const req = {};
t.deepEquals(pointInPolygon.getParameters(req), {});
t.end();
});
test('getParameters should return an empty object when req.clean.layers is undefined', (t) => {
const configBlob = {
url: 'http://localhost:1234'
};
const pointInPolygon = new PointInPolygon(configBlob);
const req = {
clean: {}
};
t.deepEquals(pointInPolygon.getParameters(req), {});
t.end();
});
test('getParameters should return object with layers property when req.clean.layers is specified', t => {
const configBlob = {
url: 'http://localhost:1234'
};
const pointInPolygon = new PointInPolygon(configBlob);
const req = {
clean: {
layers: ['layer1', 'layer2']
}
};
t.deepEquals(pointInPolygon.getParameters(req), { layers: 'layer1,layer2'});
t.end(); t.end();
}); });

Loading…
Cancel
Save