You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
Windshaft-cartodb/test/unit/template-maps-test.js

654 lines
23 KiB

'use strict';
var testHelper = require('../support/test-helper');
var assert = require('assert');
var RedisPool = require('redis-mpool');
var TemplateMaps = require('../../lib/backends/template-maps');
var step = require('step');
var _ = require('underscore');
describe('template_maps', function () {
// configure redis pool instance to use in tests
var redisPool = new RedisPool(global.environment.redis);
var keysToDelete;
beforeEach(function () {
keysToDelete = {};
});
afterEach(function (done) {
testHelper.deleteRedisKeys(keysToDelete, done);
});
var wadusLayer = {
options: {
sql: 'select 1 cartodb_id, null::geometry the_geom_webmercator',
cartocss: '#layer { marker-fill:blue; }',
cartocss_version: '2.3.0'
}
};
var LAYER_PLAIN = {
type: 'plain',
options: {
color: 'red'
}
};
it('does not accept template with unsupported version', function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var tpl = {
version: '6.6.6',
name: 'k',
auth: {},
layergroup: { layers: [wadusLayer] }
};
step(
function () {
tmap.addTemplate('me', tpl, this);
},
function checkFailed (err) {
assert.ok(err);
assert.ok(err.message.match(/unsupported.*version/i), err);
return null;
},
function finish (err) {
done(err);
}
);
});
it('does not accept template with missing name', function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var tpl = {
version: '0.0.1',
auth: {},
layergroup: { layers: [wadusLayer] }
};
step(
function () {
tmap.addTemplate('me', tpl, this);
},
function checkFailed (err) {
assert.ok(err);
assert.ok(err.message.match(/missing.*name/i), err);
return null;
},
function finish (err) {
done(err);
}
);
});
describe('naming', function () {
function createTemplate (name) {
return {
version: '0.0.1',
name: name,
auth: {},
layergroup: {
layers: [
wadusLayer
]
}
};
}
var templateMaps = new TemplateMaps(redisPool);
var invalidNames = ['ab|', 'a b', 'a@b', '-1ab', '_x', '', ' x', 'x '];
invalidNames.forEach(function (invalidName) {
it('should NOT accept template with invalid name: ' + invalidName, function (done) {
templateMaps.addTemplate('me', createTemplate(invalidName), function (err) {
assert.ok(err, "Unexpected success with invalid name '" + invalidName + "'");
assert.ok(
err.message.match(/template.*name/i),
"Unexpected error message with invalid name '" + invalidName + "': " + err.message
);
done();
});
});
});
var validNames = [
'AB', '1ab', 'DFD19A1A-0AC6-11E5-B0CA-6476BA93D4F6', '25ad8300-0ac7-11e5-b93f-6476ba93d4f6'
];
validNames.forEach(function (validName) {
it('should accept template with valid name: ' + validName, function (done) {
templateMaps.addTemplate('me', createTemplate(validName), function (err) {
assert.ok(!err, "Unexpected error with valid name '" + validName + "': " + err);
keysToDelete['map_tpl|me'] = 0;
done();
});
});
});
});
it('does not accept template with invalid placeholder name', function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var tpl = {
version: '0.0.1',
name: 'valid',
placeholders: {},
auth: {},
layergroup: { layers: [wadusLayer] }
};
var invalidnames = ['ab|', 'a b', 'a@b', '1ab', '_x', '', ' x', 'x '];
var testNext = function () {
if (!invalidnames.length) { done(); return; }
var n = invalidnames.pop();
tpl.placeholders = {};
tpl.placeholders[n] = { type: 'number', default: 1 };
tmap.addTemplate('me', tpl, function (err) {
if (!err) {
done(new Error("Unexpected success with invalid name '" + n + "'"));
} else if (!err.message.match(/invalid.*name/i)) {
done(new Error("Unexpected error message with invalid name '" + n + "': " + err));
} else {
testNext();
}
});
};
testNext();
});
it('does not accept template with missing placeholder default', function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var tpl = {
version: '0.0.1',
name: 'valid',
placeholders: { v: {} },
auth: {},
layergroup: { layers: [wadusLayer] }
};
tmap.addTemplate('me', tpl, function (err) {
if (!err) {
done(new Error('Unexpected success with missing placeholder default'));
} else if (!err.message.match(/missing default/i)) {
done(new Error('Unexpected error message with missing placeholder default: ' + err));
} else {
done();
}
});
});
it('does not accept template with missing placeholder type', function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var tpl = {
version: '0.0.1',
name: 'valid',
placeholders: { v: { default: 1 } },
auth: {},
layergroup: { layers: [wadusLayer] }
};
tmap.addTemplate('me', tpl, function (err) {
if (!err) {
done(new Error('Unexpected success with missing placeholder type'));
} else if (!err.message.match(/missing type/i)) {
done(new Error('Unexpected error message with missing placeholder default: ' + err));
} else {
done();
}
});
});
// See http://github.com/CartoDB/Windshaft-cartodb/issues/128
it('does not accept template with invalid token auth (undefined tokens)',
function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var tpl = {
version: '0.0.1',
name: 'invalid_auth1',
placeholders: { },
auth: { method: 'token' },
layergroup: { layers: [wadusLayer] }
};
tmap.addTemplate('me', tpl, function (err) {
if (!err) {
done(new Error('Unexpected success with invalid token auth (undefined tokens)'));
} else if (!err.message.match(/invalid 'token' authentication/i)) {
done(new Error('Unexpected error message with invalid token auth (undefined tokens): ' + err));
} else {
done();
}
});
});
it('add, get and delete a valid template', function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var expectedFailure = false;
var tplId;
var tpl = {
version: '0.0.1',
name: 'first',
auth: {},
layergroup: { layers: [wadusLayer] }
};
step(
function () {
tmap.addTemplate('me', tpl, this);
},
function addOmonimousTemplate (err, id) {
assert.ifError(err);
tplId = id;
assert.strictEqual(tplId, 'first');
expectedFailure = true;
// should fail, as it already exists
tmap.addTemplate('me', tpl, this);
},
function getTemplate (err) {
if (!expectedFailure && err) {
throw err;
}
assert.ok(err);
assert.ok(err.message.match(/already exists/i), err);
tmap.getTemplate('me', tplId, this);
},
function delTemplate (err, gotTpl) {
assert.ifError(err);
assert.deepStrictEqual(gotTpl, _.extend({}, tpl, { auth: { method: 'open' }, placeholders: {} }));
tmap.delTemplate('me', tplId, this);
},
function finish (err) {
done(err);
}
);
});
it('add multiple templates, list them', function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var tpl1 = { version: '0.0.1', name: 'first', auth: {}, layergroup: { layers: [wadusLayer] } };
var tpl1Id;
var tpl2 = { version: '0.0.1', name: 'second', auth: {}, layergroup: { layers: [wadusLayer] } };
var tpl2Id;
step(
function addTemplate1 () {
tmap.addTemplate('me', tpl1, this);
},
function addTemplate2 (err, id) {
assert.ifError(err);
tpl1Id = id;
tmap.addTemplate('me', tpl2, this);
},
function listTemplates (err, id) {
assert.ifError(err);
tpl2Id = id;
tmap.listTemplates('me', this);
},
function checkTemplates (err, ids) {
assert.ifError(err);
assert.strictEqual(ids.length, 2);
assert.ok(ids.indexOf(tpl1Id) !== -1, ids.join(','));
assert.ok(ids.indexOf(tpl2Id) !== -1, ids.join(','));
return null;
},
function delTemplate1 (err) {
if (tpl1Id) {
var next = this;
tmap.delTemplate('me', tpl1Id, function (e) {
if (err || e) {
next(new Error(err + '; ' + e));
} else {
next();
}
});
} else {
assert.ifError(err);
return null;
}
},
function delTemplate2 (err) {
if (tpl2Id) {
var next = this;
tmap.delTemplate('me', tpl2Id, function (e) {
if (err || e) {
next(new Error(err + '; ' + e));
} else {
next();
}
});
} else {
assert.ifError(err);
return null;
}
},
function finish (err) {
done(err);
}
);
});
it('update templates', function (done) {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var expectedFailure = false;
var owner = 'me';
var tpl = {
version: '0.0.1',
name: 'first',
auth: { method: 'open' },
layergroup: { layers: [wadusLayer] }
};
var tplId;
step(
function addTemplate () {
tmap.addTemplate(owner, tpl, this);
},
// Updating template name should fail
function updateTemplateName (err, id) {
assert.ifError(err);
tplId = id;
expectedFailure = true;
tpl.name = 'second';
tmap.updTemplate(owner, tplId, tpl, this);
},
function updateTemplateAuth (err) {
if (err && !expectedFailure) {
throw err;
}
expectedFailure = false;
assert.ok(err);
tpl.name = 'first';
tpl.auth.method = 'token';
tpl.auth.valid_tokens = ['tok1'];
tmap.updTemplate(owner, tplId, tpl, this);
},
function updateTemplateWithInvalid (err) {
assert.ifError(err);
tpl.version = '999.999.999';
expectedFailure = true;
tmap.updTemplate(owner, tplId, tpl, this);
},
function updateUnexistentTemplate (err) {
if (err && !expectedFailure) {
throw err;
}
assert.ok(err);
assert.ok(err.message.match(/unsupported.*version/i), err);
tpl.version = '0.0.1';
expectedFailure = true;
tmap.updTemplate(owner, 'unexistent', tpl, this);
},
function delTemplate (err) {
if (err && !expectedFailure) {
throw err;
}
expectedFailure = false;
assert.ok(err);
assert.ok(err.message.match(/cannot update name/i), err);
tmap.delTemplate(owner, tplId, this);
},
function finish (err) {
done(err);
}
);
});
it('instanciate templates', function () {
var tmap = new TemplateMaps(redisPool);
assert.ok(tmap);
var tpl1 = {
version: '0.0.1',
name: 'acceptance1',
auth: { method: 'open' },
placeholders: {
fill: { type: 'css_color', default: 'red' },
color: { type: 'css_color', default: '#a0fF9A' },
name: { type: 'sql_literal', default: 'test' },
zoom: { type: 'number', default: '0' },
test_number: { type: 'number', default: 23 }
},
layergroup: {
version: '1.0.0',
global_cartocss_version: '2.0.2',
layers: [
{
options: {
sql: "select '<%=name %>' || id, g from t",
cartocss: '#layer { marker-fill:<%= fill %>; marker-width: <%=test_number %>; }'
}
},
{
options: {
sql: "select fun('<%= name%>') g from x",
cartocss: '#layer { line-color:<%= color %>; marker-fill:<%= color %>; }'
}
},
{
options: {
sql: 'select g from x',
cartocss: '#layer[zoom=<%=zoom%>] { }'
}
}
]
}
};
var inst = tmap.instance(tpl1, {});
var lyr = inst.layers[0].options;
assert.strictEqual(lyr.sql, "select 'test' || id, g from t");
assert.strictEqual(lyr.cartocss, '#layer { marker-fill:red; marker-width: 23; }');
lyr = inst.layers[1].options;
assert.strictEqual(lyr.sql, "select fun('test') g from x");
assert.strictEqual(lyr.cartocss, '#layer { line-color:#a0fF9A; marker-fill:#a0fF9A; }');
inst = tmap.instance(tpl1, { color: 'yellow', name: "it's dangerous" });
lyr = inst.layers[0].options;
assert.strictEqual(lyr.sql, "select 'it''s dangerous' || id, g from t");
assert.strictEqual(lyr.cartocss, '#layer { marker-fill:red; marker-width: 23; }');
lyr = inst.layers[1].options;
assert.strictEqual(lyr.sql, "select fun('it''s dangerous') g from x");
assert.strictEqual(lyr.cartocss, '#layer { line-color:yellow; marker-fill:yellow; }');
// Invalid css_color
var err = null;
try { tmap.instance(tpl1, { color: '##ff00ff' }); } catch (e) { err = e; }
assert.ok(err);
assert.ok(err.message.match(/invalid css_color/i), err);
// Invalid css_color 2 (too few digits)
err = null;
try { tmap.instance(tpl1, { color: '#ff' }); } catch (e) { err = e; }
assert.ok(err);
assert.ok(err.message.match(/invalid css_color/i), err);
// Invalid css_color 3 (too many digits)
err = null;
try { tmap.instance(tpl1, { color: '#1234567' }); } catch (e) { err = e; }
assert.ok(err);
assert.ok(err.message.match(/invalid css_color/i), err);
// Invalid number
err = null;
try { tmap.instance(tpl1, { zoom: '#' }); } catch (e) { err = e; }
assert.ok(err);
assert.ok(err.message.match(/invalid number/i), err);
// Invalid number 2
err = null;
try { tmap.instance(tpl1, { zoom: '23e' }); } catch (e) { err = e; }
assert.ok(err);
assert.ok(err.message.match(/invalid number/i), err);
// Valid number
err = null;
try { tmap.instance(tpl1, { zoom: '-.23e10' }); } catch (e) { err = e; }
assert.ok(!err);
});
// Can set a limit on the number of user templates
it('can limit number of user templates', function (done) {
var tmap = new TemplateMaps(redisPool, {
max_user_templates: 2
});
assert.ok(tmap);
var tpl = { version: '0.0.1', auth: {}, layergroup: { layers: [wadusLayer] } };
var expectErr = false;
var idMe = [];
var idYou = [];
step(
function oneForMe () {
tpl.name = 'oneForMe';
tmap.addTemplate('me', tpl, this);
},
function twoForMe (err, id) {
assert.ifError(err);
assert.ok(id);
idMe.push(id);
tpl.name = 'twoForMe';
tmap.addTemplate('me', tpl, this);
},
function threeForMe (err, id) {
assert.ifError(err);
assert.ok(id);
idMe.push(id);
tpl.name = 'threeForMe';
expectErr = true;
tmap.addTemplate('me', tpl, this);
},
function errForMe (err/*, id */) {
if (err && !expectErr) {
throw err;
}
expectErr = false;
assert.ok(err);
assert.ok(err.message.match(/limit.*template/), err);
return null;
},
function delOneMe (err) {
assert.ifError(err);
tmap.delTemplate('me', idMe.shift(), this);
},
function threeForMeRetry (err) {
assert.ifError(err);
tpl.name = 'threeForMe';
tmap.addTemplate('me', tpl, this);
},
function oneForYou (err, id) {
assert.ifError(err);
assert.ok(id);
idMe.push(id);
tpl.name = 'oneForYou';
tmap.addTemplate('you', tpl, this);
},
function twoForYou (err, id) {
assert.ifError(err);
assert.ok(id);
idYou.push(id);
tpl.name = 'twoForYou';
tmap.addTemplate('you', tpl, this);
},
function threeForYou (err, id) {
assert.ifError(err);
assert.ok(id);
idYou.push(id);
tpl.name = 'threeForYou';
expectErr = true;
tmap.addTemplate('you', tpl, this);
},
function errForYou (err/*, id */) {
if (err && !expectErr) {
throw err;
}
expectErr = false;
assert.ok(err);
assert.ok(err.message.match(/limit.*template/), err);
return null;
},
function finish (err) {
keysToDelete['map_tpl|you'] = 0;
keysToDelete['map_tpl|me'] = 0;
// TODO: delete all templates
done(err);
}
);
});
describe('emit', function () {
var owner = 'me';
var templateName = 'emit';
var template = {
version: '0.0.1',
name: templateName,
auth: {
method: 'open'
},
placeholders: {},
layergroup: {
layers: [
wadusLayer
]
}
};
var templateUpdated = _.extend({}, template, { layergroup: { layers: [LAYER_PLAIN] } });
var templateMaps;
beforeEach(function () {
templateMaps = new TemplateMaps(redisPool);
});
it('should emit on template update', function (done) {
templateMaps.on('update', function (_owner, _templateName, _template) {
assert.strictEqual(_owner, owner);
assert.strictEqual(_templateName, templateName);
assert.deepStrictEqual(_template, templateUpdated);
templateMaps.delTemplate(owner, templateName, done);
});
templateMaps.addTemplate(owner, template, function (err, templateName, _template) {
assert.ok(!err, err);
assert.deepStrictEqual(_template, template);
templateMaps.updTemplate(owner, templateName, templateUpdated, function () {});
});
});
it('should emit on template deletion', function (done) {
templateMaps.on('delete', function (_owner, _templateName) {
assert.strictEqual(_owner, owner);
assert.strictEqual(_templateName, templateName);
done();
});
templateMaps.addTemplate(owner, template, function (err, templateName, _template) {
assert.ok(!err, err);
assert.deepStrictEqual(_template, template);
templateMaps.delTemplate(owner, templateName, function () {});
});
});
it('should NOT emit on template update when template is the same', function (done) {
templateMaps.on('update', function (_owner, _templateName, _template) {
assert.strictEqual(_owner, owner);
assert.strictEqual(_templateName, templateName);
assert.deepStrictEqual(_template, templateUpdated);
templateMaps.delTemplate(owner, templateName, done);
});
templateMaps.addTemplate(owner, template, function (err, templateName, _template) {
assert.ok(!err, err);
assert.deepStrictEqual(_template, template);
templateMaps.updTemplate(owner, templateName, template, function () {
templateMaps.updTemplate(owner, templateName, templateUpdated, function () {});
});
});
});
});
});