Port tests to mocha (closes #20)

This commit is contained in:
Sandro Santilli
2012-07-09 16:49:31 +02:00
parent e45d8339c4
commit 2b24e46f6c
5 changed files with 573 additions and 233 deletions

View File

@@ -1,4 +1,4 @@
var assert = require('assert');
var assert = require('../support/assert');
var net = require('net');
require(__dirname + '/../test_helper');
var CacheValidator = require(__dirname + '/../../lib/cartodb/cache_validator');
@@ -27,14 +27,18 @@ function VarnishEmu(on_cmd_recieved, test_callback) {
});
}
tests['should call purge on varnish when invalidate database'] = function() {
var varnish = new VarnishEmu(
function(cmds) {
assert.ok(cmds.length == 1);
assert.equal('purge obj.http.X-Cache-Channel ~ \"^test_db:(.*test_cache.*)|(table)$\"\n', cmds[0].toString('utf8'));
},
function(){
CacheValidator.init('localhost', 1337);
CacheValidator.invalidate_db('test_db', 'test_cache');
suite('cache_validator', function() {
test('should call purge on varnish when invalidate database', function(done) {
var varnish = new VarnishEmu(function(cmds) {
assert.ok(cmds.length == 1);
assert.equal('purge obj.http.X-Cache-Channel ~ \"^test_db:(.*test_cache.*)|(table)$\"\n', cmds[0].toString('utf8'));
done();
},
function() {
CacheValidator.init('localhost', 1337);
CacheValidator.invalidate_db('test_db', 'test_cache');
});
});
}
});

View File

@@ -1,200 +1,199 @@
var assert = require('assert');
var assert = require('../support/assert');
var tests = module.exports = {};
var _ = require('underscore');
var querystring = require('querystring');
require(__dirname + '/../test_helper');
var CartodbWindshaft = require(__dirname + '/../../lib/cartodb/cartodb_windshaft');
var serverOptions = require(__dirname + '/../../lib/cartodb/server_options');
var serverOptions = require(__dirname + '/../support/server_options');
var server = new CartodbWindshaft(serverOptions);
tests['true'] = function() {
assert.ok(true);
};
tests["get call to server returns 200"] = function(){
assert.response(server, {
url: '/',
method: 'GET'
},{
status: 200
suite('server', function() {
test("get call to server returns 200", function(done){
assert.response(server, {
url: '/',
method: 'GET'
},{
status: 200
}, function() { done(); });
});
};
tests["get'ing blank style returns default style"] = function(){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_table/style',
method: 'GET'
},{
status: 200,
body: '{"style":"#my_table {marker-fill: #FF6600;marker-opacity: 1;marker-width: 8;marker-line-color: white;marker-line-width: 3;marker-line-opacity: 0.9;marker-placement: point;marker-type: ellipse;marker-allow-overlap: true;}"}'
test("get'ing blank style returns default style", function(done){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_table/style',
method: 'GET'
},{
status: 200,
body: '{"style":"#my_table {marker-fill: #FF6600;marker-opacity: 1;marker-width: 8;marker-line-color: white;marker-line-width: 3;marker-line-opacity: 0.9;marker-placement: point;marker-type: ellipse;marker-allow-overlap: true;}"}'
}, function() { done(); });
});
};
tests["post'ing no style returns 400 with errors"] = function(){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_table/style',
method: 'POST'
},{
status: 400,
body: '{"error":"must send style information"}'
test("post'ing no style returns 400 with errors", function(done){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_table/style',
method: 'POST'
},{
status: 400,
body: '{"error":"must send style information"}'
}, function() { done(); });
});
};
tests["post'ing bad style returns 400 with error"] = function(){
assert.response(server, {
url: '/tiles/my_table3/style',
method: 'POST',
headers: {host: 'vizzuality.localhost.lan', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: '#my_table3{backgxxxxxround-color:#fff;}'})
},{
status: 500,
body: JSON.stringify(['style.mss:1:11 Unrecognized rule: backgxxxxxround-color'])
test("post'ing bad style returns 400 with error", function(done){
assert.response(server, {
url: '/tiles/my_table3/style',
method: 'POST',
headers: {host: 'vizzuality.localhost.lan', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: '#my_table3{backgxxxxxround-color:#fff;}'})
},{
status: 500,
body: JSON.stringify(['style.mss:1:11 Unrecognized rule: backgxxxxxround-color'])
}, function() { done(); });
});
};
tests["post'ing multiple bad styles returns 400 with error array"] = function(){
assert.response(server, {
url: '/tiles/my_table4/style',
method: 'POST',
headers: {host: 'vizzuality.localhost.lan', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: '#my_table4{backgxxxxxround-color:#fff;foo:bar}'})
},{
status: 500,
body: JSON.stringify([ 'style.mss:1:11 Unrecognized rule: backgxxxxxround-color', 'style.mss:1:38 Unrecognized rule: foo' ])
test("post'ing multiple bad styles returns 400 with error array", function(done){
assert.response(server, {
url: '/tiles/my_table4/style',
method: 'POST',
headers: {host: 'vizzuality.localhost.lan', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: '#my_table4{backgxxxxxround-color:#fff;foo:bar}'})
},{
status: 500,
body: JSON.stringify([ 'style.mss:1:11 Unrecognized rule: backgxxxxxround-color', 'style.mss:1:38 Unrecognized rule: foo' ])
}, function() { done(); });
});
};
tests["post'ing good style returns 200"] = function(){
assert.response(server, {
url: '/tiles/my_table5/style',
method: 'POST',
headers: {host: 'vizzuality.localhost.lan', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: '#my_table5{background-color:#fff;}'})
},{
status: 200
test("post'ing good style returns 200", function(done){
assert.response(server, {
url: '/tiles/my_table5/style',
method: 'POST',
headers: {host: 'vizzuality.localhost.lan', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: '#my_table5{background-color:#fff;}'})
},{
status: 200
}, function() { done(); });
});
};
test("post'ing good style returns 200 then getting returns original style", function(done){
var style = '#my_table5{background-color:#fff;}';
assert.response(server, {
url: '/tiles/my_table5/style',
method: 'POST',
headers: {host: 'vizzuality.localhost.lan', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: style})
},{
status: 200
}, function() {
tests["post'ing good style returns 200 then getting returns original style"] = function(){
var style = '#my_table5{background-color:#fff;}';
assert.response(server, {
url: '/tiles/my_table5/style',
method: 'POST',
headers: {host: 'vizzuality.localhost.lan', 'Content-Type': 'application/x-www-form-urlencoded' },
data: querystring.stringify({style: style})
},{
status: 200
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_table5/style',
method: 'GET'
},{
status: 200,
body: JSON.stringify({style: style})
}, function() { done(); });
});
});
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_table5/style',
method: 'GET'
},{
status: 200,
body: JSON.stringify({style: style})
test("get'ing blank infowindow returns blank", function(done){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_tablez/infowindow',
method: 'GET'
},{
status: 200,
body: '{"infowindow":null}'
}, function() { done(); });
});
};
tests["get'ing blank infowindow returns blank"] = function(){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_tablez/infowindow',
method: 'GET'
},{
status: 200,
body: '{"infowindow":null}'
test("get'ing blank infowindow with callback returns blank with callback", function(done){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_tablez/infowindow?callback=simon',
method: 'GET'
},{
status: 200,
body: 'simon({"infowindow":null});'
}, function() { done(); });
});
};
tests["get'ing blank infowindow with callback returns blank with callback"] = function(){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_tablez/infowindow?callback=simon',
method: 'GET'
},{
status: 200,
body: 'simon({"infowindow":null});'
test("get'ing completed infowindow with callback returns information with callback", function(done){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_table/infowindow?callback=simon',
method: 'GET'
},{
status: 200,
body: 'simon({"infowindow":"this, that, the other"});'
}, function() { done(); });
});
};
tests["get'ing completed infowindow with callback returns information with callback"] = function(){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/my_table/infowindow?callback=simon',
method: 'GET'
},{
status: 200,
body: 'simon({"infowindow":"this, that, the other"});'
test("get'ing a tile with default style should return an image", function(done){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.png?geom_type=polygon',
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'image/png' }
}, function() { done(); });
});
};
tests["get'ing a tile with default style should return an image"] = function(){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.png?geom_type=polygon',
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'image/png' }
test("get'ing a json with default style should return an grid", function(done){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.grid.json',
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'text/javascript; charset=utf-8; charset=utf-8' }
}, function() { done(); });
});
};
tests["get'ing a json with default style should return an grid"] = function(){
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.grid.json',
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'text/javascript; charset=utf-8; charset=utf-8' }
test("get'ing a json with default style and sql should return a constrained grid", function(done){
var sql = querystring.stringify({sql: "SELECT * FROM gadm4 WHERE codineprov = '08'"})
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.grid.json?' + sql,
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'text/javascript; charset=utf-8; charset=utf-8' }
}, function() { done(); });
});
};
tests["get'ing a json with default style and sql should return a constrained grid"] = function(){
var sql = querystring.stringify({sql: "SELECT * FROM gadm4 WHERE codineprov = '08'"})
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.grid.json?' + sql,
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'text/javascript; charset=utf-8; charset=utf-8' }
test("get'ing a tile with default style and sql should return a constrained image", function(done){
var sql = querystring.stringify({sql: "SELECT * FROM gadm4 WHERE codineprov = '08'"});
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.png?' + sql,
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'image/png' }
}, function() { done(); });
});
};
tests["get'ing a tile with default style and sql should return a constrained image"] = function(){
var sql = querystring.stringify({sql: "SELECT * FROM gadm4 WHERE codineprov = '08'"});
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.png?' + sql,
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'image/png' }
test("get'ing a tile with default style and complex sql should return a constrained image", function(done){
var sql = querystring.stringify({sql: "SELECT * FROM gadm4 WHERE codineprov = '08' AND codccaa > 60"})
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.png?' + sql,
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'image/png' }
}, function() { done(); });
});
};
tests["get'ing a tile with default style and complex sql should return a constrained image"] = function(){
var sql = querystring.stringify({sql: "SELECT * FROM gadm4 WHERE codineprov = '08' AND codccaa > 60"})
assert.response(server, {
headers: {host: 'vizzuality.localhost.lan'},
url: '/tiles/gadm4/6/31/24.png?' + sql,
method: 'GET'
},{
status: 200,
headers: { 'Content-Type': 'image/png' }
});
};
});

