Browse Source

Merge pull request #508 from pelias/master

Merge master into staging
pull/515/head
Julian Simioni 9 years ago
parent
commit
8bcc68f35a
  1. 38
      README.md
  2. 5
      bin/units
  3. 8
      controller/search.js
  4. 45
      helper/labelGenerator.js
  5. 79
      helper/labelSchema.js
  6. 26
      helper/text_parser.js
  7. 4
      helper/type_mapping.js
  8. 3
      middleware/localNamingConventions.js
  9. 4
      package.json
  10. 8
      query/autocomplete.js
  11. 60
      sanitiser/_size.js
  12. 8
      sanitiser/autocomplete.js
  13. 2
      sanitiser/reverse.js
  14. 2
      sanitiser/search.js
  15. 34
      test/ciao/autocomplete/layers_alias_address.coffee
  16. 47
      test/ciao/autocomplete/layers_alias_coarse.coffee
  17. 34
      test/ciao/autocomplete/layers_invalid.coffee
  18. 35
      test/ciao/autocomplete/layers_mix_invalid_valid.coffee
  19. 34
      test/ciao/autocomplete/layers_multiple.coffee
  20. 34
      test/ciao/autocomplete/layers_single.coffee
  21. 34
      test/ciao/autocomplete/size_not_default.coffee
  22. 34
      test/ciao/autocomplete/sources_invalid.coffee
  23. 37
      test/ciao/autocomplete/sources_layers_invalid_combo.coffee
  24. 35
      test/ciao/autocomplete/sources_layers_valid_combo.coffee
  25. 34
      test/ciao/autocomplete/sources_multiple.coffee
  26. 34
      test/ciao/autocomplete/sources_single.coffee
  27. 3
      test/ciao/reverse/layers_alias_coarse.coffee
  28. 2
      test/ciao/reverse/layers_invalid.coffee
  29. 2
      test/ciao/reverse/layers_mix_invalid_valid.coffee
  30. 3
      test/ciao/search/layers_alias_coarse.coffee
  31. 2
      test/ciao/search/layers_invalid.coffee
  32. 2
      test/ciao/search/layers_mix_invalid_valid.coffee
  33. 58
      test/ciao_test_data.js
  34. 15
      test/unit/controller/search.js
  35. 85
      test/unit/fixture/autocomplete_with_source_filtering.js
  36. 93
      test/unit/fixture/search_with_source_filtering.js
  37. 24
      test/unit/helper/geojsonify.js
  38. 205
      test/unit/helper/labelGenerator_CAN.js
  39. 190
      test/unit/helper/labelGenerator_GBR.js
  40. 51
      test/unit/helper/labelGenerator_SGP.js
  41. 53
      test/unit/helper/labelGenerator_SWE.js
  42. 287
      test/unit/helper/labelGenerator_USA.js
  43. 356
      test/unit/helper/labelGenerator_default.js
  44. 116
      test/unit/helper/labelGenerator_examples.js
  45. 770
      test/unit/helper/labelSchema.js
  46. 20
      test/unit/helper/text_parser.js
  47. 4
      test/unit/helper/type_mapping.js
  48. 19
      test/unit/middleware/localNamingConventions.js
  49. 8
      test/unit/mock/backend.js
  50. 13
      test/unit/query/autocomplete.js
  51. 13
      test/unit/query/search.js
  52. 4
      test/unit/run.js
  53. 14
      test/unit/sanitiser/_layers.js
  54. 8
      test/unit/sanitiser/_size.js
  55. 2
      test/unit/sanitiser/autocomplete.js

38
README.md

@ -34,3 +34,41 @@ The API recognizes the following properties under the top-level `api` key in you
Please fork and pull request against upstream master on a feature branch. Pretty please; provide unit tests and script Please fork and pull request against upstream master on a feature branch. Pretty please; provide unit tests and script
fixtures in the `test` directory. fixtures in the `test` directory.
## Unit tests
You can run the unit test suite using the command:
```bash
$ npm test
```
## HTTP tests
We have another set of tests which are used to test the HTTP API layer, these tests send expected HTTP requests and then
assert that the responses coming back have the correct geoJSON format and HTTP status codes.
You can run the HTTP test suite using the command:
```bash
$ npm run ciao
```
Note: some of the tests in this suite fail when no data is present in the index, there is a small set of test documents
provided in `./test/ciao_test_data` which can be inserted in order to avoid these errors.
To inject dummy data in to your local index:
```bash
$ node test/ciao_test_data.js
```
You can confirm the dummy data has been inserted with the command:
```bash
$ curl localhost:9200/pelias/_count?pretty
{
"count" : 9,
...
}
```

5
bin/units

@ -0,0 +1,5 @@
#!/bin/bash
set -euo pipefail
node test/unit/run.js | ./node_modules/.bin/tap-dot

8
controller/search.js

