From a0f560ca1a1500d20f52de920fbba7d50b2c6803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Simon=20Mart=C3=ADn?= Date: Tue, 20 Feb 2018 10:57:29 +0100 Subject: [PATCH] rate limits acceptance tests --- test/acceptance/rate-limit.test.js | 250 +++++++++++++++++++++++++++ test/unit/cartodb/rate-limit.test.js | 24 +-- 2 files changed, 262 insertions(+), 12 deletions(-) create mode 100644 test/acceptance/rate-limit.test.js diff --git a/test/acceptance/rate-limit.test.js b/test/acceptance/rate-limit.test.js new file mode 100644 index 00000000..ed867dd6 --- /dev/null +++ b/test/acceptance/rate-limit.test.js @@ -0,0 +1,250 @@ +require('../support/test_helper'); + +const assert = require('../support/assert'); +const TestClient = require('../support/test-client'); +const redis = require('redis'); +const { + RATE_LIMIT_ENDPOINTS_GROUPS, + RATE_LIMIT_STORE_KEY +} = require('../../lib/cartodb/middleware/rate-limit'); + + +let redisClient; +let testClient; +let keysToDelete = ['user:localhost:mapviews:global']; +const user = 'cdb'; + +const query = ` + SELECT + ST_Transform('SRID=4326;POINT(-180 85.05112877)'::geometry, 3857) the_geom_webmercator, + 1 cartodb_id, + 2 val +`; + +const createMapConfig = ({ + version = '1.6.0', + type = 'cartodb', + sql = query, + cartocss = TestClient.CARTOCSS.POINTS, + cartocss_version = '2.3.0', + interactivity = 'cartodb_id', + countBy = 'cartodb_id' +} = {}) => ({ + version, + layers: [{ + type, + options: { + source: { + id: 'a0' + }, + cartocss, + cartocss_version, + interactivity + } + }], + analyses: [ + { + id: 'a0', + type: 'source', + params: { + query: sql + } + } + ], + dataviews: { + count: { + source: { + id: 'a0' + }, + type: 'formula', + options: { + column: countBy, + operation: 'count' + } + } + } +}); + + +function setLimit(count, period, burst) { + redisClient.SELECT(8, function(err) { + if (err) { + return; + } + + const key = RATE_LIMIT_STORE_KEY + user + ':' + RATE_LIMIT_ENDPOINTS_GROUPS.ENDPOINT_1; + redisClient.hset(key, 'b', burst, 'c', count, 'p', period, function() { + keysToDelete.push(key); + }); + }); +} + +describe('rate limit acceptance', function() { + before(function() { + redisClient = redis.createClient(global.environment.redis.port); + testClient = new TestClient(createMapConfig(), 1234); + }); + + afterEach(function(done) { + keysToDelete.forEach( key => { + redisClient.del(key); + }); + + redisClient.SELECT(0, () => { + redisClient.del('user:localhost:mapviews:global'); + + redisClient.SELECT(5, () => { + redisClient.del('user:localhost:mapviews:global'); + done(); + }); + }); + + }); + + it('should not be rate limited', function (done) { + const count = 1; + const period = 1; + const burst = 1; + setLimit(count, period, burst); + + let response = { + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-Rate-Limit-Limit': '2', + 'X-Rate-Limit-Remaining': '1', + 'X-Rate-Limit-Reset': '1', + 'X-Rate-Limit-Retry-After': '-1' + } + }; + + testClient.getLayergroup({ response }, (err) => { + assert.ifError(err); + setTimeout(done, period * 1000); + }); + }); + + it("1 req/sec: 5 request (1 per 250ms) should be limited: OK, KO, KO, KO, OK", function(done) { + const count = 1; + const period = 1; + const burst = 1; + setLimit(count, period, burst); + + let response = { + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-Rate-Limit-Limit': '2', + 'X-Rate-Limit-Remaining': '1', + 'X-Rate-Limit-Reset': '1', + 'X-Rate-Limit-Retry-After': '-1' + } + }; + + testClient.getLayergroup({ response }, (err, res) => { + assert.ifError(err); + }); + + setTimeout( + function() { + let response = { + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-Rate-Limit-Limit': '2', + 'X-Rate-Limit-Remaining': '0', + 'X-Rate-Limit-Reset': '1', + 'X-Rate-Limit-Retry-After': '-1' + } + }; + + testClient.getLayergroup({ response }, (err) => { + assert.ifError(err); + }); + }, + 250 + ); + + setTimeout( + function() { + let response = { + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-Rate-Limit-Limit': '2', + 'X-Rate-Limit-Remaining': '0', + 'X-Rate-Limit-Reset': '2', + 'X-Rate-Limit-Retry-After': '-1' + } + }; + + testClient.getLayergroup({ response }, (err) => { + assert.ifError(err); + }); + }, + 500 + ); + + setTimeout( + function() { + let response = { + status: 429, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-Rate-Limit-Limit': '2', + 'X-Rate-Limit-Remaining': '0', + 'X-Rate-Limit-Reset': '2', + 'X-Rate-Limit-Retry-After': '1' + } + }; + + testClient.getLayergroup({ response }, (err) => { + assert.ifError(err); + }); + }, + 750 + ); + + setTimeout( + function() { + let response = { + status: 429, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-Rate-Limit-Limit': '2', + 'X-Rate-Limit-Remaining': '0', + 'X-Rate-Limit-Reset': '2', + 'X-Rate-Limit-Retry-After': '1' + } + }; + + testClient.getLayergroup({ response }, (err) => { + assert.ifError(err); + }); + }, + 950 + ); + + setTimeout( + function() { + let response = { + status: 200, + headers: { + 'Content-Type': 'application/json; charset=utf-8', + 'X-Rate-Limit-Limit': '2', + 'X-Rate-Limit-Remaining': '0', + 'X-Rate-Limit-Reset': '2', + 'X-Rate-Limit-Retry-After': '-1' + } + }; + + testClient.getLayergroup({ response }, (err) => { + assert.ifError(err); + done(); + }); + }, + 1050 + ); + }); + +}); \ No newline at end of file diff --git a/test/unit/cartodb/rate-limit.test.js b/test/unit/cartodb/rate-limit.test.js index 90360720..d85aefef 100644 --- a/test/unit/cartodb/rate-limit.test.js +++ b/test/unit/cartodb/rate-limit.test.js @@ -38,7 +38,7 @@ function getReqAndRes() { }; } -describe.only('rate-limit', function() { +describe('rate limit unit', function() { before(function() { const redisPool = new RedisPool(global.environment.redis); const metadataBackend = cartodbRedis({pool: redisPool}); @@ -66,7 +66,7 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": burst, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": -1 + "X-Rate-Limit-Retry-After": -1 }); setTimeout(done, period * 1000); @@ -86,7 +86,7 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": burst, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": -1 + "X-Rate-Limit-Retry-After": -1 }); }); @@ -99,7 +99,7 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": burst, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": -1 + "X-Rate-Limit-Retry-After": -1 }); }); }, @@ -115,7 +115,7 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": burst, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": -1 + "X-Rate-Limit-Retry-After": -1 }); setTimeout(done, period * 1000); @@ -151,7 +151,7 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": count - 1, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": -1 + "X-Rate-Limit-Retry-After": -1 }); }); }, @@ -167,10 +167,10 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": 0, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": 1 + "X-Rate-Limit-Retry-After": 1 }); assert.equal(err.message, 'You are over the limits.'); - assert.equal(err.http_status, 429); + assert.equal(err.http_status, 429); }); }, 500 @@ -185,7 +185,7 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": 0, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": 1 + "X-Rate-Limit-Retry-After": 1 }); assert.equal(err.message, 'You are over the limits.'); assert.equal(err.http_status, 429); @@ -203,10 +203,10 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": 0, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": 1 + "X-Rate-Limit-Retry-After": 1 }); assert.equal(err.message, 'You are over the limits.'); - assert.equal(err.http_status, 429); + assert.equal(err.http_status, 429); }); }, 950 @@ -221,7 +221,7 @@ describe.only('rate-limit', function() { "X-Rate-Limit-Limit": burst + 1, "X-Rate-Limit-Remaining": count - 1, "X-Rate-Limit-Reset": period, - "X-Rate-Limit-Retry-After": -1 + "X-Rate-Limit-Retry-After": -1 }); setTimeout(done, 1000); });