examples/MCP-tools Public
import assert from "node:assert/strict";import { test } from "node:test";import { LIMIT, msUntilFull, take, type Bucket } from "../src/rate-limiter/bucket.ts";const limit = { capacity: 3, refillPerSecond: 1 };function drain(n: number, now = 0): Bucket | undefined { let bucket: Bucket | undefined; for (let i = 0; i < n; i++) bucket = take(bucket, now, limit).bucket; return bucket;}test("a new client starts with a full bucket", () => { assert.deepEqual(take(undefined, 0, limit).decision, { ok: true, remaining: 2 });});test("an empty bucket refuses and says how long to wait", () => { const { bucket, decision } = take(drain(3), 0, limit); assert.deepEqual(decision, { ok: false, retryAfterSeconds: 1 }); assert.equal(bucket.tokens, 0, "a refused call costs nothing");});test("tokens come back over time, up to the capacity", () => { const empty = drain(3); assert.deepEqual(take(empty, 1000, limit).decision, { ok: true, remaining: 0 }); assert.deepEqual(take(empty, 60_000, limit).decision, { ok: true, remaining: 2 });});test("a partly refilled bucket rounds the wait up", () => { const empty = drain(3); const slow = { capacity: 3, refillPerSecond: 0.5 }; assert.deepEqual(take(empty, 500, slow).decision, { ok: false, retryAfterSeconds: 2 });});test("a clock that goes backwards does not mint tokens", () => { const bucket = drain(3, 10_000); assert.equal(take(bucket, 5_000, limit).decision.ok, false);});test("knows when a bucket is full again", () => { assert.equal(msUntilFull({ tokens: 1, updated: 0 }, limit), 2000); assert.equal(msUntilFull({ tokens: 0, updated: 0 }), 60_000);});test("the default allows a burst of 30 and one call every two seconds after", () => { assert.equal(LIMIT.capacity, 30); assert.equal(1 / LIMIT.refillPerSecond, 2);});