Skip to content

Commit 8f55267

Browse files
committed
test(nodejs): add comprehensive tests for rate-limit and validation middleware
Rate Limit Tests (rate-limit.test.js): - Test defaultLimiter with 100 req/min limit - Test strictLimiter with 10 req/min limit - Test highLimiter with 1000 req/min limit - Test createRateLimiter factory with custom options - Test rate limit headers (RateLimit-Limit, RateLimit-Remaining) - Test 429 response when limit exceeded - Test different limits on different routes Validation Tests (validation.test.js): - Test ValidationError class - Test requireFields for required field validation - Test validateTypes for type checking (string, number, boolean, array, object) - Test validateConstraints for min/max, minLength/maxLength, pattern, enum - Test validateParams for URL parameter validation - Test validateQuery for query parameter validation - Test validate() combined validator factory - Test error response format with requestId Coverage improvements: - validation.js: 95.08% line coverage - rate-limit.js: 66.66% line coverage - 91 tests total (90 passing, 1 pre-existing failure in prometheus metrics)
1 parent 1eea5ee commit 8f55267

2 files changed

Lines changed: 897 additions & 0 deletions

File tree

Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
/**
2+
* Rate Limiting Middleware Tests
3+
*
4+
* Tests for IP-based rate limiting middleware.
5+
*/
6+
7+
const request = require('supertest');
8+
const express = require('express');
9+
10+
// Import the rate limit middleware
11+
const {
12+
defaultLimiter,
13+
strictLimiter,
14+
highLimiter,
15+
createRateLimiter
16+
} = require('../src/middleware/rate-limit');
17+
18+
// Helper to create a test app with rate limiting
19+
function createTestApp(limiter) {
20+
const app = express();
21+
app.use(express.json());
22+
app.use(limiter);
23+
app.get('/test', (req, res) => {
24+
res.json({ status: 'ok' });
25+
});
26+
app.post('/test', (req, res) => {
27+
res.json({ status: 'ok', body: req.body });
28+
});
29+
return app;
30+
}
31+
32+
describe('Rate Limiting Middleware', () => {
33+
describe('defaultLimiter', () => {
34+
let app;
35+
36+
beforeEach(() => {
37+
app = createTestApp(defaultLimiter);
38+
});
39+
40+
it('should allow requests under the limit', async () => {
41+
const response = await request(app)
42+
.get('/test')
43+
.expect(200);
44+
45+
expect(response.body.status).toBe('ok');
46+
});
47+
48+
it('should include rate limit headers in response', async () => {
49+
const response = await request(app)
50+
.get('/test')
51+
.expect(200);
52+
53+
// express-rate-limit uses RateLimit-* headers with standardHeaders: true
54+
expect(response.headers).toHaveProperty('ratelimit-limit');
55+
expect(response.headers).toHaveProperty('ratelimit-remaining');
56+
});
57+
58+
it('should have correct limit value (100)', async () => {
59+
const response = await request(app)
60+
.get('/test')
61+
.expect(200);
62+
63+
expect(response.headers['ratelimit-limit']).toBe('100');
64+
});
65+
66+
it('should decrement remaining count', async () => {
67+
const response1 = await request(app).get('/test').expect(200);
68+
const remaining1 = parseInt(response1.headers['ratelimit-remaining']);
69+
70+
const response2 = await request(app).get('/test').expect(200);
71+
const remaining2 = parseInt(response2.headers['ratelimit-remaining']);
72+
73+
expect(remaining2).toBe(remaining1 - 1);
74+
});
75+
76+
it('should work with POST requests', async () => {
77+
const response = await request(app)
78+
.post('/test')
79+
.send({ data: 'test' })
80+
.expect(200);
81+
82+
expect(response.body.status).toBe('ok');
83+
});
84+
});
85+
86+
describe('strictLimiter', () => {
87+
let app;
88+
89+
beforeEach(() => {
90+
app = createTestApp(strictLimiter);
91+
});
92+
93+
it('should allow requests under the strict limit', async () => {
94+
const response = await request(app)
95+
.get('/test')
96+
.expect(200);
97+
98+
expect(response.body.status).toBe('ok');
99+
});
100+
101+
it('should have correct strict limit value (10)', async () => {
102+
const response = await request(app)
103+
.get('/test')
104+
.expect(200);
105+
106+
expect(response.headers['ratelimit-limit']).toBe('10');
107+
});
108+
});
109+
110+
describe('highLimiter', () => {
111+
let app;
112+
113+
beforeEach(() => {
114+
app = createTestApp(highLimiter);
115+
});
116+
117+
it('should allow requests under the high limit', async () => {
118+
const response = await request(app)
119+
.get('/test')
120+
.expect(200);
121+
122+
expect(response.body.status).toBe('ok');
123+
});
124+
125+
it('should have correct high limit value (1000)', async () => {
126+
const response = await request(app)
127+
.get('/test')
128+
.expect(200);
129+
130+
expect(response.headers['ratelimit-limit']).toBe('1000');
131+
});
132+
});
133+
134+
describe('createRateLimiter', () => {
135+
it('should create limiter with default values', async () => {
136+
const limiter = createRateLimiter();
137+
const app = createTestApp(limiter);
138+
139+
const response = await request(app)
140+
.get('/test')
141+
.expect(200);
142+
143+
expect(response.headers['ratelimit-limit']).toBe('100');
144+
});
145+
146+
it('should create limiter with custom max value', async () => {
147+
const limiter = createRateLimiter({ max: 50 });
148+
const app = createTestApp(limiter);
149+
150+
const response = await request(app)
151+
.get('/test')
152+
.expect(200);
153+
154+
expect(response.headers['ratelimit-limit']).toBe('50');
155+
});
156+
157+
it('should create limiter with custom window', async () => {
158+
const limiter = createRateLimiter({ windowMs: 30000, max: 25 });
159+
const app = createTestApp(limiter);
160+
161+
const response = await request(app)
162+
.get('/test')
163+
.expect(200);
164+
165+
expect(response.headers['ratelimit-limit']).toBe('25');
166+
});
167+
});
168+
169+
describe('Rate limit exceeded behavior', () => {
170+
it('should return 429 when limit exceeded', async () => {
171+
// Create a limiter with very low limit for testing
172+
const testLimiter = createRateLimiter({ max: 2, windowMs: 60000 });
173+
const app = createTestApp(testLimiter);
174+
175+
// Make requests up to the limit
176+
await request(app).get('/test').expect(200);
177+
await request(app).get('/test').expect(200);
178+
179+
// This request should be rate limited
180+
const response = await request(app)
181+
.get('/test')
182+
.expect(429);
183+
184+
expect(response.body).toHaveProperty('error', 'Too Many Requests');
185+
expect(response.body).toHaveProperty('message');
186+
expect(response.body).toHaveProperty('retry_after_seconds');
187+
});
188+
189+
it('should include retry_after_seconds in 429 response', async () => {
190+
const testLimiter = createRateLimiter({ max: 1, windowMs: 60000 });
191+
const app = createTestApp(testLimiter);
192+
193+
await request(app).get('/test').expect(200);
194+
195+
const response = await request(app)
196+
.get('/test')
197+
.expect(429);
198+
199+
expect(response.body.retry_after_seconds).toBe(60);
200+
});
201+
});
202+
203+
describe('Multiple endpoints with different limits', () => {
204+
it('should apply different limits to different routes', async () => {
205+
const app = express();
206+
app.use(express.json());
207+
208+
// Default limit for /api
209+
app.use('/api', defaultLimiter);
210+
app.get('/api/data', (req, res) => res.json({ route: 'api' }));
211+
212+
// High limit for /metrics
213+
app.get('/metrics', highLimiter, (req, res) => res.json({ route: 'metrics' }));
214+
215+
const apiResponse = await request(app).get('/api/data').expect(200);
216+
const metricsResponse = await request(app).get('/metrics').expect(200);
217+
218+
expect(apiResponse.headers['ratelimit-limit']).toBe('100');
219+
expect(metricsResponse.headers['ratelimit-limit']).toBe('1000');
220+
});
221+
});
222+
});

0 commit comments

Comments
 (0)