200
test/support/assert.js Normal file
View File

@@ -0,0 +1,200 @@
// Cribbed from the ever prolific Konstantin Kaefer
// https://github.com/mapbox/tilelive-mapnik/blob/master/test/support/assert.js
var fs = require('fs');
var http = require('http');
var path = require('path');
var exec = require('child_process').exec;
var assert = module.exports = exports = require('assert');
assert.imageEqualsFile = function(buffer, file_b, callback) {
if (!callback) callback = function(err) { if (err) throw err; };
file_b = path.resolve(file_b);
var file_a = '/tmp/' + (Math.random() * 1e16);
var err = fs.writeFileSync(file_a, buffer, 'binary');
if (err) throw err;
exec('compare -metric PSNR "' + file_a + '" "' +
file_b + '" /dev/null', function(err, stdout, stderr) {
if (err) {
fs.unlinkSync(file_a);
callback(err);
} else {
stderr = stderr.trim();
if (stderr === 'inf') {
fs.unlinkSync(file_a);
callback(null);
} else {
var similarity = parseFloat(stderr);
var err = new Error('Images not equal(' + similarity + '): ' +
file_a + ' ' + file_b);
err.similarity = similarity;
callback(err);
}
}
});
};
/**
* Assert response from `server` with
* the given `req` object and `res` assertions object.
*
* @param {Server} server
* @param {Object} req
* @param {Object|Function} res
* @param {String} msg
*/
assert.response = function(server, req, res, msg){
var port = 5555;
function check(){
try {
server.__port = server.address().port;
server.__listening = true;
} catch (err) {
process.nextTick(check);
return;
}
if (server.__deferred) {
server.__deferred.forEach(function(args){
assert.response.apply(assert, args);
});
server.__deferred = null;
}
}
// Check that the server is ready or defer
if (!server.fd) {
server.__deferred = server.__deferred || [];
server.listen(server.__port = port++, '127.0.0.1', check);
} else if (!server.__port) {
server.__deferred = server.__deferred || [];
process.nextTick(check);
}
// The socket was created but is not yet listening, so keep deferring
if (!server.__listening) {
server.__deferred.push(arguments);
return;
}
// Callback as third or fourth arg
var callback = typeof res === 'function'
? res
: typeof msg === 'function'
? msg
: function(){};
// Default messate to test title
if (typeof msg === 'function') msg = null;
msg = msg || assert.testTitle;
msg += '. ';
// Pending responses
server.__pending = server.__pending || 0;
server.__pending++;
// Create client
if (!server.fd) {
server.listen(server.__port = port++, '127.0.0.1', issue);
} else {
issue();
}
function issue(){
// Issue request
var timer,
method = req.method || 'GET',
status = res.status || res.statusCode,
data = req.data || req.body,
requestTimeout = req.timeout || 0,
encoding = req.encoding || 'utf8';
var request = http.request({
host: '127.0.0.1',
port: server.__port,
path: req.url,
method: method,
headers: req.headers
});
var check = function() {
if (--server.__pending === 0) {
server.close();
server.__listening = false;
}
};
// Timeout
if (requestTimeout) {
timer = setTimeout(function(){
check();
delete req.timeout;
assert.fail(msg + 'Request timed out after ' + requestTimeout + 'ms.');
}, requestTimeout);
}
if (data) request.write(data);
request.on('response', function(response){
response.body = '';
response.setEncoding(encoding);
response.on('data', function(chunk){ response.body += chunk; });
response.on('end', function(){
if (timer) clearTimeout(timer);
// Assert response body
if (res.body !== undefined) {
var eql = res.body instanceof RegExp
? res.body.test(response.body)
: res.body === response.body;
assert.ok(
eql,
msg + 'Invalid response body.\n'
+ ' Expected: ' + res.body + '\n'
+ ' Got: ' + response.body
);
}
// Assert response status
if (typeof status === 'number') {
assert.equal(
response.statusCode,
status,
msg + 'Invalid response status code.\n'
+ ' Expected: [green]{' + status + '}\n'
+ ' Got: [red]{' + response.statusCode + '}'
);
}
// 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],
eql = expected instanceof RegExp
? expected.test(actual)
: expected == actual;
assert.ok(
eql,
msg + 'Invalid response header [bold]{' + name + '}.\n'
+ ' Expected: [green]{' + expected + '}\n'
+ ' Got: [red]{' + actual + '}'
);
}
}
// Callback
callback(response);
check();
});
});
request.end();
}
};

