I have a rate limiter in my pure (no express, etc.) node.js app, which denies requests if they exceed the limit in some time window. It filters both all requests to the server and unique requests (based on ip & device header info in request). I need to fully cover it with tests, but I don't know how to do this.
- My current test does not work, beacause the array of promised requests is empty for some reason.
- Even if that would work, I do not know how to test it based on all requests (I need the ip or device header to be different on every request).
I would greatly appreciate any help! Thank you!
rateLimiter.test.js:
const http = require('http');
const request = require('supertest');
const { requestHandler } = require('../src/modules/requestHandler');
const { uniqueRequests } = require('../src/config/config').rateLimiter;
const server = http
.createServer(async (req, res) => {
requestHandler(req, res);
})
.listen(8001);
test('Login test', async () => {
const results = [];
for (let i; i < uniqueRequests + 5; i + 1) {
results.push(
request(server).post('/api/login').send({
email: 'email@email.com',
password: '555555',
})
);
}
await Promise.all(results);
console.log(results); // outputs '[]'
expect(results.find(el => el.statusCode === 500)).not.toEqual(undefined);
});
rateLimiter.js:
const { plainTextResponse } = require('./utils');
const users = new Map();
let frameStart = Date.now();
let requestsCounter = 0;
const parseIp = req =>
(typeof req.headers['x-forwarded-for'] === 'string' &&
req.headers['x-forwarded-for'].split(',').shift()) ||
req.connection?.remoteAddress ||
req.socket?.remoteAddress ||
req.connection?.socket?.remoteAddress;
async function rateLimiter(req, res, next, window, requests, uniqueRequests) {
const ip = parseIp(req);
const user = req.headers['user-agent'];
const device = ip.concat(':', user);
const deviceRequests = users.get(device);
if (requestsCounter > requests || deviceRequests > uniqueRequests) {
plainTextResponse(res, 500, 'Too much requests!');
} else {
next(req, res);
}
if (Date.now() - frameStart > window) {
users.clear();
frameStart = Date.now();
requestsCounter = 0;
}
requestsCounter += 1;
users.set(device, !deviceRequests ? 1 : deviceRequests + 1);
}
module.exports = { rateLimiter };
requestHandler.js:
const {
window,
requests,
uniqueRequests,
} = require('../config/config.js').rateLimiter;
const { rateLimiter } = require('./rateLimiter');
async function requestHandler(req, res) {
if (req.method === 'GET') {
getHandler(req, res);
} else if (req.method === 'POST') {
rateLimiter(req, res, postHandler, window, requests, uniqueRequests);
}
}
module.exports = { requestHandler };
config.js:
module.exports = {
rateLimiter: {
window: 10000,
requests: 1000,
uniqueRequests: 20,
},
};