184 lines
6.2 KiB
JavaScript
184 lines
6.2 KiB
JavaScript
|
|
const { describe, it } = require('mocha');
|
||
|
|
const { expect } = require('chai');
|
||
|
|
const RateLimiterMemory = require('../lib/RateLimiterMemory');
|
||
|
|
const BurstyLimiter = require('../lib/BurstyRateLimiter');
|
||
|
|
const RateLimiterQueue = require('../lib/RateLimiterQueue');
|
||
|
|
const RateLimiterQueueError = require('../lib/component/RateLimiterQueueError');
|
||
|
|
|
||
|
|
describe('RateLimiterQueue with FIFO queue', function RateLimiterQueueTest() {
|
||
|
|
this.timeout(5000);
|
||
|
|
|
||
|
|
it('remove 1 token works and 1 remaining', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 2, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
rlQueue.removeTokens(1)
|
||
|
|
.then((remainingTokens) => {
|
||
|
|
expect(remainingTokens).to.equal(1);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('remove 2 tokens from bursty limiter and returns correct remainingTokens 0', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 1, duration: 1 });
|
||
|
|
const blMemory = new RateLimiterMemory({ points: 1, duration: 3 });
|
||
|
|
const burstyLimiter = new BurstyLimiter(rlMemory, blMemory);
|
||
|
|
const rlQueue = new RateLimiterQueue(burstyLimiter);
|
||
|
|
const startTime = Date.now();
|
||
|
|
rlQueue.removeTokens(1)
|
||
|
|
.then((remainingTokens1) => {
|
||
|
|
expect(remainingTokens1).to.equal(0);
|
||
|
|
rlQueue.removeTokens(1)
|
||
|
|
.then((remainingTokens2) => {
|
||
|
|
expect(remainingTokens2).to.equal(0);
|
||
|
|
expect(Date.now() - startTime < 1000).to.equal(true);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('remove 2 tokens from bursty limiter and wait 1 more', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 1, duration: 1 });
|
||
|
|
const blMemory = new RateLimiterMemory({ points: 1, duration: 3 });
|
||
|
|
const burstyLimiter = new BurstyLimiter(rlMemory, blMemory);
|
||
|
|
const rlQueue = new RateLimiterQueue(burstyLimiter);
|
||
|
|
const startTime = Date.now();
|
||
|
|
rlQueue.removeTokens(1)
|
||
|
|
.then(() => {
|
||
|
|
rlQueue.removeTokens(1)
|
||
|
|
.then(() => {
|
||
|
|
rlQueue.removeTokens(1)
|
||
|
|
.then((remainingTokens) => {
|
||
|
|
expect(remainingTokens).to.equal(0);
|
||
|
|
expect(Date.now() - startTime > 1000).to.equal(true);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('remove all tokens works and 0 remaining', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 2, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
rlQueue.removeTokens(2)
|
||
|
|
.then((remainingTokens) => {
|
||
|
|
expect(remainingTokens).to.equal(0);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('return error if try to remove more tokens than allowed', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 2, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
rlQueue.removeTokens(3)
|
||
|
|
.then(() => {
|
||
|
|
})
|
||
|
|
.catch((err) => {
|
||
|
|
expect(err instanceof RateLimiterQueueError).to.equal(true);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('queues 1 request and fire it after 1 second', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 1, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
const time = Date.now();
|
||
|
|
rlQueue.removeTokens(1).then(() => {
|
||
|
|
});
|
||
|
|
rlQueue.removeTokens(1).then((remainingTokens) => {
|
||
|
|
expect(remainingTokens).to.equal(0);
|
||
|
|
expect(Date.now() - time >= 1000).to.equal(true);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('respects order of queued callbacks', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 1, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
let index;
|
||
|
|
rlQueue.removeTokens(1).then(() => {
|
||
|
|
index = 0;
|
||
|
|
});
|
||
|
|
rlQueue.removeTokens(1).then(() => {
|
||
|
|
expect(index).to.equal(0);
|
||
|
|
index = 1;
|
||
|
|
});
|
||
|
|
rlQueue.removeTokens(1).then(() => {
|
||
|
|
expect(index).to.equal(1);
|
||
|
|
index = 2;
|
||
|
|
});
|
||
|
|
rlQueue.removeTokens(1).then(() => {
|
||
|
|
expect(index).to.equal(2);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('return error if queue length reaches maximum', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 1, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory, { maxQueueSize: 1 });
|
||
|
|
rlQueue.removeTokens(1).then(() => {
|
||
|
|
});
|
||
|
|
rlQueue.removeTokens(1).then(() => {
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
rlQueue.removeTokens(1)
|
||
|
|
.then(() => {
|
||
|
|
done(new Error('must not allow to queue'));
|
||
|
|
})
|
||
|
|
.catch((err) => {
|
||
|
|
expect(err instanceof RateLimiterQueueError).to.equal(true);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('getTokensRemaining works', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 2, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
rlQueue.removeTokens(1)
|
||
|
|
.then(() => {
|
||
|
|
rlQueue.getTokensRemaining()
|
||
|
|
.then((tokensRemaining) => {
|
||
|
|
expect(tokensRemaining).to.equal(1);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('getTokensRemaining returns maximum if internal limiter by key does not exist', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 23, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
rlQueue.getTokensRemaining('test')
|
||
|
|
.then((tokensRemaining) => {
|
||
|
|
expect(tokensRemaining).to.equal(23);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('creates internal instance by key and removes tokens from it', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 2, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
rlQueue.removeTokens(1, 'customkey')
|
||
|
|
.then((remainingTokens) => {
|
||
|
|
expect(remainingTokens).to.equal(1);
|
||
|
|
rlQueue.getTokensRemaining()
|
||
|
|
.then((defaultTokensRemaining) => {
|
||
|
|
expect(defaultTokensRemaining).to.equal(2);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
it('getTokensRemaining returns maximum if internal limiter does not have data', (done) => {
|
||
|
|
const rlMemory = new RateLimiterMemory({ points: 23, duration: 1 });
|
||
|
|
const rlQueue = new RateLimiterQueue(rlMemory);
|
||
|
|
rlQueue.removeTokens(1, 'nodata')
|
||
|
|
.then(() => {
|
||
|
|
setTimeout(() => {
|
||
|
|
rlQueue.getTokensRemaining('nodata')
|
||
|
|
.then((tokensRemaining) => {
|
||
|
|
expect(tokensRemaining).to.equal(23);
|
||
|
|
done();
|
||
|
|
});
|
||
|
|
}, 1001)
|
||
|
|
})
|
||
|
|
});
|
||
|
|
});
|