mirror of https://github.com/pelias/api.git
Stephen K Hess
8 years ago
committed by
GitHub
20 changed files with 1384 additions and 71 deletions
@ -0,0 +1,129 @@
|
||||
const logger = require('pelias-logger').get('coarse_reverse'); |
||||
const _ = require('lodash'); |
||||
const Document = require('pelias-model').Document; |
||||
|
||||
const granularities = [ |
||||
'neighbourhood', |
||||
'borough', |
||||
'locality', |
||||
'localadmin', |
||||
'county', |
||||
'macrocounty', |
||||
'region', |
||||
'macroregion', |
||||
'dependency', |
||||
'country' |
||||
]; |
||||
|
||||
function getMostGranularLayer(results) { |
||||
return granularities.find((granularity) => { |
||||
return results.hasOwnProperty(granularity); |
||||
}); |
||||
} |
||||
|
||||
function hasResultsAtRequestedLayers(results, layers) { |
||||
return _.intersection(layers, Object.keys(results)).length > 0; |
||||
} |
||||
|
||||
function synthesizeDoc(results) { |
||||
// now create a model.Document from what's level, using the most granular
|
||||
// result available as the starting point
|
||||
// the immediately above cannot be re-used since county may be the most
|
||||
// granular layer requested but the results may start at region (no county found)
|
||||
const most_granular_layer = getMostGranularLayer(results); |
||||
const id = results[most_granular_layer][0].id; |
||||
|
||||
const doc = new Document('whosonfirst', most_granular_layer, id.toString()); |
||||
doc.setName('default', results[most_granular_layer][0].name); |
||||
|
||||
if (results[most_granular_layer][0].hasOwnProperty('centroid')) { |
||||
doc.setCentroid( results[most_granular_layer][0].centroid ); |
||||
} |
||||
|
||||
if (results[most_granular_layer][0].hasOwnProperty('bounding_box')) { |
||||
const parsedBoundingBox = results[most_granular_layer][0].bounding_box.split(',').map(parseFloat); |
||||
doc.setBoundingBox({ |
||||
upperLeft: { |
||||
lat: parsedBoundingBox[3], |
||||
lon: parsedBoundingBox[0] |
||||
}, |
||||
lowerRight: { |
||||
lat: parsedBoundingBox[1], |
||||
lon: parsedBoundingBox[2] |
||||
} |
||||
}); |
||||
|
||||
} |
||||
|
||||
if (_.has(results, 'country[0].abbr')) { |
||||
doc.setAlpha3(results.country[0].abbr); |
||||
} |
||||
|
||||
// assign the administrative hierarchy
|
||||
Object.keys(results).forEach((layer) => { |
||||
if (results[layer][0].hasOwnProperty('abbr')) { |
||||
doc.addParent(layer, results[layer][0].name, results[layer][0].id.toString(), results[layer][0].abbr); |
||||
} else { |
||||
doc.addParent(layer, results[layer][0].name, results[layer][0].id.toString()); |
||||
} |
||||
|
||||
}); |
||||
|
||||
const esDoc = doc.toESDocument(); |
||||
esDoc.data._id = esDoc._id; |
||||
esDoc.data._type = esDoc._type; |
||||
return esDoc.data; |
||||
|
||||
} |
||||
|
||||
function setup(service, should_execute) { |
||||
function controller(req, res, next) { |
||||
// do not run controller when a request validation error has occurred
|
||||
if (!should_execute(req, res)) { |
||||
return next(); |
||||
} |
||||
|
||||
const centroid = { |
||||
lat: req.clean['point.lat'], |
||||
lon: req.clean['point.lon'] |
||||
}; |
||||
|
||||
service(centroid, (err, results) => { |
||||
// if there's an error, log it and bail
|
||||
if (err) { |
||||
logger.error(err); |
||||
return next(); |
||||
} |
||||
|
||||
// find the finest granularity requested
|
||||
const finest_granularity_requested = granularities.findIndex((granularity) => { |
||||
return req.clean.layers.indexOf(granularity) !== -1; |
||||
}); |
||||
|
||||
// now remove everything from the response that is more granular than the
|
||||
// most granular layer requested. that is, if req.clean.layers=['county'],
|
||||
// remove neighbourhoods, localities, and localadmins
|
||||
Object.keys(results).forEach((layer) => { |
||||
if (granularities.indexOf(layer) < finest_granularity_requested) { |
||||
delete results[layer]; |
||||
} |
||||
}); |
||||
|
||||
res.meta = {}; |
||||
res.data = []; |
||||
// synthesize a doc from results if there's a result at the request layer(s)
|
||||
if (hasResultsAtRequestedLayers(results, req.clean.layers)) { |
||||
res.data.push(synthesizeDoc(results)); |
||||
} |
||||
|
||||
return next(); |
||||
|
||||
}); |
||||
|
||||
} |
||||
|
||||
return controller; |
||||
|
||||
} |
||||
|
||||
module.exports = setup; |
@ -0,0 +1,5 @@
|
||||
const _ = require('lodash'); |
||||
|
||||
module.exports = (request, response) => { |
||||
return _.get(request, 'errors', []).length > 0; |
||||
}; |
@ -0,0 +1,5 @@
|
||||
const _ = require('lodash'); |
||||
|
||||
module.exports = (request, response) => { |
||||
return _.get(response, 'data', []).length > 0; |
||||
}; |
@ -0,0 +1,9 @@
|
||||
const _ = require('lodash'); |
||||
|
||||
const non_coarse_layers = ['address', 'street', 'venue']; |
||||
|
||||
module.exports = (req, res) => { |
||||
// returns true if layers is undefined, empty, or contains 'address', 'street', or 'venue'
|
||||
return !_.isEmpty(req.clean.layers) && |
||||
_.intersection(req.clean.layers, non_coarse_layers).length === 0; |
||||
}; |
@ -0,0 +1,7 @@
|
||||
module.exports = (uri) => { |
||||
// this predicate relies upon the fact that the schema has already validated
|
||||
// that api.pipService is a URI-formatted string
|
||||
return (request, response) => { |
||||
return uri !== undefined; |
||||
}; |
||||
}; |
@ -0,0 +1,44 @@
|
||||
const logger = require( 'pelias-logger' ).get( 'pointinpolygon' ); |
||||
const request = require('request'); |
||||
const _ = require('lodash'); |
||||
|
||||
module.exports = (url) => { |
||||
if (!_.isEmpty(url)) { |
||||
logger.info(`using point-in-polygon service at ${url}`); |
||||
|
||||
return function pointinpolygon( centroid, callback ) { |
||||
const requestUrl = `${url}/${centroid.lon}/${centroid.lat}`; |
||||
|
||||
request.get(requestUrl, (err, response, body) => { |
||||
if (err) { |
||||
logger.error(JSON.stringify(err)); |
||||
callback(err); |
||||
} |
||||
else if (response.statusCode === 200) { |
||||
try { |
||||
const parsed = JSON.parse(body); |
||||
callback(err, parsed); |
||||
} |
||||
catch (err) { |
||||
logger.error(`${requestUrl}: could not parse response body: ${body}`); |
||||
callback(`${requestUrl} returned status 200 but with non-JSON response: ${body}`); |
||||
} |
||||
} |
||||
else { |
||||
logger.error(`${requestUrl} returned status ${response.statusCode}: ${body}`); |
||||
callback(`${requestUrl} returned status ${response.statusCode}: ${body}`); |
||||
} |
||||
}); |
||||
|
||||
}; |
||||
|
||||
} else { |
||||
logger.warn('point-in-polygon service disabled'); |
||||
|
||||
return (centroid, callback) => { |
||||
callback(`point-in-polygon service disabled, unable to resolve ${JSON.stringify(centroid)}`); |
||||
}; |
||||
|
||||
} |
||||
|
||||
}; |
@ -0,0 +1,578 @@
|
||||
'use strict'; |
||||
|
||||
const setup = require('../../../controller/coarse_reverse'); |
||||
const proxyquire = require('proxyquire').noCallThru(); |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = (test, common) => { |
||||
test('valid interface', (t) => { |
||||
t.equal(typeof setup, 'function', 'setup is a function'); |
||||
t.equal(typeof setup(), 'function', 'setup returns a controller'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.early_exit_conditions = (test, common) => { |
||||
test('should_execute returning false should not call service', (t) => { |
||||
const service = () => { |
||||
throw Error('service should not have been called'); |
||||
}; |
||||
|
||||
const should_execute = () => { return false; }; |
||||
const controller = setup(service, should_execute); |
||||
|
||||
const req = { |
||||
clean: { |
||||
layers: ['locality'] |
||||
}, |
||||
errors: ['error'] |
||||
}; |
||||
|
||||
// verify that next was called
|
||||
let next_was_called = false; |
||||
const next = () => { |
||||
next_was_called = true; |
||||
}; |
||||
|
||||
// passing res=undefined verifies that it wasn't interacted with
|
||||
t.doesNotThrow(controller.bind(null, req, undefined, next)); |
||||
t.ok(next_was_called); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.error_conditions = (test, common) => { |
||||
test('service error should log and call next', (t) => { |
||||
const service = (point, callback) => { |
||||
callback('this is an error'); |
||||
}; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const should_execute = () => { return true; }; |
||||
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||
'pelias-logger': logger |
||||
})(service, should_execute); |
||||
|
||||
const req = { |
||||
clean: { |
||||
layers: ['locality'], |
||||
point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// verify that next was called
|
||||
let next_was_called = false; |
||||
const next = () => { |
||||
next_was_called = true; |
||||
}; |
||||
|
||||
// passing res=undefined verifies that it wasn't interacted with
|
||||
controller(req, undefined, next); |
||||
|
||||
t.ok(logger.isErrorMessage('this is an error')); |
||||
t.ok(next_was_called); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.success_conditions = (test, common) => { |
||||
test('service returning results should use first entry for each layer', (t) => { |
||||
const service = (point, callback) => { |
||||
const results = { |
||||
neighbourhood: [ |
||||
{ |
||||
id: 10, |
||||
name: 'neighbourhood name', |
||||
abbr: 'neighbourhood abbr', |
||||
centroid: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
}, |
||||
bounding_box: '-76.345902,40.006751,-76.254038,40.072939' |
||||
}, |
||||
{ id: 11, name: 'neighbourhood name 2'} |
||||
], |
||||
borough: [ |
||||
{ id: 20, name: 'borough name', abbr: 'borough abbr'}, |
||||
{ id: 21, name: 'borough name 2'} |
||||
], |
||||
locality: [ |
||||
{ id: 30, name: 'locality name', abbr: 'locality abbr'}, |
||||
{ id: 31, name: 'locality name 2'} |
||||
], |
||||
localadmin: [ |
||||
{ id: 40, name: 'localadmin name', abbr: 'localadmin abbr'}, |
||||
{ id: 41, name: 'localadmin name 2'} |
||||
], |
||||
county: [ |
||||
{ id: 50, name: 'county name', abbr: 'county abbr'}, |
||||
{ id: 51, name: 'county name 2'} |
||||
], |
||||
macrocounty: [ |
||||
{ id: 60, name: 'macrocounty name', abbr: 'macrocounty abbr'}, |
||||
{ id: 61, name: 'macrocounty name 2'} |
||||
], |
||||
region: [ |
||||
{ id: 70, name: 'region name', abbr: 'region abbr'}, |
||||
{ id: 71, name: 'region name 2'} |
||||
], |
||||
macroregion: [ |
||||
{ id: 80, name: 'macroregion name', abbr: 'macroregion abbr'}, |
||||
{ id: 81, name: 'macroregion name 2'} |
||||
], |
||||
dependency: [ |
||||
{ id: 90, name: 'dependency name', abbr: 'dependency abbr'}, |
||||
{ id: 91, name: 'dependency name 2'} |
||||
], |
||||
country: [ |
||||
{ id: 100, name: 'country name', abbr: 'xyz'}, |
||||
{ id: 101, name: 'country name 2'} |
||||
] |
||||
}; |
||||
|
||||
callback(undefined, results); |
||||
}; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const should_execute = () => { return true; }; |
||||
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||
'pelias-logger': logger |
||||
})(service, should_execute); |
||||
|
||||
const req = { |
||||
clean: { |
||||
layers: ['neighbourhood'], |
||||
point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
} |
||||
} |
||||
}; |
||||
|
||||
const res = { }; |
||||
|
||||
// verify that next was called
|
||||
let next_was_called = false; |
||||
const next = () => { |
||||
next_was_called = true; |
||||
}; |
||||
|
||||
controller(req, res, next); |
||||
|
||||
const expected = { |
||||
meta: {}, |
||||
data: [ |
||||
{ |
||||
_id: '10', |
||||
_type: 'neighbourhood', |
||||
layer: 'neighbourhood', |
||||
source: 'whosonfirst', |
||||
source_id: '10', |
||||
name: { |
||||
'default': 'neighbourhood name' |
||||
}, |
||||
phrase: { |
||||
'default': 'neighbourhood name' |
||||
}, |
||||
parent: { |
||||
neighbourhood: ['neighbourhood name'], |
||||
neighbourhood_id: ['10'], |
||||
neighbourhood_a: ['neighbourhood abbr'], |
||||
borough: ['borough name'], |
||||
borough_id: ['20'], |
||||
borough_a: ['borough abbr'], |
||||
locality: ['locality name'], |
||||
locality_id: ['30'], |
||||
locality_a: ['locality abbr'], |
||||
localadmin: ['localadmin name'], |
||||
localadmin_id: ['40'], |
||||
localadmin_a: ['localadmin abbr'], |
||||
county: ['county name'], |
||||
county_id: ['50'], |
||||
county_a: ['county abbr'], |
||||
macrocounty: ['macrocounty name'], |
||||
macrocounty_id: ['60'], |
||||
macrocounty_a: ['macrocounty abbr'], |
||||
region: ['region name'], |
||||
region_id: ['70'], |
||||
region_a: ['region abbr'], |
||||
macroregion: ['macroregion name'], |
||||
macroregion_id: ['80'], |
||||
macroregion_a: ['macroregion abbr'], |
||||
dependency: ['dependency name'], |
||||
dependency_id: ['90'], |
||||
dependency_a: ['dependency abbr'], |
||||
country: ['country name'], |
||||
country_id: ['100'], |
||||
country_a: ['xyz'] |
||||
}, |
||||
alpha3: 'XYZ', |
||||
center_point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
}, |
||||
bounding_box: '{"min_lat":40.006751,"max_lat":40.072939,"min_lon":-76.345902,"max_lon":-76.254038}' |
||||
} |
||||
] |
||||
}; |
||||
|
||||
t.deepEquals(res, expected); |
||||
|
||||
t.notOk(logger.hasErrorMessages()); |
||||
t.ok(next_was_called); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('layers missing from results should be ignored', (t) => { |
||||
const service = (point, callback) => { |
||||
const results = { |
||||
neighbourhood: [ |
||||
{ |
||||
id: 10, |
||||
name: 'neighbourhood name', |
||||
abbr: 'neighbourhood abbr', |
||||
centroid: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
}, |
||||
bounding_box: '-76.345902,40.006751,-76.254038,40.072939' |
||||
} |
||||
] |
||||
}; |
||||
|
||||
callback(undefined, results); |
||||
}; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const should_execute = () => { return true; }; |
||||
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||
'pelias-logger': logger |
||||
})(service, should_execute); |
||||
|
||||
const req = { |
||||
clean: { |
||||
layers: ['neighbourhood'], |
||||
point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
} |
||||
} |
||||
}; |
||||
|
||||
const res = { }; |
||||
|
||||
// verify that next was called
|
||||
let next_was_called = false; |
||||
const next = () => { |
||||
next_was_called = true; |
||||
}; |
||||
|
||||
controller(req, res, next); |
||||
|
||||
const expected = { |
||||
meta: {}, |
||||
data: [ |
||||
{ |
||||
_id: '10', |
||||
_type: 'neighbourhood', |
||||
layer: 'neighbourhood', |
||||
source: 'whosonfirst', |
||||
source_id: '10', |
||||
name: { |
||||
'default': 'neighbourhood name' |
||||
}, |
||||
phrase: { |
||||
'default': 'neighbourhood name' |
||||
}, |
||||
parent: { |
||||
neighbourhood: ['neighbourhood name'], |
||||
neighbourhood_id: ['10'], |
||||
neighbourhood_a: ['neighbourhood abbr'] |
||||
}, |
||||
center_point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
}, |
||||
bounding_box: '{"min_lat":40.006751,"max_lat":40.072939,"min_lon":-76.345902,"max_lon":-76.254038}' |
||||
} |
||||
] |
||||
}; |
||||
|
||||
t.deepEquals(res, expected); |
||||
|
||||
t.notOk(logger.hasErrorMessages()); |
||||
t.ok(next_was_called); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('most granular layer missing centroid should not set', (t) => { |
||||
const service = (point, callback) => { |
||||
const results = { |
||||
neighbourhood: [ |
||||
{ |
||||
id: 10, |
||||
name: 'neighbourhood name', |
||||
abbr: 'neighbourhood abbr', |
||||
bounding_box: '-76.345902,40.006751,-76.254038,40.072939' |
||||
} |
||||
] |
||||
}; |
||||
|
||||
callback(undefined, results); |
||||
}; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const should_execute = () => { return true; }; |
||||
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||
'pelias-logger': logger |
||||
})(service, should_execute); |
||||
|
||||
const req = { |
||||
clean: { |
||||
layers: ['neighbourhood'], |
||||
point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
} |
||||
} |
||||
}; |
||||
|
||||
const res = { }; |
||||
|
||||
// verify that next was called
|
||||
let next_was_called = false; |
||||
const next = () => { |
||||
next_was_called = true; |
||||
}; |
||||
|
||||
controller(req, res, next); |
||||
|
||||
const expected = { |
||||
meta: {}, |
||||
data: [ |
||||
{ |
||||
_id: '10', |
||||
_type: 'neighbourhood', |
||||
layer: 'neighbourhood', |
||||
source: 'whosonfirst', |
||||
source_id: '10', |
||||
name: { |
||||
'default': 'neighbourhood name' |
||||
}, |
||||
phrase: { |
||||
'default': 'neighbourhood name' |
||||
}, |
||||
parent: { |
||||
neighbourhood: ['neighbourhood name'], |
||||
neighbourhood_id: ['10'], |
||||
neighbourhood_a: ['neighbourhood abbr'] |
||||
}, |
||||
bounding_box: '{"min_lat":40.006751,"max_lat":40.072939,"min_lon":-76.345902,"max_lon":-76.254038}' |
||||
} |
||||
] |
||||
}; |
||||
|
||||
t.deepEquals(res, expected); |
||||
|
||||
t.notOk(logger.hasErrorMessages()); |
||||
t.ok(next_was_called); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('most granular layer missing bounding_box should not set', (t) => { |
||||
const service = (point, callback) => { |
||||
const results = { |
||||
neighbourhood: [ |
||||
{ |
||||
id: 10, |
||||
name: 'neighbourhood name', |
||||
abbr: 'neighbourhood abbr', |
||||
centroid: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
} |
||||
} |
||||
] |
||||
}; |
||||
|
||||
callback(undefined, results); |
||||
}; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const should_execute = () => { return true; }; |
||||
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||
'pelias-logger': logger |
||||
})(service, should_execute); |
||||
|
||||
const req = { |
||||
clean: { |
||||
layers: ['neighbourhood'], |
||||
point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
} |
||||
} |
||||
}; |
||||
|
||||
const res = { }; |
||||
|
||||
// verify that next was called
|
||||
let next_was_called = false; |
||||
const next = () => { |
||||
next_was_called = true; |
||||
}; |
||||
|
||||
controller(req, res, next); |
||||
|
||||
const expected = { |
||||
meta: {}, |
||||
data: [ |
||||
{ |
||||
_id: '10', |
||||
_type: 'neighbourhood', |
||||
layer: 'neighbourhood', |
||||
source: 'whosonfirst', |
||||
source_id: '10', |
||||
name: { |
||||
'default': 'neighbourhood name' |
||||
}, |
||||
phrase: { |
||||
'default': 'neighbourhood name' |
||||
}, |
||||
parent: { |
||||
neighbourhood: ['neighbourhood name'], |
||||
neighbourhood_id: ['10'], |
||||
neighbourhood_a: ['neighbourhood abbr'] |
||||
}, |
||||
center_point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
} |
||||
} |
||||
] |
||||
}; |
||||
|
||||
t.deepEquals(res, expected); |
||||
|
||||
t.notOk(logger.hasErrorMessages()); |
||||
t.ok(next_was_called); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.failure_conditions = (test, common) => { |
||||
test('service returning 0 results at the requested layer should return nothing', (t) => { |
||||
const service = (point, callback) => { |
||||
// response without neighbourhood results
|
||||
const results = { |
||||
borough: [ |
||||
{ id: 20, name: 'borough name', abbr: 'borough abbr'}, |
||||
{ id: 21, name: 'borough name 2'} |
||||
], |
||||
locality: [ |
||||
{ id: 30, name: 'locality name', abbr: 'locality abbr'}, |
||||
{ id: 31, name: 'locality name 2'} |
||||
], |
||||
localadmin: [ |
||||
{ id: 40, name: 'localadmin name', abbr: 'localadmin abbr'}, |
||||
{ id: 41, name: 'localadmin name 2'} |
||||
], |
||||
county: [ |
||||
{ id: 50, name: 'county name', abbr: 'county abbr'}, |
||||
{ id: 51, name: 'county name 2'} |
||||
], |
||||
macrocounty: [ |
||||
{ id: 60, name: 'macrocounty name', abbr: 'macrocounty abbr'}, |
||||
{ id: 61, name: 'macrocounty name 2'} |
||||
], |
||||
region: [ |
||||
{ id: 70, name: 'region name', abbr: 'region abbr'}, |
||||
{ id: 71, name: 'region name 2'} |
||||
], |
||||
macroregion: [ |
||||
{ id: 80, name: 'macroregion name', abbr: 'macroregion abbr'}, |
||||
{ id: 81, name: 'macroregion name 2'} |
||||
], |
||||
dependency: [ |
||||
{ id: 90, name: 'dependency name', abbr: 'dependency abbr'}, |
||||
{ id: 91, name: 'dependency name 2'} |
||||
], |
||||
country: [ |
||||
{ id: 100, name: 'country name', abbr: 'xyz'}, |
||||
{ id: 101, name: 'country name 2'} |
||||
] |
||||
}; |
||||
|
||||
callback(undefined, results); |
||||
}; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const should_execute = () => { return true; }; |
||||
const controller = proxyquire('../../../controller/coarse_reverse', { |
||||
'pelias-logger': logger |
||||
})(service, should_execute); |
||||
|
||||
const req = { |
||||
clean: { |
||||
layers: ['neighbourhood'], |
||||
point: { |
||||
lat: 12.121212, |
||||
lon: 21.212121 |
||||
} |
||||
} |
||||
}; |
||||
|
||||
const res = { }; |
||||
|
||||
// verify that next was called
|
||||
let next_was_called = false; |
||||
const next = () => { |
||||
next_was_called = true; |
||||
}; |
||||
|
||||
controller(req, res, next); |
||||
|
||||
const expected = { |
||||
meta: {}, |
||||
data: [] |
||||
}; |
||||
|
||||
t.deepEquals(res, expected); |
||||
|
||||
t.notOk(logger.hasErrorMessages()); |
||||
t.ok(next_was_called); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.all = (tape, common) => { |
||||
|
||||
function test(name, testFunction) { |
||||
return tape(`GET /coarse_reverse ${name}`, testFunction); |
||||
} |
||||
|
||||
for( const testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
@ -0,0 +1,60 @@
|
||||
'use strict'; |
||||
|
||||
const _ = require('lodash'); |
||||
const has_request_errors = require('../../../../controller/predicates/has_request_errors'); |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = (test, common) => { |
||||
test('valid interface', (t) => { |
||||
t.equal(typeof has_request_errors, 'function', 'has_request_errors is a function'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.true_conditions = (test, common) => { |
||||
test('request with non-empty errors should return true', (t) => { |
||||
const req = { |
||||
errors: ['error'] |
||||
}; |
||||
const res = {}; |
||||
|
||||
t.ok(has_request_errors(req, res)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.false_conditions = (test, common) => { |
||||
test('response with undefined errors should return false', (t) => { |
||||
const req = {}; |
||||
const res = {}; |
||||
|
||||
t.notOk(has_request_errors(req, res)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('response with empty errors array should return false', (t) => { |
||||
const req = { |
||||
errors: [] |
||||
}; |
||||
const res = {}; |
||||
|
||||
t.notOk(has_request_errors(req, res)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.all = (tape, common) => { |
||||
function test(name, testFunction) { |
||||
return tape(`GET /has_request_errors ${name}`, testFunction); |
||||
} |
||||
|
||||
for( const testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
@ -0,0 +1,60 @@
|
||||
'use strict'; |
||||
|
||||
const _ = require('lodash'); |
||||
const has_response_data = require('../../../../controller/predicates/has_response_data'); |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = (test, common) => { |
||||
test('valid interface', (t) => { |
||||
t.equal(typeof has_response_data, 'function', 'has_response_data is a function'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.true_conditions = (test, common) => { |
||||
test('response with non-empty data should return true', (t) => { |
||||
const req = {}; |
||||
const res = { |
||||
data: [1] |
||||
}; |
||||
|
||||
t.ok(has_response_data(req, res)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.false_conditions = (test, common) => { |
||||
test('response with undefined data should return true', (t) => { |
||||
const req = {}; |
||||
const res = {}; |
||||
|
||||
t.notOk(has_response_data(req, res)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('response with empty data array should return true', (t) => { |
||||
const req = {}; |
||||
const res = { |
||||
data: [] |
||||
}; |
||||
|
||||
t.notOk(has_response_data(req, res)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.all = (tape, common) => { |
||||
function test(name, testFunction) { |
||||
return tape(`GET /has_response_data ${name}`, testFunction); |
||||
} |
||||
|
||||
for( const testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
@ -0,0 +1,128 @@
|
||||
'use strict'; |
||||
|
||||
const _ = require('lodash'); |
||||
const is_coarse_reverse = require('../../../../controller/predicates/is_coarse_reverse'); |
||||
|
||||
const coarse_layers = [ |
||||
'continent', |
||||
'country', |
||||
'dependency', |
||||
'macroregion', |
||||
'region', |
||||
'locality', |
||||
'localadmin', |
||||
'macrocounty', |
||||
'county', |
||||
'macrohood', |
||||
'borough', |
||||
'neighbourhood', |
||||
'microhood', |
||||
'disputed' |
||||
]; |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = (test, common) => { |
||||
test('valid interface', (t) => { |
||||
t.equal(typeof is_coarse_reverse, 'function', 'is_coarse_reverse is a function'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.false_conditions = (test, common) => { |
||||
test('request without layers should return false', (t) => { |
||||
const req = { |
||||
clean: {} |
||||
}; |
||||
|
||||
t.notOk(is_coarse_reverse(req)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('request with empty layers should return false', (t) => { |
||||
const req = { |
||||
clean: { |
||||
layers: [] |
||||
} |
||||
}; |
||||
|
||||
t.notOk(is_coarse_reverse(req)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('request with layers just "address" or "venue" return false', (t) => { |
||||
['address', 'street', 'venue'].forEach((non_coarse_layer) => { |
||||
const req = { |
||||
clean: { |
||||
layers: [non_coarse_layer] |
||||
} |
||||
}; |
||||
|
||||
t.notOk(is_coarse_reverse(req)); |
||||
|
||||
}); |
||||
|
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('request with layers containing "address" or "venue" and a coarse layer should return false', (t) => { |
||||
['address', 'street', 'venue'].forEach((non_coarse_layer) => { |
||||
const req = { |
||||
clean: { |
||||
layers: [_.sample(coarse_layers), non_coarse_layer] |
||||
} |
||||
}; |
||||
|
||||
t.notOk(is_coarse_reverse(req)); |
||||
|
||||
}); |
||||
|
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
test('request with layers containing "address" and "venue" should return false', (t) => { |
||||
const req = { |
||||
clean: { |
||||
layers: ['address', 'venue'] |
||||
} |
||||
}; |
||||
|
||||
t.notOk(is_coarse_reverse(req)); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.true_conditions = (test, common) => { |
||||
test('request with non-empty layers and not containing "address" or "venue" should return true', (t) => { |
||||
coarse_layers.forEach((coarse_layer) => { |
||||
const req = { |
||||
clean: { |
||||
layers: [coarse_layer] |
||||
} |
||||
}; |
||||
|
||||
t.ok(is_coarse_reverse(req)); |
||||
|
||||
}); |
||||
|
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.all = (tape, common) => { |
||||
function test(name, testFunction) { |
||||
return tape(`GET /is_coarse_reverse ${name}`, testFunction); |
||||
} |
||||
|
||||
for( const testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
@ -0,0 +1,42 @@
|
||||
'use strict'; |
||||
|
||||
const _ = require('lodash'); |
||||
const is_pip_service_enabled = require('../../../../controller/predicates/is_pip_service_enabled'); |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = (test, common) => { |
||||
test('valid interface', (t) => { |
||||
t.equal(typeof is_pip_service_enabled, 'function', 'is_pip_service_enabled is a function'); |
||||
t.equal(typeof is_pip_service_enabled(), 'function', 'is_pip_service_enabled() is a function'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.true_conditions = (test, common) => { |
||||
test('string uri should return true', (t) => { |
||||
t.ok(is_pip_service_enabled('pip uri')()); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.false_conditions = (test, common) => { |
||||
test('undefined uri should return false', (t) => { |
||||
t.notOk(is_pip_service_enabled()()); |
||||
t.end(); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.all = (tape, common) => { |
||||
function test(name, testFunction) { |
||||
return tape(`GET /is_pip_service_enabled ${name}`, testFunction); |
||||
} |
||||
|
||||
for( const testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
@ -0,0 +1,169 @@
|
||||
const proxyquire = require('proxyquire').noCallThru(); |
||||
|
||||
const setup = require('../../../service/pointinpolygon'); |
||||
|
||||
module.exports.tests = {}; |
||||
|
||||
module.exports.tests.interface = (test, common) => { |
||||
test('valid interface', (t) => { |
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
var service = proxyquire('../../../service/pointinpolygon', { |
||||
'pelias-logger': logger |
||||
}); |
||||
|
||||
t.equal(typeof service, 'function', 'service is a function'); |
||||
t.end(); |
||||
}); |
||||
}; |
||||
|
||||
module.exports.tests.do_nothing_service = (test, common) => { |
||||
test('undefined PiP uri should return service that logs fact that PiP service is not available', (t) => { |
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const service = proxyquire('../../../service/pointinpolygon', { |
||||
'pelias-logger': logger |
||||
})(); |
||||
|
||||
service({ lat: 12.121212, lon: 21.212121 }, (err) => { |
||||
t.deepEquals(logger.getWarnMessages(), [ |
||||
'point-in-polygon service disabled' |
||||
]); |
||||
t.equals(err, 'point-in-polygon service disabled, unable to resolve {"lat":12.121212,"lon":21.212121}'); |
||||
t.end(); |
||||
}); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.success = (test, common) => { |
||||
test('lat and lon should be passed to server', (t) => { |
||||
const pipServer = require('express')(); |
||||
pipServer.get('/:lon/:lat', (req, res, next) => { |
||||
t.equals(req.params.lat, '12.121212'); |
||||
t.equals(req.params.lon, '21.212121'); |
||||
|
||||
res.send('{ "field": "value" }'); |
||||
}); |
||||
|
||||
const server = pipServer.listen(); |
||||
const port = server.address().port; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const service = proxyquire('../../../service/pointinpolygon', { |
||||
'pelias-logger': logger |
||||
})(`http://localhost:${port}`); |
||||
|
||||
service({ lat: 12.121212, lon: 21.212121}, (err, results) => { |
||||
t.notOk(err); |
||||
t.deepEquals(results, { field: 'value' }); |
||||
|
||||
t.ok(logger.isInfoMessage(`using point-in-polygon service at http://localhost:${port}`)); |
||||
t.notOk(logger.hasErrorMessages()); |
||||
|
||||
t.end(); |
||||
|
||||
server.close(); |
||||
|
||||
}); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.tests.failure = (test, common) => { |
||||
test('server returning success but non-JSON body should log error and return no results', (t) => { |
||||
const pipServer = require('express')(); |
||||
pipServer.get('/:lon/:lat', (req, res, next) => { |
||||
t.equals(req.params.lat, '12.121212'); |
||||
t.equals(req.params.lon, '21.212121'); |
||||
|
||||
res.send('this is not JSON'); |
||||
}); |
||||
|
||||
const server = pipServer.listen(); |
||||
const port = server.address().port; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const service = proxyquire('../../../service/pointinpolygon', { |
||||
'pelias-logger': logger |
||||
})(`http://localhost:${port}`); |
||||
|
||||
service({ lat: 12.121212, lon: 21.212121}, (err, results) => { |
||||
t.equals(err, `http://localhost:${port}/21.212121/12.121212 returned status 200 but with non-JSON response: this is not JSON`); |
||||
t.notOk(results); |
||||
t.ok(logger.isErrorMessage(`http://localhost:${port}/21.212121/12.121212: could not parse response body: this is not JSON`)); |
||||
t.end(); |
||||
|
||||
server.close(); |
||||
|
||||
}); |
||||
|
||||
}); |
||||
|
||||
test('server returning error should log it and return no results', (t) => { |
||||
const server = require('express')().listen(); |
||||
const port = server.address().port; |
||||
|
||||
// immediately close the server so to ensure an error response
|
||||
server.close(); |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const service = proxyquire('../../../service/pointinpolygon', { |
||||
'pelias-logger': logger |
||||
})(`http://localhost:${port}`); |
||||
|
||||
service({ lat: 12.121212, lon: 21.212121}, (err, results) => { |
||||
t.equals(err.code, 'ECONNREFUSED'); |
||||
t.notOk(results); |
||||
t.ok(logger.isErrorMessage(/ECONNREFUSED/), 'there should be a connection refused error message'); |
||||
t.end(); |
||||
|
||||
server.close(); |
||||
|
||||
}); |
||||
|
||||
}); |
||||
|
||||
test('non-OK status should log error and return no results', (t) => { |
||||
const pipServer = require('express')(); |
||||
pipServer.get('/:lat/:lon', (req, res, next) => { |
||||
res.status(400).send('a bad request was made'); |
||||
}); |
||||
|
||||
const server = pipServer.listen(); |
||||
const port = server.address().port; |
||||
|
||||
const logger = require('pelias-mock-logger')(); |
||||
|
||||
const service = proxyquire('../../../service/pointinpolygon', { |
||||
'pelias-logger': logger |
||||
})(`http://localhost:${port}`); |
||||
|
||||
service({ lat: 12.121212, lon: 21.212121}, (err, results) => { |
||||
t.equals(err, `http://localhost:${port}/21.212121/12.121212 returned status 400: a bad request was made`); |
||||
t.notOk(results); |
||||
t.ok(logger.isErrorMessage(`http://localhost:${port}/21.212121/12.121212 returned status 400: a bad request was made`)); |
||||
t.end(); |
||||
|
||||
server.close(); |
||||
|
||||
}); |
||||
|
||||
}); |
||||
|
||||
}; |
||||
|
||||
module.exports.all = (tape, common) => { |
||||
function test(name, testFunction) { |
||||
return tape(`SERVICE /pointinpolygon ${name}`, testFunction); |
||||
} |
||||
|
||||
for( var testCase in module.exports.tests ){ |
||||
module.exports.tests[testCase](test, common); |
||||
} |
||||
}; |
Loading…
Reference in new issue