UNPKG

@fastify/rate-limit

Version:

A low overhead rate limiter for your routes

253 lines (219 loc) 7.54 kB
'use strict' const { test } = require('node:test') const assert = require('node:assert') const Fastify = require('fastify') const rateLimit = require('../index') const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) test('GroupId from routeConfig', async () => { const fastify = Fastify() // Register rate limit plugin with groupId in routeConfig await fastify.register(rateLimit, { max: 2, timeWindow: 500 }) fastify.get( '/routeWithGroupId', { config: { rateLimit: { max: 2, timeWindow: 500, groupId: 'group1' // groupId specified in routeConfig } } }, async () => 'hello from route with groupId!' ) // Test: Request should have the correct groupId in response const res = await fastify.inject({ url: '/routeWithGroupId', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1') }) test('GroupId from routeOptions', async () => { const fastify = Fastify() // Register rate limit plugin with groupId in routeOptions await fastify.register(rateLimit, { max: 2, timeWindow: 500 }) fastify.get( '/routeWithGroupIdFromOptions', { config: { rateLimit: { max: 2, timeWindow: 500 // groupId not specified here } } }, async () => 'hello from route with groupId from options!' ) // Test: Request should have the correct groupId from routeOptions const res = await fastify.inject({ url: '/routeWithGroupIdFromOptions', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1') }) test('No groupId provided', async () => { const fastify = Fastify() // Register rate limit plugin without groupId await fastify.register(rateLimit, { max: 2, timeWindow: 500 }) // Route without groupId fastify.get( '/noGroupId', { config: { rateLimit: { max: 2, timeWindow: 500 } } }, async () => 'hello from no groupId route!' ) let res // Test without groupId res = await fastify.inject({ url: '/noGroupId', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1') res = await fastify.inject({ url: '/noGroupId', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0') res = await fastify.inject({ url: '/noGroupId', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 429) assert.deepStrictEqual( res.headers['content-type'], 'application/json; charset=utf-8' ) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0') assert.deepStrictEqual(res.headers['retry-after'], '1') assert.deepStrictEqual( { statusCode: 429, error: 'Too Many Requests', message: 'Rate limit exceeded, retry in 1 second' }, JSON.parse(res.payload) ) }) test('With multiple routes and custom groupId', async () => { const fastify = Fastify() // Register rate limit plugin await fastify.register(rateLimit, { max: 2, timeWindow: 500 }) // Route 1 with groupId 'group1' fastify.get( '/route1', { config: { rateLimit: { max: 2, timeWindow: 500, groupId: 'group1' } } }, async () => 'hello from route 1!' ) // Route 2 with groupId 'group2' fastify.get( '/route2', { config: { rateLimit: { max: 2, timeWindow: 1000, groupId: 'group2' } } }, async () => 'hello from route 2!' ) let res // Test Route 1 res = await fastify.inject({ url: '/route1', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1') res = await fastify.inject({ url: '/route1', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0') res = await fastify.inject({ url: '/route1', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 429) assert.deepStrictEqual( res.headers['content-type'], 'application/json; charset=utf-8' ) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0') assert.deepStrictEqual(res.headers['retry-after'], '1') assert.deepStrictEqual( { statusCode: 429, error: 'Too Many Requests', message: 'Rate limit exceeded, retry in 1 second' }, JSON.parse(res.payload) ) // Test Route 2 res = await fastify.inject({ url: '/route2', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1') res = await fastify.inject({ url: '/route2', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0') res = await fastify.inject({ url: '/route2', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 429) assert.deepStrictEqual( res.headers['content-type'], 'application/json; charset=utf-8' ) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '0') assert.deepStrictEqual(res.headers['retry-after'], '1') assert.deepStrictEqual( { statusCode: 429, error: 'Too Many Requests', message: 'Rate limit exceeded, retry in 1 second' }, JSON.parse(res.payload) ) // Wait for the window to reset await sleep(1000) // After reset, Route 1 should succeed again res = await fastify.inject({ url: '/route1', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1') // Route 2 should also succeed after the reset res = await fastify.inject({ url: '/route2', method: 'GET' }) assert.deepStrictEqual(res.statusCode, 200) assert.deepStrictEqual(res.headers['x-ratelimit-limit'], '2') assert.deepStrictEqual(res.headers['x-ratelimit-remaining'], '1') }) test('Invalid groupId type', async () => { const fastify = Fastify() // Register rate limit plugin with a route having an invalid groupId await fastify.register(rateLimit, { max: 2, timeWindow: 1000 }) try { fastify.get( '/invalidGroupId', { config: { rateLimit: { max: 2, timeWindow: 1000, groupId: 123 // Invalid groupId type } } }, async () => 'hello with invalid groupId!' ) assert.fail('should throw') console.log('HER') } catch (err) { assert.deepStrictEqual(err.message, 'groupId must be a string') } })