Skip to content

Commit 7716d1e

Browse files
authored
test(refresh-rate-limiter): add coverage for IP throttling and window reset behavior (JhaSourav07#3161)
## Description Adds a complete test suite for services/github/refresh-rate-limiter.ts to validate refresh request throttling, cooldown window expiration, environment-based configuration, and response metadata. This ensures the rate limiter continues to protect manual refresh endpoints from excessive usage while remaining configurable through environment variables. ## Changes Added services/github/refresh-rate-limiter.test.ts Test Coverage Implemented 5 unit tests covering: - Initial refresh requests are allowed for a new IP. - Requests are blocked after the configured limit is reached. - Rate limits automatically reset after the cooldown window expires. - Reset timestamps and remaining request counts are calculated correctly. - MAX_REFRESHES_PER_HOUR environment variable overrides the default configuration Fixes JhaSourav07#2297 ## Pillar - [x] 📐 Pillar 2 — Geometric SVG Improvement - [x] 🕐 Pillar 3 — Timezone Logic Optimization ## Checklist before requesting a review: - [x] I have read the `CONTRIBUTING.md` file. - [x] I have tested these changes locally (`localhost:3000/api/streak?user=YOUR_USERNAME`). - [x] I have run `npm run format` and `npm run lint` locally and resolved all errors (CI will fail otherwise). - [x] My commits follow the Conventional Commits format (e.g., `feat(themes): ...`, `fix(calculate): ...`). - [x] I have updated `README.md` if I added a new theme or URL parameter. - [x] I have started the repo. - [x] I have made sure that i have only one commit to merge in this PR. - [x] The SVG output matches the CommitPulse "premium quality" aesthetic standard (no raw elements, smooth animations, correct fonts). - [x] (Recommended) I joined the CommitPulse Discord community for contributor discussions, mentorship, and faster PR support.
2 parents 97f348f + 4065bef commit 7716d1e

1 file changed

Lines changed: 78 additions & 0 deletions

File tree

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest';
2+
import { refreshRateLimiter } from './refresh-rate-limiter';
3+
4+
describe('RefreshRateLimiter', () => {
5+
beforeEach(() => {
6+
refreshRateLimiter.reset();
7+
delete process.env.MAX_REFRESHES_PER_HOUR;
8+
vi.restoreAllMocks();
9+
});
10+
11+
it('allows the initial refresh request from an IP', () => {
12+
const result = refreshRateLimiter.checkLimit('127.0.0.1');
13+
14+
expect(result.success).toBe(true);
15+
expect(result.limit).toBe(3);
16+
expect(result.remaining).toBe(2);
17+
});
18+
19+
it('blocks requests after the limit is reached within the same window', () => {
20+
refreshRateLimiter.setLimit(3);
21+
22+
refreshRateLimiter.checkLimit('127.0.0.1');
23+
refreshRateLimiter.checkLimit('127.0.0.1');
24+
refreshRateLimiter.checkLimit('127.0.0.1');
25+
26+
const blocked = refreshRateLimiter.checkLimit('127.0.0.1');
27+
28+
expect(blocked.success).toBe(false);
29+
expect(blocked.remaining).toBe(0);
30+
expect(blocked.limit).toBe(3);
31+
});
32+
33+
it('resets the limit after the cooldown window expires', () => {
34+
vi.useFakeTimers();
35+
36+
refreshRateLimiter.setLimit(2, 1000);
37+
38+
refreshRateLimiter.checkLimit('127.0.0.1');
39+
refreshRateLimiter.checkLimit('127.0.0.1');
40+
41+
expect(refreshRateLimiter.checkLimit('127.0.0.1').success).toBe(false);
42+
43+
vi.advanceTimersByTime(1001);
44+
45+
const result = refreshRateLimiter.checkLimit('127.0.0.1');
46+
47+
expect(result.success).toBe(true);
48+
expect(result.remaining).toBe(1);
49+
50+
vi.useRealTimers();
51+
});
52+
53+
it('returns correct reset timestamp information', () => {
54+
vi.useFakeTimers();
55+
56+
const now = new Date('2025-01-01T00:00:00Z');
57+
vi.setSystemTime(now);
58+
59+
refreshRateLimiter.setLimit(3, 60_000);
60+
61+
const result = refreshRateLimiter.checkLimit('127.0.0.1');
62+
63+
expect(result.reset).toBe(now.getTime() + 60_000);
64+
65+
vi.useRealTimers();
66+
});
67+
68+
it('uses MAX_REFRESHES_PER_HOUR environment override', () => {
69+
process.env.MAX_REFRESHES_PER_HOUR = '5';
70+
71+
refreshRateLimiter.reset();
72+
73+
const result = refreshRateLimiter.checkLimit('127.0.0.1');
74+
75+
expect(result.limit).toBe(5);
76+
expect(result.remaining).toBe(4);
77+
});
78+
});

0 commit comments

Comments
 (0)