Skip to main content
Glama
rate-limit.test.ts8.14 kB
/** * Rate Limit Middleware Tests * * Tests for rate limiting middleware functionality. * * Requirements: 18.2 */ import type { NextFunction, Request, Response } from "express"; import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; import { createRateLimitMiddleware, createRateLimiterInstance, } from "../../../../server/middleware/rate-limit.js"; describe("Rate Limit Middleware", () => { let mockReq: Partial<Request>; let mockRes: Partial<Response>; let mockNext: NextFunction; let headers: Record<string, string>; let statusCode: number; let responseBody: unknown; beforeEach(() => { vi.useFakeTimers(); headers = {}; statusCode = 200; responseBody = null; mockReq = { headers: {}, ip: "127.0.0.1", socket: { remoteAddress: "127.0.0.1" } as any, }; mockRes = { setHeader: vi.fn((key: string, value: string) => { headers[key] = value; return mockRes as Response; }), status: vi.fn((code: number) => { statusCode = code; return mockRes as Response; }), json: vi.fn((body: unknown) => { responseBody = body; return mockRes as Response; }), on: vi.fn(), statusCode: 200, }; mockNext = vi.fn(); }); afterEach(() => { vi.useRealTimers(); }); describe("createRateLimitMiddleware", () => { it("should allow requests within rate limit", () => { const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 10, }); middleware(mockReq as Request, mockRes as Response, mockNext); expect(mockNext).toHaveBeenCalled(); expect(headers["X-RateLimit-Limit"]).toBe("10"); expect(headers["X-RateLimit-Remaining"]).toBeDefined(); }); it("should set rate limit headers", () => { const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 100, headers: true, }); middleware(mockReq as Request, mockRes as Response, mockNext); expect(headers["X-RateLimit-Limit"]).toBe("100"); expect(headers["X-RateLimit-Remaining"]).toBeDefined(); expect(headers["X-RateLimit-Reset"]).toBeDefined(); }); it("should not set headers when disabled", () => { const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 100, headers: false, }); middleware(mockReq as Request, mockRes as Response, mockNext); expect(headers["X-RateLimit-Limit"]).toBeUndefined(); expect(headers["X-RateLimit-Remaining"]).toBeUndefined(); }); it("should block requests exceeding rate limit", () => { const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 2, }); // First two requests should pass middleware(mockReq as Request, mockRes as Response, mockNext); // Simulate the finish event to record the request const finishCallback = (mockRes.on as ReturnType<typeof vi.fn>).mock.calls.find( (call) => call[0] === "finish" )?.[1]; if (finishCallback) finishCallback(); middleware(mockReq as Request, mockRes as Response, mockNext); if (finishCallback) finishCallback(); // Third request should be blocked const mockNext2 = vi.fn(); middleware(mockReq as Request, mockRes as Response, mockNext2); expect(statusCode).toBe(429); expect(responseBody).toMatchObject({ success: false, code: "RATE_LIMITED", }); }); it("should return 429 with Retry-After header when rate limited", () => { const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 1, }); // First request middleware(mockReq as Request, mockRes as Response, mockNext); const finishCallback = (mockRes.on as ReturnType<typeof vi.fn>).mock.calls.find( (call) => call[0] === "finish" )?.[1]; if (finishCallback) finishCallback(); // Second request should be rate limited middleware(mockReq as Request, mockRes as Response, mockNext); expect(statusCode).toBe(429); expect(headers["Retry-After"]).toBeDefined(); }); it("should use custom key generator", () => { const keyGenerator = vi.fn().mockReturnValue("custom-key"); const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 10, keyGenerator, }); middleware(mockReq as Request, mockRes as Response, mockNext); expect(keyGenerator).toHaveBeenCalledWith(mockReq); }); it("should skip requests when skip function returns true", () => { const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 1, skip: () => true, }); // Multiple requests should all pass because skip returns true middleware(mockReq as Request, mockRes as Response, mockNext); middleware(mockReq as Request, mockRes as Response, mockNext); middleware(mockReq as Request, mockRes as Response, mockNext); expect(mockNext).toHaveBeenCalledTimes(3); }); it("should use X-Forwarded-For header when present", () => { const keyGenerator = vi.fn().mockImplementation((req: Request) => { const forwarded = req.headers["x-forwarded-for"]; if (typeof forwarded === "string") { return forwarded.split(",")[0].trim(); } return req.ip ?? "unknown"; }); const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 10, keyGenerator, }); mockReq.headers = { "x-forwarded-for": "192.168.1.1, 10.0.0.1" }; middleware(mockReq as Request, mockRes as Response, mockNext); expect(keyGenerator).toHaveBeenCalled(); }); it("should use custom handler when rate limited", () => { const customHandler = vi.fn(); const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 1, handler: customHandler, }); // First request middleware(mockReq as Request, mockRes as Response, mockNext); const finishCallback = (mockRes.on as ReturnType<typeof vi.fn>).mock.calls.find( (call) => call[0] === "finish" )?.[1]; if (finishCallback) finishCallback(); // Second request should trigger custom handler middleware(mockReq as Request, mockRes as Response, mockNext); expect(customHandler).toHaveBeenCalled(); }); it("should reset rate limit after window expires", () => { const middleware = createRateLimitMiddleware({ windowMs: 60000, maxRequests: 1, }); // First request middleware(mockReq as Request, mockRes as Response, mockNext); const finishCallback = (mockRes.on as ReturnType<typeof vi.fn>).mock.calls.find( (call) => call[0] === "finish" )?.[1]; if (finishCallback) finishCallback(); // Second request should be blocked const mockNext2 = vi.fn(); middleware(mockReq as Request, mockRes as Response, mockNext2); expect(statusCode).toBe(429); // Advance time past the window vi.advanceTimersByTime(61000); // Third request should pass (new window) const mockNext3 = vi.fn(); statusCode = 200; middleware(mockReq as Request, mockRes as Response, mockNext3); expect(mockNext3).toHaveBeenCalled(); }); }); describe("createRateLimiterInstance", () => { it("should create a rate limiter instance", () => { const limiter = createRateLimiterInstance({ windowMs: 60000, maxRequests: 100, }); expect(limiter).toBeDefined(); expect(limiter.check).toBeDefined(); expect(limiter.record).toBeDefined(); expect(limiter.consume).toBeDefined(); }); it("should use default configuration", () => { const limiter = createRateLimiterInstance(); const result = limiter.check({ ip: "127.0.0.1" }); expect(result.allowed).toBe(true); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ThoughtMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server