import test from 'node:test' import assert from 'node:assert/strict' import type { NextFunction, Request, Response } from 'express' import { createRateLimitMiddleware, resetRateLimitBucketsForTest } from './rate-limit.js' test('createRateLimitMiddleware allows requests within the window', () => { resetRateLimitBucketsForTest() const limiter = createRateLimitMiddleware({ scope: 'test:allow', windowMs: 60_000, max: 2, }) const req = createMockRequest() const res = createMockResponse() let nextCount = 0 const next: NextFunction = () => { nextCount += 1 } limiter(req, res, next) limiter(req, res, next) assert.equal(nextCount, 2) assert.equal(res.statusCode, 200) }) test('createRateLimitMiddleware blocks requests over the limit', () => { resetRateLimitBucketsForTest() const limiter = createRateLimitMiddleware({ scope: 'test:block', windowMs: 60_000, max: 1, }) const req = createMockRequest() const res = createMockResponse() let nextCount = 0 const next: NextFunction = () => { nextCount += 1 } limiter(req, res, next) limiter(req, res, next) assert.equal(nextCount, 1) assert.equal(res.statusCode, 429) assert.equal(res.headers['Retry-After'], '60') assert.equal(res.body.errorCode, 'rate_limited') }) test('createRateLimitMiddleware supports custom limit responses', () => { resetRateLimitBucketsForTest() const limiter = createRateLimitMiddleware({ scope: 'test:custom', windowMs: 60_000, max: 1, onLimit: (_req, res) => { res.status(200).json({ code: 429, message: 'limited' }) }, }) const req = createMockRequest() const res = createMockResponse() limiter(req, res, () => {}) limiter(req, res, () => {}) assert.equal(res.statusCode, 200) assert.deepEqual(res.body, { code: 429, message: 'limited' }) }) test('createRateLimitMiddleware can cap an IP across different accounts', () => { resetRateLimitBucketsForTest() const limiter = createRateLimitMiddleware({ scope: 'test:ip-wide', windowMs: 60_000, max: 2, }) const first = createMockRequest() const second = createMockRequest() const third = createMockRequest() const response = createMockResponse() let nextCount = 0 const next: NextFunction = () => { nextCount += 1 } limiter(first, response, next) limiter(second, response, next) limiter(third, response, next) assert.equal(nextCount, 2) assert.equal(response.statusCode, 429) }) function createMockRequest(): Request { return { ip: '127.0.0.1', originalUrl: '/test', headers: {}, socket: { remoteAddress: '127.0.0.1', }, } as Request } function createMockResponse(): Response & { statusCode: number headers: Record body: any } { const res = { statusCode: 200, headers: {} as Record, body: null as any, setHeader(name: string, value: string) { this.headers[name] = value return this }, status(statusCode: number) { this.statusCode = statusCode return this }, json(body: any) { this.body = body return this }, } return res as Response & { statusCode: number headers: Record body: any } }