mirror of
https://github.com/discordeno/discordeno.git
synced 2026-06-01 08:20:08 +00:00
I prefer semicolors, they also help avoiding certain pitfalls in JavaScript/TypeScript, such as the following code sample: ```js const xyz = "test" (something.else as string) = "another" ``` This results in a TypeError: "test" is not a function, this is because js thinks we are trying to call the string "test" as a function. To fix this it requires a `;` somewhere before the `(`, such as `;(something ... ` which in my opinion is ugly and less clean overall.
199 lines
5.1 KiB
TypeScript
199 lines
5.1 KiB
TypeScript
import { expect } from 'chai';
|
|
import { afterEach, beforeEach, describe, it } from 'mocha';
|
|
import sinon from 'sinon';
|
|
import { LeakyBucket } from '../src/bucket.js';
|
|
|
|
async function promiseState(p: Promise<any>): Promise<string> {
|
|
const t = {};
|
|
return await Promise.race([p, t]).then(
|
|
(v) => (v === t ? 'pending' : 'fulfilled'),
|
|
() => 'rejected',
|
|
);
|
|
}
|
|
|
|
describe('bucket.ts', () => {
|
|
let clock: sinon.SinonFakeTimers;
|
|
|
|
beforeEach(() => {
|
|
clock = sinon.useFakeTimers();
|
|
});
|
|
|
|
afterEach(() => {
|
|
sinon.restore();
|
|
clock.restore();
|
|
});
|
|
|
|
describe('LeakyBucket function', () => {
|
|
it('will return bucket with given options', () => {
|
|
const options = {
|
|
max: 6006,
|
|
refillInterval: 2002,
|
|
refillAmount: 3003,
|
|
tokens: 4004,
|
|
someThingElse: {
|
|
thing: 'else',
|
|
},
|
|
};
|
|
const bucket = new LeakyBucket(options);
|
|
expect(bucket.max).to.equal(options.max);
|
|
expect(bucket.refillInterval).to.equal(options.refillInterval);
|
|
expect(bucket.refillAmount).to.equal(options.refillAmount);
|
|
});
|
|
|
|
it('will return bucket with refillAmount within max', () => {
|
|
const options = {
|
|
max: 111,
|
|
refillInterval: 2002,
|
|
refillAmount: 3003,
|
|
tokens: 4004,
|
|
};
|
|
const bucket = new LeakyBucket(options);
|
|
expect(bucket.refillAmount).to.equal(options.max);
|
|
});
|
|
|
|
it('will return bucket with tokensState within max', () => {
|
|
const options = {
|
|
max: 111,
|
|
refillInterval: 2002,
|
|
refillAmount: 3003,
|
|
tokens: 4004,
|
|
};
|
|
const bucket = new LeakyBucket(options);
|
|
expect(bucket.refillAmount).to.equal(options.max);
|
|
});
|
|
|
|
it('will return bucket with default property', () => {
|
|
const bucket = new LeakyBucket();
|
|
expect(bucket.max).equals(1);
|
|
expect(bucket.refillInterval).equals(5000);
|
|
expect(bucket.refillAmount).equals(1);
|
|
expect(bucket.queue).to.deep.equal([]);
|
|
});
|
|
|
|
it('will acquire a request', async () => {
|
|
const bucket = new LeakyBucket({
|
|
max: 120,
|
|
refillInterval: 60000,
|
|
refillAmount: 120,
|
|
});
|
|
|
|
await bucket.acquire(true);
|
|
expect(bucket.remaining).to.be.equal(119);
|
|
expect(bucket.used).to.be.equal(1);
|
|
});
|
|
|
|
it('will handle multiple requests at once', async () => {
|
|
const bucket = new LeakyBucket({
|
|
max: 120,
|
|
refillInterval: 60000,
|
|
refillAmount: 120,
|
|
});
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
bucket.acquire();
|
|
}
|
|
});
|
|
|
|
it('will handle too many requests', async () => {
|
|
const bucket = new LeakyBucket({
|
|
max: 5,
|
|
refillInterval: 10000,
|
|
refillAmount: 5,
|
|
});
|
|
|
|
for (let i = 0; i < 10; i++) {
|
|
bucket.acquire();
|
|
}
|
|
});
|
|
|
|
it('bucket refills are done properly', async () => {
|
|
const bucket = new LeakyBucket({
|
|
max: 2,
|
|
refillInterval: 500,
|
|
refillAmount: 2,
|
|
});
|
|
|
|
await bucket.acquire();
|
|
expect(bucket.remaining).equals(1);
|
|
expect(bucket.used).equals(1);
|
|
await clock.tickAsync(1000);
|
|
expect(bucket.remaining).equals(2);
|
|
expect(bucket.used).equals(0);
|
|
|
|
await bucket.acquire();
|
|
await clock.tickAsync(1000);
|
|
});
|
|
|
|
it('bucket refills when refill amount is < max', async () => {
|
|
const bucket = new LeakyBucket({
|
|
max: 3,
|
|
refillInterval: 800,
|
|
refillAmount: 1,
|
|
});
|
|
|
|
await bucket.acquire();
|
|
await bucket.acquire();
|
|
expect(bucket.remaining).equals(1);
|
|
expect(bucket.used).equals(2);
|
|
await clock.tickAsync(1000);
|
|
expect(bucket.remaining).equals(2);
|
|
expect(bucket.used).equals(1);
|
|
|
|
await clock.tickAsync(2000);
|
|
expect(bucket.remaining).equals(3);
|
|
expect(bucket.used).equals(0);
|
|
});
|
|
|
|
it('bucket refills when refill interval is slow', async () => {
|
|
const bucket = new LeakyBucket({
|
|
max: 1,
|
|
refillInterval: 500,
|
|
refillAmount: 1,
|
|
});
|
|
|
|
const acquired1 = bucket.acquire();
|
|
const acquired2 = bucket.acquire();
|
|
|
|
// js event loop
|
|
await (async () => {})();
|
|
|
|
expect(await promiseState(acquired1)).to.equal('fulfilled');
|
|
expect(await promiseState(acquired2)).to.equal('pending');
|
|
|
|
await clock.tickAsync(499);
|
|
expect(await promiseState(acquired2)).to.equal('pending');
|
|
|
|
await clock.tickAsync(1);
|
|
expect(await promiseState(acquired2)).to.equal('fulfilled');
|
|
|
|
expect(bucket.remaining).equals(0);
|
|
expect(bucket.used).equals(1);
|
|
});
|
|
|
|
describe('remaining', () => {
|
|
it('should be 0 even used too many', () => {
|
|
const bucket = new LeakyBucket({
|
|
max: 1,
|
|
refillInterval: 500,
|
|
refillAmount: 1,
|
|
});
|
|
// max is < used
|
|
bucket.used = 2;
|
|
expect(bucket.remaining).equals(0);
|
|
});
|
|
});
|
|
|
|
it("Don't process queue twice", () => {
|
|
const bucket = new LeakyBucket({
|
|
max: 1,
|
|
refillInterval: 500,
|
|
refillAmount: 1,
|
|
});
|
|
// fake processing
|
|
bucket.processing = true;
|
|
// request when already processing
|
|
bucket.processQueue();
|
|
});
|
|
});
|
|
});
|