Windshaft-cartodb/test/acceptance/cache/surrogate_keys_invalidation.js
2019-10-07 10:10:51 +02:00

324 lines
10 KiB
JavaScript

'use strict';
var testHelper = require('../../support/test_helper');
var assert = require('../../support/assert');
var step = require('step');
var FastlyPurge = require('fastly-purge');
var _ = require('underscore');
var NamedMapsCacheEntry = require('../../../lib/cache/model/named-maps-entry');
var CartodbWindshaft = require(__dirname + '/../../../lib/server');
var nock = require('nock');
describe('templates surrogate keys', function() {
var serverOptions = require('../../../lib/server-options');
// Enable Varnish purge for tests
var varnishHost = serverOptions.varnish_host;
serverOptions.varnish_host = '127.0.0.1';
var varnishPurgeEnabled = serverOptions.varnish_purge_enabled;
serverOptions.varnish_purge_enabled = true;
var fastlyConfig = serverOptions.fastly;
var FAKE_FASTLY_API_KEY = 'fastly-api-key';
var FAKE_FASTLY_SERVICE_ID = 'fake-service-id';
serverOptions.fastly = {
enabled: true,
// the fastly api key
apiKey: FAKE_FASTLY_API_KEY,
// the service that will get surrogate key invalidation
serviceId: FAKE_FASTLY_SERVICE_ID
};
var server;
before(function () {
server = new CartodbWindshaft(serverOptions);
nock.disableNetConnect();
nock.enableNetConnect(/(127.0.0.1|cartocdn.com)/);
});
var templateOwner = 'localhost';
var templateName = 'acceptance';
var expectedTemplateId = templateName;
var template = {
version: '0.0.1',
name: templateName,
auth: {
method: 'open'
},
layergroup: {
version: '1.2.0',
layers: [
{
options: {
sql: 'select 1 cartodb_id, null::geometry as the_geom_webmercator',
cartocss: '#layer { marker-fill:blue; }',
cartocss_version: '2.3.0'
}
}
]
}
};
var templateUpdated = _.extend({}, template, {layergroup: {layers: [{
type: 'plain',
options: {
color: 'red'
}
}]} });
var expectedBody = { template_id: expectedTemplateId };
var varnishHttpUrl = [
'http://', serverOptions.varnish_host, ':', serverOptions.varnish_http_port
].join('');
var cacheEntryKey = new NamedMapsCacheEntry(templateOwner, templateName).key();
var invalidationMatchHeader = '\\b' + cacheEntryKey + '\\b';
var fastlyPurgePath = '/service/' + FAKE_FASTLY_SERVICE_ID + '/purge/' + cacheEntryKey;
after(function(done) {
serverOptions.varnish_purge_enabled = false;
serverOptions.varnish_host = varnishHost;
serverOptions.varnish_purge_enabled = varnishPurgeEnabled;
serverOptions.fastly = fastlyConfig;
nock.cleanAll();
nock.enableNetConnect();
done();
});
function createTemplate(callback) {
var postTemplateRequest = {
url: '/api/v1/map/named?api_key=1234',
method: 'POST',
headers: {
host: templateOwner,
'Content-Type': 'application/json'
},
data: JSON.stringify(template)
};
step(
function postTemplate() {
var next = this;
assert.response(server,
postTemplateRequest,
{
status: 200
},
function(res) {
next(null, res);
}
);
},
function rePostTemplate(err, res) {
if (err) {
throw err;
}
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, expectedBody);
return true;
},
function finish(err) {
callback(err);
}
);
}
it("invalidates surrogate keys on template update", function(done) {
var scope = nock(varnishHttpUrl)
.intercept('/key', 'PURGE')
.matchHeader('Invalidation-Match', invalidationMatchHeader)
.reply(204, '');
var fastlyScope = nock(FastlyPurge.FASTLY_API_ENDPOINT)
.post(fastlyPurgePath)
.matchHeader('Fastly-Key', FAKE_FASTLY_API_KEY)
.matchHeader('Accept', 'application/json')
.reply(200, {
status:'ok'
});
step(
function createTemplateToUpdate() {
createTemplate(this);
},
function putValidTemplate(err) {
if (err) {
throw err;
}
var updateTemplateRequest = {
url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
method: 'PUT',
headers: {
host: templateOwner,
'Content-Type': 'application/json'
},
data: JSON.stringify(templateUpdated)
};
var next = this;
assert.response(server,
updateTemplateRequest,
{
status: 200
},
function(res) {
setTimeout(function() {
next(null, res);
}, 50);
}
);
},
function checkValidUpdate(err, res) {
if (err) {
throw err;
}
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, expectedBody);
assert.equal(scope.pendingMocks().length, 0);
assert.equal(fastlyScope.pendingMocks().length, 0);
return null;
},
function finish(err) {
if ( err ) {
return done(err);
}
testHelper.deleteRedisKeys({'map_tpl|localhost': 0}, done);
}
);
});
it("invalidates surrogate on template deletion", function(done) {
var scope = nock(varnishHttpUrl)
.intercept('/key', 'PURGE')
.matchHeader('Invalidation-Match', invalidationMatchHeader)
.reply(204, '');
var fastlyScope = nock(FastlyPurge.FASTLY_API_ENDPOINT)
.post(fastlyPurgePath)
.matchHeader('Fastly-Key', FAKE_FASTLY_API_KEY)
.matchHeader('Accept', 'application/json')
.reply(200, {
status:'ok'
});
step(
function createTemplateToDelete() {
createTemplate(this);
},
function deleteValidTemplate(err) {
if (err) {
throw err;
}
var deleteTemplateRequest = {
url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
method: 'DELETE',
headers: {
host: templateOwner,
'Content-Type': 'application/json'
}
};
var next = this;
assert.response(server,
deleteTemplateRequest,
{
status: 204
},
function(res) {
setTimeout(function() {
next(null, res);
}, 50);
}
);
},
function checkValidUpdate(err) {
if (err) {
throw err;
}
assert.equal(scope.pendingMocks().length, 0);
assert.equal(fastlyScope.pendingMocks().length, 0);
return null;
},
function finish(err) {
done(err);
}
);
});
it("should update template even if surrogate key invalidation fails", function(done) {
var scope = nock(varnishHttpUrl)
.intercept('/key', 'PURGE')
.matchHeader('Invalidation-Match', invalidationMatchHeader)
.reply(503, '');
var fastlyScope = nock(FastlyPurge.FASTLY_API_ENDPOINT)
.post(fastlyPurgePath)
.matchHeader('Fastly-Key', FAKE_FASTLY_API_KEY)
.matchHeader('Accept', 'application/json')
.reply(200, {
status:'ok'
});
step(
function createTemplateToUpdate() {
createTemplate(this);
},
function putValidTemplate(err) {
if (err) {
throw err;
}
var updateTemplateRequest = {
url: '/api/v1/map/named/' + expectedTemplateId + '/?api_key=1234',
method: 'PUT',
headers: {
host: templateOwner,
'Content-Type': 'application/json'
},
data: JSON.stringify(templateUpdated)
};
var next = this;
assert.response(server,
updateTemplateRequest,
{
status: 200
},
function(res) {
setTimeout(function() {
next(null, res);
}, 50);
}
);
},
function checkValidUpdate(err, res) {
if (err) {
throw err;
}
var parsedBody = JSON.parse(res.body);
assert.deepEqual(parsedBody, expectedBody);
assert.equal(scope.pendingMocks().length, 0);
assert.equal(fastlyScope.pendingMocks().length, 0);
return null;
},
function finish(err) {
if ( err ) {
return done(err);
}
testHelper.deleteRedisKeys({'map_tpl|localhost': 0}, done);
}
);
});
});