@ -1,3 +1,5 @@
var _ = require('lodash');
var service = { search: require('../service/search') }; var service = { search: require('../service/search') };
var logger = require('pelias-logger').get('api:controller:search'); var logger = require('pelias-logger').get('api:controller:search');
@ -36,7 +38,11 @@ function setup( backend, query ){
// error handler // error handler
if( err ){ if( err ){
req.errors.push( err ); if (_.isObject(err) && err.message) {
req.errors.push( err.message );
} else {
req.errors.push( err );
}
} }
// set response data // set response data
else { else {

45
helper/labelGenerator.js

@ -4,37 +4,28 @@ var _ = require('lodash'),
module.exports = function( record ){ module.exports = function( record ){
var schema = getSchema(record.country_a); var schema = getSchema(record.country_a);
// in virtually all cases, this will be the `name` field
var labelParts = getInitialLabel(record); var labelParts = getInitialLabel(record);
for (var key in schema) { // iterate the schema
var valueFunction = schema[key]; for (var field in schema) {
var valueFunction = schema[field];
labelParts = valueFunction(record, labelParts); labelParts.push(valueFunction(record));
} }
// NOTE: while it may seem odd to call `uniq` on the list of label parts, // retain only things that are truthy
// the effect is quite subtle. Take, for instance, a result for "Lancaster, PA" labelParts = _.compact(labelParts);
// the pseudo-object is:
// { // first, dedupe the name and 1st label array elements
// 'name': 'Lancaster', // this is used to ensure that the `name` and first admin hierarchy elements aren't repeated
// 'locality': 'Lancaster', // eg - `["Lancaster", "Lancaster", "PA", "United States"]` -> `["Lancaster", "PA", "United States"]`
// 'region_a': 'PA', var dedupedNameAndFirstLabelElement = _.uniq([labelParts.shift(), labelParts.shift()]);
// 'country_a': 'USA'
// } // second, unshift the deduped parts back onto the labelParts
// labelParts.unshift.apply(labelParts, dedupedNameAndFirstLabelElement);
// the code up to this point generates the label:
// `Lancaster, Lancaster, PA, USA` // third, join with a comma and return
// return labelParts.join(', ');
// then the `unique` call reduces this to:
// `Lancaster, PA, USA`
//
// this code doesn't have the same effect in the case of a venue or address
// where the `name` field would contain the address or name of a point-of-interest
//
// Also see https://github.com/pelias/api/issues/429 for other ways that this is bad
//
// de-dupe, join, trim
return _.uniq( labelParts ).join(', ').trim();
}; };

79
helper/labelSchema.js

@ -1,66 +1,73 @@
var _ = require('lodash'), var _ = require('lodash');
check = require('check-types');
module.exports = { module.exports = {
'USA': { 'default': {
'local': getFirstProperty(['borough', 'localadmin', 'locality', 'neighbourhood', 'county']), 'local': getFirstProperty(['locality', 'localadmin']),
'regional': getUsState, 'country': getFirstProperty(['country'])
'country': getFirstProperty(['country_a'])
}, },
'GBR': { 'GBR': {
'local': getFirstProperty(['neighbourhood', 'county', 'localadmin', 'locality', 'macroregion', 'region']), 'local': getFirstProperty(['locality', 'localadmin']),
'regional': getFirstProperty(['county','country','region']) 'regional': getFirstProperty(['macroregion']),
}, 'country': getFirstProperty(['country'])
'SGP': {
'local': getFirstProperty(['neighbourhood', 'region', 'county', 'localadmin', 'locality']),
'regional': getFirstProperty(['county','country','region'])
}, },
'SWE': { 'USA': {
'local': getFirstProperty(['neighbourhood', 'region', 'county', 'localadmin', 'locality']), 'borough': getFirstProperty(['borough']),
'regional': getFirstProperty(['country']) 'local': getFirstProperty(['locality', 'localadmin']),
'regional': getUsOrCaState,
'country': getUSACountryValue
}, },
'default': { 'CAN': {
'local': getFirstProperty(['localadmin', 'locality', 'neighbourhood', 'county', 'macroregion', 'region']), 'local': getFirstProperty(['locality']), // no localadmins in CAN
'regional': getFirstProperty(['country']) 'regional': getUsOrCaState,
'country': getFirstProperty(['country'])
} }
}; };
// find the first field of record that has a non-empty value that's not already in labelParts // find the first field of record that has a non-empty value that's not already in labelParts
function getFirstProperty(fields) { function getFirstProperty(fields) {
return function(record, labelParts) { return function(record) {
for (var i = 0; i < fields.length; i++) { for (var i = 0; i < fields.length; i++) {
var fieldValue = record[fields[i]]; var fieldValue = record[fields[i]];
if (check.nonEmptyString(fieldValue) && !_.includes(labelParts, fieldValue)) { if (!_.isEmpty(fieldValue)) {
labelParts.push( fieldValue ); return fieldValue;
return labelParts;
} }
} }
return labelParts;
}; };
} }
// this function is exclusively used for figuring out which field to use for US States // this function is exclusively used for figuring out which field to use for US/CA States
// 1. if a US state is the most granular bit of info entered, the label should contain // 1. if a US/CA state is the most granular bit of info entered, the label should contain
// the full state name, eg: Pennsylvania, USA // the full state name, eg: Pennsylvania, USA and Ontario, CA
// 2. otherwise, the state abbreviation should be used, eg: Lancaster, PA, USA // 2. otherwise, the state abbreviation should be used, eg: Lancaster, PA, USA and Bruce, ON, CA
// 3. if for some reason the abbreviation isn't available, use the full state name // 3. if for some reason the abbreviation isn't available, use the full state name
function getUsState(record, labelParts) { function getUsOrCaState(record) {
if ('region' === record.layer && record.region) { if ('region' === record.layer && record.region) {
// add full state name when state is the most granular piece of info // return full state name when state is the most granular piece of info
labelParts.push(record.region); return record.region;
} else if (record.region_a) { } else if (record.region_a) {
// otherwise just add the region code when available // otherwise just return the region code when available
labelParts.push(record.region_a); return record.region_a;
} else if (record.region) { } else if (record.region) {
// add the full name when there's no region code available () // return the full name when there's no region code available
labelParts.push(record.region); return record.region;
} }
return labelParts; }
// this function returns the full name of a country if the result is in the
// country layer (for "United States" record). It returns the abbreviation
// otherwise (eg - Lancaster, PA, USA).
function getUSACountryValue(record) {
if ('country' === record.layer && record.country) {
return record.country;
}
return record.country_a;
} }

26
helper/text_parser.js

@ -2,11 +2,10 @@
var parser = require('addressit'); var parser = require('addressit');
var extend = require('extend'); var extend = require('extend');
var type_mapping = require('../helper/type_mapping'); var type_mapping = require('../helper/type_mapping');
var delim = ',';
var check = require('check-types'); var check = require('check-types');
var logger = require('pelias-logger').get('api'); var logger = require('pelias-logger').get('api');
module.exports = {}; var DELIM = ',';
/* /*
* For performance, and to prefer POI and admin records, express a preference * For performance, and to prefer POI and admin records, express a preference
@ -21,14 +20,21 @@ module.exports.get_layers = function get_layers(query) {
module.exports.get_parsed_address = function get_parsed_address(query) { module.exports.get_parsed_address = function get_parsed_address(query) {
var getAdminPartsBySplittingOnDelim = function(query) { var getAdminPartsBySplittingOnDelim = function(queryParts) {
// naive approach - for admin matching during query time // naive approach - for admin matching during query time
// split 'flatiron, new york, ny' into 'flatiron' and 'new york, ny' // split 'flatiron, new york, ny' into 'flatiron' and 'new york, ny'
var delimIndex = query.indexOf(delim);
var address = {}; var address = {};
if ( delimIndex !== -1 ) {
address.name = query.substring(0, delimIndex); if (queryParts.length > 1) {
address.admin_parts = query.substring(delimIndex + 1).trim(); address.name = queryParts[0].trim();
// 1. slice away all parts after the first one
// 2. trim spaces from each part just in case
// 3. join the parts back together with appropriate delimiter and spacing
address.admin_parts = queryParts.slice(1)
.map(function (part) { return part.trim(); })
.join(DELIM + ' ');
} }
return address; return address;
@ -42,8 +48,10 @@ module.exports.get_parsed_address = function get_parsed_address(query) {
} }
}; };
var addressWithAdminParts = getAdminPartsBySplittingOnDelim(query); var queryParts = query.split(DELIM);
var addressWithAddressParts= getAddressParts(query);
var addressWithAdminParts = getAdminPartsBySplittingOnDelim(queryParts);
var addressWithAddressParts= getAddressParts(queryParts.join(DELIM + ' '));
var parsedAddress = extend(addressWithAdminParts, var parsedAddress = extend(addressWithAdminParts,
addressWithAddressParts); addressWithAddressParts);

4
helper/type_mapping.js

@ -48,8 +48,8 @@ var LAYERS_BY_SOURCE = {
openstreetmap: [ 'address', 'venue' ], openstreetmap: [ 'address', 'venue' ],
openaddresses: [ 'address' ], openaddresses: [ 'address' ],
geonames: [ 'country', 'region', 'county', 'locality', 'venue' ], geonames: [ 'country', 'region', 'county', 'locality', 'venue' ],
whosonfirst: [ 'continent', 'macrocountry', 'country', 'dependency', 'region', whosonfirst: [ 'continent', 'country', 'dependency', 'macroregion', 'region',
'locality', 'localadmin', 'county', 'macrohood', 'neighbourhood', 'microhood', 'disputed'] 'locality', 'localadmin', 'macrocounty', 'county', 'macrohood', 'neighbourhood', 'microhood', 'disputed']
}; };
/* /*

3
middleware/localNamingConventions.js

@ -23,6 +23,9 @@ function applyLocalNamingConventions(req, res, next) {
// loop through data items and flip relevant number/street // loop through data items and flip relevant number/street
res.data.filter(function(place){ res.data.filter(function(place){
// do nothing for records with no admin info
if (!place.parent || !place.parent.country_a) { return false; }
// relevant for some countries // relevant for some countries
var flip = place.parent.country_a.some(function(country) { var flip = place.parent.country_a.some(function(country) {
return _.includes(flipNumberAndStreetCountries, country); return _.includes(flipNumberAndStreetCountries, country);

4
package.json

@ -9,7 +9,7 @@
"scripts": { "scripts": {
"start": "node index.js", "start": "node index.js",
"test": "npm run unit", "test": "npm run unit",
"unit": "node test/unit/run.js | tap-dot", "unit": "./bin/units",
"ciao": "node node_modules/ciao/bin/ciao -c test/ciao.json test/ciao", "ciao": "node node_modules/ciao/bin/ciao -c test/ciao.json test/ciao",
"coverage": "node_modules/.bin/istanbul cover test/unit/run.js", "coverage": "node_modules/.bin/istanbul cover test/unit/run.js",
"audit": "npm shrinkwrap; node node_modules/nsp/bin/nspCLI.js audit-shrinkwrap; rm npm-shrinkwrap.json;", "audit": "npm shrinkwrap; node node_modules/nsp/bin/nspCLI.js audit-shrinkwrap; rm npm-shrinkwrap.json;",
@ -39,7 +39,7 @@
"async": "^1.5.2", "async": "^1.5.2",
"check-types": "^6.0.0", "check-types": "^6.0.0",
"cluster2": "git://github.com/missinglink/cluster2.git#node_zero_twelve", "cluster2": "git://github.com/missinglink/cluster2.git#node_zero_twelve",
"elasticsearch": "^10.1.3", "elasticsearch": "^11.0.0",
"express": "^4.8.8", "express": "^4.8.8",
"express-http-proxy": "^0.6.0", "express-http-proxy": "^0.6.0",
"extend": "3.0.0", "extend": "3.0.0",

8
query/autocomplete.js

@ -41,6 +41,9 @@ query.score( views.focus_selected_layers( views.ngrams_strict ) );
query.score( peliasQuery.view.popularity( views.ngrams_strict ) ); query.score( peliasQuery.view.popularity( views.ngrams_strict ) );
query.score( peliasQuery.view.population( views.ngrams_strict ) ); query.score( peliasQuery.view.population( views.ngrams_strict ) );
// non-scoring hard filters
query.filter( peliasQuery.view.sources );
// -------------------------------- // --------------------------------
/** /**
@ -51,6 +54,11 @@ function generateQuery( clean ){
var vs = new peliasQuery.Vars( defaults ); var vs = new peliasQuery.Vars( defaults );
// sources
if( check.array(clean.sources) && clean.sources.length ){
vs.var( 'sources', clean.sources );
}
// mark the name as incomplete (user has not yet typed a comma) // mark the name as incomplete (user has not yet typed a comma)
vs.var( 'input:name:isComplete', false ); vs.var( 'input:name:isComplete', false );

60
sanitiser/_size.js

@ -5,32 +5,40 @@ var MIN_SIZE = 1,
DEFAULT_SIZE = 10; DEFAULT_SIZE = 10;
// validate inputs, convert types and apply defaults // validate inputs, convert types and apply defaults
function sanitize( raw, clean ){ function setup( size_min, size_max, size_def ){
// error & warning messages // allow caller to inject custom min/max/default values
var messages = { errors: [], warnings: [] }; if( !check.number( size_min ) ){ size_min = MIN_SIZE; }
if( !check.number( size_max ) ){ size_max = MAX_SIZE; }
// coercions if( !check.number( size_def ) ){ size_def = DEFAULT_SIZE; }
clean.size = parseInt( raw.size, 10 );
return function sanitize( raw, clean ){
// invalid numeric input
if( isNaN( clean.size ) ){ // error & warning messages
clean.size = DEFAULT_SIZE; var messages = { errors: [], warnings: [] };
}
// ensure size falls within defined range // coercions
else if( clean.size > MAX_SIZE ){ clean.size = parseInt( raw.size, 10 );
// set the max size
messages.warnings.push('out-of-range integer \'size\', using MAX_SIZE'); // invalid numeric input
clean.size = MAX_SIZE; if( isNaN( clean.size ) ){
} clean.size = size_def;
else if( clean.size < MIN_SIZE ){ }
// set the min size // ensure size falls within defined range
messages.warnings.push('out-of-range integer \'size\', using MIN_SIZE'); else if( clean.size > size_max ){
clean.size = MIN_SIZE; // set the max size
} messages.warnings.push('out-of-range integer \'size\', using MAX_SIZE');
clean.size = size_max;
return messages; }
else if( clean.size < size_min ){
// set the min size
messages.warnings.push('out-of-range integer \'size\', using MIN_SIZE');
clean.size = size_min;
}
return messages;
};
} }
// export function // export function
module.exports = sanitize; module.exports = setup;

8
sanitiser/autocomplete.js

@ -1,8 +1,14 @@
var type_mapping = require('../helper/type_mapping');
var sanitizeAll = require('../sanitiser/sanitizeAll'), var sanitizeAll = require('../sanitiser/sanitizeAll'),
sanitizers = { sanitizers = {
singleScalarParameters: require('../sanitiser/_single_scalar_parameters'), singleScalarParameters: require('../sanitiser/_single_scalar_parameters'),
text: require('../sanitiser/_text'), text: require('../sanitiser/_text'),
size: require('../sanitiser/_size'), size: require('../sanitiser/_size')(10, 10, 10),
layers: require('../sanitiser/_targets')('layers', type_mapping.layer_mapping),
sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping),
// depends on the layers and sources sanitisers, must be run after them
sources_and_layers: require('../sanitiser/_sources_and_layers'),
private: require('../sanitiser/_flag_bool')('private', false), private: require('../sanitiser/_flag_bool')('private', false),
geo_autocomplete: require('../sanitiser/_geo_autocomplete'), geo_autocomplete: require('../sanitiser/_geo_autocomplete'),
}; };

2
sanitiser/reverse.js

@ -8,7 +8,7 @@ var sanitizeAll = require('../sanitiser/sanitizeAll'),
sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping), sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping),
// depends on the layers and sources sanitisers, must be run after them // depends on the layers and sources sanitisers, must be run after them
sources_and_layers: require('../sanitiser/_sources_and_layers'), sources_and_layers: require('../sanitiser/_sources_and_layers'),
size: require('../sanitiser/_size'), size: require('../sanitiser/_size')(/* use defaults*/),
private: require('../sanitiser/_flag_bool')('private', false), private: require('../sanitiser/_flag_bool')('private', false),
geo_reverse: require('../sanitiser/_geo_reverse'), geo_reverse: require('../sanitiser/_geo_reverse'),
boundary_country: require('../sanitiser/_boundary_country'), boundary_country: require('../sanitiser/_boundary_country'),

2
sanitiser/search.js

@ -5,7 +5,7 @@ var sanitizeAll = require('../sanitiser/sanitizeAll'),
quattroshapes_deprecation: require('../sanitiser/_deprecate_quattroshapes'), quattroshapes_deprecation: require('../sanitiser/_deprecate_quattroshapes'),
singleScalarParameters: require('../sanitiser/_single_scalar_parameters'), singleScalarParameters: require('../sanitiser/_single_scalar_parameters'),
text: require('../sanitiser/_text'), text: require('../sanitiser/_text'),
size: require('../sanitiser/_size'), size: require('../sanitiser/_size')(/* use defaults*/),
layers: require('../sanitiser/_targets')('layers', type_mapping.layer_mapping), layers: require('../sanitiser/_targets')('layers', type_mapping.layer_mapping),
sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping), sources: require('../sanitiser/_targets')('sources', type_mapping.source_mapping),
// depends on the layers and sources sanitisers, must be run after them // depends on the layers and sources sanitisers, must be run after them

34
test/ciao/autocomplete/layers_alias_address.coffee

@ -0,0 +1,34 @@
#> layer alias
path: '/v1/autocomplete?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.layers.should.eql ["address"]

47
test/ciao/autocomplete/layers_alias_coarse.coffee

@ -0,0 +1,47 @@
#> layer alias
path: '/v1/autocomplete?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.layers.should.eql [ "continent",
"country",
"dependency",
"macroregion",
"region",
"locality",
"localadmin",
"macrocounty",
"county",
"macrohood",
"neighbourhood",
"microhood",
"disputed"
]

34
test/ciao/autocomplete/layers_invalid.coffee

