mirror of https://github.com/pelias/api.git
Diana Shkolnikov
8 years ago
committed by
GitHub
50 changed files with 4373 additions and 817 deletions
@ -0,0 +1,271 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
const logger = require('pelias-logger').get('api'); |
||||||
|
const Document = require('pelias-model').Document; |
||||||
|
const geolib = require('geolib'); |
||||||
|
|
||||||
|
// composition of toNumber and isFinite, useful for single call to convert a value
|
||||||
|
// to a number, then checking to see if it's finite
|
||||||
|
function isFiniteNumber(value) { |
||||||
|
return !_.isEmpty(_.trim(value)) && _.isFinite(_.toNumber(value)); |
||||||
|
} |
||||||
|
|
||||||
|
// returns true if value is parseable as finite non-negative number
|
||||||
|
function isNonNegativeFiniteNumber(value) { |
||||||
|
return isFiniteNumber(value) && _.gte(value, 0); |
||||||
|
} |
||||||
|
|
||||||
|
function hasLatLon(result) { |
||||||
|
return _.isFinite(_.get(result.geom, 'lat')) && _.isFinite(_.get(result.geom, 'lon')); |
||||||
|
} |
||||||
|
|
||||||
|
function getLatLon(result) { |
||||||
|
return { |
||||||
|
latitude: result.geom.lat, |
||||||
|
longitude: result.geom.lon |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
// if geom.lat/lon are parseable as finite numbers, convert to a finite number
|
||||||
|
// otherwise remove the field
|
||||||
|
function numberifyGeomLatLon(result) { |
||||||
|
['lat', 'lon'].forEach((f) => { |
||||||
|
if (isFiniteNumber(_.get(result.geom, f))) { |
||||||
|
result.geom[f] = _.toFinite(result.geom[f]); |
||||||
|
} else { |
||||||
|
// result.geom may not exist, so use unset instead of delete
|
||||||
|
_.unset(result.geom, f); |
||||||
|
} |
||||||
|
}); |
||||||
|
|
||||||
|
return result; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// returns true if all 4 ,-delimited (max) substrings are parseable as finite numbers
|
||||||
|
// '12.12,21.21,13.13,31.31' returns true
|
||||||
|
// '12.12,21.21,13.13,31.31,14.14' returns false
|
||||||
|
// '12.12,21.21,13.13,blah' returns false
|
||||||
|
// '12.12,21.21,13.13,31.31,blah' returns false
|
||||||
|
// '12.12,NaN,13.13,31.31' returns false
|
||||||
|
// '12.12,Infinity,13.13,31.31' returns false
|
||||||
|
function is4CommaDelimitedNumbers(bbox) { |
||||||
|
return _.defaultTo(bbox, ''). |
||||||
|
split(','). |
||||||
|
filter(isFiniteNumber).length === 4; |
||||||
|
} |
||||||
|
|
||||||
|
function hasName(result) { |
||||||
|
return !_.isEmpty(_.trim(result.name)); |
||||||
|
} |
||||||
|
|
||||||
|
// filter that passes only results that match on requested layers
|
||||||
|
// passes everything if req.clean.layers is not found
|
||||||
|
function getLayersFilter(clean) { |
||||||
|
if (_.isEmpty(_.get(clean, 'layers', []))) { |
||||||
|
return _.constant(true); |
||||||
|
} |
||||||
|
|
||||||
|
// otherwise return a function that checks for set inclusion of a result placetype
|
||||||
|
return (result) => { |
||||||
|
return _.includes(clean.layers, result.placetype); |
||||||
|
}; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// return true if the hierarchy does not have a country.abbr
|
||||||
|
// OR hierarchy country.abbr matches boundary.country
|
||||||
|
function matchesBoundaryCountry(boundaryCountry, hierarchy) { |
||||||
|
return !boundaryCountry || _.get(hierarchy, 'country.abbr') === boundaryCountry; |
||||||
|
} |
||||||
|
|
||||||
|
// return true if the result does not have a lineage
|
||||||
|
// OR at least one lineage matches the requested boundary.country
|
||||||
|
function atLeastOneLineageMatchesBoundaryCountry(boundaryCountry, result) { |
||||||
|
return !result.lineage || result.lineage.some(_.partial(matchesBoundaryCountry, boundaryCountry)); |
||||||
|
} |
||||||
|
|
||||||
|
// return a function that detects if a result has at least one lineage in boundary.country
|
||||||
|
// if there's no boundary.country, return a function that always returns true
|
||||||
|
function getBoundaryCountryFilter(clean) { |
||||||
|
if (_.has(clean, 'boundary.country')) { |
||||||
|
return _.partial(atLeastOneLineageMatchesBoundaryCountry, clean['boundary.country']); |
||||||
|
} |
||||||
|
|
||||||
|
return _.constant(true); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// return a function that detects if a result is inside a bbox if a bbox is available
|
||||||
|
// if there's no bbox, return a function that always returns true
|
||||||
|
function getBoundaryRectangleFilter(clean) { |
||||||
|
if (['min_lat', 'min_lon', 'max_lat', 'max_lon'].every((f) => { |
||||||
|
return _.has(clean, `boundary.rect.${f}`); |
||||||
|
})) { |
||||||
|
const polygon = [ |
||||||
|
{ latitude: clean['boundary.rect.min_lat'], longitude: clean['boundary.rect.min_lon'] }, |
||||||
|
{ latitude: clean['boundary.rect.max_lat'], longitude: clean['boundary.rect.min_lon'] }, |
||||||
|
{ latitude: clean['boundary.rect.max_lat'], longitude: clean['boundary.rect.max_lon'] }, |
||||||
|
{ latitude: clean['boundary.rect.min_lat'], longitude: clean['boundary.rect.max_lon'] } |
||||||
|
]; |
||||||
|
const isPointInsidePolygon = _.partialRight(geolib.isPointInside, polygon); |
||||||
|
|
||||||
|
return _.partial(isInsideGeometry, isPointInsidePolygon); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return _.constant(true); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// return a function that detects if a result is inside a circle if a circle is available
|
||||||
|
// if there's no circle, return a function that always returns true
|
||||||
|
function getBoundaryCircleFilter(clean) { |
||||||
|
if (['lat', 'lon', 'radius'].every((f) => { |
||||||
|
return _.has(clean, `boundary.circle.${f}`); |
||||||
|
})) { |
||||||
|
const center = { |
||||||
|
latitude: clean['boundary.circle.lat'], |
||||||
|
longitude: clean['boundary.circle.lon'] |
||||||
|
}; |
||||||
|
const radiusInMeters = clean['boundary.circle.radius'] * 1000; |
||||||
|
const isPointInCircle = _.partialRight(geolib.isPointInCircle, center, radiusInMeters); |
||||||
|
|
||||||
|
return _.partial(isInsideGeometry, isPointInCircle); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return _.constant(true); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
// helper that calls an "is inside some geometry" function
|
||||||
|
function isInsideGeometry(f, result) { |
||||||
|
return hasLatLon(result) ? f(getLatLon(result)) : false; |
||||||
|
} |
||||||
|
|
||||||
|
function placetypeHasNameAndId(hierarchyElement) { |
||||||
|
return !_.isEmpty(_.trim(hierarchyElement.name)) && |
||||||
|
!_.isEmpty(_.trim(hierarchyElement.id)); |
||||||
|
} |
||||||
|
|
||||||
|
// synthesize an ES doc from a placeholder result
|
||||||
|
function synthesizeDocs(boundaryCountry, result) { |
||||||
|
const doc = new Document('whosonfirst', result.placetype, result.id.toString()); |
||||||
|
doc.setName('default', result.name); |
||||||
|
|
||||||
|
// only assign centroid if both lat and lon are finite numbers
|
||||||
|
if (hasLatLon(result)) { |
||||||
|
doc.setCentroid( { lat: result.geom.lat, lon: result.geom.lon } ); |
||||||
|
} else { |
||||||
|
logger.error(`could not parse centroid for id ${result.id}`); |
||||||
|
} |
||||||
|
|
||||||
|
// lodash conformsTo verifies that an object has a property with a certain format
|
||||||
|
if (_.conformsTo(result.geom, { 'bbox': is4CommaDelimitedNumbers } )) { |
||||||
|
const parsedBoundingBox = result.geom.bbox.split(',').map(_.toFinite); |
||||||
|
doc.setBoundingBox({ |
||||||
|
upperLeft: { |
||||||
|
lat: parsedBoundingBox[3], |
||||||
|
lon: parsedBoundingBox[0] |
||||||
|
}, |
||||||
|
lowerRight: { |
||||||
|
lat: parsedBoundingBox[1], |
||||||
|
lon: parsedBoundingBox[2] |
||||||
|
} |
||||||
|
}); |
||||||
|
} else { |
||||||
|
logger.error(`could not parse bbox for id ${result.id}: ${_.get(result, 'geom.bbox')}`); |
||||||
|
} |
||||||
|
|
||||||
|
// set population and popularity if parseable as finite number
|
||||||
|
if (isNonNegativeFiniteNumber(result.population)) { |
||||||
|
doc.setPopulation(_.toFinite(result.population)); |
||||||
|
} |
||||||
|
if (isNonNegativeFiniteNumber(result.popularity)) { |
||||||
|
doc.setPopularity(_.toFinite(result.popularity)); |
||||||
|
} |
||||||
|
|
||||||
|
_.defaultTo(result.lineage, []) |
||||||
|
// remove all lineages that don't match an explicit boundary.country
|
||||||
|
.filter(_.partial(matchesBoundaryCountry, boundaryCountry)) |
||||||
|
// add all the lineages to the doc
|
||||||
|
.map((hierarchy) => { |
||||||
|
Object.keys(hierarchy) |
||||||
|
.filter(doc.isSupportedParent) |
||||||
|
.filter((placetype) => { |
||||||
|
return placetypeHasNameAndId(hierarchy[placetype]); |
||||||
|
}) |
||||||
|
.forEach((placetype) => { |
||||||
|
doc.addParent( |
||||||
|
placetype, |
||||||
|
hierarchy[placetype].name, |
||||||
|
hierarchy[placetype].id.toString(), |
||||||
|
hierarchy[placetype].abbr); |
||||||
|
}); |
||||||
|
}); |
||||||
|
|
||||||
|
return buildESDoc(doc); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
function buildESDoc(doc) { |
||||||
|
const esDoc = doc.toESDocument(); |
||||||
|
return _.extend(esDoc.data, { _id: esDoc._id, _type: esDoc._type }); |
||||||
|
} |
||||||
|
|
||||||
|
function setup(placeholderService, should_execute) { |
||||||
|
function controller( req, res, next ){ |
||||||
|
// bail early if req/res don't pass conditions for execution
|
||||||
|
if (!should_execute(req, res)) { |
||||||
|
return next(); |
||||||
|
} |
||||||
|
|
||||||
|
placeholderService(req, (err, results) => { |
||||||
|
if (err) { |
||||||
|
// bubble up an error if one occurred
|
||||||
|
if (_.isObject(err) && err.message) { |
||||||
|
req.errors.push( err.message ); |
||||||
|
} else { |
||||||
|
req.errors.push( err ); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
const boundaryCountry = _.get(req, ['clean', 'boundary.country']); |
||||||
|
|
||||||
|
// convert results to ES docs
|
||||||
|
// boundary.country filter must happen after synthesis since multiple
|
||||||
|
// lineages may produce different country docs
|
||||||
|
res.meta = {}; |
||||||
|
res.data = results |
||||||
|
// filter out results that don't have a name
|
||||||
|
.filter(hasName) |
||||||
|
// filter out results that don't match on requested layer(s)
|
||||||
|
.filter(getLayersFilter(req.clean)) |
||||||
|
// filter out results that don't match on any lineage country
|
||||||
|
.filter(getBoundaryCountryFilter(req.clean)) |
||||||
|
// clean up geom.lat/lon for boundary rect/circle checks
|
||||||
|
.map(numberifyGeomLatLon) |
||||||
|
// filter out results that aren't in the boundary.rect
|
||||||
|
.filter(getBoundaryRectangleFilter(req.clean)) |
||||||
|
// filter out results that aren't in the boundary.circle
|
||||||
|
.filter(getBoundaryCircleFilter(req.clean)) |
||||||
|
// convert results to ES docs
|
||||||
|
.map(_.partial(synthesizeDocs, boundaryCountry)); |
||||||
|
|
||||||
|
const messageParts = [ |
||||||
|
'[controller:placeholder]', |
||||||
|
`[result_count:${_.defaultTo(res.data, []).length}]` |
||||||
|
]; |
||||||
|
|
||||||
|
logger.info(messageParts.join(' ')); |
||||||
|
} |
||||||
|
|
||||||
|
return next(); |
||||||
|
}); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return controller; |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = setup; |
@ -0,0 +1,19 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
// returns a function that returns true if any result.layer is in any of the
|
||||||
|
// supplied layers using array intersection
|
||||||
|
|
||||||
|
// example usage: determining if the response contains only admin results
|
||||||
|
|
||||||
|
module.exports = (layers) => { |
||||||
|
return (request, response) => { |
||||||
|
return !_.isEmpty( |
||||||
|
_.intersection( |
||||||
|
// convert layers to an array if it isn't already one
|
||||||
|
_.castArray(layers), |
||||||
|
// pull all the layer properties into an array
|
||||||
|
_.map(response.data, _.property('layer')) |
||||||
|
)); |
||||||
|
}; |
||||||
|
|
||||||
|
}; |
@ -0,0 +1,13 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
module.exports = (request, response) => { |
||||||
|
if (!request.clean.hasOwnProperty('parsed_text')) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
// return true only if all non-admin properties of parsed_text are empty
|
||||||
|
return ['number', 'street', 'query', 'category'].every((prop) => { |
||||||
|
return _.isEmpty(request.clean.parsed_text[prop]); |
||||||
|
}); |
||||||
|
|
||||||
|
}; |
@ -1,7 +0,0 @@ |
|||||||
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,5 @@ |
|||||||
|
module.exports = (uri) => { |
||||||
|
return (request, response) => { |
||||||
|
return uri !== undefined; |
||||||
|
}; |
||||||
|
}; |
@ -0,0 +1,35 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
const logger = require('pelias-logger').get('api'); |
||||||
|
|
||||||
|
function setup(comparator, should_execute) { |
||||||
|
function middleware(req, res, next) { |
||||||
|
// bail early if req/res don't pass conditions for execution or there's no data to sort
|
||||||
|
if (!should_execute(req, res) || _.isEmpty(res.data)) { |
||||||
|
return next(); |
||||||
|
} |
||||||
|
|
||||||
|
// capture the pre-sort order
|
||||||
|
const presort_order = res.data.map(_.property('_id')); |
||||||
|
|
||||||
|
// sort operates on array in place
|
||||||
|
res.data.sort(comparator(req.clean)); |
||||||
|
|
||||||
|
// capture the post-sort order
|
||||||
|
const postsort_order = res.data.map(_.property('_id')); |
||||||
|
|
||||||
|
// log it for debugging purposes
|
||||||
|
logger.debug([ |
||||||
|
`req.clean: ${JSON.stringify(req.clean)}`, |
||||||
|
`pre-sort: [${presort_order}]`, |
||||||
|
`post-sort: [${postsort_order}]` |
||||||
|
].join(', ')); |
||||||
|
|
||||||
|
next(); |
||||||
|
} |
||||||
|
|
||||||
|
return middleware; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
module.exports = setup; |
@ -0,0 +1,25 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
/** |
||||||
|
with the release of coarse reverse as a separate thing and ES reverse only |
||||||
|
handling venues, addresses, and streets, geonames make no sense in the reverse context |
||||||
|
**/ |
||||||
|
|
||||||
|
function sanitize( raw, clean, opts ) { |
||||||
|
// error & warning messages
|
||||||
|
const messages = { errors: [], warnings: [] }; |
||||||
|
|
||||||
|
if (_.isEqual(clean.sources, ['geonames']) || _.isEqual(clean.sources, ['gn'])) { |
||||||
|
messages.errors.push('/reverse does not support geonames'); |
||||||
|
|
||||||
|
} else if (_.includes(clean.sources, 'geonames') || _.includes(clean.sources, 'gn')) { |
||||||
|
clean.sources = _.without(clean.sources, 'geonames', 'gn'); |
||||||
|
messages.warnings.push('/reverse does not support geonames'); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return messages; |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
module.exports = sanitize; |
@ -0,0 +1,37 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
const non_admin_fields = ['number', 'street', 'query', 'category']; |
||||||
|
|
||||||
|
function hasAnyNonAdminFields(parsed_text) { |
||||||
|
return !_.isEmpty( |
||||||
|
_.intersection( |
||||||
|
_.keys(parsed_text), |
||||||
|
non_admin_fields)); |
||||||
|
} |
||||||
|
|
||||||
|
function sanitize( raw, clean ){ |
||||||
|
// error & warning messages
|
||||||
|
const messages = { errors: [], warnings: [] }; |
||||||
|
|
||||||
|
// bail early if analysis isn't admin-only
|
||||||
|
if (_.isUndefined(clean.parsed_text) || hasAnyNonAdminFields(clean.parsed_text)) { |
||||||
|
return messages; |
||||||
|
} |
||||||
|
|
||||||
|
// the analysis is admin-only, so add errors or warnings if geonames was requested
|
||||||
|
if (_.isEqual(clean.sources, ['geonames'])) { |
||||||
|
// if requested sources is only geonames, return an error
|
||||||
|
messages.errors.push('input contains only administrative area data, ' + |
||||||
|
'no results will be returned when sources=geonames'); |
||||||
|
|
||||||
|
} else if (_.includes(clean.sources, 'geonames')) { |
||||||
|
// if there are other sources besides geonames, return an warning
|
||||||
|
messages.warnings.push('input contains only administrative area data, ' + |
||||||
|
'geonames results will not be returned'); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
return messages; |
||||||
|
} |
||||||
|
|
||||||
|
module.exports = sanitize; |
@ -0,0 +1,32 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const url = require('url'); |
||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
const ServiceConfiguration = require('pelias-microservice-wrapper').ServiceConfiguration; |
||||||
|
|
||||||
|
class PlaceHolder extends ServiceConfiguration { |
||||||
|
constructor(o) { |
||||||
|
super('placeholder', o); |
||||||
|
} |
||||||
|
|
||||||
|
getParameters(req) { |
||||||
|
const parameters = { |
||||||
|
text: req.clean.text |
||||||
|
}; |
||||||
|
|
||||||
|
if (_.has(req.clean, 'lang.iso6393')) { |
||||||
|
parameters.lang = req.clean.lang.iso6393; |
||||||
|
} |
||||||
|
|
||||||
|
return parameters; |
||||||
|
} |
||||||
|
|
||||||
|
getUrl(req) { |
||||||
|
return url.resolve(this.baseUrl, 'parser/search'); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
module.exports = PlaceHolder; |
@ -0,0 +1,21 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const url = require('url'); |
||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
const ServiceConfiguration = require('pelias-microservice-wrapper').ServiceConfiguration; |
||||||
|
|
||||||
|
class PointInPolygon extends ServiceConfiguration { |
||||||
|
constructor(o) { |
||||||
|
super('pip', o); |
||||||
|
} |
||||||
|
|
||||||
|
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']}`); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
module.exports = PointInPolygon; |
@ -1,86 +0,0 @@ |
|||||||
const logger = require( 'pelias-logger' ).get( 'pointinpolygon' ); |
|
||||||
const request = require('request'); |
|
||||||
const _ = require('lodash'); |
|
||||||
|
|
||||||
function sanitizeUrl(requestUrl) { |
|
||||||
return requestUrl.replace(/^(.+)\/.+\/.+$/, (match, base) => { |
|
||||||
return `${base}/[removed]/[removed]`; |
|
||||||
}); |
|
||||||
} |
|
||||||
|
|
||||||
function parseErrorMessage(requestUrl, body, do_not_track) { |
|
||||||
if (do_not_track) { |
|
||||||
return `${sanitizeUrl(requestUrl)} returned status 200 but with non-JSON response: ${body}`; |
|
||||||
} |
|
||||||
|
|
||||||
return `${requestUrl} returned status 200 but with non-JSON response: ${body}`; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
function serviceErrorMessage(requestUrl, statusCode, body, do_not_track) { |
|
||||||
if (do_not_track) { |
|
||||||
return `${sanitizeUrl(requestUrl)} returned status ${statusCode}: ${body}`; |
|
||||||
|
|
||||||
} else { |
|
||||||
return `${requestUrl} returned status ${statusCode}: ${body}`; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
module.exports = (url) => { |
|
||||||
if (!_.isEmpty(url)) { |
|
||||||
logger.info(`using point-in-polygon service at ${url}`); |
|
||||||
|
|
||||||
return function pointinpolygon( centroid, do_not_track, callback ) { |
|
||||||
const requestUrl = `${url}/${centroid.lon}/${centroid.lat}`; |
|
||||||
|
|
||||||
const options = { |
|
||||||
method: 'GET', |
|
||||||
url: requestUrl |
|
||||||
}; |
|
||||||
|
|
||||||
if (do_not_track) { |
|
||||||
options.headers = { |
|
||||||
dnt: '1' |
|
||||||
}; |
|
||||||
} |
|
||||||
|
|
||||||
request(options, (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) { |
|
||||||
const parseMsg = parseErrorMessage(requestUrl, body, do_not_track); |
|
||||||
|
|
||||||
logger.error(parseMsg); |
|
||||||
callback(parseMsg); |
|
||||||
} |
|
||||||
} |
|
||||||
else { |
|
||||||
const errorMsg = serviceErrorMessage(requestUrl, response.statusCode, body, do_not_track); |
|
||||||
|
|
||||||
logger.error(errorMsg); |
|
||||||
callback(errorMsg); |
|
||||||
} |
|
||||||
}); |
|
||||||
|
|
||||||
}; |
|
||||||
|
|
||||||
} else { |
|
||||||
logger.warn('point-in-polygon service disabled'); |
|
||||||
|
|
||||||
return (centroid, do_not_track, callback) => { |
|
||||||
callback(`point-in-polygon service disabled, unable to resolve ` + |
|
||||||
(do_not_track ? 'centroid' : JSON.stringify(centroid))); |
|
||||||
}; |
|
||||||
|
|
||||||
} |
|
||||||
|
|
||||||
}; |
|
@ -0,0 +1,39 @@ |
|||||||
|
|
||||||
|
#> bounding circle |
||||||
|
path: '/v1/reverse?layers=coarse&point.lat=40.744243&point.lon=-73.990342&boundary.circle.radius=999.9' |
||||||
|
|
||||||
|
#? 200 ok |
||||||
|
response.statusCode.should.be.equal 200 |
||||||
|
response.should.have.header 'charset', 'utf8' |
||||||
|
response.should.have.header 'content-type', 'application/json; charset=utf-8' |
||||||
|
|
||||||
|
#? valid geocoding block |
||||||
|
should.exist json.geocoding |
||||||
|
should.exist json.geocoding.version |
||||||
|
should.exist json.geocoding.attribution |
||||||
|
should.exist json.geocoding.query |
||||||
|
should.exist json.geocoding.engine |
||||||
|
should.exist json.geocoding.engine.name |
||||||
|
should.exist json.geocoding.engine.author |
||||||
|
should.exist json.geocoding.engine.version |
||||||
|
should.exist json.geocoding.timestamp |
||||||
|
|
||||||
|
#? valid geojson |
||||||
|
json.type.should.be.equal 'FeatureCollection' |
||||||
|
json.features.should.be.instanceof Array |
||||||
|
|
||||||
|
#? expected errors |
||||||
|
should.not.exist json.geocoding.errors |
||||||
|
|
||||||
|
#? expected warnings |
||||||
|
should.exist json.geocoding.warnings |
||||||
|
json.geocoding.warnings.should.eql [ 'boundary.circle.radius is not applicable for coarse reverse' ] |
||||||
|
|
||||||
|
#? inputs |
||||||
|
json.geocoding.query['size'].should.eql 10 |
||||||
|
json.geocoding.query['layers'].should.eql 'coarse' |
||||||
|
json.geocoding.query['point.lat'].should.eql 40.744243 |
||||||
|
json.geocoding.query['point.lon'].should.eql -73.990342 |
||||||
|
json.geocoding.query['boundary.circle.lat'].should.eql 40.744243 |
||||||
|
json.geocoding.query['boundary.circle.lon'].should.eql -73.990342 |
||||||
|
json.geocoding.query['boundary.circle.radius'].should.eql 999.9 |
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,122 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
const has_results_at_layers = require('../../../../controller/predicates/has_results_at_layers'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = (test, common) => { |
||||||
|
test('valid interface', (t) => { |
||||||
|
t.equal(typeof has_results_at_layers, 'function', 'has_results_at_layers is a function'); |
||||||
|
t.equal(has_results_at_layers.length, 1); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.true_conditions = (test, common) => { |
||||||
|
test('should return true when any result.layer matches any layer in array', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = { |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
layer: 'layer 1' |
||||||
|
}, |
||||||
|
{ |
||||||
|
layer: 'layer 2' |
||||||
|
}, |
||||||
|
{ |
||||||
|
layer: 'layer 3' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
t.ok(has_results_at_layers(['layer 2', 'layer 4'])(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('should return true when any result.layer matches layer string', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = { |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
layer: 'layer 1' |
||||||
|
}, |
||||||
|
{ |
||||||
|
layer: 'layer 2' |
||||||
|
}, |
||||||
|
{ |
||||||
|
layer: 'layer 3' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
t.ok(has_results_at_layers('layer 2')(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.false_conditions = (test, common) => { |
||||||
|
test('should return false when response has undefined data', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
t.notOk(has_results_at_layers('layer')(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('should return false when response has empty data array', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = { |
||||||
|
data: [] |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(has_results_at_layers('layer')(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('should return false when layer is a substring of non-array string layers parameter', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = { |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
layer: 'aye' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(has_results_at_layers('layer')(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('should return false when no results have layer in supplied layers', (t) => { |
||||||
|
const req = {}; |
||||||
|
const res = { |
||||||
|
data: [ |
||||||
|
{ |
||||||
|
layer: 'layer 1' |
||||||
|
} |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
t.notOk(has_results_at_layers(['layer 2', 'layer 3'])(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`GET /has_results_at_layers ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( const testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,77 @@ |
|||||||
|
'use strict'; |
||||||
|
|
||||||
|
const _ = require('lodash'); |
||||||
|
const is_admin_only_analysis = require('../../../../controller/predicates/is_admin_only_analysis'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.interface = (test, common) => { |
||||||
|
test('valid interface', (t) => { |
||||||
|
t.equal(typeof is_admin_only_analysis, 'function', 'is_admin_only_analysis is a function'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.true_conditions = (test, common) => { |
||||||
|
test('parsed_text with admin-only properties should return true', (t) => { |
||||||
|
['neighbourhood', 'borough', 'city', 'county', 'state', 'postalcode', 'country'].forEach((property) => { |
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
parsed_text: {} |
||||||
|
} |
||||||
|
}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
req.clean.parsed_text[property] = `${property} value`; |
||||||
|
|
||||||
|
t.ok(is_admin_only_analysis(req, res)); |
||||||
|
|
||||||
|
}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.false_conditions = (test, common) => { |
||||||
|
test('req.clean with no parsed_text should return false', (t) => { |
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
} |
||||||
|
}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
t.notOk(is_admin_only_analysis(req, res)); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('parsed_text with non-admin properties should return false', (t) => { |
||||||
|
['number', 'street', 'query', 'category'].forEach((property) => { |
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
parsed_text: {} |
||||||
|
} |
||||||
|
}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
req.clean.parsed_text[property] = `${property} value`; |
||||||
|
|
||||||
|
t.notOk(is_admin_only_analysis(req, res)); |
||||||
|
|
||||||
|
}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`GET /is_admin_only_analysis ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( const testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,41 @@ |
|||||||
|
var vs = require('../../../query/reverse_defaults'); |
||||||
|
|
||||||
|
module.exports = { |
||||||
|
'query': { |
||||||
|
'bool': { |
||||||
|
'filter': [ |
||||||
|
{ |
||||||
|
'geo_distance': { |
||||||
|
'distance': '3km', |
||||||
|
'distance_type': 'plane', |
||||||
|
'optimize_bbox': 'indexed', |
||||||
|
'center_point': { |
||||||
|
'lat': 29.49136, |
||||||
|
'lon': -82.50622 |
||||||
|
} |
||||||
|
} |
||||||
|
}, |
||||||
|
{ |
||||||
|
'terms': { |
||||||
|
'layer': ['venue', 'street'] |
||||||
|
} |
||||||
|
} |
||||||
|
] |
||||||
|
} |
||||||
|
}, |
||||||
|
'sort': [ |
||||||
|
'_score', |
||||||
|
{ |
||||||
|
'_geo_distance': { |
||||||
|
'center_point': { |
||||||
|
'lat': 29.49136, |
||||||
|
'lon': -82.50622 |
||||||
|
}, |
||||||
|
'order': 'asc', |
||||||
|
'distance_type': 'plane' |
||||||
|
} |
||||||
|
} |
||||||
|
], |
||||||
|
'size': vs.size, |
||||||
|
'track_scores': true |
||||||
|
}; |
@ -0,0 +1,156 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
const proxyquire = require('proxyquire').noCallThru(); |
||||||
|
const mock_logger = require('pelias-mock-logger'); |
||||||
|
|
||||||
|
const sortResponseData = require('../../../middleware/sortResponseData'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.should_execute_failure = (test, common) => { |
||||||
|
test('should_execute returning false should call next w/o invoking comparator', (t) => { |
||||||
|
t.plan(2, 'this ensures that should_execute was invoked'); |
||||||
|
|
||||||
|
const comparator = () => { |
||||||
|
throw Error('should not have been called'); |
||||||
|
}; |
||||||
|
|
||||||
|
const should_execute = (req, res) => { |
||||||
|
t.deepEquals(req, { a: 1 }); |
||||||
|
t.deepEquals(res, { b: 2 }); |
||||||
|
return false; |
||||||
|
}; |
||||||
|
|
||||||
|
const sort = sortResponseData(comparator, should_execute); |
||||||
|
|
||||||
|
const req = { a: 1 }; |
||||||
|
const res = { b: 2 }; |
||||||
|
|
||||||
|
sort(req, res, () => { |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.general_tests = (test, common) => { |
||||||
|
test('req.clean should be passed to sort', (t) => { |
||||||
|
t.plan(1, 'this ensures that comparator was invoked'); |
||||||
|
|
||||||
|
const comparator = (clean) => { |
||||||
|
t.deepEquals(clean, { a: 1 }); |
||||||
|
return () => { |
||||||
|
throw Error('should not have been called'); |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const sort = sortResponseData(comparator, _.constant(true)); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
a: 1 |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
const res = { |
||||||
|
data: [ {} ] |
||||||
|
}; |
||||||
|
|
||||||
|
sort(req, res, () => { |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('undefined res.data should return without interacting with comparator', (t) => { |
||||||
|
const comparator = () => { |
||||||
|
throw Error('should not have been called'); |
||||||
|
}; |
||||||
|
|
||||||
|
const sort = sortResponseData(comparator, _.constant(true)); |
||||||
|
|
||||||
|
const req = {}; |
||||||
|
const res = {}; |
||||||
|
|
||||||
|
sort(req, res, () => { |
||||||
|
t.deepEquals(res, {}); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('empty res.data should return without interacting with comparator', (t) => { |
||||||
|
const comparator = () => { |
||||||
|
throw Error('should not have been called'); |
||||||
|
}; |
||||||
|
|
||||||
|
const sort = sortResponseData(comparator, _.constant(true)); |
||||||
|
|
||||||
|
const req = {}; |
||||||
|
const res = { |
||||||
|
data: [] |
||||||
|
}; |
||||||
|
|
||||||
|
sort(req, res, () => { |
||||||
|
t.deepEquals(res.data, [], 'res.data should still be empty'); |
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.successful_sort = (test, common) => { |
||||||
|
test('comparator should be sort res.data', (t) => { |
||||||
|
const logger = mock_logger(); |
||||||
|
|
||||||
|
const comparator = () => { |
||||||
|
return (a, b) => { |
||||||
|
return a._id > b._id; |
||||||
|
}; |
||||||
|
}; |
||||||
|
|
||||||
|
const sortResponseData = proxyquire('../../../middleware/sortResponseData', { |
||||||
|
'pelias-logger': logger |
||||||
|
}); |
||||||
|
|
||||||
|
const sort = sortResponseData(comparator, _.constant(true)); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
field: 'value' |
||||||
|
} |
||||||
|
}; |
||||||
|
const res = { |
||||||
|
data: [ |
||||||
|
{ _id: 3 }, |
||||||
|
{ _id: 2 }, |
||||||
|
{ _id: 1 }, |
||||||
|
] |
||||||
|
}; |
||||||
|
|
||||||
|
sort(req, res, () => { |
||||||
|
t.deepEquals(res.data.shift(), { _id: 1 }); |
||||||
|
t.deepEquals(res.data.shift(), { _id: 2 }); |
||||||
|
t.deepEquals(res.data.shift(), { _id: 3 }); |
||||||
|
|
||||||
|
t.ok(logger.isDebugMessage( |
||||||
|
'req.clean: {"field":"value"}, pre-sort: [3,2,1], post-sort: [1,2,3]')); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
}); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
|
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`[middleware] sortResponseData: ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,82 @@ |
|||||||
|
const geonames_deprecation = require('../../../sanitizer/_geonames_deprecation'); |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.no_warnings_or_errors_conditions = (test, common) => { |
||||||
|
test('undefined sources should add neither warnings nor errors', (t) => { |
||||||
|
const clean = {}; |
||||||
|
|
||||||
|
const messages = geonames_deprecation(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(clean, {}); |
||||||
|
t.deepEquals(messages, { errors: [], warnings: [] }); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('geonames/gn not in sources should add neither warnings nor errors', (t) => { |
||||||
|
const clean = { |
||||||
|
sources: ['source 1', 'source 2'], |
||||||
|
}; |
||||||
|
|
||||||
|
const messages = geonames_deprecation(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(clean.sources, ['source 1', 'source 2']); |
||||||
|
t.deepEquals(messages, { errors: [], warnings: [] }); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.error_conditions = (test, common) => { |
||||||
|
test('only geonames in sources should not modify clean.sources and add error message', (t) => { |
||||||
|
['gn', 'geonames'].forEach((sources) => { |
||||||
|
const clean = { |
||||||
|
sources: [sources] |
||||||
|
}; |
||||||
|
|
||||||
|
const messages = geonames_deprecation(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(clean.sources, [sources]); |
||||||
|
t.deepEquals(messages.errors, ['/reverse does not support geonames']); |
||||||
|
t.deepEquals(messages.warnings, []); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.warning_conditions = (test, common) => { |
||||||
|
test('only geonames in sources should not modify clean.sources and add error message', (t) => { |
||||||
|
['gn', 'geonames'].forEach((sources) => { |
||||||
|
const clean = { |
||||||
|
sources: ['another source', sources, 'yet another source'] |
||||||
|
}; |
||||||
|
|
||||||
|
const messages = geonames_deprecation(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(clean.sources, ['another source', 'yet another source']); |
||||||
|
t.deepEquals(messages.errors, []); |
||||||
|
t.deepEquals(messages.warnings, ['/reverse does not support geonames']); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`SANTIZE _geonames_deprecation ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,111 @@ |
|||||||
|
const _ = require('lodash'); |
||||||
|
|
||||||
|
const geonames_warnings = require('../../../sanitizer/_geonames_warnings'); |
||||||
|
|
||||||
|
const nonAdminProperties = ['number', 'street', 'query', 'category']; |
||||||
|
const adminProperties = ['neighbourhood', 'borough', 'city', 'county', 'state', 'postalcode', 'country']; |
||||||
|
|
||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
module.exports.tests.no_errors = (test, common) => { |
||||||
|
test('undefined clean.parsed_text should exit early', (t) => { |
||||||
|
const clean = { |
||||||
|
sources: ['geonames'], |
||||||
|
}; |
||||||
|
|
||||||
|
const messages = geonames_warnings(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(messages, { errors: [], warnings: [] }); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('any non-admin analysis field with only geonames sources should exit early', (t) => { |
||||||
|
adminProperties.forEach((adminProperty) => { |
||||||
|
nonAdminProperties.forEach((nonAdminProperty) => { |
||||||
|
const clean = { |
||||||
|
sources: ['geonames'], |
||||||
|
parsed_text: {} |
||||||
|
}; |
||||||
|
clean.parsed_text[nonAdminProperty] = `${nonAdminProperty} value`; |
||||||
|
clean.parsed_text[adminProperty] = `${adminProperty} value`; |
||||||
|
|
||||||
|
const messages = geonames_warnings(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(messages, { errors: [], warnings: [] }); |
||||||
|
|
||||||
|
}); |
||||||
|
}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('any non-admin analysis field with non-geonames sources should exit early', (t) => { |
||||||
|
adminProperties.forEach((adminProperty) => { |
||||||
|
nonAdminProperties.forEach((nonAdminProperty) => { |
||||||
|
const clean = { |
||||||
|
sources: ['this is not geonames'], |
||||||
|
parsed_text: {} |
||||||
|
}; |
||||||
|
clean.parsed_text[nonAdminProperty] = `${nonAdminProperty} value`; |
||||||
|
clean.parsed_text[adminProperty] = `${adminProperty} value`; |
||||||
|
|
||||||
|
const messages = geonames_warnings(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(messages, { errors: [], warnings: [] }); |
||||||
|
|
||||||
|
}); |
||||||
|
}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.error_conditions = (test, common) => { |
||||||
|
test('any admin analysis field and only geonames sources should return error', (t) => { |
||||||
|
adminProperties.forEach((property) => { |
||||||
|
const clean = _.set({ sources: ['geonames'] }, |
||||||
|
['parsed_text', property], `${property} value`); |
||||||
|
|
||||||
|
const messages = geonames_warnings(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(messages.errors, ['input contains only administrative area data, ' + |
||||||
|
'no results will be returned when sources=geonames']); |
||||||
|
t.deepEquals(messages.warnings, []); |
||||||
|
|
||||||
|
}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.tests.warning_conditions = (test, common) => { |
||||||
|
test('any admin analysis field and only geonames sources should return warning', (t) => { |
||||||
|
adminProperties.forEach((property) => { |
||||||
|
const clean = _.set({ sources: ['source 1', 'geonames', 'source 2'] }, |
||||||
|
['parsed_text', property], `${property} value`); |
||||||
|
|
||||||
|
const messages = geonames_warnings(undefined, clean); |
||||||
|
|
||||||
|
t.deepEquals(messages.errors, []); |
||||||
|
t.deepEquals(messages.warnings, ['input contains only administrative area data, ' + |
||||||
|
'geonames results will not be returned']); |
||||||
|
|
||||||
|
}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`SANTIZE _geonames_warnings ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,139 @@ |
|||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
const PlaceHolder = require('../../../../service/configurations/PlaceHolder'); |
||||||
|
|
||||||
|
module.exports.tests.all = (test, common) => { |
||||||
|
test('getName should return \'placeholder\'', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const placeholder = new PlaceHolder(configBlob); |
||||||
|
|
||||||
|
t.equals(placeholder.getName(), 'placeholder'); |
||||||
|
t.equals(placeholder.getBaseUrl(), 'http://localhost:1234/'); |
||||||
|
t.equals(placeholder.getTimeout(), 17); |
||||||
|
t.equals(placeholder.getRetries(), 19); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('getUrl should return value passed to constructor', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const placeholder = new PlaceHolder(configBlob); |
||||||
|
|
||||||
|
t.equals(placeholder.getUrl(), 'http://localhost:1234/parser/search'); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('getParameters should return object with text and lang from req', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const placeholder = new PlaceHolder(configBlob); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
text: 'text value', |
||||||
|
lang: { |
||||||
|
iso6393: 'lang value' |
||||||
|
} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
t.deepEquals(placeholder.getParameters(req), { text: 'text value', lang: 'lang value' }); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('getHeaders should return empty object', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'base url', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const placeholder = new PlaceHolder(configBlob); |
||||||
|
|
||||||
|
t.deepEquals(placeholder.getHeaders(), {}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('getParameters should not include lang if req.clean.lang is unavailable', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'base url', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const placeholder = new PlaceHolder(configBlob); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
text: 'text value' |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
t.deepEquals(placeholder.getParameters(req), { text: 'text value' }); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('getParameters should not include lang if req.clean.lang.iso6393 is unavailable', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'base url', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const placeholder = new PlaceHolder(configBlob); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { |
||||||
|
text: 'text value', |
||||||
|
lang: {} |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
t.deepEquals(placeholder.getParameters(req), { text: 'text value' }); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('baseUrl ending in / should not have double /\'s return by getUrl', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234/', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const placeholder = new PlaceHolder(configBlob); |
||||||
|
|
||||||
|
t.deepEquals(placeholder.getUrl(), 'http://localhost:1234/parser/search'); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`SERVICE CONFIGURATION /PlaceHolder ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,101 @@ |
|||||||
|
module.exports.tests = {}; |
||||||
|
|
||||||
|
const PointInPolygon = require('../../../../service/configurations/PointInPolygon'); |
||||||
|
|
||||||
|
module.exports.tests.all = (test, common) => { |
||||||
|
test('getName should return \'pip\'', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const pointInPolygon = new PointInPolygon(configBlob); |
||||||
|
|
||||||
|
t.equals(pointInPolygon.getName(), 'pip'); |
||||||
|
t.equals(pointInPolygon.getBaseUrl(), 'http://localhost:1234/'); |
||||||
|
t.equals(pointInPolygon.getTimeout(), 17); |
||||||
|
t.equals(pointInPolygon.getRetries(), 19); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('getUrl should return value formatted with point.lon/lat passed to constructor', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234' |
||||||
|
}; |
||||||
|
|
||||||
|
const pointInPolygon = new PointInPolygon(configBlob); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { } |
||||||
|
}; |
||||||
|
|
||||||
|
req.clean['point.lon'] = 21.212121; |
||||||
|
req.clean['point.lat'] = 12.121212; |
||||||
|
|
||||||
|
t.equals(pointInPolygon.getUrl(req), 'http://localhost:1234/21.212121/12.121212'); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('getHeaders should return an empty object', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const pointInPolygon = new PointInPolygon(configBlob); |
||||||
|
|
||||||
|
t.deepEquals(pointInPolygon.getHeaders(), {}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('getParameters should return an empty object', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const pointInPolygon = new PointInPolygon(configBlob); |
||||||
|
|
||||||
|
t.deepEquals(pointInPolygon.getParameters(), {}); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
test('baseUrl ending in / should not have double /\'s return by getUrl', (t) => { |
||||||
|
const configBlob = { |
||||||
|
url: 'http://localhost:1234/', |
||||||
|
timeout: 17, |
||||||
|
retries: 19 |
||||||
|
}; |
||||||
|
|
||||||
|
const pointInPolygon = new PointInPolygon(configBlob); |
||||||
|
|
||||||
|
const req = { |
||||||
|
clean: { } |
||||||
|
}; |
||||||
|
|
||||||
|
req.clean['point.lon'] = 21.212121; |
||||||
|
req.clean['point.lat'] = 12.121212; |
||||||
|
|
||||||
|
t.equals(pointInPolygon.getUrl(req), 'http://localhost:1234/21.212121/12.121212'); |
||||||
|
t.end(); |
||||||
|
|
||||||
|
}); |
||||||
|
|
||||||
|
}; |
||||||
|
|
||||||
|
module.exports.all = (tape, common) => { |
||||||
|
function test(name, testFunction) { |
||||||
|
return tape(`SERVICE CONFIGURATION /PointInPolygon ${name}`, testFunction); |
||||||
|
} |
||||||
|
|
||||||
|
for( var testCase in module.exports.tests ){ |
||||||
|
module.exports.tests[testCase](test, common); |
||||||
|
} |
||||||
|
}; |
@ -1,323 +0,0 @@ |
|||||||
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 }, false, (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(); |
|
||||||
}); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
test('service unavailable message should not output centroid when do_not_track is true', (t) => { |
|
||||||
const logger = require('pelias-mock-logger')(); |
|
||||||
|
|
||||||
const service = proxyquire('../../../service/pointinpolygon', { |
|
||||||
'pelias-logger': logger |
|
||||||
})(); |
|
||||||
|
|
||||||
service({ lat: 12.121212, lon: 21.212121 }, true, (err) => { |
|
||||||
t.deepEquals(logger.getWarnMessages(), [ |
|
||||||
'point-in-polygon service disabled' |
|
||||||
]); |
|
||||||
t.equals(err, 'point-in-polygon service disabled, unable to resolve centroid'); |
|
||||||
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}, false, (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(); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
test('do_not_track=true should pass DNT header', (t) => { |
|
||||||
const pipServer = require('express')(); |
|
||||||
pipServer.get('/:lon/:lat', (req, res, next) => { |
|
||||||
t.ok(req.headers.hasOwnProperty('dnt')); |
|
||||||
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}, true, (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(); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
test('do_not_track=false should not pass DNT header', (t) => { |
|
||||||
const pipServer = require('express')(); |
|
||||||
pipServer.get('/:lon/:lat', (req, res, next) => { |
|
||||||
t.notOk(req.headers.hasOwnProperty('dnt')); |
|
||||||
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}, false, (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}`); |
|
||||||
|
|
||||||
const expectedErrorMsg = `http://localhost:${port}/21.212121/12.121212 returned ` + |
|
||||||
`status 200 but with non-JSON response: this is not JSON`; |
|
||||||
|
|
||||||
service({ lat: 12.121212, lon: 21.212121}, false, (err, results) => { |
|
||||||
t.equals(err, expectedErrorMsg); |
|
||||||
t.notOk(results); |
|
||||||
t.ok(logger.isErrorMessage(expectedErrorMsg)); |
|
||||||
t.end(); |
|
||||||
|
|
||||||
server.close(); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
test('server returning 200 & non-JSON body should log sanitized error and return no results when do_not_track', (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}`); |
|
||||||
|
|
||||||
const expectedErrorMsg = `http://localhost:${port}/[removed]/[removed] returned ` + |
|
||||||
`status 200 but with non-JSON response: this is not JSON`; |
|
||||||
|
|
||||||
service({ lat: 12.121212, lon: 21.212121}, true, (err, results) => { |
|
||||||
t.equals(err, expectedErrorMsg); |
|
||||||
t.notOk(results); |
|
||||||
t.ok(logger.isErrorMessage(expectedErrorMsg)); |
|
||||||
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}, false, (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}`); |
|
||||||
|
|
||||||
const expectedErrorMsg = `http://localhost:${port}/21.212121/12.121212 returned ` + |
|
||||||
`status 400: a bad request was made`; |
|
||||||
|
|
||||||
service({ lat: 12.121212, lon: 21.212121}, false, (err, results) => { |
|
||||||
t.equals(err, expectedErrorMsg); |
|
||||||
t.notOk(results); |
|
||||||
t.ok(logger.isErrorMessage(expectedErrorMsg)); |
|
||||||
t.end(); |
|
||||||
|
|
||||||
server.close(); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
}); |
|
||||||
|
|
||||||
test('non-OK status should log sanitized error and return no results when do_not_track=true', (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}`); |
|
||||||
|
|
||||||
const expectedErrorMsg = `http://localhost:${port}/[removed]/[removed] returned ` + |
|
||||||
`status 400: a bad request was made`; |
|
||||||
|
|
||||||
service({ lat: 12.121212, lon: 21.212121}, true, (err, results) => { |
|
||||||
t.equals(err, expectedErrorMsg); |
|
||||||
t.notOk(results); |
|
||||||
t.ok(logger.isErrorMessage(expectedErrorMsg)); |
|
||||||
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