Files
discordeno/packages/utils/tests/bucket.spec.ts
Fleny b0c1b9f795 ci: Use mocha for both deno and bun (#4067)
* Use mocha for both deno and bun

* Use any Bun 1.1

* remove build:type dependency in lib-check.yml

* Pin point 1.1.42

* remove packages/bot/.mocharc.json
2025-01-01 09:59:06 -08:00

199 lines
5.0 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()
})
})
})