Browse Source

Merge pull request #280 from pelias/spec_conformance

progress commit
improved_bias
Peter Johnson a.k.a. insertcoffee 9 years ago
parent
commit
c5ab64ffbf
  1. 39
      middleware/_types.js
  2. 38
      query/reverse.js
  3. 32
      query/search.js
  4. 26
      sanitiser/_boundary_country.js
  5. 4
      sanitiser/_geo_autocomplete.js
  6. 176
      sanitiser/_geo_common.js
  7. 19
      sanitiser/_geo_reverse.js
  8. 8
      sanitiser/_geo_search.js
  9. 5
      test/ciao/autocomplete/null_island.coffee
  10. 4
      test/ciao/reverse/basic_reverse.coffee
  11. 38
      test/ciao/reverse/boundary_circle_invalid_radius.coffee
  12. 37
      test/ciao/reverse/boundary_circle_valid_radius.coffee
  13. 34
      test/ciao/reverse/boundary_country_invalid_alpha2.coffee
  14. 34
      test/ciao/reverse/boundary_country_invalid_alpha3.coffee
  15. 34
      test/ciao/reverse/boundary_country_invalid_iso3166.coffee
  16. 33
      test/ciao/reverse/boundary_country_valid_alpha2.coffee
  17. 33
      test/ciao/reverse/boundary_country_valid_alpha3.coffee
  18. 35
      test/ciao/reverse/point_invalid_lat.coffee
  19. 35
      test/ciao/reverse/point_invalid_lon.coffee
  20. 35
      test/ciao/reverse/point_missing_lat.coffee
  21. 35
      test/ciao/reverse/point_missing_lon.coffee
  22. 8
      test/ciao/reverse/point_null_island.coffee
  23. 34
      test/ciao/reverse/point_valid_duo.coffee
  24. 33
      test/ciao/reverse/size_over_max.coffee
  25. 33
      test/ciao/reverse/size_under_min.coffee
  26. 32
      test/ciao/reverse/size_valid.coffee
  27. 37
      test/ciao/search/boundary_circle_invalid_lat_lon_types.coffee
  28. 37
      test/ciao/search/boundary_circle_invalid_radius.coffee
  29. 37
      test/ciao/search/boundary_circle_missing_lat.coffee
  30. 37
      test/ciao/search/boundary_circle_missing_lon.coffee
  31. 36
      test/ciao/search/boundary_circle_valid_duo.coffee
  32. 36
      test/ciao/search/boundary_circle_valid_trio.coffee
  33. 35
      test/ciao/search/boundary_country_invalid_alpha2.coffee
  34. 35
      test/ciao/search/boundary_country_invalid_alpha3.coffee
  35. 35
      test/ciao/search/boundary_country_invalid_iso3166.coffee
  36. 34
      test/ciao/search/boundary_country_valid_alpha2.coffee
  37. 34
      test/ciao/search/boundary_country_valid_alpha3.coffee
  38. 38
      test/ciao/search/boundary_rect_partially_specified.coffee
  39. 37
      test/ciao/search/boundary_rect_valid.coffee
  40. 36
      test/ciao/search/focus_point_invalid_lat.coffee
  41. 36
      test/ciao/search/focus_point_invalid_lon.coffee
  42. 36
      test/ciao/search/focus_point_missing_lat.coffee
  43. 36
      test/ciao/search/focus_point_missing_lon.coffee
  44. 6
      test/ciao/search/focus_point_null_island.coffee
  45. 35
      test/ciao/search/focus_point_valid_duo.coffee
  46. 35
      test/ciao/search/layers_alias_address.coffee
  47. 35
      test/ciao/search/layers_alias_coarse.coffee
  48. 36
      test/ciao/search/layers_invalid.coffee
  49. 36
      test/ciao/search/layers_mix_invalid_valid.coffee
  50. 35
      test/ciao/search/layers_multiple.coffee
  51. 35
      test/ciao/search/layers_single.coffee
  52. 34
      test/ciao/search/privacy_false.coffee
  53. 34
      test/ciao/search/privacy_true.coffee
  54. 34
      test/ciao/search/size_over_max.coffee
  55. 34
      test/ciao/search/size_under_min.coffee
  56. 33
      test/ciao/search/size_valid.coffee
  57. 36
      test/ciao/search/sources_invalid.coffee
  58. 37
      test/ciao/search/sources_layers_invalid_combo.coffee
  59. 35
      test/ciao/search/sources_layers_valid_combo.coffee
  60. 35
      test/ciao/search/sources_multiple.coffee
  61. 35
      test/ciao/search/sources_single.coffee
  62. 45
      test/unit/query/reverse.js
  63. 20
      test/unit/query/search.js
  64. 14
      test/unit/sanitiser/_boundary_country.js
  65. 354
      test/unit/sanitiser/_geo_common.js
  66. 11
      test/unit/sanitiser/reverse.js
  67. 68
      test/unit/sanitiser/search.js

39
middleware/_types.js