@ -0,0 +1,34 @@
#> layer alias
path: '/v1/autocomplete?text=a&layers=notlayer'
#? 200 ok
response.statusCode.should.be.equal 400
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: coarse,address,venue,country,macroregion,region,county,locality,continent,macrocounty,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10

35
test/ciao/autocomplete/layers_mix_invalid_valid.coffee

@ -0,0 +1,35 @@
#> layer alias
path: '/v1/autocomplete?text=a&layers=country,notlayer'
#? 200 ok
response.statusCode.should.be.equal 400
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: coarse,address,venue,country,macroregion,region,county,locality,continent,macrocounty,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ]
#? 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['layers']

34
test/ciao/autocomplete/layers_multiple.coffee

@ -0,0 +1,34 @@
#> layer alias
path: '/v1/autocomplete?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.layers.should.eql ["country","region"]

34
test/ciao/autocomplete/layers_single.coffee

@ -0,0 +1,34 @@
#> layer alias
path: '/v1/autocomplete?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.layers.should.eql ["country"]

34
test/ciao/autocomplete/size_not_default.coffee

@ -0,0 +1,34 @@
#> set size (autocomplete does not allow size to be changed)
path: '/v1/autocomplete?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.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 10 # should remain the default size

34
test/ciao/autocomplete/sources_invalid.coffee

@ -0,0 +1,34 @@
#> sources filter
path: '/v1/autocomplete?text=a&sources=openstreetmap,notasource'
#? 200 ok
response.statusCode.should.be.equal 400
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: osm,oa,gn,wof,openstreetmap,openaddresses,geonames,whosonfirst' ]
#? expected warnings
should.not.exist json.geocoding.warnings
#? inputs
json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10

37
test/ciao/autocomplete/sources_layers_invalid_combo.coffee

@ -0,0 +1,37 @@
#> sources and layers specified (invalid combo)
path: '/v1/autocomplete?text=a&sources=whosonfirst&layers=address'
#? 200 ok
response.statusCode.should.be.equal 400
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: the whosonfirst source has nothing in the address layer' ]
#? 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.layers.should.eql ["address"]
json.geocoding.query.sources.should.eql ["whosonfirst"]
should.not.exist json.geocoding.query['type']

35
test/ciao/autocomplete/sources_layers_valid_combo.coffee

@ -0,0 +1,35 @@
#> sources and layers specified
path: '/v1/autocomplete?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.layers.should.eql ["address"]
json.geocoding.query.sources.should.eql ["openaddresses"]

34
test/ciao/autocomplete/sources_multiple.coffee

@ -0,0 +1,34 @@
#> sources filter
path: "/v1/autocomplete?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.sources.should.eql ["openstreetmap", "geonames"]

34
test/ciao/autocomplete/sources_single.coffee

@ -0,0 +1,34 @@
#> sources filter
path: '/v1/autocomplete?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.sources.should.eql ["openstreetmap"]

3
test/ciao/reverse/layers_alias_coarse.coffee

@ -31,12 +31,13 @@ should.not.exist json.geocoding.warnings
#? inputs #? inputs
json.geocoding.query['size'].should.eql 10 json.geocoding.query['size'].should.eql 10
json.geocoding.query.layers.should.eql [ "continent", json.geocoding.query.layers.should.eql [ "continent",
"macrocountry",
"country", "country",
"dependency", "dependency",
"macroregion",
"region", "region",
"locality", "locality",
"localadmin", "localadmin",
"macrocounty",
"county", "county",
"macrohood", "macrohood",
"neighbourhood", "neighbourhood",

2
test/ciao/reverse/layers_invalid.coffee

@ -24,7 +24,7 @@ json.features.should.be.instanceof Array
#? expected errors #? expected errors
should.exist json.geocoding.errors should.exist json.geocoding.errors
json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,region,county,locality,continent,macrocountry,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ] json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,macroregion,region,county,locality,continent,macrocounty,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ]
#? expected warnings #? expected warnings
should.not.exist json.geocoding.warnings should.not.exist json.geocoding.warnings

2
test/ciao/reverse/layers_mix_invalid_valid.coffee

@ -24,7 +24,7 @@ json.features.should.be.instanceof Array
#? expected errors #? expected errors
should.exist json.geocoding.errors should.exist json.geocoding.errors
json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,region,county,locality,continent,macrocountry,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ] json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,macroregion,region,county,locality,continent,macrocounty,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ]
#? expected warnings #? expected warnings
should.not.exist json.geocoding.warnings should.not.exist json.geocoding.warnings

3
test/ciao/search/layers_alias_coarse.coffee

@ -32,12 +32,13 @@ should.not.exist json.geocoding.warnings
json.geocoding.query['text'].should.eql 'a' json.geocoding.query['text'].should.eql 'a'
json.geocoding.query['size'].should.eql 10 json.geocoding.query['size'].should.eql 10
json.geocoding.query.layers.should.eql [ "continent", json.geocoding.query.layers.should.eql [ "continent",
"macrocountry",
"country", "country",
"dependency", "dependency",
"macroregion",
"region", "region",
"locality", "locality",
"localadmin", "localadmin",
"macrocounty",
"county", "county",
"macrohood", "macrohood",
"neighbourhood", "neighbourhood",

2
test/ciao/search/layers_invalid.coffee

@ -24,7 +24,7 @@ json.features.should.be.instanceof Array
#? expected errors #? expected errors
should.exist json.geocoding.errors should.exist json.geocoding.errors
json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,region,county,locality,continent,macrocountry,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ] json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,macroregion,region,county,locality,continent,macrocounty,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ]
#? expected warnings #? expected warnings
should.not.exist json.geocoding.warnings should.not.exist json.geocoding.warnings

2
test/ciao/search/layers_mix_invalid_valid.coffee

@ -24,7 +24,7 @@ json.features.should.be.instanceof Array
#? expected errors #? expected errors
should.exist json.geocoding.errors should.exist json.geocoding.errors
json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,region,county,locality,continent,macrocountry,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ] json.geocoding.errors.should.eql [ '\'notlayer\' is an invalid layers parameter. Valid options: coarse,address,venue,country,macroregion,region,county,locality,continent,macrocounty,dependency,localadmin,macrohood,neighbourhood,microhood,disputed' ]
#? expected warnings #? expected warnings
should.not.exist json.geocoding.warnings should.not.exist json.geocoding.warnings

58
test/ciao_test_data.js

@ -0,0 +1,58 @@
/**
Test data required by the ciao test suite.
Some tests will fail when run against an empty index, you can use this script
to insert some dummy data in to your index before running the tests.
note: as this is dummy data, care should be taken in order to make sure these
documents don't end up in your production index; for that reason the HTTP port
has been hard-coded as port:9200.
**/
// we use the default config to avoid making calls to
// a cluster running on a non-standard port.
var client = require('elasticsearch').Client(),
async = require('async'),
actions = [];
// add one record per 'type' in order to cause the _default_ mapping
// to be copied when the new type is created.
var types = ['venue','address','county','region','county','country','admin0','admin1','admin2'],
sources = ['test'],
layers = ['geonames'];
// iterate over all types/sources/layers and index a test document
types.forEach( function( type, i1 ){
sources.forEach( function( source, i2 ){
layers.forEach( function( layer, i3 ){
actions.push( function( done ){
client.index({
index: 'pelias',
type: type,
id: [i1,i2,i3].join(':'),
body: {
source: source,
layer: layer,
name: { default: 'test' },
phrase: { default: 'test' },
parent: {
country_a: ['USA']
}
}
});
done();
});
});
});
});
// call refresh so the index merges the changes
actions.push( function( done ){
client.indices.refresh( { index: 'pelias' }, done);
});
// perform all actions in series
async.series( actions, function( err, resp ){
console.log('test data inported');
});

15
test/unit/controller/search.js

@ -123,6 +123,21 @@ module.exports.tests.functional_failure = function(test, common) {
}); });
}; };
module.exports.tests.timeout = function(test, common) {
test('timeout', function(t) {
var backend = mockBackend( 'client/search/timeout/1', function( cmd ){
t.deepEqual(cmd, { body: { a: 'b' }, index: 'pelias', searchType: 'dfs_query_then_fetch' }, 'correct backend command');
});
var controller = setup( backend, mockQuery() );
var req = { clean: { a: 'b' }, errors: [], warnings: [] };
var next = function(){
t.equal(req.errors[0],'Request Timeout after 5000ms');
t.end();
};
controller(req, undefined, next );
});
};
module.exports.all = function (tape, common) { module.exports.all = function (tape, common) {
function test(name, testFunction) { function test(name, testFunction) {

85
test/unit/fixture/autocomplete_with_source_filtering.js

@ -0,0 +1,85 @@
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'analyzer': 'peliasPhrase',
'boost': 100,
'query': 'test',
'type': 'phrase',
'operator': 'and'
}
}
}],
'should':[{
'function_score': {
'query': {
'match': {
'name.default': {
'analyzer': 'peliasPhrase',
'boost': 100,
'query': 'test',
'type': 'phrase',
'operator': 'and'
}
}
},
'max_boost': 20,
'score_mode': 'first',
'boost_mode': 'replace',
'functions': [{
'field_value_factor': {
'modifier': 'log1p',
'field': 'popularity',
'missing': 1
},
'weight': 1
}]
}
},{
'function_score': {
'query': {
'match': {
'name.default': {
'analyzer': 'peliasPhrase',
'boost': 100,
'query': 'test',
'type': 'phrase',
'operator': 'and'
}
}
},
'max_boost': 20,
'score_mode': 'first',
'boost_mode': 'replace',
'functions': [{
'field_value_factor': {
'modifier': 'log1p',
'field': 'population',
'missing': 1
},
'weight': 3
}]
}
}]
}
},
'filter': {
'bool': {
'must': [{
'terms': {
'source': ['test_source']
}
}]
}
}
}
},
'sort': [ '_score' ],
'size': 20,
'track_scores': true
};

93
test/unit/fixture/search_with_source_filtering.js

@ -0,0 +1,93 @@
module.exports = {
'query': {
'filtered': {
'query': {
'bool': {
'must': [{
'match': {
'name.default': {
'query': 'test',
'boost': 1,
'analyzer': 'peliasOneEdgeGram'
}
}
}],
'should': [{
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'boost': 1,
'slop': 2
}
}
},{
'function_score': {
'query': {
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2,
'boost': 1
}
}
},
'max_boost': 20,
'score_mode': 'first',
'boost_mode': 'replace',
'functions': [{
'field_value_factor': {
'modifier': 'log1p',
'field': 'popularity',
'missing': 1
},
'weight': 1
}]
}
},{
'function_score': {
'query': {
'match': {
'phrase.default': {
'query': 'test',
'analyzer': 'peliasPhrase',
'type': 'phrase',
'slop': 2,
'boost': 1
}
}
},
'max_boost': 20,
'score_mode': 'first',
'boost_mode': 'replace',
'functions': [{
'field_value_factor': {
'modifier': 'log1p',
'field': 'population',
'missing': 1
},
'weight': 2
}]
}
}]
}
},
'filter': {
'bool': {
'must': [{
'terms': {
'source': ['test_source']
}
}]
}
}
}
},
'sort': [ '_score' ],
'size': 20,
'track_scores': true
};

24
test/unit/helper/geojsonify.js

