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
env:
global:
- CXX=g++-4.8
- BUILD_LEADER_ID=2
script: npm run travis
addons:
apt:
sources:
- ubuntu-toolchain-r-test
packages:
- g++-4.8
before_install:
- npm i -g npm@^3.0.0
before_script:
- npm prune
after_success:
- npm run semantic-release
- npm install -g npx
- npx -p node@8 npm run semantic-release
branches:
except:
- /^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',
'macroregion',
'dependency',
'country'
'country',
'empire',
'continent',
'ocean',
'marinearea'
];
// remove non-coarse layers and return what's left (or all if empty)
@ -68,7 +72,7 @@ function synthesizeDoc(results) {
// assign the administrative hierarchy
_.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

111
helper/geojsonify.js

@ -1,28 +1,32 @@
var GeoJSON = require('geojson');
var extent = require('@mapbox/geojson-extent');
var logger = require('pelias-logger').get('api');
var type_mapping = require('./type_mapping');
var _ = require('lodash');
var addDetails = require('./geojsonify_place_details');
var addMetaData = require('./geojsonify_meta_data');
const GeoJSON = require('geojson');
const extent = require('@mapbox/geojson-extent');
const logger = require('pelias-logger').get('geojsonify');
const collectDetails = require('./geojsonify_place_details');
const _ = require('lodash');
const Document = require('pelias-model').Document;
function geojsonifyPlaces( params, docs ){
// flatten & expand data for geojson conversion
var geodata = docs
.map(geojsonifyPlace.bind(null, params))
.filter( function( doc ){
return !!doc;
});
const geodata = docs
.filter(doc => {
if (!_.has(doc, 'center_point')) {
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
// to be used for computing the overall bounding_box for the FeatureCollection
var extentPoints = extractExtentPoints(geodata);
const extentPoints = extractExtentPoints(geodata);
// convert to geojson
var geojson = GeoJSON.parse( geodata, { Point: ['lat', 'lng'] });
var geojsonExtentPoints = GeoJSON.parse( extentPoints, { Point: ['lat', 'lng'] });
const geojson = GeoJSON.parse( geodata, { 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
// initial geojson construction is finished
@ -35,36 +39,29 @@ function geojsonifyPlaces( params, docs ){
}
function geojsonifyPlace(params, place) {
// something went very wrong
if( !place || !place.hasOwnProperty( 'center_point' ) ) {
return warning('No doc or center_point property');
// setup the base doc
const doc = {
id: place._id,
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 = {};
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);
// assign all the details info into the doc
Object.assign(doc, collectDetails(params, place));
return output;
}
/**
* 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;
return doc;
}
/**
@ -73,12 +70,7 @@ function addName(src, dst) {
* @param {object} geojson
*/
function addBBoxPerFeature(geojson) {
geojson.features.forEach(function (feature) {
if (!feature.properties.hasOwnProperty('bounding_box')) {
return;
}
geojson.features.forEach(feature => {
if (feature.properties.bounding_box) {
feature.bbox = [
feature.properties.bounding_box.min_lon,
@ -101,8 +93,8 @@ function addBBoxPerFeature(geojson) {
* @returns {Array}
*/
function extractExtentPoints(geodata) {
var extentPoints = [];
geodata.forEach(function (place) {
return geodata.reduce((extentPoints, place) => {
// if there's a bounding_box, use the LL/UR for the extent
if (place.bounding_box) {
extentPoints.push({
lng: place.bounding_box.min_lon,
@ -112,16 +104,20 @@ function extractExtentPoints(geodata) {
lng: place.bounding_box.max_lon,
lat: place.bounding_box.max_lat
});
}
else {
// otherwise, use the point for the extent
extentPoints.push({
lng: place.lng,
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;

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
// 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' }
var DETAILS_PROPS = [
const DETAILS_PROPS = [
{ name: 'housenumber', type: 'string' },
{ name: 'street', type: 'string' },
{ name: 'postalcode', type: 'string' },
@ -41,6 +43,15 @@ var DETAILS_PROPS = [
{ name: 'borough_a', type: 'string' },
{ name: 'neighbourhood', 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: 'label', type: 'string' },
{ name: 'category', type: 'array', condition: checkCategoryParam }
@ -51,58 +62,44 @@ function checkCategoryParam(params) {
}
/**
* Add details properties
*
* @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.
* Collect the specified properties from source into an object and return it
* Ignore missing properties.
*
* @param {object} params clean query params
* @param {object} source
* @param {[]} props
* @param {object} dst
*/
function copyProperties( params, source, props, dst ) {
props.forEach( function ( prop ) {
// if condition isn't met, just return without setting the property
function collectProperties( params, source ) {
return DETAILS_PROPS.reduce((result, prop) => {
// if condition isn't met, don't set the property
if (_.isFunction(prop.condition) && !prop.condition(params)) {
return;
return result;
}
var property = {
name: prop.name || prop,
type: prop.type || 'default'
};
if ( source.hasOwnProperty( prop.name ) ) {
let value = null;
var value = null;
if ( source.hasOwnProperty( property.name ) ) {
switch (property.type) {
switch (prop.type) {
case 'string':
value = getStringValue(source[property.name]);
value = getStringValue(source[prop.name]);
break;
case 'array':
value = getArrayValue(source[property.name]);
value = getArrayValue(source[prop.name]);
break;
// default behavior is to copy property exactly as is
default:
value = source[property.name];
value = source[prop.name];
}
if (_.isNumber(value) || (value && !_.isEmpty(value))) {
dst[property.name] = value;
result[prop.name] = value;
}
}
});
return result;
}, {});
}
function getStringValue(property) {
@ -123,7 +120,6 @@ function getStringValue(property) {
return _.toString(property);
}
function getArrayValue(property) {
// isEmpty check works for all types of values: strings, arrays, objects
if (_.isEmpty(property)) {
@ -137,4 +133,4 @@ function getArrayValue(property) {
return [property];
}
module.exports = addDetails;
module.exports = collectProperties;

4
helper/placeTypes.js

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

12
helper/type_mapping.js

@ -49,9 +49,10 @@ var LAYERS_BY_SOURCE = {
openaddresses: [ 'address' ],
geonames: [ 'country','macroregion', 'region', 'county','localadmin',
'locality','borough', 'neighbourhood', 'venue' ],
whosonfirst: [ 'continent', 'country', 'dependency', 'macroregion', 'region',
whosonfirst: [ 'continent', 'empire', 'country', 'dependency', 'macroregion', 'region',
'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
*/
var LAYER_ALIASES = {
'coarse': [ 'continent', 'country', 'dependency', 'macroregion', 'region',
'locality', 'localadmin', 'macrocounty', 'county', 'macrohood', 'borough',
'neighbourhood', 'microhood', 'disputed', 'postalcode' ]
'coarse': [ 'continent', 'empire', 'country', 'dependency', 'macroregion',
'region', 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood',
'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode',
'continent', 'ocean', 'marinearea']
};
// 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-Methods', 'GET, OPTIONS');
res.header('Access-Control-Allow-Headers', 'X-Requested-With,content-type');
res.header('Access-Control-Allow-Credentials', true);
next();
}
module.exports = middleware;
module.exports = middleware;

18
package.json

@ -19,7 +19,8 @@
"validate": "npm ls",
"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))\"",
"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": {
"type": "git",
@ -55,14 +56,14 @@
"markdown": "0.5.0",
"morgan": "^1.8.2",
"pelias-categories": "1.2.0",
"pelias-config": "2.12.0",
"pelias-config": "2.12.1",
"pelias-labels": "1.6.0",
"pelias-logger": "0.2.0",
"pelias-microservice-wrapper": "1.2.0",
"pelias-model": "5.0.1",
"pelias-microservice-wrapper": "1.2.1",
"pelias-model": "5.1.0",
"pelias-query": "9.1.0",
"pelias-sorting": "1.0.1",
"pelias-text-analyzer": "1.9.1",
"pelias-text-analyzer": "1.9.2",
"predicates": "^1.0.1",
"retry": "^0.10.1",
"stats-lite": "^2.0.4",
@ -75,11 +76,11 @@
"jshint": "^2.5.6",
"npm-check": "git://github.com/orangejulius/npm-check.git#disable-update-check",
"nsp": "^2.2.0",
"pelias-mock-logger": "1.1.1",
"pelias-mock-logger": "1.2.0",
"precommit-hook": "^3.0.0",
"proxyquire": "^1.7.10",
"semantic-release": "^7.0.1",
"source-map": "^0.5.6",
"semantic-release": "^8.0.0",
"source-map": "^0.6.0",
"tap-dot": "1.0.5",
"tape": "^4.5.1",
"tmp": "0.0.33",
@ -87,6 +88,7 @@
},
"pre-commit": [
"lint",
"prune",
"validate",
"test",
"check-dependencies"

10
service/configurations/PointInPolygon.js

@ -11,6 +11,16 @@ class PointInPolygon extends ServiceConfiguration {
super('pip', o);
}
getParameters(req) {
if (_.has(req, 'clean.layers')) {
return {
layers: _.join(req.clean.layers, ',')
};
}
return {};
}
getUrl(req) {
// use resolve to eliminate possibility of duplicate /'s in URL
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-Methods','GET, OPTIONS'
response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type'
response.should.have.header 'Access-Control-Allow-Credentials','true'

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-Methods','GET, OPTIONS'
response.should.have.header 'Access-Control-Allow-Headers','X-Requested-With,content-type'
response.should.have.header 'Access-Control-Allow-Credentials','true'

106
test/unit/controller/coarse_reverse.js

@ -210,6 +210,22 @@ module.exports.tests.success_conditions = (test, common) => {
country: [
{ id: 100, name: 'country name', abbr: 'xyz'},
{ 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'],
country: ['country name'],
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: {
lat: 12.121212,
@ -822,6 +850,22 @@ module.exports.tests.failure_conditions = (test, common) => {
country: [
{ id: 100, name: 'country name', abbr: 'xyz'},
{ 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();
});
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) => {

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

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

2
test/unit/controller/search_with_ids.js

@ -388,7 +388,7 @@ module.exports.tests.service_errors = (test, common) => {
const next = () => {
t.deepEqual(logger.getInfoMessages(), [
'[req]',
'[req] endpoint=undefined {}',
'request timed out on attempt 1, retrying',
'request timed out on attempt 2, 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 = {};
@ -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',
'_type': 'layer1',
'source': 'source1',
'source_id': 'source_id_1',
'layer': 'layer1',
'center_point': {
'lat': 51.5337144,
'lon': -0.1069716
_id: 'id 1',
source: 'source 1',
source_id: 'source_id 1',
layer: 'layer 1',
name: {
default: 'name 1',
},
'name': {
'default': '\'Round Midnight Jazz and Blues Bar'
bounding_box: {
min_lon: 1,
min_lat: 1,
max_lon: 2,
max_lat: 2
},
'housenumber': '13',
'street': 'Liverpool Road',
'postalcode': 'N1 0RW',
'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'
center_point: {
lat: 12.121212,
lon: 21.212121
}
},
{
'_id': 'id2',
'_type': 'layer2',
'source': 'source2',
'source_id': 'source_id_2',
'layer': 'layer2',
'name': {
'default': 'Blues Cafe'
_id: 'id 2',
source: 'source 2',
source_id: 'source_id 2',
layer: 'layer 2',
name: {
default: 'name 2',
},
'center_point': {
'lat': '51.517806',
'lon': '-0.101795'
bounding_box: {
min_lon: -3,
min_lat: -3,
max_lon: -1,
max_lat: -1
},
'country_a': 'GBR',
'country': 'United Kingdom',
'dependency': 'dependency name',
'region': 'City And County Of The City Of London',
'region_a': 'COL',
'macroregion': 'England',
'county': 'Smithfield',
'localadmin': 'test1',
'locality': 'test2',
'neighbourhood': 'test3',
'label': 'label for id id2'
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'
},
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',
'_type': 'venue',
'source': 'openstreetmap',
'source_id': 'source_id_3',
'layer': 'venue',
'name': {
'default': 'Empire State Building'
_id: 'id 2',
source: 'source 2',
source_id: 'source_id 2',
layer: 'layer 2',
name: {
default: 'name 2',
},
'center_point': {
'lat': '40.748432',
'lon': '-73.985656'
bounding_box: {
min_lon: -3,
min_lat: -3,
max_lon: -1,
max_lat: -1
},
'country_a': 'USA',
'country': 'United States',
'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'
center_point: {
lat: 13.131313,
lon: 31.313131
}
}
];
var expected = {
'type': 'FeatureCollection',
'bbox': [ -73.985656, 40.748432, -0.101795, 51.5337144 ],
'features': [
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': [
-0.1069716,
51.5337144
]
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 21.212121, 12.121212 ]
},
'properties': {
'id': 'id1',
'gid': 'source1:layer1:id1',
'layer': 'layer1',
'source': 'source1',
'source_id': 'source_id_1',
'name': '\'Round Midnight Jazz and Blues Bar',
'country_a': 'GBR',
'country': 'United Kingdom',
'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'
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': [
-0.101795,
51.517806
]
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 31.313131, 13.131313 ]
},
'properties': {
'id': 'id2',
'gid': 'source2:layer2:id2',
'layer': 'layer2',
'source': 'source2',
'source_id': 'source_id_2',
'name': 'Blues Cafe',
'country_a': 'GBR',
'country': 'United Kingdom',
'dependency': 'dependency name',
'macroregion': 'England',
'region': 'City And County Of The City Of London',
'region_a': 'COL',
'county': 'Smithfield',
'localadmin': 'test1',
'locality': 'test2',
'neighbourhood': 'test3',
'label': 'label for id id2'
}
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, 21.212121, 12.121212 ]
};
t.deepEquals(actual, expected);
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',
'geometry': {
'type': 'Point',
'coordinates': [
-73.985656,
40.748432
]
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 21.212121, 12.121212 ]
},
'properties': {
'id': 'node:34633854',
'gid': 'openstreetmap:venue:node:34633854',
'layer': 'venue',
'source': 'openstreetmap',
'source_id': 'source_id_3',
'name': 'Empire State Building',
'country_a': 'USA',
'country': 'United States',
'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'
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: [21.212121, 12.121212, 21.212121, 12.121212]
};
var json = geojsonify( {categories: 'foo'}, input );
t.deepEqual(json, expected, 'all docs mapped');
t.deepEquals(actual, expected);
t.ok(logger.isWarnMessage('No doc or center_point property'));
t.end();
});
test('filtering out empty items', function (t) {
var input = [
test('places w/o center_point should log warnings and be ignored', t => {
const logger = require('pelias-mock-logger')();
const input = [
{
'bounding_box': {
'min_lat': 40.6514712164,
'max_lat': 40.6737320588,
'min_lon': -73.8967895508,
'max_lon': -73.8665771484
},
'locality': [
'New York'
],
'source': 'whosonfirst',
'layer': 'neighbourhood',
'population': 173198,
'popularity': 495,
'center_point': {
'lon': -73.881319,
'lat': 40.663303
},
'name': {
'default': 'East New York'
_id: 'id 1',
source: 'source 1',
source_id: 'source_id 1',
layer: 'layer 1',
name: {
default: 'name 1',
}
},
{
_id: 'id 2',
source: 'source 2',
source_id: 'source_id 2',
layer: 'layer 2',
name: {
default: 'name 2',
},
'source_id': '85816607',
'category': ['government'],
'_id': '85816607',
'_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'
center_point: {
lat: 13.131313,
lon: 31.313131
}
}
];
var expected = {
'type': 'FeatureCollection',
'bbox': [-73.8967895508, 40.6514712164, -73.8665771484, 40.6737320588],
'features': [
const geojsonify = proxyquire('../../../helper/geojsonify', {
'./geojsonify_place_details': (params, source, dst) => {
if (source._id === 'id 2') {
return {
property3: 'property 3',
property4: 'property 4'
};
}
},
'pelias-logger': logger
});
const actual = geojsonify({}, input);
const expected = {
type: 'FeatureCollection',
features: [
{
'type': 'Feature',
'properties': {
'id': '85816607',
'gid': 'whosonfirst:neighbourhood:85816607',
'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'
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 31.313131, 13.131313 ]
},
'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588],
'geometry': {
'type': 'Point',
'coordinates': [
-73.881319,
40.663303
]
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: [31.313131, 13.131313, 31.313131, 13.131313]
};
var json = geojsonify( {categories: 'foo'}, input );
t.deepEqual(json, expected, 'all wanted properties exposed');
t.deepEquals(actual, expected);
t.ok(logger.isWarnMessage('No doc or center_point property'));
t.end();
});
};
module.exports.tests.categories = function (test, common) {
test('only set category if categories filter was used', function (t) {
var input = [
test('places w/o names should log warnings and be ignored', t => {
const logger = require('pelias-mock-logger')();
const input = [
{
'_id': '85816607',
'bounding_box': {
'min_lat': 40.6514712164,
'max_lat': 40.6737320588,
'min_lon': -73.8967895508,
'max_lon': -73.8665771484
},
'source': 'whosonfirst',
'layer': 'neighbourhood',
'center_point': {
'lon': -73.881319,
'lat': 40.663303
},
'name': {
'default': 'East New York'
_id: 'id 1',
source: 'source 1',
source_id: 'source_id 1',
layer: 'layer 1',
center_point: {
lat: 12.121212,
lon: 21.212121
}
},
{
_id: 'id 2',
source: 'source 2',
source_id: 'source_id 2',
layer: 'layer 2',
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',
'category': ['government'],
'label': 'label for id 85816607'
center_point: {
lat: 14.141414,
lon: 41.414141
}
}
];
var expected = {
'type': 'FeatureCollection',
'bbox': [-73.8967895508, 40.6514712164, -73.8665771484, 40.6737320588],
'features': [
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'
};
} 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',
'properties': {
'id': '85816607',
'gid': 'whosonfirst:neighbourhood:85816607',
'layer': 'neighbourhood',
'source': 'whosonfirst',
'source_id': '85816607',
'name': 'East New York',
'category': ['government'],
'label': 'label for id 85816607'
type: 'Feature',
geometry: {
type: 'Point',
coordinates: [ 21.212121, 12.121212 ]
},
'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588],
'geometry': {
'type': 'Point',
'coordinates': [
-73.881319,
40.663303
]
properties: {
id: 'id 1',
gid: 'source 1:layer 1:id 1',
layer: 'layer 1',
source: 'source 1',
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();
});
};
module.exports.all = function (tape, common) {
};
module.exports.all = (tape, common) => {
function test(name, testFunction) {
return tape('geojsonify: ' + name, testFunction);
return tape(`geojsonify: ${name}`, testFunction);
}
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) {
t.deepEquals(type_mapping.layer_mapping.coarse,
[ 'continent', 'country', 'dependency', 'macroregion',
[ 'continent', 'empire', 'country', 'dependency', 'macroregion',
'region', 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood',
'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode' ]);
'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode',
'continent', 'ocean', 'marinearea']);
t.end();
});

1
test/unit/run.js

@ -31,6 +31,7 @@ var tests = [
require('./controller/predicates/is_request_sources_only_whosonfirst'),
require('./helper/debug'),
require('./helper/diffPlaces'),
require('./helper/geojsonify_place_details'),
require('./helper/geojsonify'),
require('./helper/logging'),
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);
var admin_layers = [ 'continent', 'country', 'dependency',
'macroregion', 'region', 'locality', 'localadmin', 'macrocounty', 'county',
'macrohood', 'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode' ];
var admin_layers = [ 'continent', 'empire', 'country', 'dependency', 'macroregion',
'region', 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood',
'borough', 'neighbourhood', 'microhood', 'disputed', 'postalcode', 'ocean',
'marinearea' ];
t.deepEqual(clean.layers, admin_layers, 'coarse layers set');
t.end();
@ -76,9 +77,10 @@ module.exports.tests.sanitize_layers = function(test, common) {
sanitizer.sanitize(raw, clean);
var expected_layers = [ 'continent', 'country', 'dependency',
var expected_layers = [ 'continent', 'empire', 'country', 'dependency',
'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.end();
@ -112,10 +114,10 @@ module.exports.tests.sanitize_layers = function(test, common) {
sanitizer.sanitize(raw, clean);
var coarse_layers = [ 'continent',
var coarse_layers = [ 'continent', 'empire',
'country', 'dependency', 'macroregion', 'region', 'locality', 'localadmin',
'macrocounty', 'county', 'macrohood', 'borough', 'neighbourhood', 'microhood',
'disputed', 'postalcode' ];
'disputed', 'postalcode', 'ocean', 'marinearea' ];
var venue_layers = [ 'venue' ];
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 = {
url: 'http://localhost:1234',
timeout: 17,
retries: 19
url: 'http://localhost:1234'
};
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();
});

Loading…
Cancel
Save