View File

@@ -0,0 +1,126 @@
var _ = require('underscore')
, Step = require('step')
, cartoData = require('../../lib/cartodb/carto_data');
module.exports = function(){
var me = {
base_url: '/tiles/:table',
grainstore: {datasource: global.environment.postgres},
redis: global.environment.redis,
enable_cors: global.environment.enable_cors,
varnish_host: global.environment.varnish.host,
varnish_port: global.environment.varnish.port,
cache_enabled: global.environment.cache_enabled,
log_format: '[:date] :req[X-Real-IP] \033[90m:method\033[0m \033[36m:req[Host]:url\033[0m \033[90m:status :response-time ms -> :res[Content-Type]\033[0m'
};
/**
* Whitelist input and get database name & default geometry type from
* subdomain/user metadata held in CartoDB Redis
* @param req - standard express request obj. Should have host & table
* @param callback
*/
me.req2params = function(req, callback){
// Whitelist query parameters and attach format
var good_query = ['sql', 'geom_type', 'cache_buster','callback', 'interactivity', 'map_key', 'style'];
var bad_query = _.difference(_.keys(req.query), good_query);
_.each(bad_query, function(key){ delete req.query[key]; });
req.params = _.extend({}, req.params); // shuffle things as request is a strange array/object
// bring all query values onto req.params object
_.extend(req.params, req.query);
// for cartodb, ensure interactivity is cartodb_id or user specified
req.params.interactivity = req.params.interactivity || 'cartodb_id';
Step(
function getPrivacy(){
cartoData.authorize(req, this);
},
function gatekeep(err, data){
if(err) throw err;
if(data === "0") throw new Error("Sorry, you are unauthorized");
return data;
},
function getDatabase(err, data){
if(err) throw err;
cartoData.getDatabase(req, this);
},
function getGeometryType(err, data){
if (err) throw err;
_.extend(req.params, {dbname:data});
cartoData.getGeometryType(req, this);
},
function finishSetup(err, data){
if (!_.isNull(data))
_.extend(req.params, {geom_type: data});
callback(err, req);
}
);
};
/**
* Little helper method to get the current list of infowindow variables and return to client
* @param req
* @param callback
*/
me.getInfowindow = function(req, callback){
var that = this;
Step(
function(){
that.req2params(req, this);
},
function(err, data){
if (err) throw err;
cartoData.getInfowindow(data, callback);
}
);
};
/**
* Little helper method to get map metadata and return to client
* @param req
* @param callback
*/
me.getMapMetadata = function(req, callback){
var that = this;
Step(
function(){
that.req2params(req, this);
},
function(err, data){
if (err) throw err;
cartoData.getMapMetadata(data, callback);
}
);
};
/**
* Helper to clear out tile cache on request
* @param req
* @param callback
*/
me.flushCache = function(req, Cache, callback){
var that = this;
Step(
function(){
that.req2params(req, this);
},
function(err, data){
if (err) throw err;
Cache.invalidate_db(req.params.dbname, req.params.table);
callback(null, true);
}
);
};
return me;
}();

