diff --git a/.jshintignore b/.jshintignore index d781eace..39e650b4 100644 --- a/.jshintignore +++ b/.jshintignore @@ -1,4 +1,3 @@ test/results/ test/monkey/ test/benchmark.js -test/support/ diff --git a/test/support/assert.js b/test/support/assert.js index c5e22e93..bcb186aa 100644 --- a/test/support/assert.js +++ b/test/support/assert.js @@ -77,7 +77,6 @@ function randomImagePath() { return path.resolve('test/results/png/image-test-' + Date.now() + '.png'); } -// jshint maxcomplexity:9 assert.response = function(server, req, res, callback) { if (!callback) { callback = res; @@ -106,7 +105,6 @@ assert.response = function(server, req, res, callback) { // jshint maxcomplexity:9 function onServerListening() { - var status = res.status || res.statusCode; var requestParams = { url: 'http://' + host + ':' + port + req.url, method: req.method || 'GET', @@ -122,61 +120,74 @@ assert.response = function(server, req, res, callback) { request(requestParams, function assert$response$requestHandler(error, response, body) { listener.close(function() { response.body = response.body || body; - - // Assert response body - if (res.body) { - var eql = res.body instanceof RegExp ? res.body.test(response.body) : res.body === response.body; - if (!eql) { - return callback(response, new Error(colorize( - '[red]{Invalid response body.}\n' + - ' Expected: [green]{' + res.body + '}\n' + - ' Got: [red]{' + response.body + '}')) - ); - } - } - - // Assert response status - if (typeof status === 'number') { - if (response.statusCode != status) { - return callback(response, new Error(colorize( - '[red]{Invalid response status code.}\n' + - ' Expected: [green]{' + status + '}\n' + - ' Got: [red]{' + response.statusCode + '}\n' + - ' Body: ' + response.body)) - ); - } - } - - // Assert response headers - if (res.headers) { - var keys = Object.keys(res.headers); - for (var i = 0, len = keys.length; i < len; ++i) { - var name = keys[i], - actual = response.headers[name.toLowerCase()], - expected = res.headers[name], - headerEql = expected instanceof RegExp ? expected.test(actual) : expected === actual; - if (!headerEql) { - return callback(response, new Error(colorize( - 'Invalid response header [bold]{' + name + '}.\n' + - ' Expected: [green]{' + expected + '}\n' + - ' Got: [red]{' + actual + '}')) - ); - } - } - } - - // Callback - callback(response); + var err = validateResponse(response, res); + return callback(response, err); }); }); } }; -// jshint maxcomplexity:6 + +function validateResponseBody(response, expected) { + if (expected.body) { + var eql = expected.body instanceof RegExp ? + expected.body.test(response.body) : + expected.body === response.body; + if (!eql) { + return new Error(colorize( + '[red]{Invalid response body.}\n' + + ' Expected: [green]{' + expected.body + '}\n' + + ' Got: [red]{' + response.body + '}') + ); + } + } +} + +function validateResponseStatus(response, expected) { + var status = expected.status || expected.statusCode; + // Assert response status + if (typeof status === 'number') { + if (response.statusCode !== status) { + return new Error(colorize( + '[red]{Invalid response status code.}\n' + + ' Expected: [green]{' + status + '}\n' + + ' Got: [red]{' + response.statusCode + '}\n' + + ' Body: ' + response.body) + ); + } + } +} + +function validateResponseHeaders(response, expected) { + // Assert response headers + if (expected.headers) { + var keys = Object.keys(expected.headers); + for (var i = 0, len = keys.length; i < len; ++i) { + var name = keys[i], + actual = response.headers[name.toLowerCase()], + expectedHeader = expected.headers[name], + headerEql = expectedHeader instanceof RegExp ? expectedHeader.test(actual) : expectedHeader === actual; + if (!headerEql) { + return new Error(colorize( + 'Invalid response header [bold]{' + name + '}.\n' + + ' Expected: [green]{' + expectedHeader + '}\n' + + ' Got: [red]{' + actual + '}') + ); + } + } + } +} + +function validateResponse(response, expected) { + // Assert response body + return validateResponseBody(response, expected) || + validateResponseStatus(response, expected) || + validateResponseHeaders(response, expected); +} // @param tolerance number of tolerated grid cell differences -// jshint maxcomplexity:9 assert.utfgridEqualsFile = function(buffer, file_b, tolerance, callback) { + // jshint maxcomplexity:9 fs.writeFileSync('/tmp/grid.json', buffer, 'binary'); // <-- to debug/update var expected_json = JSON.parse(fs.readFileSync(file_b, 'utf8')); diff --git a/test/support/config.js b/test/support/config.js deleted file mode 100644 index 4b8102f7..00000000 --- a/test/support/config.js +++ /dev/null @@ -1,21 +0,0 @@ -var _ = require('underscore'); - - -require(__dirname + '/test_helper'); - -module.exports = function(opts) { - - var config = { - redis_pool: { - max: 10, - idleTimeoutMillis: 1, - reapIntervalMillis: 1, - port: global.environment.redis.port - } - } - - _.extend(config, opts || {}); - - return config; -}(); - diff --git a/test/support/test-client.js b/test/support/test-client.js index 1f5272f6..fe619531 100644 --- a/test/support/test-client.js +++ b/test/support/test-client.js @@ -21,7 +21,7 @@ const MAPNIK_SUPPORTED_FORMATS = { 'png32': true, 'grid.json': true, 'mvt': true -} +}; function TestClient(config, apiKey) { this.mapConfig = isMapConfig(config) ? config : null; @@ -114,7 +114,7 @@ module.exports.CARTOCSS = { module.exports.SQL = { EMPTY: 'select 1 as cartodb_id, null::geometry as the_geom_webmercator', ONE_POINT: 'select 1 as cartodb_id, \'SRID=3857;POINT(0 0)\'::geometry the_geom_webmercator' -} +}; TestClient.prototype.getWidget = function(widgetName, params, callback) { var self = this; @@ -129,7 +129,6 @@ TestClient.prototype.getWidget = function(widgetName, params, callback) { url += '?' + qs.stringify({ filters: JSON.stringify(params.filters) }); } - var layergroupId; step( function createLayergroup() { var next = this; @@ -175,11 +174,12 @@ TestClient.prototype.getWidget = function(widgetName, params, callback) { } ); }, - function getWidgetResult(err, _layergroupId) { + function getWidgetResult(err, layergroupId) { assert.ifError(err); var next = this; - layergroupId = _layergroupId; + self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; + self.keysToDelete['user:localhost:mapviews:global'] = 5; var urlParams = { own_filter: params.hasOwnProperty('own_filter') ? params.own_filter : 1 @@ -216,8 +216,6 @@ TestClient.prototype.getWidget = function(widgetName, params, callback) { ); }, function finish(err, res) { - self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; - self.keysToDelete['user:localhost:mapviews:global'] = 5; var widget; if (!err && res.body) { widget = JSON.parse(res.body); @@ -240,7 +238,6 @@ TestClient.prototype.widgetSearch = function(widgetName, userQuery, params, call url += '?' + qs.stringify({ filters: JSON.stringify(params.filters) }); } - var layergroupId; step( function createLayergroup() { var next = this; @@ -286,11 +283,12 @@ TestClient.prototype.widgetSearch = function(widgetName, userQuery, params, call } ); }, - function getWidgetSearchResult(err, _layergroupId) { + function getWidgetSearchResult(err, layergroupId) { assert.ifError(err); var next = this; - layergroupId = _layergroupId; + self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; + self.keysToDelete['user:localhost:mapviews:global'] = 5; var urlParams = { q: userQuery, @@ -325,8 +323,6 @@ TestClient.prototype.widgetSearch = function(widgetName, userQuery, params, call ); }, function finish(err, res) { - self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; - self.keysToDelete['user:localhost:mapviews:global'] = 5; var searchResult; if (!err && res.body) { searchResult = JSON.parse(res.body); @@ -364,7 +360,6 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) { } }; - var layergroupId; step( function createLayergroup() { var next = this; @@ -400,11 +395,12 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) { } ); }, - function getDataviewResult(err, _layergroupId) { + function getDataviewResult(err, layergroupId) { assert.ifError(err); var next = this; - layergroupId = _layergroupId; + self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; + self.keysToDelete['user:localhost:mapviews:global'] = 5; var urlParams = { own_filter: params.hasOwnProperty('own_filter') ? params.own_filter : 1 @@ -443,12 +439,6 @@ TestClient.prototype.getDataview = function(dataviewName, params, callback) { if (err) { return callback(err); } - - if (layergroupId) { - self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; - self.keysToDelete['user:localhost:mapviews:global'] = 5; - } - return callback(null, dataview); } ); @@ -482,7 +472,6 @@ TestClient.prototype.getFeatureAttributes = function(featureId, layerId, params, } }; - var layergroupId; step( function createLayergroup() { var next = this; @@ -571,7 +560,7 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) { var layergroupId; if (params.layergroupid) { - layergroupId = params.layergroupid + layergroupId = params.layergroupid; } step( @@ -586,7 +575,7 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) { return next(new Error('apiKey param is mandatory to create a new template')); } - params.placeholders = params.placeholders || {}; + params.placeholders = params.placeholders || {}; assert.response(self.server, { @@ -619,7 +608,7 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) { return next(null, layergroupId); } - var data = templateId ? params.placeholders : self.mapConfig + var data = templateId ? params.placeholders : self.mapConfig; var path = templateId ? urlNamed + '/' + templateId + '?' + qs.stringify({api_key: self.apiKey}) : url; @@ -648,11 +637,12 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) { } ); }, - function getTileResult(err, _layergroupId) { + function getTileResult(err, layergroupId) { + // jshint maxcomplexity:12 assert.ifError(err); - var next = this; - layergroupId = _layergroupId; + self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; + self.keysToDelete['user:localhost:mapviews:global'] = 5; url = '/api/v1/map/' + layergroupId + '/'; @@ -726,35 +716,31 @@ TestClient.prototype.getTile = function(z, x, y, params, callback) { expectedResponse.headers['Content-Type'] = 'application/json; charset=utf-8'; } - assert.response(self.server, request, expectedResponse, function(res, err) { - assert.ifError(err); - - var body; - switch (res.headers['content-type']) { - case 'image/png': - body = mapnik.Image.fromBytes(new Buffer(res.body, 'binary')); - break; - case 'application/x-protobuf': - body = new mapnik.VectorTile(z, x, y); - body.setDataSync(new Buffer(res.body, 'binary')); - break; - case 'application/json; charset=utf-8': - body = JSON.parse(res.body); - break; - default: - body = res.body - break; - } - - next(null, res, body); - }); + assert.response(self.server, request, expectedResponse, this); }, - function finish(err, res, image) { - if (layergroupId) { - self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; - self.keysToDelete['user:localhost:mapviews:global'] = 5; + function finish(res, err) { + if (err) { + return callback(err); } - return callback(err, res, image); + + var body; + switch (res.headers['content-type']) { + case 'image/png': + body = mapnik.Image.fromBytes(new Buffer(res.body, 'binary')); + break; + case 'application/x-protobuf': + body = new mapnik.VectorTile(z, x, y); + body.setDataSync(new Buffer(res.body, 'binary')); + break; + case 'application/json; charset=utf-8': + body = JSON.parse(res.body); + break; + default: + body = res.body; + break; + } + + return callback(err, res, body); } ); }; @@ -790,10 +776,11 @@ TestClient.prototype.getLayergroup = function(expectedResponse, callback) { }, expectedResponse, function(res, err) { + var parsedBody; // If there is a response, we are still interested in catching the created keys // to be able to delete them on the .drain() method. if (res) { - var parsedBody = JSON.parse(res.body); + parsedBody = JSON.parse(res.body); if (parsedBody.layergroupid) { self.keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0; self.keysToDelete['user:localhost:mapviews:global'] = 5; @@ -811,7 +798,7 @@ TestClient.prototype.getLayergroup = function(expectedResponse, callback) { TestClient.prototype.getStaticCenter = function (params, callback) { var self = this; - let { layergroupid, z, lat, lng, width, height, format } = params + let { layergroupid, z, lat, lng, width, height, format } = params; var url = `/api/v1/map/`; @@ -827,7 +814,7 @@ TestClient.prototype.getStaticCenter = function (params, callback) { return next(null, layergroupid); } - var data = self.mapConfig + var data = self.mapConfig; var path = url; assert.response(self.server, @@ -854,14 +841,13 @@ TestClient.prototype.getStaticCenter = function (params, callback) { } ); }, - function getStaticResult(err, _layergroupid) { + function getStaticResult(err, layergroupId) { assert.ifError(err); - var next = this; + self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; + self.keysToDelete['user:localhost:mapviews:global'] = 5; - layergroupid = _layergroupid; - - url = `/api/v1/map/static/center/${layergroupid}/${z}/${lat}/${lng}/${width}/${height}.${format}` + url = `/api/v1/map/static/center/${layergroupId}/${z}/${lat}/${lng}/${width}/${height}.${format}`; if (self.apiKey) { url += '?' + qs.stringify({api_key: self.apiKey}); @@ -883,31 +869,27 @@ TestClient.prototype.getStaticCenter = function (params, callback) { } }, params.response); - assert.response(self.server, request, expectedResponse, function(res, err) { - assert.ifError(err); - - var body; - switch (res.headers['content-type']) { - case 'image/png': - body = mapnik.Image.fromBytes(new Buffer(res.body, 'binary')); - break; - case 'application/json; charset=utf-8': - body = JSON.parse(res.body); - break; - default: - body = res.body - break; - } - - next(null, res, body); - }); + assert.response(self.server, request, expectedResponse, this); }, - function finish(err, res, image) { - if (layergroupid) { - self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupid).token] = 0; - self.keysToDelete['user:localhost:mapviews:global'] = 5; + function(res, err) { + if (err) { + return callback(err); } - return callback(err, res, image); + + var body; + switch (res.headers['content-type']) { + case 'image/png': + body = mapnik.Image.fromBytes(new Buffer(res.body, 'binary')); + break; + case 'application/json; charset=utf-8': + body = JSON.parse(res.body); + break; + default: + body = res.body; + break; + } + + return callback(err, res, body); } ); }; @@ -921,7 +903,6 @@ TestClient.prototype.getNodeStatus = function(nodeName, callback) { url += '?' + qs.stringify({api_key: this.apiKey}); } - var layergroupId; var nodes = {}; step( function createLayergroup() { @@ -960,11 +941,11 @@ TestClient.prototype.getNodeStatus = function(nodeName, callback) { } ); }, - function getNodeStatusResult(err, _layergroupId) { + function getNodeStatusResult(err, layergroupId) { assert.ifError(err); - var next = this; - layergroupId = _layergroupId; + self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; + self.keysToDelete['user:localhost:mapviews:global'] = 5; url = urlParser.parse(nodes[nodeName]).path; @@ -987,15 +968,13 @@ TestClient.prototype.getNodeStatus = function(nodeName, callback) { } }; - assert.response(self.server, request, expectedResponse, function(res, err) { - assert.ifError(err); - next(null, res, JSON.parse(res.body)); - }); + assert.response(self.server, request, expectedResponse, this); }, - function finish(err, res, image) { - self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; - self.keysToDelete['user:localhost:mapviews:global'] = 5; - return callback(err, res, image); + function finish(res, err) { + if (err) { + return callback(err); + } + return callback(null, res, JSON.parse(res.body)); } ); }; @@ -1004,11 +983,11 @@ TestClient.prototype.getAttributes = function(params, callback) { var self = this; if (!Number.isFinite(params.featureId)) { - throw new Error('featureId param must be a number') + throw new Error('featureId param must be a number'); } if (!Number.isFinite(params.layer)) { - throw new Error('layer param must be a number') + throw new Error('layer param must be a number'); } var url = '/api/v1/map'; @@ -1020,7 +999,7 @@ TestClient.prototype.getAttributes = function(params, callback) { var layergroupid; if (params.layergroupid) { - layergroupid = params.layergroupid + layergroupid = params.layergroupid; } step( @@ -1057,14 +1036,13 @@ TestClient.prototype.getAttributes = function(params, callback) { } ); }, - function getAttributes(err, _layergroupid) { + function getAttributes(err, layergroupId) { assert.ifError(err); - var next = this; + self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupId).token] = 0; + self.keysToDelete['user:localhost:mapviews:global'] = 5; - layergroupid = _layergroupid; - - url = `/api/v1/map/${layergroupid}/${params.layer}/attributes/${params.featureId}`; + url = `/api/v1/map/${layergroupId}/${params.layer}/attributes/${params.featureId}`; if (self.apiKey) { url += '?' + qs.stringify({api_key: self.apiKey}); @@ -1085,21 +1063,14 @@ TestClient.prototype.getAttributes = function(params, callback) { } }; - assert.response(self.server, request, expectedResponse, function (res, err) { - assert.ifError(err); - - var attributes = JSON.parse(res.body); - - next(null, res, attributes); - }); + assert.response(self.server, request, expectedResponse, this); }, - function finish(err, res, attributes) { - if (layergroupid) { - self.keysToDelete['map_cfg|' + LayergroupToken.parse(layergroupid).token] = 0; - self.keysToDelete['user:localhost:mapviews:global'] = 5; + function finish(res, err) { + if (err) { + return callback(err); } - - return callback(err, res, attributes); + var attributes = JSON.parse(res.body); + return callback(null, res, attributes); } ); }; @@ -1143,7 +1114,7 @@ module.exports.getStaticMap = function getStaticMap(templateName, params, callba // this could be removed once named maps are invalidated, otherwise you hits the cache var server = new CartodbWindshaft(serverOptions); - assert.response(self.server, requestOptions, expectedResponse, function (res, err) { + assert.response(server, requestOptions, expectedResponse, function (res, err) { helper.deleteRedisKeys({'user:localhost:mapviews:global': 5}, function() { return callback(err, mapnik.Image.fromBytes(new Buffer(res.body, 'binary'))); }); @@ -1165,12 +1136,11 @@ TestClient.prototype.setUserRenderTimeoutLimit = function (user, userTimeoutLimi TestClient.prototype.setUserDatabaseTimeoutLimit = function (timeoutLimit, callback) { const dbname = _.template(global.environment.postgres_auth_user, { user_id: 1 }) + '_db'; - const dbuser = _.template(global.environment.postgres_auth_user, { user_id: 1 }) - const pass = _.template(global.environment.postgres_auth_pass, { user_id: 1 }) + const dbuser = _.template(global.environment.postgres_auth_user, { user_id: 1 }); const publicuser = global.environment.postgres.user; // we need to guarantee all new connections have the new settings - helper.cleanPGPoolConnections() + helper.cleanPGPoolConnections(); const psql = new PSQL({ user: 'postgres',