Windshaft-cartodb/test/acceptance/ported/torque-test.js

456 lines
19 KiB
JavaScript
Raw Normal View History

'use strict';
var testHelper = require('../../support/test-helper');
2015-07-08 05:46:58 +08:00
var assert = require('../../support/assert');
var _ = require('underscore');
var step = require('step');
2019-10-07 15:40:50 +08:00
var cartodbServer = require('../../../lib/server');
var ServerOptions = require('./support/ported-server-options');
2015-07-08 05:46:58 +08:00
2019-10-07 15:40:50 +08:00
var LayergroupToken = require('../../../lib/models/layergroup-token');
2019-10-22 01:07:24 +08:00
describe('torque', function () {
var server;
2015-07-08 05:46:58 +08:00
before(function () {
server = cartodbServer(ServerOptions);
server.setMaxListeners(0);
});
2015-07-08 05:46:58 +08:00
2015-09-25 22:16:33 +08:00
var keysToDelete;
2019-10-22 01:07:24 +08:00
beforeEach(function () {
2015-09-26 01:16:57 +08:00
keysToDelete = {};
2015-09-25 22:16:33 +08:00
});
2019-10-22 01:07:24 +08:00
afterEach(function (done) {
2015-09-25 22:16:33 +08:00
testHelper.deleteRedisKeys(keysToDelete, done);
});
2019-10-22 01:07:24 +08:00
function checkCORSHeaders (res) {
assert.strictEqual(
res.headers['access-control-allow-headers'],
'X-Requested-With, X-Prototype-Version, X-CSRF-Token, Authorization'
);
assert.strictEqual(res.headers['access-control-allow-origin'], '*');
2015-07-08 05:46:58 +08:00
}
2019-10-22 01:07:24 +08:00
it('missing required property from torque layer', function (done) {
var layergroup = {
version: '1.1.0',
layers: [
{
type: 'torque',
options: {
sql: 'select cartodb_id, the_geom from test_table',
geom_column: 'the_geom',
srid: 4326,
cartocss: 'Map { marker-fill:blue; }'
}
}
]
};
2015-07-08 05:46:58 +08:00
2019-10-22 01:07:24 +08:00
step(
2019-11-13 23:00:39 +08:00
function doPost1 () {
2019-10-22 01:07:24 +08:00
var next = this;
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function (res) { next(null, res); });
},
function checkResponse (err, res) {
assert.ifError(err);
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors, parsed);
var error = parsed.errors[0];
assert.strictEqual(error,
2019-10-22 01:07:24 +08:00
"Missing required property '-torque-frame-count' in torque layer CartoCSS");
return null;
},
2019-11-13 23:00:39 +08:00
function doPost2 (err) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
var next = this;
var css = 'Map { -torque-frame-count: 2; }';
layergroup.layers[0].options.cartocss = css;
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function (res) { next(null, res); });
},
function checkResponse2 (err, res) {
assert.ifError(err);
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors, parsed);
var error = parsed.errors[0];
assert.strictEqual(error,
2019-10-22 01:07:24 +08:00
"Missing required property '-torque-resolution' in torque layer CartoCSS");
return null;
},
2019-11-13 23:00:39 +08:00
function doPost3 (err) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
var next = this;
var css = 'Map { -torque-frame-count: 2; -torque-resolution: 3; }';
layergroup.layers[0].options.cartocss = css;
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function (res) { next(null, res); });
},
function checkResponse3 (err, res) {
assert.ifError(err);
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors, parsed);
var error = parsed.errors[0];
assert.strictEqual(error,
2019-10-22 01:07:24 +08:00
"Missing required property '-torque-aggregation-function' in torque layer CartoCSS");
return null;
},
function finish (err) {
done(err);
}
);
2015-07-08 05:46:58 +08:00
});
// See http://github.com/CartoDB/Windshaft/issues/150
2019-10-22 01:07:24 +08:00
it.skip('unquoted property in torque layer', function (done) {
var layergroup = {
version: '1.1.0',
layers: [
{
type: 'torque',
options: {
sql: 'select updated_at as d, cartodb_id as id, the_geom from test_table',
geom_column: 'the_geom',
srid: 4326,
cartocss: 'Map { -torque-frame-count:2; -torque-resolution:3; -torque-time-attribute:"d"; ' +
2015-07-08 05:46:58 +08:00
'-torque-aggregation-function:count(id); }'
2019-10-22 01:07:24 +08:00
}
}
]
};
step(
2019-11-13 23:00:39 +08:00
function doPost1 () {
2019-10-22 01:07:24 +08:00
var next = this;
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function (res) { next(null, res); });
},
function checkResponse (err, res) {
assert.ifError(err);
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors, parsed);
var error = parsed.errors[0];
assert.strictEqual(error, 'Something meaningful here');
2019-10-22 01:07:24 +08:00
return null;
},
function finish (err) {
done(err);
}
);
2015-07-08 05:46:58 +08:00
});
2019-10-22 01:07:24 +08:00
it('can render tile for valid mapconfig', function (done) {
var mapconfig = {
version: '1.1.0',
layers: [
{
type: 'torque',
options: {
sql: "select 1 as id, '1970-01-02'::date as d, 'POINT(0 0)'::geometry as the_geom UNION ALL select 2, " +
2015-07-08 05:46:58 +08:00
"'1970-01-01'::date, 'POINT(1 1)'::geometry",
2019-10-22 01:07:24 +08:00
geom_column: 'the_geom',
cartocss: 'Map { -torque-frame-count:2; -torque-resolution:3; -torque-time-attribute:d; ' +
2015-07-08 05:46:58 +08:00
'-torque-aggregation-function:\'count(id)\'; }',
2019-10-22 01:07:24 +08:00
cartocss_version: '2.0.1'
}
}
]
};
2015-07-08 05:46:58 +08:00
2019-11-13 23:00:39 +08:00
var expectedToken;
2019-10-22 01:07:24 +08:00
step(
2019-11-13 23:00:39 +08:00
function doPost () {
2019-10-22 01:07:24 +08:00
var next = this;
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(mapconfig)
}, {}, function (res, err) { next(err, res); });
},
function checkPost (err, res) {
assert.ifError(err);
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
2019-10-22 01:07:24 +08:00
// CORS headers should be sent with response
// from layergroup creation via POST
checkCORSHeaders(res);
var parsedBody = JSON.parse(res.body);
2019-11-13 23:00:39 +08:00
if (expectedToken) {
assert.deepStrictEqual(parsedBody, { layergroupid: expectedToken, layercount: 2 });
2019-10-22 01:07:24 +08:00
} else {
2019-11-13 23:00:39 +08:00
expectedToken = parsedBody.layergroupid;
2019-10-22 01:07:24 +08:00
}
var meta = parsedBody.metadata;
assert.ok(!_.isUndefined(meta),
'No metadata in torque MapConfig creation response: ' + res.body);
var tm = meta.torque;
assert.ok(tm,
'No "torque" in metadata:' + JSON.stringify(meta));
var tm0 = tm[0];
assert.ok(tm0,
'No layer 0 in "torque" in metadata:' + JSON.stringify(tm));
var expectedTorqueMetadata = { start: 0, end: 86400000, data_steps: 2, column_type: 'date' };
Object.keys(expectedTorqueMetadata).forEach(function (k) {
assert.strictEqual(tm0[k], expectedTorqueMetadata[k]);
assert.strictEqual(meta.layers[0].meta[k], expectedTorqueMetadata[k]);
2019-10-22 01:07:24 +08:00
});
return null;
},
2019-11-13 23:00:39 +08:00
function doGetTile (err) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
var next = this;
assert.response(server, {
2019-11-13 23:00:39 +08:00
url: '/api/v1/map/' + expectedToken + '/0/0/0.png',
2019-10-22 01:07:24 +08:00
method: 'GET',
encoding: 'binary',
headers: {
host: 'localhost'
}
}, {}, function (res, err) { next(err, res); });
},
2019-11-13 23:00:39 +08:00
function checkMapnikError1 (err, res) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
assert.strictEqual(res.statusCode, 400, res.statusCode + (res.statusCode !== 200 ? (': ' + res.body) : ''));
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.strictEqual(parsed.errors.length, 1);
assert.strictEqual(parsed.errors[0], "No 'mapnik' layers in MapConfig");
2019-10-22 01:07:24 +08:00
return null;
},
2019-11-13 23:00:39 +08:00
function doGetGrid0 (err) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
var next = this;
assert.response(server, {
2019-11-13 23:00:39 +08:00
url: '/api/v1/map/' + expectedToken + '/0/0/0/0.grid.json',
2019-10-22 01:07:24 +08:00
method: 'GET',
headers: {
host: 'localhost'
}
}, {}, function (res, err) { next(err, res); });
},
2019-11-13 23:00:39 +08:00
function checkMapnikError2 (err, res) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
assert.strictEqual(res.statusCode, 400, res.statusCode + (res.statusCode !== 200 ? (': ' + res.body) : ''));
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.strictEqual(parsed.errors.length, 1);
assert.strictEqual(parsed.errors[0], 'Unsupported format grid.json');
2019-10-22 01:07:24 +08:00
return null;
},
2019-11-13 23:00:39 +08:00
function doGetTorque0 (err) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
var next = this;
assert.response(server, {
2019-11-13 23:00:39 +08:00
url: '/api/v1/map/' + expectedToken + '/0/0/0/0.json.torque',
2019-10-22 01:07:24 +08:00
method: 'GET',
headers: {
host: 'localhost'
}
}, {}, function (res, err) { next(err, res); });
},
2019-11-13 23:00:39 +08:00
function checkTorque0Response (err, res) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
assert.strictEqual(res.statusCode, 200, res.body);
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
2019-11-13 23:00:39 +08:00
var tileContent = [{ x__uint8: 43, y__uint8: 43, vals__uint8: [1, 1], dates__uint16: [0, 1] }];
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
2019-11-13 23:00:39 +08:00
assert.deepStrictEqual(tileContent, parsed);
2019-10-22 01:07:24 +08:00
return null;
},
2019-11-13 23:00:39 +08:00
function doGetTorque01 (err) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
var next = this;
assert.response(server, {
2019-11-13 23:00:39 +08:00
url: '/api/v1/map/' + expectedToken + '/0/0/0/0.torque.json',
2019-10-22 01:07:24 +08:00
method: 'GET',
headers: {
host: 'localhost'
}
}, {}, function (res, err) { next(err, res); });
},
2019-11-13 23:00:39 +08:00
function checkTorque0Response1 (err, res) {
2019-10-22 01:07:24 +08:00
assert.ifError(err);
assert.strictEqual(res.statusCode, 200, res.body);
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
2019-11-13 23:00:39 +08:00
var tileContent = [{ x__uint8: 43, y__uint8: 43, vals__uint8: [1, 1], dates__uint16: [0, 1] }];
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
2019-11-13 23:00:39 +08:00
assert.deepStrictEqual(tileContent, parsed);
2019-10-22 01:07:24 +08:00
return null;
},
function finish (err) {
2019-11-13 23:00:39 +08:00
keysToDelete['map_cfg|' + LayergroupToken.parse(expectedToken).token] = 0;
2019-10-22 01:07:24 +08:00
keysToDelete['user:localhost:mapviews:global'] = 5;
done(err);
}
);
2015-07-08 05:46:58 +08:00
});
// Test that you cannot write to the database from a torque tile request
//
// Test for http://github.com/CartoDB/Windshaft/issues/130
//
2019-10-22 01:07:24 +08:00
it('database access is read-only', function (done) {
var mapconfig = {
version: '1.1.0',
layers: [
{
type: 'torque',
options: {
sql: "select 'SRID=3857;POINT(0 0)'::geometry as g, now() as d,* from " +
2015-07-08 05:46:58 +08:00
"test_table_inserter(st_setsrid(st_point(0,0),4326),'write')",
2019-10-22 01:07:24 +08:00
geom_column: 'g',
cartocss: 'Map { -torque-frame-count:2; -torque-resolution:3; -torque-time-attribute:d; ' +
2015-07-08 05:46:58 +08:00
'-torque-aggregation-function:\'count(*)\'; }',
2019-10-22 01:07:24 +08:00
cartocss_version: '2.0.1'
}
}
]
};
step(
2019-11-13 23:00:39 +08:00
function doPost () {
2019-10-22 01:07:24 +08:00
var next = this;
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(mapconfig)
}, {}, function (res, err) { next(err, res); });
},
function checkPost (err, res) {
assert.ifError(err);
// TODO: should be 403 Forbidden
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + (res.statusCode === 200 ? '...' : res.body));
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors);
assert.strictEqual(parsed.errors.length, 1);
2019-10-22 01:07:24 +08:00
var msg = parsed.errors[0];
assert.strictEqual(msg, 'TorqueRenderer: cannot execute INSERT in a read-only transaction');
2019-10-22 01:07:24 +08:00
return null;
},
function finish (err) {
done(err);
}
);
2015-07-08 05:46:58 +08:00
});
// See http://github.com/CartoDB/Windshaft/issues/164
2019-10-22 01:07:24 +08:00
it('gives a 500 on database connection refused', function (done) {
var mapconfig = {
version: '1.1.0',
layers: [
{
type: 'torque',
options: {
sql: "select 1 as id, '1970-01-03'::date as d, 'POINT(0 0)'::geometry as the_geom UNION ALL select 2, " +
2015-07-08 05:46:58 +08:00
"'1970-01-01'::date, 'POINT(1 1)'::geometry",
2019-10-22 01:07:24 +08:00
geom_column: 'the_geom',
cartocss: 'Map { -torque-frame-count:2; -torque-resolution:3; -torque-time-attribute:d; ' +
2015-07-08 05:46:58 +08:00
'-torque-aggregation-function:\'count(id)\'; }',
2019-10-22 01:07:24 +08:00
cartocss_version: '2.0.1'
}
}
]
};
2015-07-08 05:46:58 +08:00
2019-10-22 01:07:24 +08:00
const defautlPort = global.environment.postgres.port;
2019-10-22 01:07:24 +08:00
step(
2019-11-13 23:00:39 +08:00
function doPost () {
2019-10-22 01:07:24 +08:00
var next = this;
global.environment.postgres.port = 54777;
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(mapconfig)
}, {}, function (res, err) { next(err, res); });
},
function checkPost (err, res) {
assert.ifError(err);
2019-10-22 01:07:24 +08:00
global.environment.postgres.port = defautlPort;
assert.strictEqual(res.statusCode, 500, res.statusCode + ': ' + res.body);
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors, parsed);
var error = parsed.errors[0];
assert.strictEqual(error, 'TorqueRenderer: cannot connect to the database');
2019-10-22 01:07:24 +08:00
return null;
},
function finish (err) {
done(err);
}
);
2015-07-08 05:46:58 +08:00
});
2019-10-22 01:07:24 +08:00
it('checks types for torque-specific styles', function (done) {
var wrongStyle = ['Map {',
'-torque-frame-count:512;',
'-torque-animation-duration:30;',
"-torque-time-attribute:'cartodb_id';",
'-torque-aggregation-function:count(cartodb_id);', // unquoted aggregation function
'-torque-resolution:4;',
'-torque-data-aggregation:linear;',
'}'].join(' ');
var layergroup = {
version: '1.1.0',
layers: [
{
type: 'torque',
options: {
sql: 'select cartodb_id, the_geom from test_table',
geom_column: 'the_geom',
srid: 4326,
cartocss: wrongStyle
}
}
]
};
2015-07-08 05:46:58 +08:00
2019-10-22 01:07:24 +08:00
step(
function request () {
var next = this;
assert.response(server, {
url: '/api/v1/map',
method: 'POST',
headers: { host: 'localhost', 'Content-Type': 'application/json' },
data: JSON.stringify(layergroup)
}, {}, function (res) { next(null, res); });
},
function checkResponse (err, res) {
assert.ifError(err);
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
2019-10-22 01:07:24 +08:00
var parsed = JSON.parse(res.body);
assert.ok(parsed.errors, parsed);
var error = parsed.errors[0];
assert.strictEqual(error,
2019-10-22 01:07:24 +08:00
"Unexpected type for property '-torque-aggregation-function', expected string");
done();
return null;
}
);
});
2015-07-08 05:46:58 +08:00
});