1383 lines
62 KiB
JavaScript
1383 lines
62 KiB
JavaScript
'use strict';
|
|
|
|
var path = require('path');
|
|
var assert = require('../support/assert');
|
|
var _ = require('underscore');
|
|
var redis = require('redis');
|
|
var step = require('step');
|
|
var strftime = require('strftime');
|
|
var redisStatsDb = 5;
|
|
|
|
var mapnik = require('windshaft').mapnik;
|
|
var semver = require('semver');
|
|
|
|
var helper = require('../support/test-helper');
|
|
var LayergroupToken = require('../../lib/models/layergroup-token');
|
|
|
|
var windshaftFixtures = path.join(__dirname, '/../../node_modules/windshaft/test/fixtures');
|
|
|
|
var IMAGE_EQUALS_TOLERANCE_PER_MIL = 20;
|
|
var IMAGE_EQUALS_HIGHER_TOLERANCE_PER_MIL = 25;
|
|
|
|
var CartodbWindshaft = require('../../lib/server');
|
|
var serverOptions = require('../../lib/server-options');
|
|
|
|
var QueryTables = require('cartodb-query-tables').queryTables;
|
|
|
|
['/api/v1/map', '/user/localhost/api/v1/map'].forEach(function (layergroupUrl) {
|
|
var suiteName = 'multilayer:postgres=layergroupUrl=' + layergroupUrl;
|
|
describe(suiteName, function () {
|
|
var server;
|
|
|
|
before(function () {
|
|
server = new CartodbWindshaft(serverOptions);
|
|
server.setMaxListeners(0);
|
|
});
|
|
|
|
var keysToDelete;
|
|
|
|
beforeEach(function () {
|
|
keysToDelete = {};
|
|
});
|
|
|
|
afterEach(function (done) {
|
|
helper.deleteRedisKeys(keysToDelete, done);
|
|
});
|
|
|
|
var cdbQueryTablesFromPostgresEnabledValue = true;
|
|
|
|
var expectedLastUpdatedEpoch = 1234567890123; // this is hard-coded into SQLAPIEmu
|
|
var expectedLastUpdated = new Date(expectedLastUpdatedEpoch).toISOString();
|
|
|
|
var testUser = _.template(global.environment.postgres_auth_user, { user_id: 1 });
|
|
var testDatabase = testUser + '_db';
|
|
|
|
it('layergroup with 2 layers, each with its style', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
|
|
' from test_table limit 2',
|
|
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.1',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
},
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, -5e6, 0) as the_geom_webmercator' +
|
|
' from test_table limit 2 offset 2',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
var expectedToken; // = "e34dd7e235138a062f8ba7ad051aa3a7";
|
|
step(
|
|
function doPost () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.strictEqual(parsedBody.last_updated, expectedLastUpdated);
|
|
assert.strictEqual(res.headers['x-layergroup-id'], parsedBody.layergroupid);
|
|
expectedToken = parsedBody.layergroupid;
|
|
next(null, res);
|
|
});
|
|
},
|
|
function doGetTile (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb0/0/0/0.png',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
|
|
// Check Cache-Control
|
|
var cc = res.headers['cache-control'];
|
|
assert.strictEqual(cc, 'public,max-age=31536000'); // 1 year
|
|
|
|
// Check X-Cache-Channel
|
|
cc = res.headers['x-cache-channel'];
|
|
assert.ok(cc);
|
|
var dbname = testDatabase;
|
|
assert.strictEqual(cc.substring(0, dbname.length), dbname);
|
|
if (!cdbQueryTablesFromPostgresEnabledValue) { // only test if it was using the SQL API
|
|
var jsonquery = cc.substring(dbname.length + 1);
|
|
var sentquery = JSON.parse(jsonquery);
|
|
var expectedQuery = [
|
|
layergroup.layers[0].options.sql, ';',
|
|
layergroup.layers[1].options.sql
|
|
].join('');
|
|
assert.strictEqual(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$' +
|
|
expectedQuery +
|
|
'$windshaft$) as tablenames )' +
|
|
' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max' +
|
|
' FROM CDB_TableMetadata m' +
|
|
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
|
|
}
|
|
|
|
assert.imageBufferIsSimilarToFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.png',
|
|
IMAGE_EQUALS_HIGHER_TOLERANCE_PER_MIL, function (err/*, similarity */) {
|
|
next(err);
|
|
}
|
|
);
|
|
});
|
|
},
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/170
|
|
function doGetTileNoSignature (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/localhost@' + expectedToken + ':cb0/0/0/0.png',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 403, res.statusCode + ':' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
var msg = parsed.errors[0];
|
|
assert.ok(msg.match(/permission denied/i), msg);
|
|
next(err);
|
|
});
|
|
},
|
|
function doGetGridLayer0 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + '/0/0/0/0.grid.json',
|
|
headers: { host: 'localhost' },
|
|
method: 'GET'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.layer0.grid.json', 2,
|
|
function (err/*, similarity */) {
|
|
next(err);
|
|
});
|
|
});
|
|
},
|
|
function doGetGridLayer1 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + '/1/0/0/0.grid.json',
|
|
headers: { host: 'localhost' },
|
|
method: 'GET'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_table_0_0_0_multilayer1.layer1.grid.json', 2,
|
|
function (err/*, similarity */) {
|
|
next(err);
|
|
});
|
|
});
|
|
},
|
|
function finish (err) {
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(expectedToken).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
describe('server-metadata', function () {
|
|
var serverMetadata;
|
|
beforeEach(function () {
|
|
serverMetadata = global.environment.serverMetadata;
|
|
global.environment.serverMetadata = { cdn_url: { http: 'test', https: 'tests' } };
|
|
});
|
|
|
|
afterEach(function () {
|
|
global.environment.serverMetadata = serverMetadata;
|
|
});
|
|
|
|
it('should include serverMedata in the response', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
|
|
' from test_table limit 2',
|
|
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
step(
|
|
function doCreateGet () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '?config=' + encodeURIComponent(JSON.stringify(layergroup)),
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
}, {}, function (res, err) { next(err, res); });
|
|
},
|
|
function doCheckCreate (err, res) {
|
|
assert.ifError(err);
|
|
var parsed = JSON.parse(res.body);
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsed.layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
assert.ok(_.isEqual(parsed.cdn_url, global.environment.serverMetadata.cdn_url));
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
});
|
|
|
|
it('get creation requests has cache', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, the_geom_webmercator from test_table',
|
|
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.1',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
},
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, the_geom_webmercator from test_table_2',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
var expectedToken;
|
|
step(
|
|
function doCreateGet () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '?config=' + encodeURIComponent(JSON.stringify(layergroup)),
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
}, {}, function (res, err) { next(err, res); });
|
|
},
|
|
function doCheckCreate (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
expectedToken = parsedBody.layergroupid.split(':')[0];
|
|
helper.checkCache(res);
|
|
helper.checkSurrogateKey(res, new QueryTables.QueryMetadata([
|
|
{ dbname: 'test_windshaft_cartodb_user_1_db', table_name: 'test_table', schema_name: 'public' },
|
|
{ dbname: 'test_windshaft_cartodb_user_1_db', table_name: 'test_table_2', schema_name: 'public' }
|
|
]).key().join(' '));
|
|
|
|
keysToDelete['map_cfg|' + expectedToken] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('get creation has no cache if sql is bogus', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select bogus(0,0) as the_geom_webmercator',
|
|
cartocss: '#layer { polygon-fill: red; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
assert.response(server, {
|
|
url: layergroupUrl + '?config=' + encodeURIComponent(JSON.stringify(layergroup)),
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
}, {}, function (res) {
|
|
assert.notStrictEqual(res.statusCode, 200);
|
|
helper.checkNoCache(res);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('get creation has no cache if cartocss is not valid', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
|
|
' from test_table limit 2',
|
|
cartocss: '#layer { invalid-rule:red; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
assert.response(server, {
|
|
url: layergroupUrl + '?config=' + encodeURIComponent(JSON.stringify(layergroup)),
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
}, {}, function (res) {
|
|
assert.notStrictEqual(res.statusCode, 200);
|
|
helper.checkNoCache(res);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('layergroup can hold substitution tokens', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select 1 as cartodb_id, ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!))' +
|
|
' as the_geom_webmercator from test_table limit 1',
|
|
cartocss: '#layer { polygon-fill:red; }',
|
|
cartocss_version: '2.0.1',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
var expectedToken; // = "6d8e4ad5458e2d25cf0eef38e38717a6";
|
|
step(
|
|
function doPost () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.strictEqual(parsedBody.last_updated, expectedLastUpdated);
|
|
if (expectedToken) {
|
|
assert.strictEqual(parsedBody.layergroupid, expectedToken + ':' + expectedLastUpdatedEpoch);
|
|
} else {
|
|
expectedToken = parsedBody.layergroupid.split(':')[0];
|
|
}
|
|
next(null, res);
|
|
});
|
|
},
|
|
function doGetTile1 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb10/1/0/0.png',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
|
|
// Check X-Cache-Channel
|
|
var cc = res.headers['x-cache-channel'];
|
|
assert.ok(cc);
|
|
var dbname = testDatabase;
|
|
assert.strictEqual(cc.substring(0, dbname.length), dbname);
|
|
if (!cdbQueryTablesFromPostgresEnabledValue) { // only test if it was using the SQL API
|
|
var jsonquery = cc.substring(dbname.length + 1);
|
|
var sentquery = JSON.parse(jsonquery);
|
|
var expectedQuery = layergroup.layers[0].options.sql
|
|
.replace(/!bbox!/g, 'ST_MakeEnvelope(0,0,0,0)')
|
|
.replace(/!pixel_width!/g, '1')
|
|
.replace(/!pixel_height!/g, '1');
|
|
assert.strictEqual(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$' +
|
|
expectedQuery +
|
|
'$windshaft$) as tablenames )' +
|
|
' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max' +
|
|
' FROM CDB_TableMetadata m' +
|
|
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
|
|
}
|
|
|
|
var referenceImagePath = 'test/fixtures/test_multilayer_bbox.png';
|
|
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath, IMAGE_EQUALS_TOLERANCE_PER_MIL,
|
|
function (err/*, similarity */) {
|
|
next(err);
|
|
});
|
|
});
|
|
},
|
|
function doGetTile4 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb11/4/0/0.png',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
|
|
// Check X-Cache-Channel
|
|
var cc = res.headers['x-cache-channel'];
|
|
assert.ok(cc);
|
|
var dbname = testDatabase;
|
|
assert.strictEqual(cc.substring(0, dbname.length), dbname);
|
|
if (!cdbQueryTablesFromPostgresEnabledValue) { // only test if it was using the SQL API
|
|
var jsonquery = cc.substring(dbname.length + 1);
|
|
var sentquery = JSON.parse(jsonquery);
|
|
var expectedQuery = layergroup.layers[0].options.sql
|
|
.replace('!bbox!', 'ST_MakeEnvelope(0,0,0,0)')
|
|
.replace('!pixel_width!', '1')
|
|
.replace('!pixel_height!', '1');
|
|
assert.strictEqual(sentquery.q, 'WITH querytables AS ( SELECT * FROM CDB_QueryTables($windshaft$' +
|
|
expectedQuery +
|
|
'$windshaft$) as tablenames )' +
|
|
' SELECT (SELECT tablenames FROM querytables), EXTRACT(EPOCH FROM max(updated_at)) as max' +
|
|
' FROM CDB_TableMetadata m' +
|
|
' WHERE m.tabname = any ((SELECT tablenames from querytables)::regclass[])');
|
|
}
|
|
|
|
var referenceImagePath = 'test/fixtures/test_multilayer_bbox.png';
|
|
assert.imageBufferIsSimilarToFile(res.body, referenceImagePath, IMAGE_EQUALS_TOLERANCE_PER_MIL,
|
|
function (err/*, similarity */) {
|
|
next(err);
|
|
});
|
|
});
|
|
},
|
|
function doGetGrid1 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + '/0/1/0/0.grid.json',
|
|
headers: { host: 'localhost' },
|
|
method: 'GET'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.grid.json', 2,
|
|
function (err/*, similarity */) {
|
|
next(err);
|
|
});
|
|
});
|
|
},
|
|
function doGetGrid4 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + '/0/4/0/0.grid.json',
|
|
headers: { host: 'localhost' },
|
|
method: 'GET'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
assert.utfgridEqualsFile(res.body, 'test/fixtures/test_multilayer_bbox.grid.json', 2, next);
|
|
});
|
|
},
|
|
function finish (err) {
|
|
keysToDelete['map_cfg|' + expectedToken] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
it('layergroup creation raises mapviews counter', function (done) {
|
|
var layergroup = {
|
|
stat_tag: 'random_tag',
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select 1 as cartodb_id, !pixel_height! as h,' +
|
|
' ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!)) as the_geom_webmercator',
|
|
cartocss: '#layer { polygon-fill:red; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
var statskey = 'user:localhost:mapviews';
|
|
var redisStatsClient = redis.createClient(global.environment.redis.port);
|
|
var expectedToken; // will be set on first post and checked on second
|
|
var now = strftime('%Y%m%d', new Date());
|
|
step(
|
|
function cleanStats () {
|
|
var next = this;
|
|
redisStatsClient.select(redisStatsDb, function (err) {
|
|
if (err) {
|
|
next(err);
|
|
} else {
|
|
redisStatsClient.del(statskey + ':global', next);
|
|
}
|
|
});
|
|
},
|
|
function doPost1 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
expectedToken = JSON.parse(res.body).layergroupid;
|
|
redisStatsClient.zscore(statskey + ':global', now, next);
|
|
});
|
|
},
|
|
function checkGlobalStats1 (err, val) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(val, '1', 'Expected score of ' + now + ' in ' + statskey + ':global to be 1, got ' + val);
|
|
redisStatsClient.zscore(statskey + ':stat_tag:random_tag', now, this);
|
|
},
|
|
function checkTagStats1DoPost2 (err, val) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(val, '1', 'Expected score of ' + now + ' in ' + statskey + ':stat_tag:' + layergroup.stat_tag +
|
|
' to be 1, got ' + val);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(JSON.parse(res.body).layergroupid, expectedToken);
|
|
redisStatsClient.zscore(statskey + ':global', now, next);
|
|
});
|
|
},
|
|
function checkGlobalStats2 (err, val) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(val, '2', 'Expected score of ' + now + ' in ' + statskey + ':global to be 2, got ' + val);
|
|
redisStatsClient.zscore(statskey + ':stat_tag:' + layergroup.stat_tag, now, this);
|
|
},
|
|
function checkTagStats2 (err, val) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(val, '2', 'Expected score of ' + now + ' in ' + statskey + ':stat_tag:' + layergroup.stat_tag +
|
|
' to be 2, got ' + val);
|
|
return 1;
|
|
},
|
|
function finish (err) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
// strip epoch
|
|
expectedToken = expectedToken.split(':')[0];
|
|
keysToDelete['map_cfg|' + expectedToken] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
keysToDelete[statskey + ':stat_tag:' + layergroup.stat_tag] = 5;
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('layergroup creation fails if CartoCSS is bogus', function (done) {
|
|
var layergroup = {
|
|
stat_tag: 'random_tag',
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select 1 as cartodb_id, !pixel_height! as h,' +
|
|
'ST_Buffer(!bbox!, -32*greatest(!pixel_width!,!pixel_height!)) as the_geom_webmercator',
|
|
cartocss: '#layer { polygon-fit:red; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 400, res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(parsed.errors[0].match(/^style0/));
|
|
assert.ok(parsed.errors[0].match(/Unrecognized rule: polygon-fit/));
|
|
done();
|
|
});
|
|
});
|
|
|
|
// Also tests that server doesn't crash:
|
|
// see http://github.com/CartoDB/Windshaft-cartodb/issues/109
|
|
it('layergroup creation fails if sql is bogus', function (done) {
|
|
var layergroup = {
|
|
stat_tag: 'random_tag',
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select bogus(0,0) as the_geom_webmercator',
|
|
cartocss: '#layer { polygon-fill:red; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
var msg = parsed.errors[0];
|
|
assert.ok(msg.match(/bogus.*exist/), msg);
|
|
helper.checkNoCache(res);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('layergroup with 2 private-table layers', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 where cartodb_id=1',
|
|
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.1.0',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
},
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 where cartodb_id=2',
|
|
cartocss: '#layer { marker-fill:blue; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.1.0',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
var expectedToken; // = "b4ed64d93a411a59f330ab3d798e4009";
|
|
step(
|
|
function doPost () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '?map_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.strictEqual(parsedBody.last_updated, expectedLastUpdated);
|
|
if (expectedToken) {
|
|
assert.strictEqual(parsedBody.layergroupid, expectedToken + ':' + expectedLastUpdatedEpoch);
|
|
} else {
|
|
expectedToken = parsedBody.layergroupid.split(':')[0];
|
|
}
|
|
next(null, res);
|
|
});
|
|
},
|
|
function doGetTile (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb0/0/0/0.png?map_key=1234',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
|
|
// Check X-Cache-Channel
|
|
var cc = res.headers['x-cache-channel'];
|
|
assert.ok(cc);
|
|
var dbname = testDatabase;
|
|
assert.strictEqual(cc.substring(0, dbname.length), dbname);
|
|
next(err);
|
|
});
|
|
},
|
|
function doGetGridLayer0 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + '/0/0/0/0.grid.json?map_key=1234',
|
|
headers: { host: 'localhost' },
|
|
method: 'GET'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
next(err);
|
|
});
|
|
},
|
|
function doGetGridLayer1 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + '/1/0/0/0.grid.json?map_key=1234',
|
|
headers: { host: 'localhost' },
|
|
method: 'GET'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'application/json; charset=utf-8');
|
|
next(err);
|
|
});
|
|
},
|
|
function doGetTileUnauth (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb0/0/0/0.png',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 403);
|
|
var re = new RegExp('permission denied');
|
|
assert.ok(res.body.match(re), 'No "permission denied" error: ' + res.body);
|
|
next(err);
|
|
});
|
|
},
|
|
function doGetGridLayer0Unauth (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + '/0/0/0/0.grid.json',
|
|
headers: { host: 'localhost' },
|
|
method: 'GET'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 403);
|
|
var re = new RegExp('permission denied');
|
|
assert.ok(res.body.match(re), 'No "permission denied" error: ' + res.body);
|
|
next(err);
|
|
});
|
|
},
|
|
function doGetGridLayer1Unauth (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + '/1/0/0/0.grid.json',
|
|
headers: { host: 'localhost' },
|
|
method: 'GET'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 403);
|
|
var re = new RegExp('permission denied');
|
|
assert.ok(res.body.match(re), 'No "permission denied" error: ' + res.body);
|
|
next(err);
|
|
});
|
|
},
|
|
function finish (err) {
|
|
keysToDelete['map_cfg|' + expectedToken] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/152
|
|
it('x-cache-channel still works for GETs after tiler restart', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table where cartodb_id=1',
|
|
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.1.0',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
var expectedToken; // = "b4ed64d93a411a59f330ab3d798e4009";
|
|
step(
|
|
function doPost () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '?map_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res, err) { next(err, res); });
|
|
},
|
|
function checkPost (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.strictEqual(parsedBody.last_updated, expectedLastUpdated);
|
|
if (expectedToken) {
|
|
assert.strictEqual(parsedBody.layergroupid, expectedToken + ':' + expectedLastUpdatedEpoch);
|
|
} else {
|
|
expectedToken = parsedBody.layergroupid.split(':')[0];
|
|
}
|
|
return null;
|
|
},
|
|
function doGet0 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb0/0/0/0.png?map_key=1234',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res, err) { next(err, res); });
|
|
},
|
|
function doCheck0 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
|
|
// Check X-Cache-Channel
|
|
var cc = res.headers['x-cache-channel'];
|
|
assert.ok(cc, 'Missing X-Cache-Channel');
|
|
var dbname = testDatabase;
|
|
assert.strictEqual(cc.substring(0, dbname.length), dbname);
|
|
return null;
|
|
},
|
|
function doRestartServer (err/*, res */) {
|
|
assert.ifError(err);
|
|
// hack simulating restart...
|
|
server = new CartodbWindshaft(serverOptions);
|
|
return null;
|
|
},
|
|
function doGet1 (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb0/0/0/0.png?map_key=1234',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res, err) { next(err, res); });
|
|
},
|
|
function doCheck1 (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
|
|
// Check X-Cache-Channel
|
|
var cc = res.headers['x-cache-channel'];
|
|
assert.ok(cc, 'Missing X-Cache-Channel on restart');
|
|
var dbname = testDatabase;
|
|
assert.strictEqual(cc.substring(0, dbname.length), dbname);
|
|
return null;
|
|
},
|
|
function finish (err) {
|
|
keysToDelete['map_cfg|' + expectedToken] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
// https://github.com/cartodb/Windshaft-cartodb/issues/81
|
|
it('invalid text-name in CartoCSS', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.1',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: "select 1 as cartodb_id, 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
|
|
cartocss: '#sample { text-name: cartodb_id; text-face-name: "Dejagnu"; }',
|
|
cartocss_version: '2.1.0'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.strictEqual(parsed.errors.length, 1);
|
|
var errmsg = parsed.errors[0];
|
|
assert.ok(errmsg.match(/text-face-name.*Dejagnu/), parsed.errors.toString());
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('quotes CartoCSS', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.1',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: "select 'single''quote' as n, 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
|
|
cartocss: '#s [n="single\'quote" ] { marker-fill:red; }',
|
|
cartocss_version: '2.1.0'
|
|
}
|
|
},
|
|
{
|
|
options: {
|
|
sql: "select 'double\"quote' as n, 'SRID=3857;POINT(2 0)'::geometry as the_geom_webmercator",
|
|
cartocss: '#s [n="double\\"quote" ] { marker-fill:red; }',
|
|
cartocss_version: '2.1.0'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(JSON.parse(res.body).layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
done();
|
|
});
|
|
});
|
|
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/87
|
|
it('exponential notation in CartoCSS filter values', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.1',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: "select .4 as n, 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
|
|
cartocss: '#s [n<=.2e-2] { marker-fill:red; }',
|
|
cartocss_version: '2.1.0'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(JSON.parse(res.body).layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
done();
|
|
});
|
|
});
|
|
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/93
|
|
if (semver.satisfies(mapnik.versions.mapnik, '2.3.x')) {
|
|
it('accepts unused directives', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: "select 'SRID=3857;POINT(0 0)'::geometry as the_geom_webmercator",
|
|
cartocss: '#layer { point-transform:"scale(20)"; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
var expectedToken; // = "e34dd7e235138a062f8ba7ad051aa3a7";
|
|
step(
|
|
function doPost () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
if (expectedToken) {
|
|
assert.strictEqual(parsedBody.layergroupid, expectedToken + ':' + expectedLastUpdatedEpoch);
|
|
assert.strictEqual(res.headers['x-layergroup-id'], parsedBody.layergroupid);
|
|
} else {
|
|
var tokenComponents = parsedBody.layergroupid.split(':');
|
|
expectedToken = tokenComponents[0];
|
|
expectedLastUpdatedEpoch = tokenComponents[1];
|
|
}
|
|
next(null, res);
|
|
});
|
|
},
|
|
function doGetTile (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb0/0/0/0.png',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res) {
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
assert.strictEqual(res.headers['content-type'], 'image/png');
|
|
assert.imageBufferIsSimilarToFile(res.body, windshaftFixtures + '/test_default_mapnik_point.png',
|
|
IMAGE_EQUALS_TOLERANCE_PER_MIL, function (err/*, similarity */) {
|
|
next(err);
|
|
}
|
|
);
|
|
});
|
|
},
|
|
function finish (err) {
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
keysToDelete['map_cfg|' + expectedToken] = 0;
|
|
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/91
|
|
// and https://github.com/CartoDB/Windshaft-cartodb/issues/38
|
|
it('tiles for private tables can be fetched with api_key', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1 LIMIT 0',
|
|
cartocss: '#layer { marker-fill:red; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
var expectedToken; // = "e34dd7e235138a062f8ba7ad051aa3a7";
|
|
step(
|
|
function doPost () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res) { next(null, res); });
|
|
},
|
|
function checkResult (err, res) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
if (expectedToken) {
|
|
assert.strictEqual(parsedBody.layergroupid, expectedToken + ':' + expectedLastUpdatedEpoch);
|
|
assert.strictEqual(res.headers['x-layergroup-id'], parsedBody.layergroupid);
|
|
} else {
|
|
var tokenComponents = parsedBody.layergroupid.split(':');
|
|
expectedToken = tokenComponents[0];
|
|
expectedLastUpdatedEpoch = tokenComponents[1];
|
|
}
|
|
next(null, res);
|
|
},
|
|
function doGetTile (err) {
|
|
assert.ifError(err);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '/' + expectedToken + ':cb0/0/0/0.png?api_key=1234',
|
|
method: 'GET',
|
|
headers: { host: 'localhost' },
|
|
encoding: 'binary'
|
|
}, {}, function (res) { next(null, res); });
|
|
},
|
|
function checkGetTile (err, res) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
assert.strictEqual(res.statusCode, 200, res.body);
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
keysToDelete['map_cfg|' + expectedToken] = 0;
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
// SQL strings can be of arbitrary length, when using POST
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/111
|
|
it('sql string can be very long', function (done) {
|
|
var longVal = 'pretty';
|
|
for (var i = 0; i < 1024; ++i) {
|
|
longVal += ' long';
|
|
}
|
|
longVal += ' string';
|
|
var sql = 'SELECT ';
|
|
for (i = 0; i < 16; ++i) {
|
|
sql += "'" + longVal + "'::text as pretty_long_field_name_" + i + ', ';
|
|
}
|
|
sql += 'cartodb_id, the_geom_webmercator FROM gadm4 g';
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: sql,
|
|
cartocss: '#layer { marker-fill:red; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
var expectedToken;
|
|
step(
|
|
function doPost () {
|
|
var data = JSON.stringify(layergroup);
|
|
assert.ok(data.length > 1024 * 64);
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl + '?api_key=1234',
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: data
|
|
}, {}, function (res) { next(null, res); });
|
|
},
|
|
function checkResult (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 200, res.statusCode + ': ' + res.body);
|
|
var parsedBody = JSON.parse(res.body);
|
|
var tokenComponents = parsedBody.layergroupid.split(':');
|
|
expectedToken = tokenComponents[0];
|
|
return null;
|
|
},
|
|
function cleanup (err) {
|
|
if (err) {
|
|
return done(err);
|
|
}
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
keysToDelete['map_cfg|' + expectedToken] = 0;
|
|
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
// WARN: MapConfig with mapnik layer and no cartocss it's valid since
|
|
// vector & raster aggregation project, now we can request MVT format w/o defining styles
|
|
// for the layer.
|
|
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/133
|
|
it.skip('MapConfig with mapnik layer and no cartocss', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select cartodb_id, ST_Translate(the_geom_webmercator, 5e6, 0) as the_geom_webmercator' +
|
|
' from test_table limit 2',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
step(
|
|
function doPost () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res, err) { next(err, res); });
|
|
},
|
|
function checkPost (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(parsed.errors, 'Missing "errors" in response: ' + JSON.stringify(parsed));
|
|
assert.strictEqual(parsed.errors.length, 1);
|
|
var msg = parsed.errors[0];
|
|
assert.strictEqual(msg, 'Missing cartocss for layer 0 options');
|
|
return null;
|
|
},
|
|
function finish (err) {
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
|
|
if (!cdbQueryTablesFromPostgresEnabledValue) { // only test if it was using the SQL API
|
|
// See https://github.com/CartoDB/Windshaft-cartodb/issues/167
|
|
it('lack of response from sql-api will result in a timeout', function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: "select *, 'SQLAPINOANSWER' from test_table",
|
|
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.1.0'
|
|
}
|
|
}
|
|
]
|
|
};
|
|
|
|
step(
|
|
function doPost () {
|
|
var next = this;
|
|
assert.response(server, {
|
|
url: layergroupUrl,
|
|
method: 'POST',
|
|
headers: { host: 'localhost', 'Content-Type': 'application/json' },
|
|
data: JSON.stringify(layergroup)
|
|
}, {}, function (res, err) { next(err, res); });
|
|
},
|
|
function checkPost (err, res) {
|
|
assert.ifError(err);
|
|
assert.strictEqual(res.statusCode, 400, res.statusCode + ': ' + res.body);
|
|
var parsed = JSON.parse(res.body);
|
|
assert.ok(parsed.errors, 'Missing "errors" in response: ' + JSON.stringify(parsed));
|
|
assert.strictEqual(parsed.errors.length, 1);
|
|
var msg = parsed.errors[0];
|
|
assert.ok(msg, /could not fetch source tables/, msg);
|
|
return null;
|
|
},
|
|
function finish (err) {
|
|
done(err);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
var layergroupTtlRequest = {
|
|
url: layergroupUrl + '?config=' + encodeURIComponent(JSON.stringify({
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table limit 2',
|
|
cartocss: '#layer { marker-fill:red; marker-width:32; marker-allow-overlap:true; }',
|
|
cartocss_version: '2.0.1'
|
|
}
|
|
}
|
|
]
|
|
})),
|
|
method: 'GET',
|
|
headers: { host: 'localhost' }
|
|
};
|
|
var layergroupTtlResponseExpectation = {
|
|
status: 200
|
|
};
|
|
|
|
it('cache control for layergroup default value', function (done) {
|
|
global.environment.varnish.layergroupTtl = null;
|
|
|
|
var server = new CartodbWindshaft(serverOptions);
|
|
|
|
assert.response(server, layergroupTtlRequest, layergroupTtlResponseExpectation,
|
|
function (res) {
|
|
assert.strictEqual(res.headers['cache-control'], 'public,max-age=86400,must-revalidate');
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(JSON.parse(res.body).layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('cache control for layergroup uses configuration for max-age', function (done) {
|
|
var layergroupTtl = 300;
|
|
global.environment.varnish.layergroupTtl = layergroupTtl;
|
|
|
|
var server = new CartodbWindshaft(serverOptions);
|
|
|
|
assert.response(server, layergroupTtlRequest, layergroupTtlResponseExpectation,
|
|
function (res) {
|
|
assert.strictEqual(res.headers['cache-control'], 'public,max-age=' + layergroupTtl + ',must-revalidate');
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(JSON.parse(res.body).layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it("it's not possible to override authorization with a crafted layergroup", function (done) {
|
|
var layergroup = {
|
|
version: '1.0.0',
|
|
layers: [
|
|
{
|
|
options: {
|
|
sql: 'select * from test_table_private_1',
|
|
cartocss: '#layer { marker-fill:red; }',
|
|
cartocss_version: '2.3.0',
|
|
interactivity: 'cartodb_id'
|
|
}
|
|
}
|
|
],
|
|
template: {
|
|
auth: {
|
|
method: 'open'
|
|
},
|
|
name: 'open'
|
|
}
|
|
};
|
|
|
|
assert.response(
|
|
server,
|
|
{
|
|
url: '/api/v1/map?signer=localhost',
|
|
method: 'POST',
|
|
headers: {
|
|
host: 'localhost',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
data: JSON.stringify(layergroup)
|
|
},
|
|
{
|
|
status: 403
|
|
},
|
|
function (res) {
|
|
assert.ok(res.body.match(/permission denied for .+?test_table_private_1/));
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
|
|
it('should response to empty layers mapconfig', function (done) {
|
|
var layergroup = {
|
|
layers: []
|
|
};
|
|
|
|
assert.response(
|
|
server,
|
|
{
|
|
url: '/api/v1/map',
|
|
method: 'POST',
|
|
headers: {
|
|
host: 'localhost',
|
|
'Content-Type': 'application/json'
|
|
},
|
|
data: JSON.stringify(layergroup)
|
|
},
|
|
{
|
|
status: 200
|
|
},
|
|
function (res, err) {
|
|
assert.ok(!err);
|
|
|
|
var parsedBody = JSON.parse(res.body);
|
|
assert.ok(parsedBody.layergroupid);
|
|
|
|
keysToDelete['map_cfg|' + LayergroupToken.parse(parsedBody.layergroupid).token] = 0;
|
|
keysToDelete['user:localhost:mapviews:global'] = 5;
|
|
|
|
done();
|
|
}
|
|
);
|
|
});
|
|
});
|
|
});
|