View File

@@ -1,54 +1,65 @@
var assert = require('assert')
var assert = require('../../support/assert')
, _ = require('underscore')
, RedisPool = require('../../../lib/cartodb/redis_pool')
, tests = module.exports = {};
// configure redis pool instance to use in tests
var test_opts = {
max: 10,
idleTimeoutMillis: 1,
reapIntervalMillis: 1
};
suite('redis_pool', function() {
var redis_pool = new RedisPool(test_opts);
// configure redis pool instance to use in tests
var test_opts = {
max: 10,
idleTimeoutMillis: 1,
reapIntervalMillis: 1
};
var redis_pool = new RedisPool(test_opts);
tests['truth'] = function(){
assert.ok(true, 'it is');
};
test('RedisPool object exists', function(done){
assert.ok(RedisPool);
done();
});
test('RedisPool can create new redis_pool objects with default settings', function(done){
var redis_pool = new RedisPool();
done();
});
test('RedisPool can create new redis_pool objects with specific settings', function(done){
var redis_pool = new RedisPool(_.extend({host:'127.0.0.1', port: '6379'}, test_opts));
done();
});
test('pool object has an acquire function', function(done){
var found=false;
var functions = _.functions(redis_pool);
for (var i=0; i<functions.length; ++i) {
if ( functions[i] == 'acquire' ) { found=true; break; }
}
assert.ok(found);
done();
});
test('calling aquire returns a redis client object that can get/set', function(done){
redis_pool.acquire(0, function(err, client){
client.set("key","value");
client.get("key", function(err,data){
assert.equal(data, "value");
redis_pool.release(0, client); // needed to exit tests
done();
})
});
});
test('calling aquire on another DB returns a redis client object that can get/set', function(done){
redis_pool.acquire(2, function(err, client){
client.set("key","value");
client.get("key", function(err,data){
assert.equal(data, "value");
redis_pool.release(2, client); // needed to exit tests
done();
})
});
});
tests['RedisPool object exists'] = function(){
assert.ok(RedisPool);
};
tests['RedisPool can create new redis_pool objects with default settings'] = function(){
var redis_pool = new RedisPool();
};
tests['RedisPool can create new redis_pool objects with specific settings'] = function(){
var redis_pool = new RedisPool(_.extend({host:'127.0.0.1', port: '6379'}, test_opts));
};
tests['pool object has an aquire function'] = function(){
assert.includes(_.functions(redis_pool), 'acquire');
};
tests['calling aquire returns a redis client object that can get/set'] = function(){
redis_pool.acquire(0, function(err, client){
client.set("key","value");
client.get("key", function(err,data){
assert.eql(data, "value");
redis_pool.release(0, client); // needed to exit tests
})
});
};
tests['calling aquire on another DB returns a redis client object that can get/set'] = function(){
redis_pool.acquire(2, function(err, client){
client.set("key","value");
client.get("key", function(err,data){
assert.eql(data, "value");
redis_pool.release(2, client); // needed to exit tests
})
});
};
});