express-rate-limiter-ts
Version:
A powerful, flexible and modern rate limiter middleware for Express.js written in TypeScript.
120 lines (119 loc) • 6.02 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
const express_1 = __importDefault(require("express"));
const supertest_1 = __importDefault(require("supertest"));
const rateLimiter_1 = require("../src/rateLimiter");
const memoryStore_1 = require("../src/storage/memoryStore");
const dummyStore_1 = require("../src/storage/dummyStore");
describe('RateLimiter Otomasyon Testleri', () => {
let app;
let store;
beforeEach(() => {
app = (0, express_1.default)();
store = new memoryStore_1.MemoryStore(1000);
app.use((0, rateLimiter_1.rateLimiter)({
windowMs: 1000,
max: 2,
message: 'Limit aşıldı',
whitelist: ['127.0.0.1'],
blacklist: (req) => req.header('x-api-key') === 'banned-key',
customResponse: (req, res, info) => {
if (info.banned) {
res.status(429).json({ error: 'Banlandınız', banExpiresAt: info.banExpiresAt });
}
else if (info.exceeded) {
res.status(429).json({ error: 'Limit aşıldı', kalan: 0 });
}
},
banSeconds: 2, // 2 saniye ban
dynamicMax: (req) => req.header('x-api-key') === 'premium' ? 5 : 2,
identifierType: 'header',
identifierHeader: 'x-api-key',
}, store));
app.get('/api', (_req, res) => res.send('ok'));
});
it('Normal kullanıcı 2 istekte limitlenir', () => __awaiter(void 0, void 0, void 0, function* () {
yield (0, supertest_1.default)(app).get('/api').expect(200);
yield (0, supertest_1.default)(app).get('/api').expect(200);
yield (0, supertest_1.default)(app).get('/api').expect(429);
}));
it('Whitelist kullanıcı hiç limitlenmez', () => __awaiter(void 0, void 0, void 0, function* () {
app.set('trust proxy', true);
for (let i = 0; i < 10; i++) {
yield (0, supertest_1.default)(app).get('/api').set('X-Forwarded-For', '127.0.0.1').expect(200);
}
}));
it('Blacklist kullanıcı anında engellenir', () => __awaiter(void 0, void 0, void 0, function* () {
yield (0, supertest_1.default)(app).get('/api').set('x-api-key', 'banned-key').expect(403);
}));
it('Premium kullanıcı daha fazla istek atabilir', () => __awaiter(void 0, void 0, void 0, function* () {
for (let i = 0; i < 5; i++) {
yield (0, supertest_1.default)(app).get('/api').set('x-api-key', 'premium').expect(200);
}
yield (0, supertest_1.default)(app).get('/api').set('x-api-key', 'premium').expect(429);
}));
it('Ban süresi dolunca tekrar erişim sağlanır', () => __awaiter(void 0, void 0, void 0, function* () {
yield (0, supertest_1.default)(app).get('/api').expect(200);
yield (0, supertest_1.default)(app).get('/api').expect(200);
yield (0, supertest_1.default)(app).get('/api').expect(429);
// Ban süresi kadar bekle
yield new Promise((r) => setTimeout(r, 2100));
yield (0, supertest_1.default)(app).get('/api').expect(200);
}));
});
describe('Header ve Yapılandırma Testleri', () => {
let app;
let store;
beforeEach(() => {
app = (0, express_1.default)();
store = new memoryStore_1.MemoryStore(1000);
app.use((0, rateLimiter_1.rateLimiter)({
windowMs: 1000,
max: 2,
identifierType: 'header',
identifierHeader: 'x-api-key',
}, store));
app.get('/api', (_req, res) => res.send('ok'));
});
it('X-RateLimit-* ve RateLimit-* headerları doğru set edilir', () => __awaiter(void 0, void 0, void 0, function* () {
const res = yield (0, supertest_1.default)(app).get('/api').set('x-api-key', 'test');
expect(res.headers['x-ratelimit-limit']).toBeDefined();
expect(res.headers['x-ratelimit-remaining']).toBeDefined();
expect(res.headers['x-ratelimit-reset']).toBeDefined();
expect(res.headers['ratelimit-limit']).toBeDefined();
expect(res.headers['ratelimit-remaining']).toBeDefined();
expect(res.headers['ratelimit-reset']).toBeDefined();
}));
});
describe('Yanlış yapılandırma uyarı testi', () => {
it('Eksik windowMs veya max ile başlatılırsa consola uyarı basılır', () => {
const spy = jest.spyOn(console, 'warn').mockImplementation(() => { });
const app = (0, express_1.default)();
app.use((0, rateLimiter_1.rateLimiter)({ windowMs: 0, max: 0 }));
expect(spy).toHaveBeenCalled();
spy.mockRestore();
});
});
describe('DummyStore ile temel limitleme', () => {
it('DummyStore ile her zaman limit aşılmaz', () => __awaiter(void 0, void 0, void 0, function* () {
const app = (0, express_1.default)();
const dummyStore = new dummyStore_1.DummyStore();
app.use((0, rateLimiter_1.rateLimiter)({ windowMs: 1000, max: 1 }, dummyStore));
app.get('/api', (_req, res) => res.send('ok'));
for (let i = 0; i < 5; i++) {
yield (0, supertest_1.default)(app).get('/api').expect(200);
}
}));
});