@ -10,25 +10,34 @@ var types_helper = require( '../helper/types' );
function middleware(req, res, next) {
req.clean = req.clean || {};
if (req.clean.hasOwnProperty('types') === false) {
return next();
}
if (req.clean.hasOwnProperty('types')) {
try {
var types = types_helper(req.clean.types);
if ((types instanceof Array) && types.length === 0) {
var err = 'You have specified both the `sources` and `layers` ' +
'parameters in a combination that will return no results.';
req.errors.push( err );
}
try {
var types = types_helper(req.clean.types);
else {
req.clean.type = types;
}
if ((types instanceof Array) && types.length === 0) {
var err = 'You have specified both the `sources` and `layers` ' +
'parameters in a combination that will return no results.';
res.status(400); // 400 Bad Request
return next(err);
}
req.clean.type = types;
}
catch (err) {
// this means there were no types specified
delete req.clean.types;
// @todo: refactor this flow, it is confusing as `types_helper()` can throw
// with an error "clean_types should not be null or undefined" which is
// not returned to the user yet the return value CAN trigger a user error.
// I would have liked to throw for BOTH cases and then handle the users errors
// inside the 'catch' but this is not possible.
// also: why are we deleting things from $clean?
catch (err) {
// this means there were no types specified
delete req.clean.types;
}
}
next();

38
query/reverse.js

@ -24,7 +24,8 @@ function generateQuery( clean ){
// set defaults
vs.set({
'size': 1
'size': 1,
'boundary:circle:radius': '500km'
});
// set size
@ -32,30 +33,33 @@ function generateQuery( clean ){
vs.var( 'size', clean.size );
}
// set radius, default to 500km if not specified in request
var radius = 500;
if (clean.hasOwnProperty('boundary.circle.radius')) {
radius = clean['boundary.circle.radius'];
}
// focus point centroid
if( check.number(clean['point.lat']) && check.number(clean['point.lon']) ){
// focus point to score by distance
if( check.number(clean['point.lat']) &&
check.number(clean['point.lon']) ){
vs.set({
// focus point to score by distance
'focus:point:lat': clean['point.lat'],
'focus:point:lon': clean['point.lon'],
'focus:point:lon': clean['point.lon']
});
}
// bounding circle
'boundary:circle:lat': clean['point.lat'],
'boundary:circle:lon': clean['point.lon'],
'boundary:circle:radius': radius + 'km'
// bounding circle
// note: the sanitizers will take care of the case
// where point.lan/point.lon are provided in the
// absense of boundary.circle.lat/boundary.circle.lon
if( check.number(clean['boundary.circle.lat']) &&
check.number(clean['boundary.circle.lon']) &&
check.number(clean['boundary.circle.radius']) ){
vs.set({
'boundary:circle:lat': clean['boundary.circle.lat'],
'boundary:circle:lon': clean['boundary.circle.lon'],
'boundary:circle:radius': clean['boundary.circle.radius'] + 'km'
});
}
// boundary country
if( clean.boundary && clean.boundary.country ){
if( check.string(clean['boundary.country']) ){
vs.set({
'boundary:country': clean.boundary.country
'boundary:country': clean['boundary.country']
});
}

32
query/search.js

@ -59,7 +59,8 @@ function generateQuery( clean ){
}
// focus point
if( check.number(clean['focus.point.lat']) && check.number(clean['focus.point.lon']) ){
if( check.number(clean['focus.point.lat']) &&
check.number(clean['focus.point.lon']) ){
vs.set({
'focus:point:lat': clean['focus.point.lat'],
'focus:point:lon': clean['focus.point.lon']
@ -78,27 +79,36 @@ function generateQuery( clean ){
}
// boundary rect
if( clean.bbox ){
if( check.number(clean['boundary.rect.min_lat']) &&
check.number(clean['boundary.rect.max_lat']) &&
check.number(clean['boundary.rect.min_lon']) &&
check.number(clean['boundary.rect.max_lon']) ){
vs.set({
'boundary:rect:top': clean.bbox.top,
'boundary:rect:right': clean.bbox.right,
'boundary:rect:bottom': clean.bbox.bottom,
'boundary:rect:left': clean.bbox.left
'boundary:rect:top': clean['boundary.rect.min_lat'],
'boundary:rect:right': clean['boundary.rect.max_lon'],
'boundary:rect:bottom': clean['boundary.rect.max_lat'],
'boundary:rect:left': clean['boundary.rect.min_lon']
});
}
// boundary circle
// @todo: change these to the correct request variable names
if( clean.boundary && clean.boundary.circle ){
if( check.number(clean['boundary.circle.lat']) &&
check.number(clean['boundary.circle.lon']) ){
vs.set({
'boundary:circle:lat': clean.boundary.circle.lat,
'boundary:circle:lon': clean.boundary.circle.lon,
'boundary:circle:radius': clean.boundary.circle.radius + 'm'
'boundary:circle:lat': clean['boundary.circle.lat'],
'boundary:circle:lon': clean['boundary.circle.lon']
});
if( check.number(clean['boundary.circle.radius']) ){
vs.set({
'boundary:circle:radius': Math.round( clean['boundary.circle.radius'] ) + 'km'
});
}
}
// boundary country
if( clean['boundary.country'] ){
if( check.string(clean['boundary.country']) ){
vs.set({
'boundary:country': clean['boundary.country']
});

26
sanitiser/_boundary_country.js

@ -5,37 +5,37 @@ function sanitize(raw, clean) {
// error & warning messages
var messages = { errors: [], warnings: [] };
// init clean.boundary (if not already init)
clean.boundary = clean.boundary || {};
// target input param
var country = raw['boundary.country'];
if (check.assigned(raw['boundary.country'])) {
var country = raw['boundary.country'];
// param 'boundary.country' is optional and should not
// error when simply not set by the user
if (check.assigned(country)){
if (!check.string(country)) {
// must be valid string
if (!check.unemptyString(country)) {
messages.errors.push('boundary.country is not a string');
delete clean.boundary.country;
}
// must be a valid ISO 3166 code
else if (!containsIsoCode(country.toUpperCase())) {
messages.errors.push(country + ' is not a valid ISO2/ISO3 country code');
delete clean.boundary.country;
}
// valid ISO 3166 country code, set alpha3 code on 'clean.boundary.country'
else {
// the only way for boundary.country to be assigned is if input is
// a string and a known ISO2 or ISO3
clean.boundary.country = iso3166.to3(country.toUpperCase());
clean['boundary.country'] = iso3166.to3(country.toUpperCase());
}
} else {
delete clean.boundary.country;
}
return messages;
}
function containsIsoCode(isoCode) {
return iso3166.list().some(function(row) {
return row.alpha2 === isoCode || row.alpha3 === isoCode;
return row.alpha2 === isoCode || row.alpha3 === isoCode;
});
}

4
sanitiser/_geo_autocomplete.js

@ -8,8 +8,8 @@ module.exports = function sanitize( raw, clean ){
var messages = { errors: [], warnings: [] };
try {
geo_common.sanitize_coord( 'lat', clean, raw['focus.point.lat'], LAT_LON_IS_REQUIRED );
geo_common.sanitize_coord( 'lon', clean, raw['focus.point.lon'], LAT_LON_IS_REQUIRED );
geo_common.sanitize_coord( 'focus.point.lat', clean, raw['focus.point.lat'], LAT_LON_IS_REQUIRED );
geo_common.sanitize_coord( 'focus.point.lon', clean, raw['focus.point.lon'], LAT_LON_IS_REQUIRED );
}
catch (err) {
messages.errors.push( err.message );

176
sanitiser/_geo_common.js

@ -5,89 +5,157 @@ var util = require('util'),
check = require('check-types');
/**
* Parse and validate bbox parameter
* bbox = bottom_left lon, bottom_left lat, top_right lon, top_right lat
* bbox = left, bottom, right, top
* bbox = min Longitude, min Latitude, max Longitude, max Latitude
* Parse and validate rect parameter
*
* @param {object} raw
* @param {string} key_prefix
* @param {object} clean
* @param {object} raw
* @param {bool} bbox_is_required
*/
function sanitize_bbox( raw, clean ) {
if( !check.unemptyString( raw.bbox ) ) {
return;
}
function sanitize_rect( key_prefix, clean, raw, bbox_is_required ) {
var bboxArr = raw.bbox.split( ',' );
// the names we use to define the corners of the rect
var mandatoryProps = [ 'min_lat', 'max_lat', 'min_lon', 'max_lon' ];
if( Array.isArray( bboxArr ) && bboxArr.length === 4 ) {
var bbox = bboxArr.map(parseFloat);
if (bbox.some(isNaN)) {
return;
// count up how many fields the user actually specified
var totalFieldsSpecified = 0;
mandatoryProps.forEach( function( prop ){
if( raw.hasOwnProperty( key_prefix + '.' + prop ) ){
totalFieldsSpecified++;
}
});
clean.bbox = {
right: Math.max( bbox[0], bbox[2] ),
top: Math.max( bbox[1], bbox[3] ),
left: Math.min( bbox[0], bbox[2] ),
bottom: Math.min( bbox[1], bbox[3] )
};
// all fields specified
if( 4 === totalFieldsSpecified ) {
// reuse the coord sanitizer and set required:true so we get a fatal error if
// any one of the corners is not specified.
sanitize_coord( key_prefix + '.min_lat', clean, raw[ key_prefix + '.min_lat' ], true );
sanitize_coord( key_prefix + '.max_lat', clean, raw[ key_prefix + '.max_lat' ], true );
sanitize_coord( key_prefix + '.min_lon', clean, raw[ key_prefix + '.min_lon' ], true );
sanitize_coord( key_prefix + '.max_lon', clean, raw[ key_prefix + '.max_lon' ], true );
}
// fields only partially specified
else if( totalFieldsSpecified > 0 ){
var format1 = 'missing rect param \'%s\' requires all of: \'%s\' to be present';
throw new Error( util.format( format1, key_prefix, mandatoryProps.join('\',\'') ) );
}
// fields required, eg. ( totalFieldsSpecified === 0 && bbox_is_required === true )
else if( bbox_is_required ){
var format2 = 'missing rect param \'%s\' requires all of: \'%s\' to be present';
throw new Error( util.format( format2, key_prefix, mandatoryProps.join('\',\'') ) );
}
}
/**
* Validate lat,lon values
* Parse and validate circle parameter
*
* @param {string} coord lat|lon
* @param {string} key_prefix
* @param {object} clean
* @param {string} param
* @param {bool} latlon_is_required
* @param {object} raw
* @param {bool} circle_is_required
*/
function sanitize_coord( coord, clean, param, latlon_is_required ) {
var value = parseFloat( param );
if ( !isNaN( value ) ) {
clean[coord] = value;
function sanitize_circle( key_prefix, clean, raw, circle_is_required ) {
// the names we use to define the centroid
var mandatoryProps = [ 'lat', 'lon' ];
// count up how many fields the user actually specified
var totalFieldsSpecified = 0;
mandatoryProps.forEach( function( prop ){
if( raw.hasOwnProperty( key_prefix + '.' + prop ) ){
totalFieldsSpecified++;
}
});
// all fields specified
if( 2 === totalFieldsSpecified ) {
// reuse the coord sanitizer and set required:true so we get a fatal error if
// any one of the coords is not specified.
sanitize_coord( key_prefix + '.lat', clean, raw[ key_prefix + '.lat' ], true );
sanitize_coord( key_prefix + '.lon', clean, raw[ key_prefix + '.lon' ], true );
if( check.assigned( raw[ key_prefix + '.radius' ] ) ){
sanitize_coord( key_prefix + '.radius', clean, raw[ key_prefix + '.radius' ], true );
}
}
else if (latlon_is_required) {
throw new Error( util.format( 'missing param \'%s\'', coord ) );
// fields only partially specified
else if( totalFieldsSpecified > 0 ){
var format1 = 'missing circle param \'%s\' requires all of: \'%s\' to be present';
throw new Error( util.format( format1, key_prefix, mandatoryProps.join('\',\'') ) );
}
// radius was specified without lat or lon
else if( raw.hasOwnProperty( key_prefix + '.radius' ) ){
var format2 = 'missing circle param \'%s\' requires all of: \'%s\' to be present';
throw new Error( util.format( format2, key_prefix, mandatoryProps.join('\',\'') ) );
}
// fields required, eg. ( totalFieldsSpecified === 0 && bbox_is_required === true )
else if( circle_is_required ){
var format3 = 'missing circle param \'%s\' requires all of: \'%s\' to be present';
throw new Error( util.format( format3, key_prefix, mandatoryProps.join('\',\'') ) );
}
}
/**
* Validate circle geometry values
* Parse and validate point parameter
*
* @param {string} key_prefix
* @param {object} clean
* @param {object} params
* @param {bool} is_required
* @param {bool} all_required
* @param {object} raw
* @param {bool} point_is_required
*/
function sanitize_boundary_circle( clean, params, is_required, all_required ) {
var props = {
lat: 'boundary.circle.lat',
lon: 'boundary.circle.lon',
rad: 'boundary.circle.radius'
};
function sanitize_point( key_prefix, clean, raw, point_is_required ) {
// get values for each property
sanitize_coord(props.lat, clean, params['boundary.circle.lat'], all_required && is_required);
sanitize_coord(props.lon, clean, params['boundary.circle.lon'], all_required && is_required);
sanitize_coord(props.rad, clean, params['boundary.circle.radius'], all_required && is_required);
// the names we use to define the point
var mandatoryProps = [ 'lat', 'lon' ];
// count up how many fields the user actually specified
var totalFieldsSpecified = 0;
mandatoryProps.forEach( function( prop ){
if( raw.hasOwnProperty( key_prefix + '.' + prop ) ){
totalFieldsSpecified++;
}
});
// if all are required, check if some are set but not all, throw an error if missing
if (all_required &&
(clean.hasOwnProperty(props.lat) || clean.hasOwnProperty(props.lon) || clean.hasOwnProperty(props.rad)) &&
!(clean.hasOwnProperty(props.lat) && clean.hasOwnProperty(props.lon) && clean.hasOwnProperty(props.rad))) {
throw new Error('missing part of circle: needs lat,lon,radius');
// all fields specified
if( 2 === totalFieldsSpecified ) {
// reuse the coord sanitizer and set required:true so we get a fatal error if
// any one of the coords is not specified.
sanitize_coord( key_prefix + '.lat', clean, raw[ key_prefix + '.lat' ], true );
sanitize_coord( key_prefix + '.lon', clean, raw[ key_prefix + '.lon' ], true );
}
// fields only partially specified
else if( totalFieldsSpecified > 0 ){
var format1 = 'missing point param \'%s\' requires all of: \'%s\' to be present';
throw new Error( util.format( format1, key_prefix, mandatoryProps.join('\',\'') ) );
}
// fields required, eg. ( totalFieldsSpecified === 0 && bbox_is_required === true )
else if( point_is_required ){
var format2 = 'missing point param \'%s\' requires all of: \'%s\' to be present';
throw new Error( util.format( format2, key_prefix, mandatoryProps.join('\',\'') ) );
}
}
if (is_required && !(clean.hasOwnProperty(props.lat) || clean.hasOwnProperty(props.lon) || clean.hasOwnProperty(props.rad))) {
throw new Error('missing param boundary.circle: should be a trio of lat,lon,radius');
/**
* Validate lat,lon values
*
* @param {string} key
* @param {object} clean
* @param {string} param
* @param {bool} latlon_is_required
*/
function sanitize_coord( key, clean, param, latlon_is_required ) {
var value = parseFloat( param );
if ( !isNaN( value ) ) {
clean[key] = value;
}
else if (latlon_is_required) {
throw new Error( util.format( 'missing param \'%s\'', key ) );
}
}
module.exports = {
sanitize_bbox: sanitize_bbox,
sanitize_rect: sanitize_rect,
sanitize_coord: sanitize_coord,
sanitize_boundary_circle: sanitize_boundary_circle
sanitize_circle: sanitize_circle,
sanitize_point: sanitize_point
};

19
sanitiser/_geo_reverse.js

@ -11,19 +11,18 @@ module.exports = function sanitize( raw, clean ){
var messages = { errors: [], warnings: [] };
try {
geo_common.sanitize_coord( 'point.lat', clean, raw['point.lat'], LAT_LON_IS_REQUIRED );
geo_common.sanitize_coord( 'point.lon', clean, raw['point.lon'], LAT_LON_IS_REQUIRED );
geo_common.sanitize_point( 'point', clean, raw, LAT_LON_IS_REQUIRED );
// remove both if only one is set
// @todo: clean this up!
if( !clean.hasOwnProperty('point.lat') || !clean.hasOwnProperty('point.lon') ){
delete clean['point.lat'];
delete clean['point.lon'];
// this hack is to allow point.lat/point.lon to be used interchanagbly
// with boundary.circle.lat/boundary.circle/lon
if( !clean.hasOwnProperty('boundary.circle.lat') && clean.hasOwnProperty('point.lat') ){
raw['boundary.circle.lat'] = clean['point.lat'];
}
if( !clean.hasOwnProperty('boundary.circle.lon') && clean.hasOwnProperty('point.lon') ){
raw['boundary.circle.lon'] = clean['point.lon'];
}
// boundary.circle.* is not mandatory, and only specifying radius is fine,
// as point.lat/lon will be used to fill those values by default
geo_common.sanitize_boundary_circle( clean, raw, CIRCLE_IS_REQUIRED, CIRCLE_MUST_BE_COMPLETE);
geo_common.sanitize_circle( 'boundary.circle', clean, raw, CIRCLE_IS_REQUIRED );
}
catch (err) {
messages.errors.push( err.message );

8
sanitiser/_geo_search.js

@ -1,5 +1,7 @@
var geo_common = require ('./_geo_common');
var LAT_LON_IS_REQUIRED = false;
var RECT_IS_REQUIRED = false;
var CIRCLE_IS_REQUIRED = false;
// validate inputs, convert types and apply defaults
module.exports = function sanitize( raw, clean ){
@ -8,9 +10,9 @@ module.exports = function sanitize( raw, clean ){
var messages = { errors: [], warnings: [] };
try {
geo_common.sanitize_coord( 'focus.point.lat', clean, raw['focus.point.lat'], LAT_LON_IS_REQUIRED );
geo_common.sanitize_coord( 'focus.point.lon', clean, raw['focus.point.lon'], LAT_LON_IS_REQUIRED );
geo_common.sanitize_bbox(raw, clean);
geo_common.sanitize_point( 'focus.point', clean, raw, LAT_LON_IS_REQUIRED );
geo_common.sanitize_rect( 'boundary.rect', clean, raw, RECT_IS_REQUIRED );
geo_common.sanitize_circle( 'boundary.circle', clean, raw, CIRCLE_IS_REQUIRED );
}
catch (err) {
messages.errors.push( err.message );

5
test/ciao/autocomplete/null_island.coffee

@ -29,6 +29,7 @@ should.not.exist json.geocoding.errors
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['lat'].should.eql 0
json.geocoding.query['lon'].should.eql 0
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['focus.point.lat'].should.eql 0
json.geocoding.query['focus.point.lon'].should.eql 0
json.geocoding.query['size'].should.eql 10

4
test/ciao/reverse/basic_reverse.coffee

@ -29,6 +29,6 @@ should.not.exist json.geocoding.errors
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['lat'].should.eql 1
json.geocoding.query['lon'].should.eql 2
json.geocoding.query['point.lat'].should.eql 1
json.geocoding.query['point.lon'].should.eql 2
json.geocoding.query['size'].should.eql 10

38
test/ciao/reverse/boundary_circle_invalid_radius.coffee

@ -0,0 +1,38 @@
#> bounding circle
path: '/v1/reverse?point.lat=40.744243&point.lon=-73.990342&boundary.circle.radius=foo'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing param \'boundary.circle.radius\'' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
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
should.not.exist json.geocoding.query['boundary.circle.radius']

37
test/ciao/reverse/boundary_circle_valid_radius.coffee

@ -0,0 +1,37 @@
#> bounding circle
path: '/v1/reverse?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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
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

34
test/ciao/reverse/boundary_country_invalid_alpha2.coffee

@ -0,0 +1,34 @@
#> bounding country
path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=ZZ'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'ZZ is not a valid ISO2/ISO3 country code' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.country']

34
test/ciao/reverse/boundary_country_invalid_alpha3.coffee

@ -0,0 +1,34 @@
#> bounding country
path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=ZZZ'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'ZZZ is not a valid ISO2/ISO3 country code' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.country']

34
test/ciao/reverse/boundary_country_invalid_iso3166.coffee

@ -0,0 +1,34 @@
#> bounding country
path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=FOOBAR'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'FOOBAR is not a valid ISO2/ISO3 country code' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.country']

33
test/ciao/reverse/boundary_country_valid_alpha2.coffee

@ -0,0 +1,33 @@
#> bounding country
path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=US'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
json.geocoding.query['boundary.country'].should.eql 'USA'

33
test/ciao/reverse/boundary_country_valid_alpha3.coffee

@ -0,0 +1,33 @@
#> bounding country
path: '/v1/reverse?point.lat=1&point.lon=1&boundary.country=USA'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
json.geocoding.query['boundary.country'].should.eql 'USA'

35
test/ciao/reverse/point_invalid_lat.coffee

@ -0,0 +1,35 @@
#> point
path: '/v1/reverse?point.lat=foo&point.lon=-73.990342'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing param \'point.lat\'' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['point.lat']
should.not.exist json.geocoding.query['point.lon']

35
test/ciao/reverse/point_invalid_lon.coffee

@ -0,0 +1,35 @@
#> point
path: '/v1/reverse?point.lat=40.744243&point.lon='
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing param \'point.lon\'' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
json.geocoding.query['point.lat'].should.eql 40.744243
should.not.exist json.geocoding.query['point.lon']

35
test/ciao/reverse/point_missing_lat.coffee

@ -0,0 +1,35 @@
#> point
path: '/v1/reverse?point.lon=-73.990342'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing point param \'point\' requires all of: \'lat\',\'lon\' to be present' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['point.lat']
should.not.exist json.geocoding.query['point.lon']

35
test/ciao/reverse/point_missing_lon.coffee

@ -0,0 +1,35 @@
#> point
path: '/v1/reverse?point.lat=40.744243'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing point param \'point\' requires all of: \'lat\',\'lon\' to be present' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['point.lat']
should.not.exist json.geocoding.query['point.lon']

8
test/ciao/reverse/null_island.coffee → test/ciao/reverse/point_null_island.coffee

@ -1,5 +1,5 @@
#> null island
#> point null island
path: '/v1/reverse?point.lat=0&point.lon=0'
#? 200 ok
@ -29,6 +29,6 @@ should.not.exist json.geocoding.errors
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['lat'].should.eql 0
json.geocoding.query['lon'].should.eql 0
json.geocoding.query['size'].should.eql 10
json.geocoding.query['size'].should.eql 10
json.geocoding.query['point.lat'].should.eql 0
json.geocoding.query['point.lon'].should.eql 0

34
test/ciao/reverse/point_valid_duo.coffee

@ -0,0 +1,34 @@
#> point
path: '/v1/reverse?point.lat=40.744243&point.lon=-73.990342'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 10
json.geocoding.query['point.lat'].should.eql 40.744243
json.geocoding.query['point.lon'].should.eql -73.990342

33
test/ciao/reverse/size_over_max.coffee

@ -0,0 +1,33 @@
#> set size
path: '/v1/reverse?point.lat=1&point.lon=1&size=999'
#? 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 [ 'out-of-range integer \'size\', using MAX_SIZE' ]
#? inputs
json.geocoding.query['size'].should.eql 40

33
test/ciao/reverse/size_under_min.coffee

@ -0,0 +1,33 @@
#> set size
path: '/v1/reverse?point.lat=1&point.lon=1&size=0'
#? 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 [ 'out-of-range integer \'size\', using MIN_SIZE' ]
#? inputs
json.geocoding.query['size'].should.eql 1

32
test/ciao/reverse/size_valid.coffee

@ -0,0 +1,32 @@
#> set size
path: '/v1/reverse?point.lat=1&point.lon=1&size=3'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['size'].should.eql 3

37
test/ciao/search/boundary_circle_invalid_lat_lon_types.coffee

@ -0,0 +1,37 @@
#> bounding circle
path: '/v1/search?text=a&boundary.circle.lat=foo&boundary.circle.lon=bar'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing param \'boundary.circle.lat\'' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.circle.lat']
should.not.exist json.geocoding.query['boundary.circle.lon']
should.not.exist json.geocoding.query['boundary.circle.radius']

37
test/ciao/search/boundary_circle_invalid_radius.coffee

@ -0,0 +1,37 @@
#> bounding circle
path: '/v1/search?text=a&boundary.circle.lat=40.744243&boundary.circle.lon=-73.990342&boundary.circle.radius=foo'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing param \'boundary.circle.radius\'' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['boundary.circle.lat'].should.eql 40.744243
json.geocoding.query['boundary.circle.lon'].should.eql -73.990342
should.not.exist json.geocoding.query['boundary.circle.radius']

37
test/ciao/search/boundary_circle_missing_lat.coffee

@ -0,0 +1,37 @@
#> bounding circle
path: '/v1/search?text=a&boundary.circle.lon=-73.990342&boundary.circle.radius=100'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing circle param \'boundary.circle\' requires all of: \'lat\',\'lon\' to be present' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.circle.lat']
should.not.exist json.geocoding.query['boundary.circle.lon']
should.not.exist json.geocoding.query['boundary.circle.radius']

37
test/ciao/search/boundary_circle_missing_lon.coffee

@ -0,0 +1,37 @@
#> bounding circle
path: '/v1/search?text=a&boundary.circle.lat=40.744243&boundary.circle.radius=100'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing circle param \'boundary.circle\' requires all of: \'lat\',\'lon\' to be present' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.circle.lat']
should.not.exist json.geocoding.query['boundary.circle.lon']
should.not.exist json.geocoding.query['boundary.circle.radius']

36
test/ciao/search/boundary_circle_valid_duo.coffee

@ -0,0 +1,36 @@
#> bounding circle
path: '/v1/search?text=a&boundary.circle.lat=40.744243&boundary.circle.lon=-73.990342'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['boundary.circle.lat'].should.eql 40.744243
json.geocoding.query['boundary.circle.lon'].should.eql -73.990342
should.not.exist json.geocoding.query['boundary.circle.radius']

36
test/ciao/search/boundary_circle_valid_trio.coffee

@ -0,0 +1,36 @@
#> bounding circle
path: '/v1/search?text=a&boundary.circle.lat=40.744243&boundary.circle.lon=-73.990342&boundary.circle.radius=100'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
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 100

35
test/ciao/search/boundary_country_invalid_alpha2.coffee

@ -0,0 +1,35 @@
#> bounding country
path: '/v1/search?text=a&boundary.country=ZZ'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'ZZ is not a valid ISO2/ISO3 country code' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.country']

35
test/ciao/search/boundary_country_invalid_alpha3.coffee

@ -0,0 +1,35 @@
#> bounding country
path: '/v1/search?text=a&boundary.country=ZZZ'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'ZZZ is not a valid ISO2/ISO3 country code' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.country']

35
test/ciao/search/boundary_country_invalid_iso3166.coffee

@ -0,0 +1,35 @@
#> bounding country
path: '/v1/search?text=a&boundary.country=FOOBAR'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'FOOBAR is not a valid ISO2/ISO3 country code' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.country']

34
test/ciao/search/boundary_country_valid_alpha2.coffee

@ -0,0 +1,34 @@
#> bounding country
path: '/v1/search?text=a&boundary.country=US'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['boundary.country'].should.eql 'USA'

34
test/ciao/search/boundary_country_valid_alpha3.coffee

@ -0,0 +1,34 @@
#> bounding country
path: '/v1/search?text=a&boundary.country=USA'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['boundary.country'].should.eql 'USA'

38
test/ciao/search/boundary_rect_partially_specified.coffee

@ -0,0 +1,38 @@
#> bounding rectangle
path: '/v1/search?text=a&boundary.rect.min_lat=-40.659'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing rect param \'boundary.rect\' requires all of: \'min_lat\',\'max_lat\',\'min_lon\',\'max_lon\' to be present' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['boundary.rect.min_lat']
should.not.exist json.geocoding.query['boundary.rect.max_lat']
should.not.exist json.geocoding.query['boundary.rect.min_lon']
should.not.exist json.geocoding.query['boundary.rect.max_lon']

37
test/ciao/search/boundary_rect_valid.coffee

@ -0,0 +1,37 @@
#> bounding rectangle
path: '/v1/search?text=a&boundary.rect.min_lat=-40.659&boundary.rect.max_lat=-41.614&boundary.rect.min_lon=174.612&boundary.rect.max_lon=176.333'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['boundary.rect.min_lat'].should.eql -40.659
json.geocoding.query['boundary.rect.max_lat'].should.eql -41.614
json.geocoding.query['boundary.rect.min_lon'].should.eql 174.612
json.geocoding.query['boundary.rect.max_lon'].should.eql 176.333

36
test/ciao/search/focus_point_invalid_lat.coffee

@ -0,0 +1,36 @@
#> focus point
path: '/v1/search?text=a&focus.point.lat=foo&focus.point.lon=-73.990342'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing param \'focus.point.lat\'' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['focus.point.lat']
should.not.exist json.geocoding.query['focus.point.lon']

36
test/ciao/search/focus_point_invalid_lon.coffee

@ -0,0 +1,36 @@
#> focus point
path: '/v1/search?text=a&focus.point.lat=40.744243&focus.point.lon='
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing param \'focus.point.lon\'' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['focus.point.lat'].should.eql 40.744243
should.not.exist json.geocoding.query['focus.point.lon']

36
test/ciao/search/focus_point_missing_lat.coffee

@ -0,0 +1,36 @@
#> focus point
path: '/v1/search?text=a&focus.point.lon=-73.990342'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing point param \'focus.point\' requires all of: \'lat\',\'lon\' to be present' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['focus.point.lat']
should.not.exist json.geocoding.query['focus.point.lon']

36
test/ciao/search/focus_point_missing_lon.coffee

@ -0,0 +1,36 @@
#> focus point
path: '/v1/search?text=a&focus.point.lat=40.744243'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'missing point param \'focus.point\' requires all of: \'lat\',\'lon\' to be present' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['focus.point.lat']
should.not.exist json.geocoding.query['focus.point.lon']

6
test/ciao/search/null_island.coffee → test/ciao/search/focus_point_null_island.coffee

@ -1,5 +1,5 @@
#> null island
#> focus point null island
path: '/v1/search?text=a&focus.point.lat=0&focus.point.lon=0'
#? 200 ok
@ -31,5 +31,5 @@ should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['lat'].should.eql 0
json.geocoding.query['lon'].should.eql 0
json.geocoding.query['focus.point.lat'].should.eql 0
json.geocoding.query['focus.point.lon'].should.eql 0

35
test/ciao/search/focus_point_valid_duo.coffee

@ -0,0 +1,35 @@
#> focus point
path: '/v1/search?text=a&focus.point.lat=40.744243&focus.point.lon=-73.990342'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['focus.point.lat'].should.eql 40.744243
json.geocoding.query['focus.point.lon'].should.eql -73.990342

35
test/ciao/search/layers_alias_address.coffee

@ -0,0 +1,35 @@
#> layer alias
path: '/v1/search?text=a&layers=address'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query.types['from_layers'].should.eql ["osmaddress","openaddresses"]
json.geocoding.query['type'].should.eql ["osmaddress","openaddresses"]

35
test/ciao/search/layers_alias_coarse.coffee

@ -0,0 +1,35 @@
#> layer alias
path: '/v1/search?text=a&layers=coarse'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query.types['from_layers'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"]
json.geocoding.query['type'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"]

36
test/ciao/search/layers_invalid.coffee

@ -0,0 +1,36 @@
#> layer alias
path: '/v1/search?text=a&layers=notlayer'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: venue,address,country,region,county,locality,localadmin,neighbourhood,coarse' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['types']
should.not.exist json.geocoding.query['type']

36
test/ciao/search/layers_mix_invalid_valid.coffee

@ -0,0 +1,36 @@
#> layer alias
path: '/v1/search?text=a&layers=country,notlayer'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: venue,address,country,region,county,locality,localadmin,neighbourhood,coarse' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['types']
should.not.exist json.geocoding.query['type']

35
test/ciao/search/layers_multiple.coffee

@ -0,0 +1,35 @@
#> layer alias
path: '/v1/search?text=a&layers=country,region'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query.types['from_layers'].should.eql ["admin0","admin1"]
json.geocoding.query['type'].should.eql ["admin0","admin1"]

35
test/ciao/search/layers_single.coffee

@ -0,0 +1,35 @@
#> layer alias
path: '/v1/search?text=a&layers=country'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query.types['from_layers'].should.eql ["admin0"]
json.geocoding.query['type'].should.eql ["admin0"]

34
test/ciao/search/privacy_false.coffee

@ -0,0 +1,34 @@
#> accept privacy var
path: '/v1/search?text=a&private=false'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['private'].should.eql false

34
test/ciao/search/privacy_true.coffee

@ -0,0 +1,34 @@
#> accept privacy var
path: '/v1/search?text=a&private=true'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query['private'].should.eql true

34
test/ciao/search/size_over_max.coffee

@ -0,0 +1,34 @@
#> set size
path: '/v1/search?text=a&size=999'
#? 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 [ 'out-of-range integer \'size\', using MAX_SIZE' ]
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 40

34
test/ciao/search/size_under_min.coffee

@ -0,0 +1,34 @@
#> set size
path: '/v1/search?text=a&size=0'
#? 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 [ 'out-of-range integer \'size\', using MIN_SIZE' ]
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 1

33
test/ciao/search/size_valid.coffee

@ -0,0 +1,33 @@
#> set size
path: '/v1/search?text=a&size=3'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 3

36
test/ciao/search/sources_invalid.coffee

@ -0,0 +1,36 @@
#> sources filter
path: '/v1/search?text=a&sources=openstreetmap,notasource'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ '\'notasource\' is an invalid sources parameter. Valid options: gn,geonames,oa,openaddresses,qs,quattroshapes,osm,openstreetmap' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
should.not.exist json.geocoding.query['types']
should.not.exist json.geocoding.query['type']

37
test/ciao/search/sources_layers_invalid_combo.coffee

@ -0,0 +1,37 @@
#> sources and layers specified (invalid combo)
path: '/v1/search?text=a&sources=quattroshapes&layers=address'
#? 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.exist json.geocoding.errors
json.geocoding.errors.should.eql [ 'You have specified both the `sources` and `layers` parameters in a combination that will return no results.' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query.types['from_layers'].should.eql ["osmaddress","openaddresses"]
json.geocoding.query.types['from_sources'].should.eql ["admin0","admin1","admin2","neighborhood","locality","local_admin"]
should.not.exist json.geocoding.query['type']

35
test/ciao/search/sources_layers_valid_combo.coffee

@ -0,0 +1,35 @@
#> sources and layers specified
path: '/v1/search?text=a&sources=openaddresses&layers=address'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query.types['from_layers'].should.eql ["osmaddress","openaddresses"]
json.geocoding.query['type'].should.eql ["openaddresses"]

35
test/ciao/search/sources_multiple.coffee

@ -0,0 +1,35 @@
#> sources filter
path: '/v1/search?text=a&sources=openstreetmap,geonames'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query.types['from_sources'].should.eql ["osmaddress","osmnode","osmway","geoname"]
json.geocoding.query['type'].should.eql ["osmaddress","osmnode","osmway","geoname"]

35
test/ciao/search/sources_single.coffee

@ -0,0 +1,35 @@
#> sources filter
path: '/v1/search?text=a&sources=openstreetmap'
#? 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.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10
json.geocoding.query.types['from_sources'].should.eql ["osmaddress","osmnode","osmway"]
json.geocoding.query['type'].should.eql ["osmaddress","osmnode","osmway"]

45
test/unit/query/reverse.js

@ -12,7 +12,11 @@ module.exports.tests.interface = function(test, common) {
module.exports.tests.query = function(test, common) {
test('valid query', function(t) {
var query = generate({
'point.lat': 29.49136, 'point.lon': -82.50622
'point.lat': 29.49136,
'point.lon': -82.50622,
'boundary.circle.lat': 29.49136,
'boundary.circle.lon': -82.50622,
'boundary.circle.radius': 500
});
var compiled = JSON.parse( JSON.stringify( query ) );
@ -22,9 +26,13 @@ module.exports.tests.query = function(test, common) {
t.end();
});
test('valid query', function(t) {
test('valid query - null island', function(t) {
var query = generate({
'point.lat': 0, 'point.lon': 0
'point.lat': 0,
'point.lon': 0,
'boundary.circle.lat': 0,
'boundary.circle.lon': 0,
'boundary.circle.radius': 500
});
var compiled = JSON.parse( JSON.stringify( query ) );
@ -36,29 +44,36 @@ module.exports.tests.query = function(test, common) {
test('valid query with radius', function(t) {
var query = generate({
'point.lat': 29.49136, 'point.lon': -82.50622, 'boundary.circle.radius': 123
'point.lat': 29.49136,
'point.lon': -82.50622,
'boundary.circle.lat': 29.49136,
'boundary.circle.lon': -82.50622,
'boundary.circle.radius': 123
});
var compiled = JSON.parse( JSON.stringify( query )).query.filtered.filter.bool.must[0].geo_distance.distance;
var compiled = JSON.parse( JSON.stringify( query ) );
var expected = '123km';
t.deepEqual(compiled, expected, 'distance set to boundary circle radius');
t.deepEqual(compiled.query.filtered.filter.bool.must[0].geo_distance.distance, expected, 'distance set to boundary circle radius');
t.end();
});
test('valid query with boundary.circle lat/lon/radius', function(t) {
test('boundary.circle lat/lon/radius - overrides point.lat/lon when set', function(t) {
var clean = {
'point.lat': 29.49136,
'point.lon': -82.50622,
'boundary.circle.lat': 111,
'boundary.circle.long': 333
'boundary.circle.lon': 333,
'boundary.circle.radius': 500
};
var query = generate(clean);
var compiled = JSON.parse( JSON.stringify( query ) );
var compiled = JSON.parse( JSON.stringify( query )).query.filtered.filter.bool.must[0].geo_distance.center_point;
var expected = { lat: clean['point.lat'], lon: clean['point.lon'] };
// this should not equal `point.lat` and `point.lon` as it was explitely specified
var expected = { lat: clean['boundary.circle.lat'], lon: clean['boundary.circle.lon'] };
var centroid = compiled.query.filtered.filter.bool.must[0].geo_distance.center_point;
t.deepEqual(compiled, expected, 'point.lat/lon overrides boundary.circle.lat/lon');
t.deepEqual(centroid, expected, 'boundary.circle/lon overrides point.lat/lon');
t.end();
});
@ -78,8 +93,12 @@ module.exports.tests.query = function(test, common) {
test('valid boundary.country reverse search', function(t) {
var query = generate({
'point.lat': 29.49136, 'point.lon': -82.50622,
boundary: { country: 'ABC' }
'point.lat': 29.49136,
'point.lon': -82.50622,
'boundary.circle.lat': 29.49136,
'boundary.circle.lon': -82.50622,
'boundary.circle.radius': 500,
'boundary.country': 'ABC'
});
var compiled = JSON.parse( JSON.stringify( query ) );

20
test/unit/query/search.js

@ -17,12 +17,10 @@ module.exports.tests.query = function(test, common) {
var query = generate({
text: 'test', size: 10,
'focus.point.lat': 29.49136, 'focus.point.lon': -82.50622,
bbox: {
top: 47.47,
right: -61.84,
bottom: 11.51,
left: -103.16
},
'boundary.rect.min_lat': 47.47,
'boundary.rect.max_lon': -61.84,
'boundary.rect.max_lat': 11.51,
'boundary.rect.min_lon': -103.16,
layers: ['test']
});
@ -37,12 +35,10 @@ module.exports.tests.query = function(test, common) {
test('valid search + bbox', function(t) {
var query = generate({
text: 'test', size: 10,
bbox: {
top: 47.47,
right: -61.84,
bottom: 11.51,
left: -103.16
},
'boundary.rect.min_lat': 47.47,
'boundary.rect.max_lon': -61.84,
'boundary.rect.max_lat': 11.51,
'boundary.rect.min_lon': -103.16,
layers: ['test']
});

14
test/unit/sanitiser/_boundary_country.js

@ -7,7 +7,7 @@ module.exports.tests.sanitize_boundary_country = function(test, common) {
var raw = { };
var clean = {};
var errorsAndWarnings = sanitize(raw, clean);
t.equals(clean.boundary.country, undefined, 'should be undefined');
t.equals(clean['boundary.country'], undefined, 'should be undefined');
t.deepEquals(errorsAndWarnings, { errors: [], warnings: [] }, 'no warnings or errors');
t.end();
});
@ -16,7 +16,7 @@ module.exports.tests.sanitize_boundary_country = function(test, common) {
var raw = { 'boundary.country': undefined };
var clean = {};
var errorsAndWarnings = sanitize(raw, clean);
t.equals(clean.boundary.country, undefined, 'should be undefined');
t.equals(clean['boundary.country'], undefined, 'should be undefined');
t.deepEquals(errorsAndWarnings, { errors: [], warnings: [] }, 'no warnings or errors');
t.end();
});
@ -25,7 +25,7 @@ module.exports.tests.sanitize_boundary_country = function(test, common) {
var raw = { 'boundary.country': ['this isn\'t a string primitive'] };
var clean = {};
var errorsAndWarnings = sanitize(raw, clean);
t.equals(clean.boundary.country, undefined, 'should be undefined');
t.equals(clean['boundary.country'], undefined, 'should be undefined');
t.deepEquals(errorsAndWarnings, { errors: ['boundary.country is not a string'], warnings: [] }, 'non-string country warning');
t.end();
});
@ -34,7 +34,7 @@ module.exports.tests.sanitize_boundary_country = function(test, common) {
var raw = { 'boundary.country': 'aq' };
var clean = {};
var errorsAndWarnings = sanitize(raw, clean);
t.equals(clean.boundary.country, 'ATA', 'should be uppercased ISO3');
t.equals(clean['boundary.country'], 'ATA', 'should be uppercased ISO3');
t.deepEquals(errorsAndWarnings, { errors: [], warnings: [] }, 'no warnings or errors');
t.end();
});
@ -43,7 +43,7 @@ module.exports.tests.sanitize_boundary_country = function(test, common) {
var raw = { 'boundary.country': 'aTa' };
var clean = {};
var errorsAndWarnings = sanitize(raw, clean);
t.equals(clean.boundary.country, 'ATA', 'should be uppercased ISO3');
t.equals(clean['boundary.country'], 'ATA', 'should be uppercased ISO3');
t.deepEquals(errorsAndWarnings, { errors: [], warnings: [] }, 'no warnings or errors');
t.end();
});
@ -52,7 +52,7 @@ module.exports.tests.sanitize_boundary_country = function(test, common) {
var raw = { 'boundary.country': 'zq' };
var clean = {};
var errorsAndWarnings = sanitize(raw, clean);
t.equals(clean.boundary.country, undefined, 'should be undefined');
t.equals(clean['boundary.country'], undefined, 'should be undefined');
t.deepEquals(errorsAndWarnings, { errors: ['zq is not a valid ISO2/ISO3 country code'], warnings: [] }, 'country not found warning`');
t.end();
});
@ -61,7 +61,7 @@ module.exports.tests.sanitize_boundary_country = function(test, common) {
var raw = { 'boundary.country': 'zqx' };
var clean = {};
var errorsAndWarnings = sanitize(raw, clean);
t.equals(clean.boundary.country, undefined, 'should be undefined');
t.equals(clean['boundary.country'], undefined, 'should be undefined');
t.deepEquals(errorsAndWarnings, { errors: ['zqx is not a valid ISO2/ISO3 country code'], warnings: [] }, 'country not found warning`');
t.end();
});

354
test/unit/sanitiser/_geo_common.js

@ -4,14 +4,212 @@ module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('valid interface', function(t) {
t.equal(typeof sanitize.sanitize_bbox, 'function', 'sanitize_bbox is a valid function');
t.equal(typeof sanitize.sanitize_rect, 'function', 'sanitize_rect is a valid function');
t.equal(typeof sanitize.sanitize_coord, 'function', 'sanitize_coord is a valid function');
t.equal(typeof sanitize.sanitize_boundary_circle, 'function', 'sanitize_boundary_circle is a valid function');
t.equal(typeof sanitize.sanitize_circle, 'function', 'sanitize_circle is a valid function');
t.equal(typeof sanitize.sanitize_point, 'function', 'sanitize_point is a valid function');
t.end();
});
};
module.exports.tests.sanitize = function(test, common) {
module.exports.tests.coord = function(test, common) {
test('valid coord', function (t) {
var clean = {};
var params = {
'foo': -40.659
};
var mandatory = false;
sanitize.sanitize_coord( 'foo', clean, params.foo, mandatory );
t.equal(clean.foo, params.foo);
t.end();
});
test('invalid coord', function (t) {
var clean = {};
var params = {
'foo': 'bar'
};
var mandatory = false;
sanitize.sanitize_coord( 'foo', clean, params.foo, mandatory );
t.equal(clean.foo, undefined, 'not set');
t.end();
});
test('nothing specified', function (t) {
var clean = {};
var params = {};
var mandatory = false;
t.doesNotThrow( function(){
sanitize.sanitize_coord( 'foo', clean, params.foo, mandatory );
});
t.end();
});
test('nothing specified - mandatory', function (t) {
var clean = {};
var params = {};
var mandatory = true;
t.throws( function(){
sanitize.sanitize_coord( 'foo', clean, params.foo, mandatory );
});
t.end();
});
};
module.exports.tests.point = function(test, common) {
test('valid point duo', function (t) {
var clean = {};
var params = {
'foo.bar.lat': 11,
'foo.bar.lon': 22
};
var mandatory = false;
sanitize.sanitize_point( 'foo.bar', clean, params, mandatory );
t.equal(clean['foo.bar.lat'], params['foo.bar.lat'], 'lat approved');
t.equal(clean['foo.bar.lon'], params['foo.bar.lon'], 'lon approved');
t.end();
});
test('invalid point, lat only', function (t) {
var clean = {};
var params = {
'foo.bar.lat': 11
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_point( 'foo.bar', clean, params, mandatory );
});
t.end();
});
test('invalid point, lon only', function (t) {
var clean = {};
var params = {
'foo.bar.lon': 22,
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_point( 'foo.bar', clean, params, mandatory );
});
t.end();
});
test('invalid lat value specified', function (t) {
var clean = {};
var params = {
'foo.bar.lat': 'foobar',
'foo.bar.lon': 22,
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_point( 'foo.bar', clean, params, mandatory );
});
t.end();
});
test('invalid lon value specified', function (t) {
var clean = {};
var params = {
'foo.bar.lat': 11,
'foo.bar.lon': 'foobar'
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_point( 'foo.bar', clean, params, mandatory );
});
t.end();
});
test('nothing specified', function (t) {
var clean = {};
var params = {};
var mandatory = false;
t.doesNotThrow( function(){
sanitize.sanitize_point( 'foo.bar', clean, params, mandatory );
});
t.end();
});
test('nothing specified - mandatory', function (t) {
var clean = {};
var params = {};
var mandatory = true;
t.throws( function(){
sanitize.sanitize_point( 'foo.bar', clean, params, mandatory );
});
t.end();
});
};
module.exports.tests.rect = function(test, common) {
test('valid rect quad', function (t) {
var clean = {};
var params = {
'boundary.rect.min_lat': -40.659,
'boundary.rect.max_lat': -41.614,
'boundary.rect.min_lon': 174.612,
'boundary.rect.max_lon': 176.333
};
var mandatory = false;
sanitize.sanitize_rect( 'boundary.rect', clean, params, mandatory );
t.equal(clean['boundary.rect.min_lat'], params['boundary.rect.min_lat'], 'min_lat approved');
t.equal(clean['boundary.rect.max_lat'], params['boundary.rect.max_lat'], 'min_lat approved');
t.equal(clean['boundary.rect.min_lon'], params['boundary.rect.min_lon'], 'min_lat approved');
t.equal(clean['boundary.rect.max_lon'], params['boundary.rect.max_lon'], 'min_lat approved');
t.end();
});
test('invalid rect - partially specified', function (t) {
var clean = {};
var params = {
'boundary.rect.min_lat': -40.659
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_rect( 'boundary.rect', clean, params, mandatory );
});
t.end();
});
test('nothing specified', function (t) {
var clean = {};
var params = {};
var mandatory = false;
t.doesNotThrow( function(){
sanitize.sanitize_rect( 'boundary.rect', clean, params, mandatory );
});
t.end();
});
test('nothing specified - mandatory', function (t) {
var clean = {};
var params = {};
var mandatory = true;
t.throws( function(){
sanitize.sanitize_rect( 'boundary.rect', clean, params, mandatory );
});
t.end();
});
};
module.exports.tests.circle = function(test, common) {
test('valid circle trio', function (t) {
var clean = {};
var params = {
@ -19,26 +217,160 @@ module.exports.tests.sanitize = function(test, common) {
'boundary.circle.lon': 22,
'boundary.circle.radius': 33
};
var is_required = true;
var all_required = true;
var mandatory = false;
sanitize.sanitize_boundary_circle(clean, params, is_required, all_required);
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
t.equal(clean['boundary.circle.lat'], params['boundary.circle.lat'], 'lat approved');
t.equal(clean['boundary.circle.lon'], params['boundary.circle.lon'], 'lon approved');
t.equal(clean['boundary.circle.radius'], params['boundary.circle.radius'], 'radius approved');
t.end();
});
test('valid circle, radius only, all not required', function (t) {
test('valid circle duo', function (t) {
var clean = {};
var params = {
'boundary.circle.lat': 11,
'boundary.circle.lon': 22
};
var mandatory = false;
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
t.equal(clean['boundary.circle.lat'], params['boundary.circle.lat'], 'lat approved');
t.equal(clean['boundary.circle.lon'], params['boundary.circle.lon'], 'lon approved');
t.end();
});
test('invalid circle, lat only', function (t) {
var clean = {};
var params = {
'boundary.circle.lat': 11,
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('invalid circle, lon only', function (t) {
var clean = {};
var params = {
'boundary.circle.lon': 22,
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('invalid circle, radius only', function (t) {
var clean = {};
var params = {
'boundary.circle.radius': 33
};
var is_required = true;
var all_required = false;
var mandatory = false;
sanitize.sanitize_boundary_circle(clean, params, is_required, all_required);
t.equal(clean['boundary.circle.radius'], params['boundary.circle.radius'], 'radius approved');
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('invalid circle, lon and radius only', function (t) {
var clean = {};
var params = {
'boundary.circle.lon': 22,
'boundary.circle.radius': 33
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('invalid circle, lat and radius only', function (t) {
var clean = {};
var params = {
'boundary.circle.lat': 11,
'boundary.circle.radius': 33
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('invalid lat value specified', function (t) {
var clean = {};
var params = {
'boundary.circle.lat': 'foobar',
'boundary.circle.lon': 22,
'boundary.circle.radius': 33
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('invalid lon value specified', function (t) {
var clean = {};
var params = {
'boundary.circle.lat': 11,
'boundary.circle.lon': 'foobar',
'boundary.circle.radius': 33
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('invalid radius value specified', function (t) {
var clean = {};
var params = {
'boundary.circle.lat': 11,
'boundary.circle.lon': 22,
'boundary.circle.radius': 'foobar'
};
var mandatory = false;
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('nothing specified', function (t) {
var clean = {};
var params = {};
var mandatory = false;
t.doesNotThrow( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
test('nothing specified - mandatory', function (t) {
var clean = {};
var params = {};
var mandatory = true;
t.throws( function(){
sanitize.sanitize_circle( 'boundary.circle', clean, params, mandatory );
});
t.end();
});
};

11
test/unit/sanitiser/reverse.js

@ -6,17 +6,18 @@ var reverse = require('../../../sanitiser/reverse'),
middleware = reverse.middleware,
defaultError = 'missing param \'lat\'',
defaultClean = { 'point.lat': 0,
'point.lon': 0,
'boundary.circle.lat': 0,
'boundary.circle.lon': 0,
types: {
},
'point.lon': 0,
size: 10,
private: false,
boundary: { }
private: false
};
// these are the default values you would expect when no input params are specified.
// @todo: why is this different from $defaultClean?
var emptyClean = { boundary: {}, private: false, size: 10, types: {} };
var emptyClean = { private: false, size: 10, types: {} };
module.exports.tests = {};
@ -101,7 +102,7 @@ module.exports.tests.sanitize_lon = function(test, common) {
var req = { query: { 'point.lat': 0, 'point.lon': lon } };
// @todo: why is lat set?
var expected = { boundary: {}, 'point.lat': 0, private: false, size: 10, types: {} };
var expected = { 'point.lat': 0, private: false, size: 10, types: {} };
sanitize(req, function(){
t.equal(req.errors[0], 'missing param \'point.lon\'', 'longitude is a required field');
t.deepEqual(req.clean, expected, 'clean only has default values set');

68
test/unit/sanitiser/search.js

@ -1,11 +1,12 @@
var search = require('../../../sanitiser/search'),
var extend = require('extend'),
search = require('../../../sanitiser/search'),
parser = require('../../../helper/query_parser'),
sanitize = search.sanitize,
middleware = search.middleware,
defaultError = 'invalid param \'text\': text length, must be >0';
// these are the default values you would expect when no input params are specified.
// @todo: why is this different from $defaultClean?
var emptyClean = { boundary: {}, private: false, size: 10, types: {} };
var emptyClean = { private: false, size: 10, types: {} };
module.exports.tests = {};
@ -166,8 +167,9 @@ module.exports.tests.sanitize_optional_geo = function(test, common) {
var req = { query: { text: 'test', 'focus.point.lon': 0 } };
sanitize(req, function(){
var expected_lon = 0;
t.equal(req.errors[0], undefined, 'no error');
t.deepEqual(req.clean['focus.point.lon'], expected_lon, 'clean set correctly (without any lat)');
t.equal(req.errors[0], 'missing point param \'focus.point\' requires all of: \'lat\',\'lon\' to be present');
t.equal(req.clean['focus.point.lat'], undefined);
t.equal(req.clean['focus.point.lon'], undefined);
});
t.end();
});
@ -175,14 +177,28 @@ module.exports.tests.sanitize_optional_geo = function(test, common) {
var req = { query: { text: 'test', 'focus.point.lat': 0 } };
sanitize(req, function(){
var expected_lat = 0;
t.equal(req.errors[0], undefined, 'no error');
t.deepEqual(req.clean['focus.point.lat'], expected_lat, 'clean set correctly (without any lon)');
t.equal(req.errors[0], 'missing point param \'focus.point\' requires all of: \'lat\',\'lon\' to be present');
t.equal(req.clean['focus.point.lat'], undefined);
t.equal(req.clean['focus.point.lon'], undefined);
});
t.end();
});
};
module.exports.tests.sanitize_bbox = function(test, common) {
module.exports.tests.sanitize_bounding_rect = function(test, common) {
// convernience function to avoid refactoring the succict geojson bbox
// fixtures in to the more verbose bounding.rect format.
var mapGeoJsonBboxFormatToBoundingRectFormat = function( bbox ){
var split = bbox.split(',');
return {
'boundary.rect.min_lon': split[0],
'boundary.rect.max_lat': split[1],
'boundary.rect.max_lon': split[2],
'boundary.rect.min_lat': split[3]
};
};
var bboxes = {
invalid: [
'91;-181,-91,181', // invalid - semicolon between coordinates
@ -192,7 +208,8 @@ module.exports.tests.sanitize_bbox = function(test, common) {
'91, -181, -91', // invalid - missing a coordinate
'123,12', // invalid - missing coordinates
'' // invalid - empty param
],
].map(mapGeoJsonBboxFormatToBoundingRectFormat),
valid: [
'-179,90,34,-80', // valid top_right lon/lat, bottom_left lon/lat
'0,0,0,0',
@ -207,34 +224,33 @@ module.exports.tests.sanitize_bbox = function(test, common) {
'-181,91,181,-91',
'91, -181,-91,11',
'91, -11,-91,181'
]
].map(mapGeoJsonBboxFormatToBoundingRectFormat)
};
test('invalid bbox', function(t) {
test('invalid bounding rect', function(t) {
bboxes.invalid.forEach( function( bbox ){
var req = { query: { text: 'test', bbox: bbox } };
var req = { query: { text: 'test' } };
extend( req.query, bbox );
sanitize(req, function(){
t.equal(req.errors[0], undefined, 'no error');
t.equal(req.clean.bbox, undefined, 'falling back on 50km distance from centroid');
t.equal(req.clean['boundary.rect.min_lon'], undefined);
t.equal(req.clean['boundary.rect.max_lat'], undefined);
t.equal(req.clean['boundary.rect.max_lon'], undefined);
t.equal(req.clean['boundary.rect.min_lat'], undefined);
t.equal(req.errors.length, 1, 'bounding error');
});
});
t.end();
});
test('valid bbox', function(t) {
test('valid bounding rect', function(t) {
bboxes.valid.forEach( function( bbox ){
var req = { query: { text: 'test', bbox: bbox } };
var req = { query: { text: 'test' } };
extend( req.query, bbox );
sanitize(req, function(){
var bboxArray = bbox.split(',').map(function(i) {
return parseInt(i, 10);
});
var expected_bbox = {
right: Math.max(bboxArray[0], bboxArray[2]),
top: Math.max(bboxArray[1], bboxArray[3]),
left: Math.min(bboxArray[0], bboxArray[2]),
bottom: Math.min(bboxArray[1], bboxArray[3])
};
t.equal(req.errors[0], undefined, 'no error');
t.deepEqual(req.clean.bbox, expected_bbox, 'clean set correctly (' + bbox + ')');
t.equal(req.clean['boundary.rect.min_lon'], parseFloat(bbox['boundary.rect.min_lon']));
t.equal(req.clean['boundary.rect.max_lat'], parseFloat(bbox['boundary.rect.max_lat']));
t.equal(req.clean['boundary.rect.max_lon'], parseFloat(bbox['boundary.rect.max_lon']));
t.equal(req.clean['boundary.rect.min_lat'], parseFloat(bbox['boundary.rect.min_lat']));
});
});
t.end();

Loading…
Cancel
Save