@ -59,6 +59,7 @@ module.exports.tests.search = function(test, common) {
'country': 'United Kingdom', 'country': 'United Kingdom',
'region': 'Islington', 'region': 'Islington',
'region_a': 'ISL', 'region_a': 'ISL',
'macroregion': 'England',
'county': 'Angel', 'county': 'Angel',
'localadmin': 'test1', 'localadmin': 'test1',
'locality': 'test2', 'locality': 'test2',
@ -84,6 +85,7 @@ module.exports.tests.search = function(test, common) {
'country': 'United Kingdom', 'country': 'United Kingdom',
'region': 'City And County Of The City Of London', 'region': 'City And County Of The City Of London',
'region_a': 'COL', 'region_a': 'COL',
'macroregion': 'England',
'county': 'Smithfield', 'county': 'Smithfield',
'localadmin': 'test1', 'localadmin': 'test1',
'locality': 'test2', 'locality': 'test2',
@ -106,7 +108,7 @@ module.exports.tests.search = function(test, common) {
'region': 'New York', 'region': 'New York',
'region_a': 'NY', 'region_a': 'NY',
'county': 'New York', 'county': 'New York',
'localadmin': 'Manhattan', 'borough': 'Manhattan',
'locality': 'New York', 'locality': 'New York',
'neighbourhood': 'Koreatown', 'neighbourhood': 'Koreatown',
'category': [ 'category': [
@ -134,10 +136,11 @@ module.exports.tests.search = function(test, common) {
'gid': 'source1:layer1:id1', 'gid': 'source1:layer1:id1',
'layer': 'layer1', 'layer': 'layer1',
'source': 'source1', 'source': 'source1',
'label': '\'Round Midnight Jazz and Blues Bar, test3, Angel', 'label': '\'Round Midnight Jazz and Blues Bar, test2, England, United Kingdom',
'name': '\'Round Midnight Jazz and Blues Bar', 'name': '\'Round Midnight Jazz and Blues Bar',
'country_a': 'GBR', 'country_a': 'GBR',
'country': 'United Kingdom', 'country': 'United Kingdom',
'macroregion': 'England',
'region': 'Islington', 'region': 'Islington',
'region_a': 'ISL', 'region_a': 'ISL',
'county': 'Angel', 'county': 'Angel',
@ -163,10 +166,11 @@ module.exports.tests.search = function(test, common) {
'gid': 'source2:layer2:id2', 'gid': 'source2:layer2:id2',
'layer': 'layer2', 'layer': 'layer2',
'source': 'source2', 'source': 'source2',
'label': 'Blues Cafe, test3, Smithfield', 'label': 'Blues Cafe, test2, England, United Kingdom',
'name': 'Blues Cafe', 'name': 'Blues Cafe',
'country_a': 'GBR', 'country_a': 'GBR',
'country': 'United Kingdom', 'country': 'United Kingdom',
'macroregion': 'England',
'region': 'City And County Of The City Of London', 'region': 'City And County Of The City Of London',
'region_a': 'COL', 'region_a': 'COL',
'county': 'Smithfield', 'county': 'Smithfield',
@ -189,14 +193,14 @@ module.exports.tests.search = function(test, common) {
'gid': 'openstreetmap:venue:node:34633854', 'gid': 'openstreetmap:venue:node:34633854',
'layer': 'venue', 'layer': 'venue',
'source': 'openstreetmap', 'source': 'openstreetmap',
'label': 'Empire State Building, Manhattan, NY, USA', 'label': 'Empire State Building, Manhattan, New York, NY, USA',
'name': 'Empire State Building', 'name': 'Empire State Building',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States',
'region': 'New York', 'region': 'New York',
'region_a': 'NY', 'region_a': 'NY',
'county': 'New York', 'county': 'New York',
'localadmin': 'Manhattan', 'borough': 'Manhattan',
'locality': 'New York', 'locality': 'New York',
'neighbourhood': 'Koreatown' 'neighbourhood': 'Koreatown'
} }
@ -206,6 +210,7 @@ module.exports.tests.search = function(test, common) {
test('geojsonify.search(doc)', function(t) { test('geojsonify.search(doc)', function(t) {
var json = geojsonify.search( input ); var json = geojsonify.search( input );
t.deepEqual(json, expected, 'all docs mapped'); t.deepEqual(json, expected, 'all docs mapped');
t.end(); t.end();
}); });
@ -284,13 +289,13 @@ module.exports.tests.search = function(test, common) {
'county_a': [ 'county_a': [
null null
], ],
'localadmin': [ 'borough': [
'Brooklyn' 'Brooklyn'
], ],
'localadmin_gid': [ 'localadmin_gid': [
'404521211' '404521211'
], ],
'localadmin_a': [ 'borough_a': [
null null
], ],
'locality_gid': [ 'locality_gid': [
@ -330,12 +335,12 @@ module.exports.tests.search = function(test, common) {
'macrocounty_gid': 'MacroCounty Id', 'macrocounty_gid': 'MacroCounty Id',
'macrocounty_a': 'MacroCounty Abbreviation', 'macrocounty_a': 'MacroCounty Abbreviation',
'county': 'Kings County', 'county': 'Kings County',
'borough': 'Brooklyn',
'county_gid': '102082361', 'county_gid': '102082361',
'localadmin': 'Brooklyn',
'localadmin_gid': '404521211', 'localadmin_gid': '404521211',
'locality': 'New York', 'locality': 'New York',
'locality_gid': '85977539', 'locality_gid': '85977539',
'label': 'East New York, Brooklyn, NY, USA' 'label': 'East New York, Brooklyn, New York, NY, USA'
}, },
'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588], 'bbox': [-73.8967895508,40.6514712164,-73.8665771484,40.6737320588],
'geometry': { 'geometry': {
@ -350,6 +355,7 @@ module.exports.tests.search = function(test, common) {
}; };
var json = geojsonify.search( input ); var json = geojsonify.search( input );
t.deepEqual(json, expected, 'all wanted properties exposed'); t.deepEqual(json, expected, 'all wanted properties exposed');
t.end(); t.end();
}); });

205
test/unit/helper/labelGenerator_CAN.js

@ -0,0 +1,205 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
module.exports.tests.canada = function(test, common) {
test('venue', function(t) {
var doc = {
'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'venue name, locality name, region abbr, Canada');
t.end();
});
test('street', function(t) {
var doc = {
'name': 'house number street name',
'layer': 'address',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'house number street name, locality name, region abbr, Canada');
t.end();
});
test('neighbourhood', function(t) {
var doc = {
'name': 'neighbourhood name',
'layer': 'neighbourhood',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'neighbourhood name, locality name, region abbr, Canada');
t.end();
});
test('locality', function(t) {
var doc = {
'name': 'locality name',
'layer': 'locality',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'locality name, region abbr, Canada');
t.end();
});
test('localadmin', function(t) {
var doc = {
'name': 'localadmin name',
'layer': 'localadmin',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'localadmin name, region abbr, Canada');
t.end();
});
test('county', function(t) {
var doc = {
'name': 'county name',
'layer': 'county',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'county name, region abbr, Canada');
t.end();
});
test('macrocounty', function(t) {
var doc = {
'name': 'macrocounty name',
'layer': 'macrocounty',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'macrocounty name, region abbr, Canada');
t.end();
});
test('region', function(t) {
var doc = {
'name': 'region name',
'layer': 'region',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'region name, Canada');
t.end();
});
test('macroregion', function(t) {
var doc = {
'name': 'macroregion name',
'layer': 'macroregion',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'macroregion name, Canada');
t.end();
});
test('country', function(t) {
var doc = {
'name': 'Canada',
'layer': 'country',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'Canada');
t.end();
});
test('region should be used when region_a is not available', function(t) {
var doc = {
'name': 'locality name',
'layer': 'region',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'locality name, region name, Canada', 'region should be used');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator (CAN): ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

190
test/unit/helper/labelGenerator_GBR.js

@ -10,69 +10,183 @@ module.exports.tests.interface = function(test, common) {
}); });
}; };
// GBR street address module.exports.tests.united_kingdom = function(test, common) {
module.exports.tests.one_main_street_uk = function(test, common) { test('venue', function(t) {
test('one main street uk', function(t) {
var doc = { var doc = {
'name': '1 Main St', 'name': 'venue name',
'housenumber': '1', 'layer': 'venue',
'street': 'Main St', 'housenumber': 'house number',
'postalcode': 'BT77 0BG', 'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR', 'country_a': 'GBR',
'country': 'United Kingdom', 'country': 'United Kingdom'
'region': 'Dungannon'
}; };
t.equal(generator(doc),'1 Main St, Dungannon, United Kingdom'); t.equal(generator(doc),'venue name, locality name, macroregion name, United Kingdom');
t.end(); t.end();
}); });
};
// GBR venue test('localadmin value should be used when locality is not available', function(t) {
module.exports.tests.hackney_city_farm = function(test, common) {
test('hackney city farm', function(t) {
var doc = { var doc = {
'name': 'Hackney City Farm', 'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR', 'country_a': 'GBR',
'country': 'United Kingdom', 'country': 'United Kingdom'
'region': 'Hackney',
'county': 'Greater London',
'locality': 'London',
'neighbourhood': 'Haggerston'
}; };
t.equal(generator(doc),'Hackney City Farm, Haggerston, Greater London'); t.equal(generator(doc),'venue name, localadmin name, macroregion name, United Kingdom');
t.end(); t.end();
}); });
};
// GBR country test('street', function(t) {
module.exports.tests.wales = function(test, common) {
test('wales', function(t) {
var doc = { var doc = {
'name': 'Wales', 'name': 'house number street name',
'layer': 'address',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR', 'country_a': 'GBR',
'country': 'United Kingdom', 'country': 'United Kingdom'
'region': 'Wales'
}; };
t.equal(generator(doc),'Wales, United Kingdom'); t.equal(generator(doc),'house number street name, locality name, macroregion name, United Kingdom');
t.end();
});
test('neighbourhood', function(t) {
var doc = {
'name': 'neighbourhood name',
'layer': 'neighbourhood',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'neighbourhood name, locality name, macroregion name, United Kingdom');
t.end();
});
test('locality', function(t) {
var doc = {
'name': 'locality name',
'layer': 'locality',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'locality name, macroregion name, United Kingdom');
t.end();
});
test('localadmin', function(t) {
var doc = {
'name': 'localadmin name',
'layer': 'localadmin',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'localadmin name, macroregion name, United Kingdom');
t.end();
});
test('county', function(t) {
var doc = {
'name': 'county name',
'layer': 'county',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'county name, macroregion name, United Kingdom');
t.end();
});
test('macrocounty', function(t) {
var doc = {
'name': 'macrocounty name',
'layer': 'macrocounty',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'macrocounty name, macroregion name, United Kingdom');
t.end(); t.end();
}); });
};
// GBR macroregion test('region', function(t) {
module.exports.tests.macroregion_trumps_region = function(test, common) {
test('macroregion should trump region when none of neighbourhood, county, localadmin, locality are available', function(t) {
var doc = { var doc = {
'name': 'Name', 'name': 'region name',
'layer': 'region',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'GBR', 'country_a': 'GBR',
'country': 'Country Name', 'country': 'United Kingdom'
'macroregion': 'Macroregion Name',
'region': 'Region Name'
}; };
t.equal(generator(doc),'region name, macroregion name, United Kingdom');
t.end();
});
t.equal(generator(doc), 'Name, Macroregion Name, Country Name'); test('macroregion', function(t) {
var doc = {
'name': 'macroregion name',
'layer': 'macroregion',
'macroregion': 'macroregion name',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'macroregion name, United Kingdom');
t.end(); t.end();
});
test('country', function(t) {
var doc = {
'name': 'United Kingdom',
'layer': 'country',
'postalcode': 'postalcode',
'country_a': 'GBR',
'country': 'United Kingdom'
};
t.equal(generator(doc),'United Kingdom');
t.end();
}); });
}; };
module.exports.all = function (tape, common) { module.exports.all = function (tape, common) {

51
test/unit/helper/labelGenerator_SGP.js

@ -1,51 +0,0 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
// SGP region
module.exports.tests.north_west_singapore = function(test, common) {
test('north west singapore', function(t) {
var doc = {
'name': 'North West',
'country_a': 'SGP',
'country': 'Singapore',
'region': 'North West'
};
t.equal(generator(doc),'North West, Singapore');
t.end();
});
};
// SGP venue
module.exports.tests.singapore_mcdonalds = function(test, common) {
test('singapore_mcdonalds', function(t) {
var doc = {
'name': 'McDonald\'s',
'country_a': 'SGP',
'country': 'Singapore',
'region': 'Central Singapore',
'locality': 'Singapore'
};
t.equal(generator(doc),'McDonald\'s, Central Singapore, Singapore');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator: ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

53
test/unit/helper/labelGenerator_SWE.js

@ -1,53 +0,0 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
// SWE city
module.exports.tests.skane1 = function(test, common) {
test('skĂĄne 1', function(t) {
var doc = {
'name': 'Malmö',
'country_a': 'SWE',
'country': 'Sweden',
'region': 'SkĂĄne',
'county': 'Malmö'
};
t.equal(generator(doc),'Malmö, Skåne, Sweden');
t.end();
});
};
// SWE city
module.exports.tests.skane2 = function(test, common) {
test('skĂĄne 2', function(t) {
var doc = {
'name': 'Malmö',
'country_a': 'SWE',
'country': 'Sweden',
'region': 'SkĂĄne',
'county': 'Malmö',
'locality': 'Malmö'
};
t.equal(generator(doc),'Malmö, Skåne, Sweden');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator: ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

287
test/unit/helper/labelGenerator_USA.js

@ -9,218 +9,233 @@ module.exports.tests.interface = function(test, common) {
}); });
}; };
module.exports.tests.localadmin = function(test, common) { module.exports.tests.united_states = function(test, common) {
test('localadmin should trump locality, neighbourhood, and county', function(t) { test('venue', function(t) {
var doc = { var doc = {
'name': 'Default Name', 'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'Region Name',
'region_a': 'Region Abbr',
'county': 'County Name',
'localadmin': 'LocalAdmin Name',
'locality': 'Locality Name',
'neighbourhood': 'Neighbourhood Name'
}; };
t.equal(generator(doc),'Default Name, LocalAdmin Name, Region Abbr, USA'); t.equal(generator(doc),'venue name, locality name, region abbr, USA');
t.end(); t.end();
}); });
};
module.exports.tests.locality = function(test, common) { test('localadmin value should be used when there is no locality', function(t) {
test('locality should trump neighbourhood and county when localadmin not available', function(t) {
var doc = { var doc = {
'name': 'Default Name', 'name': 'venue name',
'layer': 'venue',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'Region Name',
'region_a': 'Region Abbr',
'county': 'County Name',
'locality': 'Locality Name',
'neighbourhood': 'Neighbourhood Name'
}; };
t.equal(generator(doc),'Default Name, Locality Name, Region Abbr, USA'); t.equal(generator(doc),'venue name, localadmin name, region abbr, USA');
t.end(); t.end();
}); });
};
module.exports.tests.neighbourhood = function(test, common) { test('street', function(t) {
test('neighbourhood should trump county when neither localadmin nor locality', function(t) {
var doc = { var doc = {
'name': 'Default Name', 'name': 'house number street name',
'layer': 'address',
'housenumber': 'house number',
'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'Region Name',
'region_a': 'Region Abbr',
'county': 'County Name',
'neighbourhood': 'Neighbourhood Name'
}; };
t.equal(generator(doc),'Default Name, Neighbourhood Name, Region Abbr, USA'); t.equal(generator(doc),'house number street name, locality name, region abbr, USA');
t.end(); t.end();
}); });
};
module.exports.tests.county = function(test, common) { test('neighbourhood', function(t) {
test('county should be used when localadmin, locality, and neighbourhood are not available', function(t) {
var doc = { var doc = {
'name': 'Default Name', 'name': 'neighbourhood name',
'layer': 'neighbourhood',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'Region Name',
'region_a': 'Region Abbr',
'county': 'County Name'
}; };
t.equal(generator(doc),'Default Name, County Name, Region Abbr, USA'); t.equal(generator(doc),'neighbourhood name, locality name, region abbr, USA');
t.end(); t.end();
}); });
};
module.exports.tests.region = function(test, common) { test('venue in borough', function(t) {
test('region should be used when region_a is not available', function(t) {
var doc = { var doc = {
'name': 'Default Name', 'name': 'venue name',
'layer': 'borough',
'neighbourhood': 'neighbourhood name',
'borough': 'borough name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'Region Name'
}; };
t.equal(generator(doc),'Default Name, Region Name, USA'); t.equal(generator(doc),'venue name, borough name, locality name, region abbr, USA');
t.end(); t.end();
}); });
};
// USA geonames state test('locality', function(t) {
module.exports.tests.region_geonames = function(test, common) {
test('default name should not be prepended when source=geonames and layer=region', function(t) {
var doc = { var doc = {
'name': 'Region Name', 'name': 'locality name',
'layer': 'locality',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'Region Name',
'region_a': 'Region Abbr',
'source': 'geonames',
'layer': 'region'
}; };
t.equal(generator(doc),'Region Name, USA'); t.equal(generator(doc),'locality name, region abbr, USA');
t.end(); t.end();
}); });
};
// USA whosonfirst state test('localadmin', function(t) {
module.exports.tests.region_whosonfirst = function(test, common) {
test('default name should not be prepended when source=whosonfirst and layer=region', function(t) {
var doc = { var doc = {
'name': 'Region Name', 'name': 'localadmin name',
'layer': 'localadmin',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'Region Name',
'region_a': 'Region Abbr',
'source': 'whosonfirst',
'layer': 'region'
}; };
t.equal(generator(doc),'Region Name, USA'); t.equal(generator(doc),'localadmin name, region abbr, USA');
t.end(); t.end();
}); });
};
// USA non-geonames/whosonfirst state test('county', function(t) {
module.exports.tests.region_other_source = function(test, common) {
test('default name should be prepended when layer=region and source is not whosonfirst or geonames', function(t) {
var doc = { var doc = {
'name': 'Default Name', 'name': 'county name',
'layer': 'county',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'Region Name',
'region_a': 'Region Abbr',
'source': 'not geonames or whosonfirst',
'layer': 'region'
}; };
t.equal(generator(doc),'Default Name, Region Name, USA',generator(doc)); t.equal(generator(doc),'county name, region abbr, USA');
t.end(); t.end();
}); });
};
// major USA city test('macrocounty', function(t) {
module.exports.tests.san_francisco = function(test, common) {
test('san francisco', function(t) {
var doc = { var doc = {
'name': 'San Francisco', 'name': 'macrocounty name',
'layer': 'macrocounty',
'macrocounty': 'macrocounty name',
'region_a': 'region abbr',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'California',
'region_a': 'CA',
'county': 'San Francisco County',
'locality': 'San Francisco'
}; };
t.equal(generator(doc),'San Francisco, San Francisco County, CA, USA'); t.equal(generator(doc),'macrocounty name, region abbr, USA');
t.end(); t.end();
}); });
};
// USA venue test('region', function(t) {
module.exports.tests.nyc_office = function(test, common) {
test('30 West 26th Street', function(t) {
var doc = { var doc = {
'name': '30 West 26th Street', 'name': 'region name',
'housenumber': '30', 'layer': 'region',
'street': 'West 26th Street', 'region_a': 'region abbr',
'postalcode': '10010', 'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'New York',
'region_a': 'NY',
'county': 'New York County',
'localadmin': 'Manhattan',
'locality': 'New York',
'neighbourhood': 'Flatiron District'
}; };
t.equal(generator(doc),'30 West 26th Street, Manhattan, NY, USA'); t.equal(generator(doc),'region name, USA');
t.end(); t.end();
}); });
};
// USA NYC eatery test('macroregion', function(t) {
module.exports.tests.nyc_bakery = function(test, common) {
test('New York Bakery', function(t) {
var doc = { var doc = {
'name': 'New York Bakery', 'name': 'macroregion name',
'housenumber': '51 W', 'layer': 'macroregion',
'street': '29th', 'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'New York',
'region_a': 'NY',
'county': 'New York County',
'localadmin': 'Manhattan',
'locality': 'New York',
'neighbourhood': 'Koreatown'
}; };
t.equal(generator(doc),'New York Bakery, Manhattan, NY, USA'); t.equal(generator(doc),'macroregion name, USA');
t.end();
});
test('country', function(t) {
var doc = {
'name': 'United States',
'layer': 'country',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'United States');
t.end(); t.end();
}); });
};
// USA SFC building test('region should be used when region_a is not available', function(t) {
module.exports.tests.ferry_building = function(test, common) {
test('Ferry Building', function(t) {
var doc = { var doc = {
'name': 'Ferry Building', 'name': 'locality name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'USA', 'country_a': 'USA',
'country': 'United States', 'country': 'United States'
'region': 'California',
'region_a': 'CA',
'county': 'San Francisco County',
'locality': 'San Francisco',
'neighbourhood': 'Financial District'
}; };
t.equal(generator(doc),'Ferry Building, San Francisco, CA, USA'); t.equal(generator(doc),'locality name, region name, USA', 'region should be used');
t.end(); t.end();
}); });
}; };
module.exports.all = function (tape, common) { module.exports.all = function (tape, common) {
function test(name, testFunction) { function test(name, testFunction) {
return tape('label generator: ' + name, testFunction); return tape('label generator (USA): ' + name, testFunction);
} }
for( var testCase in module.exports.tests ){ for( var testCase in module.exports.tests ){

356
test/unit/helper/labelGenerator_default.js

@ -10,296 +10,182 @@ module.exports.tests.interface = function(test, common) {
}); });
}; };
// AUS state module.exports.tests.default_country = function(test, common) {
module.exports.tests.new_south_wales = function(test, common) { test('venue', function(t) {
test('new south wales', function(t) {
var doc = { var doc = {
'name': 'New South Wales', 'name': 'venue name',
'country_a': 'AUS', 'layer': 'venue',
'country': 'Australia', 'housenumber': 'house number',
'region': 'New South Wales' 'street': 'street name',
'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
}; };
t.equal(generator(doc),'New South Wales, Australia'); t.equal(generator(doc),'venue name, locality name, country name');
t.end(); t.end();
}); });
};
// IND state
module.exports.tests.west_bengal = function(test, common) {
test('west bengal', function(t) {
var doc = {
'name': 'West Bengal',
'country_a': 'IND',
'country': 'India',
'region': 'West Bengal'
};
t.equal(generator(doc),'West Bengal, India');
t.end();
});
};
// IND city
module.exports.tests.bangalore = function(test, common) {
test('bangalore', function(t) {
var doc = {
'name': 'Bangalore',
'country_a': 'IND',
'country': 'India',
'region': 'Karnataka',
'county': 'Bangalore',
'locality': 'Bangalore'
};
t.equal(generator(doc),'Bangalore, Karnataka, India');
t.end();
});
};
// IND region of city
module.exports.tests.sarjapur = function(test, common) {
test('Sarjapur', function(t) {
var doc = {
'name': 'Sarjapur',
'country_a': 'IND',
'country': 'India',
'region': 'Karnataka'
};
t.equal(generator(doc),'Sarjapur, Karnataka, India');
t.end();
});
};
// IND region of city 2 test('localadmin value should be used when locality is not available', function(t) {
module.exports.tests.bengaluru_east = function(test, common) {
test('Bengaluru East', function(t) {
var doc = { var doc = {
'name': 'Bengaluru East', 'name': 'venue name',
'country_a': 'IND', 'layer': 'venue',
'country': 'India', 'housenumber': 'house number',
'region': 'Karnataka', 'street': 'street name',
'county': 'Bangalore', 'neighbourhood': 'neighbourhood name',
'locality': 'Bangalore', 'localadmin': 'localadmin name',
'neighbourhood': 'Fraser Town' 'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
}; };
t.equal(generator(doc),'Bengaluru East, Bangalore, India'); t.equal(generator(doc),'venue name, localadmin name, country name');
t.end(); t.end();
}); });
};
// AUS area test('street', function(t) {
// https://en.wikipedia.org/wiki/Shire_of_Wellington
module.exports.tests.wellington_victoria = function(test, common) {
test('Wellington, Victoria, Australia', function(t) {
var doc = { var doc = {
'name': 'Wellington', 'name': 'house number street name',
'country_a': 'AUS', 'layer': 'address',
'country': 'Australia', 'housenumber': 'house number',
'region': 'Victoria', 'street': 'street name',
'county': 'Wellington' 'neighbourhood': 'neighbourhood name',
'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
}; };
t.equal(generator(doc),'Wellington, Victoria, Australia'); t.equal(generator(doc),'house number street name, locality name, country name');
t.end(); t.end();
}); });
};
// IRQ region test('neighbourhood', function(t) {
module.exports.tests.arbil = function(test, common) {
test('arbil', function(t) {
var doc = { var doc = {
'name': 'Arbil', 'name': 'neighbourhood name',
'country_a': 'IRQ', 'layer': 'neighbourhood',
'country': 'Iraq', 'neighbourhood': 'neighbourhood name',
'region': 'Arbil' 'locality': 'locality name',
'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
}; };
t.equal(generator(doc),'Arbil, Iraq'); t.equal(generator(doc),'neighbourhood name, locality name, country name');
t.end(); t.end();
}); });
};
// ESP city test('locality', function(t) {
module.exports.tests.madrid = function(test, common) {
test('madrid', function(t) {
var doc = { var doc = {
'name': 'Madrid', 'name': 'locality name',
'country_a': 'ESP', 'layer': 'locality',
'country': 'Spain', 'locality': 'locality name',
'region': 'Madrid' 'localadmin': 'localadmin name',
'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
}; };
t.equal(generator(doc),'Madrid, Spain'); t.equal(generator(doc),'locality name, country name');
t.end(); t.end();
}); });
};
// DEU street address test('localadmin', function(t) {
module.exports.tests.one_grolmanstrasse = function(test, common) {
test('one grolmanstrasse', function(t) {
var doc = { var doc = {
'name': '1 GrolmanstraĂźe', 'name': 'localadmin name',
'housenumber': '1', 'layer': 'localadmin',
'street': 'GrolmanstraĂźe', 'localadmin': 'localadmin name',
'postalcode': '10623', 'county': 'county name',
'country_a': 'DEU', 'macrocounty': 'macrocounty name',
'country': 'Germany', 'region': 'region name',
'region': 'Berlin', 'macroregion': 'macroregion name',
'county': 'Berlin', 'country_a': 'country code',
'locality': 'Berlin', 'country': 'country name'
'neighbourhood': 'Halensee'
}; };
t.equal(generator(doc),'1 GrolmanstraĂźe, Berlin, Germany'); t.equal(generator(doc),'localadmin name, country name');
t.end(); t.end();
}); });
};
// NZD country test('county', function(t) {
module.exports.tests.new_zealand = function(test, common) {
test('new zealand', function(t) {
var doc = { var doc = {
'name': 'New Zealand', 'name': 'county name',
'country_a': 'NZL', 'layer': 'county',
'country': 'New Zealand' 'county': 'county name',
'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
}; };
t.equal(generator(doc),'New Zealand'); t.equal(generator(doc),'county name, country name');
t.end(); t.end();
}); });
};
// IRL country test('macrocounty', function(t) {
module.exports.tests.republic_of_ireland = function(test, common) {
test('northern ireland', function(t) {
var doc = { var doc = {
'name': 'Ireland', 'name': 'macrocounty name',
'country_a': 'IRL', 'layer': 'macrocounty',
'country': 'Ireland' 'macrocounty': 'macrocounty name',
'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
}; };
// !! this is not part of the UK !! t.equal(generator(doc),'macrocounty name, country name');
t.equal(generator(doc),'Ireland');
t.end(); t.end();
}); });
};
// THA province test('region', function(t) {
module.exports.tests.krabi_province = function(test, common) {
test('Krabi Provence', function(t) {
var doc = { var doc = {
'name': 'Krabi', 'name': 'region name',
'country_a': 'THA',
'country': 'Thailand',
'region': 'Krabi'
};
t.equal(generator(doc),'Krabi, Thailand');
t.end();
});
};
// THA island
module.exports.tests.koh_lanta = function(test, common) {
test('Koh Lanta', function(t) {
var doc = {
'name': 'Ko Lanta',
'country_a': 'THA',
'country': 'Thailand',
'region': 'Krabi'
};
t.equal(generator(doc),'Ko Lanta, Krabi, Thailand');
t.end();
});
};
// NZD cafe
module.exports.tests.black_dog_cafe = function(test, common) {
test('Black Dog Cafe', function(t) {
var doc = {
'name': 'Black Dog Cafe',
'country_a': 'NZL',
'country': 'New Zealand',
'region': 'Auckland Region',
'county': 'Auckland'
};
t.equal(generator(doc),'Black Dog Cafe, Auckland, New Zealand');
t.end();
});
};
// NZD cafe 2
module.exports.tests.beach_bablyon = function(test, common) {
test('Beach Bablyon', function(t) {
var doc = {
'name': 'Beach Bablyon',
'country_a': 'NZL',
'country': 'New Zealand',
'region': 'Wellington Region',
'county': 'Wellington City',
'locality': 'Wellington',
'neighbourhood': 'Oriental Bay'
};
t.equal(generator(doc),'Beach Bablyon, Wellington, New Zealand');
t.end();
});
};
// NZD tourism
module.exports.tests.waiotapu = function(test, common) {
test('Waiotapu', function(t) {
var doc = {
'name': 'Waiotapu',
'country_a': 'NZL',
'country': 'New Zealand',
'region': 'Bay of Plenty',
'county': 'Rotorua District'
};
t.equal(generator(doc),'Waiotapu, Rotorua District, New Zealand');
t.end();
});
};
module.exports.tests.non_us_or_ca_region = function(test, common) {
test('geonames US', function(t) {
var doc = {
'name': 'Default Name',
'country_a': 'XYZ',
'country': 'Full Country Name',
'region': 'Full Region Name',
'layer': 'region', 'layer': 'region',
'source': 'geonames' 'region': 'region name',
'macroregion': 'macroregion name',
'country_a': 'country code',
'country': 'country name'
}; };
t.equal(generator(doc),'region name, country name');
t.equal(generator(doc), 'Default Name, Full Region Name, Full Country Name');
t.end(); t.end();
}); });
test('whosonfirst US', function(t) { test('macroregion', function(t) {
var doc = { var doc = {
'name': 'Default Name', 'name': 'macroregion name',
'country_a': 'XYZ', 'layer': 'macroregion',
'country': 'Full Country Name', 'macroregion': 'macroregion name',
'region': 'Full Region Name', 'country_a': 'country code',
'layer': 'region', 'country': 'country name'
'source': 'whosonfirst'
}; };
t.equal(generator(doc),'macroregion name, country name');
t.equal(generator(doc), 'Default Name, Full Region Name, Full Country Name');
t.end(); t.end();
}); });
}; test('country', function(t) {
// macroregion
module.exports.tests.macroregion_trumps_region = function(test, common) {
test('macroregion should trump region when none of localadmin, locality, neighbourhood, county are available', function(t) {
var doc = { var doc = {
'name': 'Name', 'name': 'country name',
'country_a': 'Country abbreviation', 'layer': 'country',
'country': 'Country Name', 'country_a': 'country code',
'macroregion': 'Macroregion Name', 'country': 'country name'
'region': 'Region Name'
}; };
t.equal(generator(doc),'country name');
t.equal(generator(doc), 'Name, Macroregion Name, Country Name');
t.end(); t.end();
}); });
}; };
module.exports.all = function (tape, common) { module.exports.all = function (tape, common) {

116
test/unit/helper/labelGenerator_examples.js

@ -0,0 +1,116 @@
var generator = require('../../../helper/labelGenerator');
module.exports.tests = {};
module.exports.tests.interface = function(test, common) {
test('interface', function(t) {
t.equal(typeof generator, 'function', 'valid function');
t.end();
});
};
module.exports.tests.canada = function(test, common) {
test('venue', function(t) {
var doc = {
'name': 'Tim Horton\'s',
'layer': 'venue',
'housenumber': '1',
'street': 'Main St',
'neighbourhood': 'College Heights',
'locality': 'Thunder Bay',
'region_a': 'ON',
'region': 'Ontario',
'country_a': 'CAN',
'country': 'Canada'
};
t.equal(generator(doc),'Tim Horton\'s, Thunder Bay, ON, Canada');
t.end();
});
test('address', function(t) {
var doc = {
'name': '1 Main St',
'layer': 'venue',
'housenumber': '1',
'street': 'Main St',
'locality': 'Truth or Consequences',
'region_a': 'NM',
'region': 'New Mexico',
'country_a': 'USA',
'country': 'United States'
};
t.equal(generator(doc),'1 Main St, Truth or Consequences, NM, USA');
t.end();
});
};
module.exports.tests.france = function(test, common) {
test('eiffel tower', function(t) {
var doc = {
'name': 'Tour Eiffel',
'layer': 'venue',
'neighbourhood': 'Quartier du Gros-Caillou',
'locality': 'Paris',
'region': 'Paris',
'country_a': 'FRA',
'country': 'France'
};
t.equal(generator(doc),'Tour Eiffel, Paris, France');
t.end();
});
test('France street address', function(t) {
var doc = {
'name': '74 rue de rivoli',
'layer': 'address',
'housenumber': '74',
'street': 'Rue de Rivoli',
'neighbourhood': 'Quartier Saint-Merri',
'locality': 'Paris',
'region': 'Paris',
'country_a': 'FRA',
'country': 'France'
};
t.equal(generator(doc),'74 rue de rivoli, Paris, France');
t.end();
});
test('France neighbourhood', function(t) {
var doc = {
'name': 'Grange aux Belles Terrage',
'layer': 'neighbourhood',
'neighbourhood': 'Grange aux Belles Terrage',
'locality': 'Paris',
'region': 'Paris',
'country_a': 'FRA',
'country': 'France'
};
t.equal(generator(doc),'Grange aux Belles Terrage, Paris, France');
t.end();
});
test('Luxembourg (the city) in Luxembourg', function(t) {
var doc = {
'name': 'Luxembourg',
'layer': 'locality',
'locality': 'Luxembourg',
'country_a': 'LUX',
'country': 'Luxembourg'
};
// console.error(generator(doc));
t.equal(generator(doc),'Luxembourg, Luxembourg');
t.end();
});
};
module.exports.all = function (tape, common) {
function test(name, testFunction) {
return tape('label generator (CAN): ' + name, testFunction);
}
for( var testCase in module.exports.tests ){
module.exports.tests[testCase](test, common);
}
};

770
test/unit/helper/labelSchema.js

@ -1,4 +1,3 @@
var schemas = require('../../../helper/labelSchema'); var schemas = require('../../../helper/labelSchema');
var alpha3 = require('../mock/alpha3.json'); var alpha3 = require('../mock/alpha3.json');
@ -13,20 +12,18 @@ module.exports.tests.interface = function(test, common) {
}; };
module.exports.tests.supported_countries = function(test, common) { module.exports.tests.supported_countries = function(test, common) {
test('support countries', function(t) { test('supported countries', function(t) {
var supported_countries = Object.keys(schemas); var supported_countries = Object.keys(schemas);
t.notEquals(supported_countries.indexOf('USA'), -1); t.notEquals(supported_countries.indexOf('USA'), -1);
t.notEquals(supported_countries.indexOf('CAN'), -1);
t.notEquals(supported_countries.indexOf('GBR'), -1); t.notEquals(supported_countries.indexOf('GBR'), -1);
t.notEquals(supported_countries.indexOf('SGP'), -1);
t.notEquals(supported_countries.indexOf('SWE'), -1);
t.notEquals(supported_countries.indexOf('default'), -1); t.notEquals(supported_countries.indexOf('default'), -1);
t.equals(supported_countries.length, 5); t.equals(supported_countries.length, 4);
t.equals(Object.keys(schemas.USA).length, 3); t.equals(Object.keys(schemas.USA).length, 4);
t.equals(Object.keys(schemas.GBR).length, 2); t.equals(Object.keys(schemas.CAN).length, 3);
t.equals(Object.keys(schemas.SGP).length, 2); t.equals(Object.keys(schemas.GBR).length, 3);
t.equals(Object.keys(schemas.SWE).length, 2);
t.equals(Object.keys(schemas.default).length, 2); t.equals(Object.keys(schemas.default).length, 2);
t.end(); t.end();
@ -34,761 +31,6 @@ module.exports.tests.supported_countries = function(test, common) {
}); });
}; };
module.exports.tests.usa = function(test, common) {
test('USA.local should use localadmin value over locality, neighbourhood, and county', function(t) {
var record = {
localadmin: 'localadmin value',
locality: 'locality value',
neighbourhood: 'neighbourhood value',
county: 'county value'
};
var labelParts = ['initial value'];
var f = schemas.USA.local;
t.deepEqual(f(record, labelParts), ['initial value', 'localadmin value']);
t.end();
});
test('USA.local should use locality value over neighbourhood and county when no localadmin', function(t) {
var record = {
locality: 'locality value',
neighbourhood: 'neighbourhood value',
county: 'county value'
};
var labelParts = ['initial value'];
var f = schemas.USA.local;
t.deepEqual(f(record, labelParts), ['initial value', 'locality value']);
t.end();
});
test('USA.local should use neighbourhood value over county when no localadmin or locality', function(t) {
var record = {
neighbourhood: 'neighbourhood value',
county: 'county value'
};
var labelParts = ['initial value'];
var f = schemas.USA.local;
t.deepEqual(f(record, labelParts), ['initial value', 'neighbourhood value']);
t.end();
});
test('USA.local should use county value when no localadmin, locality, or neighbourhood', function(t) {
var record = {
county: 'county value'
};
var labelParts = ['initial value'];
var f = schemas.USA.local;
t.deepEqual(f(record, labelParts), ['initial value', 'county value']);
t.end();
});
test('USA.local should not modify labelParts if none of localadmin, locality, neighbourhood, or county is available', function(t) {
var record = {};
var labelParts = ['initial value'];
var f = schemas.USA.local;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
test('USA.regional should use region when layer=region and region is available', function(t) {
var record = {
layer: 'region',
region: 'region name',
region_a: 'region_a name'
};
var labelParts = ['initial value'];
var f = schemas.USA.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'region name']);
t.end();
});
test('USA.regional should use region_a when layer=region and region is unavailable', function(t) {
var record = {
layer: 'region',
region_a: 'region_a name'
};
var labelParts = ['initial value'];
var f = schemas.USA.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'region_a name']);
t.end();
});
test('USA.regional should use region_a when layer!=region and both region and region_a are available', function(t) {
var record = {
layer: 'not region',
region: 'region name',
region_a: 'region_a name'
};
var labelParts = ['initial value'];
var f = schemas.USA.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'region_a name']);
t.end();
});
test('USA.regional should use region when layer!=region and region_a is unavailable', function(t) {
var record = {
layer: 'region',
region: 'region name',
region_a: 'region_a name'
};
var labelParts = ['initial value'];
var f = schemas.USA.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'region name'], 'region should have been appended');
t.end();
});
test('USA.regional should not append anything when neither region nor region_a are available', function(t) {
var record = {
layer: 'region',
};
var labelParts = ['initial value'];
var f = schemas.USA.regional;
t.deepEqual(f(record, labelParts), ['initial value'], 'no USA.region should have appended');
t.end();
});
test('USA.country should append country_a when available', function(t) {
var record = {
country_a: 'country_a name',
country: 'country name'
};
var labelParts = ['initial value'];
var f = schemas.USA.country;
t.deepEqual(f(record, labelParts), ['initial value', 'country_a name'], 'country_a should have appended');
t.end();
});
test('USA.country should not append anything when country_a is unavailable', function(t) {
var record = {
country: 'country name'
};
var labelParts = ['initial value'];
var f = schemas.USA.country;
t.deepEqual(f(record, labelParts), ['initial value'], 'no USA.country should have appended');
t.end();
});
};
module.exports.tests.gbr = function(test, common) {
test('GBR.local should use neighbourhood value over county, localadmin, locality, region', function(t) {
var record = {
neighbourhood: 'neighbourhood value',
county: 'county value',
localadmin: 'localadmin value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.GBR.local;
t.deepEqual(f(record, labelParts), ['initial value', 'neighbourhood value']);
t.end();
});
test('GBR.local should use county value over county, localadmin, locality, region', function(t) {
var record = {
county: 'county value',
localadmin: 'localadmin value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.GBR.local;
t.deepEqual(f(record, labelParts), ['initial value', 'county value']);
t.end();
});
test('GBR.local should use localadmin value over locality, region', function(t) {
var record = {
localadmin: 'localadmin value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.GBR.local;
t.deepEqual(f(record, labelParts), ['initial value', 'localadmin value']);
t.end();
});
test('GBR.local should use locality value over region', function(t) {
var record = {
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.GBR.local;
t.deepEqual(f(record, labelParts), ['initial value', 'locality value']);
t.end();
});
test('GBR.local should use region value when nothing else is available', function(t) {
var record = {
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.GBR.local;
t.deepEqual(f(record, labelParts), ['initial value', 'region value']);
t.end();
});
test('GBR.local should not append anything when none of neighbourhood, county, localadmin, locality, region are available', function(t) {
var record = {};
var labelParts = ['initial value'];
var f = schemas.GBR.local;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
test('GBR.regional should use county over country and region', function(t) {
var record = {
county: 'county value',
country: 'country value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.GBR.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'county value']);
t.end();
});
test('GBR.regional should use country over region', function(t) {
var record = {
country: 'country value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.GBR.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'country value']);
t.end();
});
test('GBR.regional should use region when county and country aren not available', function(t) {
var record = {
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.GBR.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'region value']);
t.end();
});
test('GBR.regional should not append anything when none of county, country, or region are available', function(t) {
var record = {};
var labelParts = ['initial value'];
var f = schemas.GBR.regional;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
};
module.exports.tests.sgp = function(test, common) {
test('SGP.local should use neighbourhood value over region, county, localadmin, locality', function(t) {
var record = {
neighbourhood: 'neighbourhood value',
county: 'county value',
localadmin: 'localadmin value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.local;
t.deepEqual(f(record, labelParts), ['initial value', 'neighbourhood value']);
t.end();
});
test('SGP.local should use region value over county, localadmin, locality', function(t) {
var record = {
county: 'county value',
localadmin: 'localadmin value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.local;
t.deepEqual(f(record, labelParts), ['initial value', 'region value']);
t.end();
});
test('SGP.local should use county value over localadmin, locality', function(t) {
var record = {
localadmin: 'localadmin value',
locality: 'locality value',
county: 'county value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.local;
t.deepEqual(f(record, labelParts), ['initial value', 'county value']);
t.end();
});
test('SGP.local should use localadmin value over locality', function(t) {
var record = {
localadmin: 'localadmin value',
locality: 'locality value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.local;
t.deepEqual(f(record, labelParts), ['initial value', 'localadmin value']);
t.end();
});
test('SGP.local should use locality value when nothing else is available', function(t) {
var record = {
locality: 'locality value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.local;
t.deepEqual(f(record, labelParts), ['initial value', 'locality value']);
t.end();
});
test('SGP.local should not append anything when none of neighbourhood, region, county, localadmin, locality are available', function(t) {
var record = {};
var labelParts = ['initial value'];
var f = schemas.SGP.local;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
test('SGP.regional should use county over country and region', function(t) {
var record = {
county: 'county value',
country: 'country value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'county value']);
t.end();
});
test('SGP.regional should use country over region', function(t) {
var record = {
country: 'country value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'country value']);
t.end();
});
test('SGP.regional should use region when county and country aren not available', function(t) {
var record = {
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'region value']);
t.end();
});
test('SGP.regional should not append anything when none of county, country, or region are available', function(t) {
var record = {};
var labelParts = ['initial value'];
var f = schemas.SGP.regional;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
};
module.exports.tests.swe = function(test, common) {
test('SWE.local should use neighbourhood value over region, county, localadmin, locality', function(t) {
var record = {
neighbourhood: 'neighbourhood value',
county: 'county value',
localadmin: 'localadmin value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.SWE.local;
t.deepEqual(f(record, labelParts), ['initial value', 'neighbourhood value']);
t.end();
});
test('SWE.local should use region value over county, localadmin, locality', function(t) {
var record = {
county: 'county value',
localadmin: 'localadmin value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.SWE.local;
t.deepEqual(f(record, labelParts), ['initial value', 'region value']);
t.end();
});
test('SWE.local should use county value over localadmin, locality', function(t) {
var record = {
localadmin: 'localadmin value',
locality: 'locality value',
county: 'county value'
};
var labelParts = ['initial value'];
var f = schemas.SWE.local;
t.deepEqual(f(record, labelParts), ['initial value', 'county value']);
t.end();
});
test('SWE.local should use localadmin value over locality', function(t) {
var record = {
localadmin: 'localadmin value',
locality: 'locality value'
};
var labelParts = ['initial value'];
var f = schemas.SWE.local;
t.deepEqual(f(record, labelParts), ['initial value', 'localadmin value']);
t.end();
});
test('SWE.local should use locality value when nothing else is available', function(t) {
var record = {
locality: 'locality value'
};
var labelParts = ['initial value'];
var f = schemas.SWE.local;
t.deepEqual(f(record, labelParts), ['initial value', 'locality value']);
t.end();
});
test('SWE.local should not append anything when none of neighbourhood, region, county, localadmin, locality are available', function(t) {
var record = {};
var labelParts = ['initial value'];
var f = schemas.SWE.local;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
test('SGP.regional should use country when available', function(t) {
var record = {
country: 'country value',
country_a: 'country_a value',
};
var labelParts = ['initial value'];
var f = schemas.SGP.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'country value']);
t.end();
});
test('SGP.regional should not append anything when country is not available', function(t) {
var record = {
country_a: 'country_a value'
};
var labelParts = ['initial value'];
var f = schemas.SGP.regional;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
};
module.exports.tests.default = function(test, common) {
test('default.local should use localadmin value over locality, neighbourhood, county, region', function(t) {
var record = {
neighbourhood: 'neighbourhood value',
county: 'county value',
localadmin: 'localadmin value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.default.local;
t.deepEqual(f(record, labelParts), ['initial value', 'localadmin value']);
t.end();
});
test('default.local should use locality value over neighbourhood, county, region', function(t) {
var record = {
neighbourhood: 'neighbourhood value',
county: 'county value',
locality: 'locality value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.default.local;
t.deepEqual(f(record, labelParts), ['initial value', 'locality value']);
t.end();
});
test('default.local should use neighbourhood value over county, region', function(t) {
var record = {
neighbourhood: 'neighbourhood value',
county: 'county value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.default.local;
t.deepEqual(f(record, labelParts), ['initial value', 'neighbourhood value']);
t.end();
});
test('default.local should use county value over region', function(t) {
var record = {
county: 'county value',
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.default.local;
t.deepEqual(f(record, labelParts), ['initial value', 'county value']);
t.end();
});
test('default.local should use region value when nothing else is available', function(t) {
var record = {
region: 'region value'
};
var labelParts = ['initial value'];
var f = schemas.default.local;
t.deepEqual(f(record, labelParts), ['initial value', 'region value']);
t.end();
});
test('default.local should not append anything when none of neighbourhood, region, county, localadmin, ' +
'locality are available', function(t) {
var record = {};
var labelParts = ['initial value'];
var f = schemas.default.local;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
test('default.regional should use country over region, region_a, or country_a', function(t) {
var record = {
region: 'region value',
region_a: 'region_a value',
country: 'country value',
country_a: 'country_a value'
};
var labelParts = ['initial value'];
var f = schemas.default.regional;
t.deepEqual(f(record, labelParts), ['initial value', 'country value']);
t.end();
});
test('default.regional should not append any value if country is not available', function(t) {
var record = {
region: 'region value',
region_a: 'region_a value',
country_a: 'country_a value'
};
var labelParts = ['initial value'];
var f = schemas.default.regional;
t.deepEqual(f(record, labelParts), ['initial value']);
t.end();
});
test('USA.local borough should be shown if available', function(t) {
var record = {
borough: 'borough value',
region: 'region value',
region_a: 'region_a value',
country_a: 'country_a value'
};
var labelParts = ['initial value'];
var f = schemas.USA.local;
t.deepEqual(f(record, labelParts), ['initial value', 'borough value']);
t.end();
});
};
module.exports.all = function (tape, common) { module.exports.all = function (tape, common) {
function test(name, testFunction) { function test(name, testFunction) {

20
test/unit/helper/text_parser.js

@ -29,6 +29,15 @@ module.exports.tests.split_on_comma = function(test, common) {
t.equal(address.admin_parts, query.admin_parts, 'admin_parts set correctly to ' + address.admin_parts); t.equal(address.admin_parts, query.admin_parts, 'admin_parts set correctly to ' + address.admin_parts);
t.end(); t.end();
}); });
test('naive parsing ' + query + 'without spaces', function(t) {
var address = parser.get_parsed_address(query.name + ',' + query.admin_parts);
t.equal(typeof address, 'object', 'valid object');
t.equal(address.name, query.name, 'name set correctly to ' + address.name);
t.equal(address.admin_parts, query.admin_parts, 'admin_parts set correctly to ' + address.admin_parts);
t.end();
});
}); });
}; };
@ -115,6 +124,17 @@ module.exports.tests.parse_address = function(test, common) {
t.equal(address.postalcode, '06410', 'parsed zip'); t.equal(address.postalcode, '06410', 'parsed zip');
t.end(); t.end();
}); });
test('valid address without spaces after commas', function(t) {
var query_string = '339 W Main St,Lancaster,PA';
var address = parser.get_parsed_address(query_string);
t.equal(typeof address, 'object', 'valid object for the address');
t.equal(address.number, '339', 'parsed house number');
t.equal(address.street, 'W Main St', 'parsed street');
t.deepEqual(address.regions, ['Lancaster'], 'parsed city');
t.deepEqual(address.state, 'PA', 'parsed state');
t.end();
});
}; };

4
test/unit/helper/type_mapping.js

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

19
test/unit/middleware/localNamingConventions.js

@ -77,8 +77,21 @@ module.exports.tests.flipNumberAndStreet = function(test, common) {
} }
}; };
var unknownCountryAddress = {
'_id': 'test4',
'_type': 'test',
'name': { 'default': '123 Main Street' },
'center_point': { 'lon': 30.1, 'lat': -50 },
'address_parts': {
'number': '123',
'street': 'Main Street'
},
'parent': {
}
};
var req = {}, var req = {},
res = { data: [ ukAddress, deAddress, nlAddress ] }, res = { data: [ ukAddress, deAddress, nlAddress, unknownCountryAddress ] },
middleware = localNamingConventions(); middleware = localNamingConventions();
test('flipNumberAndStreet', function(t) { test('flipNumberAndStreet', function(t) {
@ -96,6 +109,10 @@ module.exports.tests.flipNumberAndStreet = function(test, common) {
// this definition comes from pelias configuration // this definition comes from pelias configuration
t.equal( res.data[2].name.default, 'Keizersgracht 117', 'flipped name' ); t.equal( res.data[2].name.default, 'Keizersgracht 117', 'flipped name' );
// addresses without a known country (either due to missing data or admin lookup
// being disabled), don't have the name flipped
t.equal( res.data[3].name.default, '123 Main Street', 'standard name');
t.end(); t.end();
}); });
}); });

8
test/unit/mock/backend.js

@ -33,6 +33,14 @@ responses['client/search/fail/1'] = function( cmd, cb ){
return cb( 'a backend error occurred' ); return cb( 'a backend error occurred' );
}; };
responses['client/search/timeout/1'] = function( cmd, cb) {
// timeout errors are objects
return cb({
status: 408,
message: 'Request Timeout after 5000ms'
});
};
responses['client/mget/ok/1'] = function( cmd, cb ){ responses['client/mget/ok/1'] = function( cmd, cb ){
return cb( undefined, mgetEnvelope([{ return cb( undefined, mgetEnvelope([{
_id: 'myid1', _id: 'myid1',

13
test/unit/query/autocomplete.js

@ -95,6 +95,19 @@ module.exports.tests.query = function(test, common) {
t.deepEqual(compiled, expected, 'valid autocomplete query'); t.deepEqual(compiled, expected, 'valid autocomplete query');
t.end(); t.end();
}); });
test('valid sources filter', function(t) {
var query = generate({
'text': 'test',
'sources': ['test_source']
});
var compiled = JSON.parse( JSON.stringify( query ) );
var expected = require('../fixture/autocomplete_with_source_filtering');
t.deepEqual(compiled, expected, 'valid autocomplete query with source filtering');
t.end();
});
}; };
module.exports.all = function (tape, common) { module.exports.all = function (tape, common) {

13
test/unit/query/search.js

@ -182,6 +182,19 @@ module.exports.tests.query = function(test, common) {
t.end(); t.end();
}); });
test('valid sources filter', function(t) {
var query = generate({
'text': 'test',
'sources': ['test_source']
});
var compiled = JSON.parse( JSON.stringify( query ) );
var expected = require('../fixture/search_with_source_filtering');
t.deepEqual(compiled, expected, 'valid search query with source filtering');
t.end();
});
}; };
module.exports.all = function (tape, common) { module.exports.all = function (tape, common) {

4
test/unit/run.js

@ -13,10 +13,10 @@ var tests = [
require('./controller/place'), require('./controller/place'),
require('./controller/search'), require('./controller/search'),
require('./helper/geojsonify'), require('./helper/geojsonify'),
require('./helper/labelGenerator_examples'),
require('./helper/labelGenerator_default'), require('./helper/labelGenerator_default'),
require('./helper/labelGenerator_CAN'),
require('./helper/labelGenerator_GBR'), require('./helper/labelGenerator_GBR'),
require('./helper/labelGenerator_SGP'),
require('./helper/labelGenerator_SWE'),
require('./helper/labelGenerator_USA'), require('./helper/labelGenerator_USA'),
require('./helper/labelSchema'), require('./helper/labelSchema'),
require('./helper/text_parser'), require('./helper/text_parser'),

14
test/unit/sanitiser/_layers.js

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

8
test/unit/sanitiser/_size.js

@ -6,7 +6,7 @@ module.exports.tests.sanitize_size = function(test, common) {
test('size=0', function(t) { test('size=0', function(t) {
var raw = { size: 0 }; var raw = { size: 0 };
var clean = {}; var clean = {};
var res = sanitize(raw, clean); var res = sanitize(/*defaults*/)(raw, clean);
t.equal(res.errors.length, 0, 'should return no errors'); t.equal(res.errors.length, 0, 'should return no errors');
t.equal(res.warnings.length, 1, 'should return warning'); t.equal(res.warnings.length, 1, 'should return warning');
t.equal(res.warnings[0], 'out-of-range integer \'size\', using MIN_SIZE', 'check warning text'); t.equal(res.warnings[0], 'out-of-range integer \'size\', using MIN_SIZE', 'check warning text');
@ -17,7 +17,7 @@ module.exports.tests.sanitize_size = function(test, common) {
test('size=10000', function(t) { test('size=10000', function(t) {
var raw = { size: 10000 }; var raw = { size: 10000 };
var clean = {}; var clean = {};
var res = sanitize(raw, clean); var res = sanitize(/*defaults*/)(raw, clean);
t.equal(res.errors.length, 0, 'should return no errors'); t.equal(res.errors.length, 0, 'should return no errors');
t.equal(res.warnings.length, 1, 'should return warning'); t.equal(res.warnings.length, 1, 'should return warning');
t.equal(res.warnings[0], 'out-of-range integer \'size\', using MAX_SIZE', 'check warning text'); t.equal(res.warnings[0], 'out-of-range integer \'size\', using MAX_SIZE', 'check warning text');
@ -28,7 +28,7 @@ module.exports.tests.sanitize_size = function(test, common) {
test('size not set', function(t) { test('size not set', function(t) {
var raw = {}; var raw = {};
var clean = {}; var clean = {};
var res = sanitize(raw, clean); var res = sanitize(/*defaults*/)(raw, clean);
t.equal(res.errors.length, 0, 'should return no errors'); t.equal(res.errors.length, 0, 'should return no errors');
t.equal(res.warnings.length, 0, 'should return no warning'); t.equal(res.warnings.length, 0, 'should return no warning');
t.equal(clean.size, 10, 'default to 10'); t.equal(clean.size, 10, 'default to 10');
@ -41,7 +41,7 @@ module.exports.tests.sanitize_size = function(test, common) {
test('size=' + size, function (t) { test('size=' + size, function (t) {
var raw = {size: size}; var raw = {size: size};
var clean = {}; var clean = {};
var res = sanitize(raw, clean); var res = sanitize(/*defaults*/)(raw, clean);
t.equal(res.errors.length, 0, 'should return no errors'); t.equal(res.errors.length, 0, 'should return no errors');
t.equal(res.warnings.length, 0, 'should return warning'); t.equal(res.warnings.length, 0, 'should return warning');
t.equal(clean.size, 5, 'set to correct integer'); t.equal(clean.size, 5, 'set to correct integer');

2
test/unit/sanitiser/autocomplete.js

@ -4,7 +4,7 @@ module.exports.tests = {};
module.exports.tests.sanitisers = function(test, common) { module.exports.tests.sanitisers = function(test, common) {
test('check sanitiser list', function (t) { test('check sanitiser list', function (t) {
var expected = ['singleScalarParameters', 'text', 'size', 'private', 'geo_autocomplete' ]; var expected = ['singleScalarParameters', 'text', 'size', 'layers', 'sources', 'sources_and_layers', 'private', 'geo_autocomplete' ];
t.deepEqual(Object.keys(autocomplete.sanitiser_list), expected); t.deepEqual(Object.keys(autocomplete.sanitiser_list), expected);
t.end(); t.end();
}); });

Loading…
Cancel
Save