Skip to main content
Glama
rate-limiter.test.ts8.09 kB
/** * Tests for rate limiting */ import { rateLimiter } from '../../src/security/rate-limiter'; import { securityAuditor } from '../../src/security/security-audit'; import { performanceMonitor } from '../../src/utils/performance-monitor'; import { resourceManager } from '../../src/utils/resource-manager'; import { cacheManager } from '../../src/utils/cache-manager'; describe('RateLimiter', () => { beforeEach(() => { // Reset rate limiter state rateLimiter.reset(); }); afterAll(() => { // Clean up intervals and timers rateLimiter.destroy(); securityAuditor.destroy(); performanceMonitor.destroy(); resourceManager.destroy(); cacheManager.destroy(); }); describe('checkRateLimit', () => { it('should allow requests within limits', () => { const result = rateLimiter.checkRateLimit('convert_color', 'test-client'); expect(result.allowed).toBe(true); expect(result.remainingRequests).toBe(999); // 1000 - 1 expect(result.resetTime).toBeGreaterThan(Date.now()); }); it('should track requests per client', () => { rateLimiter.checkRateLimit('convert_color', 'client1'); rateLimiter.checkRateLimit('convert_color', 'client2'); const result1 = rateLimiter.checkRateLimit('convert_color', 'client1'); const result2 = rateLimiter.checkRateLimit('convert_color', 'client2'); expect(result1.remainingRequests).toBe(998); // 1000 - 2 expect(result2.remainingRequests).toBe(998); // 1000 - 2 }); it('should block requests when limit is exceeded', () => { // Use a low-limit operation for testing const operation = 'extract_palette_from_image'; // 10 requests per minute // Make 10 requests (the limit) for (let i = 0; i < 10; i++) { const result = rateLimiter.checkRateLimit(operation, 'test-client'); expect(result.allowed).toBe(true); } // 11th request should be blocked const blockedResult = rateLimiter.checkRateLimit( operation, 'test-client' ); expect(blockedResult.allowed).toBe(false); expect(blockedResult.remainingRequests).toBe(0); expect(blockedResult.retryAfter).toBeGreaterThan(0); }); it('should reset window after expiration', async () => { // Use custom config with short window for testing const customConfig = { windowMs: 100, maxRequests: 2 }; // Make 2 requests (the limit) rateLimiter.checkRateLimit('test_operation', 'test-client', customConfig); rateLimiter.checkRateLimit('test_operation', 'test-client', customConfig); // 3rd request should be blocked let result = rateLimiter.checkRateLimit( 'test_operation', 'test-client', customConfig ); expect(result.allowed).toBe(false); // Wait for window to expire await new Promise(resolve => setTimeout(resolve, 150)); // Should be allowed again result = rateLimiter.checkRateLimit( 'test_operation', 'test-client', customConfig ); expect(result.allowed).toBe(true); expect(result.remainingRequests).toBe(1); }); it('should use default limits for unknown operations', () => { const result = rateLimiter.checkRateLimit( 'unknown_operation', 'test-client' ); expect(result.allowed).toBe(true); expect(result.remainingRequests).toBe(49); // 50 - 1 (default limit) }); it('should handle different rate limits for different operations', () => { // High-limit operation const convertResult = rateLimiter.checkRateLimit( 'convert_color', 'test-client' ); expect(convertResult.remainingRequests).toBe(999); // 1000 - 1 // Low-limit operation const pngResult = rateLimiter.checkRateLimit( 'create_palette_png', 'test-client' ); expect(pngResult.remainingRequests).toBe(19); // 20 - 1 }); }); describe('getRemainingRequests', () => { it('should return correct remaining requests', () => { rateLimiter.checkRateLimit('convert_color', 'test-client'); rateLimiter.checkRateLimit('convert_color', 'test-client'); const remaining = rateLimiter.getRemainingRequests( 'convert_color', 'test-client' ); expect(remaining).toBe(998); // 1000 - 2 }); it('should return max requests for new clients', () => { const remaining = rateLimiter.getRemainingRequests( 'convert_color', 'new-client' ); expect(remaining).toBe(1000); }); }); describe('getResetTime', () => { it('should return reset time for active windows', () => { rateLimiter.checkRateLimit('convert_color', 'test-client'); const resetTime = rateLimiter.getResetTime( 'convert_color', 'test-client' ); expect(resetTime).toBeGreaterThan(Date.now()); }); it('should return null for inactive windows', () => { const resetTime = rateLimiter.getResetTime('convert_color', 'new-client'); expect(resetTime).toBeNull(); }); }); describe('updateLimits', () => { it('should update limits for specific operations', () => { rateLimiter.updateLimits('test_operation', { maxRequests: 5, windowMs: 60000, }); // Make 5 requests for (let i = 0; i < 5; i++) { const result = rateLimiter.checkRateLimit( 'test_operation', 'test-client' ); expect(result.allowed).toBe(true); } // 6th request should be blocked const blockedResult = rateLimiter.checkRateLimit( 'test_operation', 'test-client' ); expect(blockedResult.allowed).toBe(false); }); }); describe('getStats', () => { it('should return statistics about rate limiting', () => { rateLimiter.checkRateLimit('convert_color', 'client1'); rateLimiter.checkRateLimit('convert_color', 'client1'); rateLimiter.checkRateLimit('analyze_color', 'client2'); const stats = rateLimiter.getStats(); expect(stats.totalEntries).toBeGreaterThan(0); expect(stats.activeWindows).toBeGreaterThan(0); expect(stats.topOperations).toEqual( expect.arrayContaining([ expect.objectContaining({ operation: 'convert_color', requests: 2, }), expect.objectContaining({ operation: 'analyze_color', requests: 1, }), ]) ); }); }); describe('reset', () => { it('should reset all rate limits', () => { rateLimiter.checkRateLimit('convert_color', 'test-client'); rateLimiter.reset(); const stats = rateLimiter.getStats(); expect(stats.totalEntries).toBe(0); expect(stats.activeWindows).toBe(0); }); it('should reset specific operation', () => { rateLimiter.checkRateLimit('convert_color', 'test-client'); rateLimiter.checkRateLimit('analyze_color', 'test-client'); rateLimiter.reset('convert_color'); const convertRemaining = rateLimiter.getRemainingRequests( 'convert_color', 'test-client' ); const analyzeRemaining = rateLimiter.getRemainingRequests( 'analyze_color', 'test-client' ); expect(convertRemaining).toBe(1000); // Reset to max expect(analyzeRemaining).toBe(499); // Still has used request }); it('should reset specific client for operation', () => { rateLimiter.checkRateLimit('convert_color', 'client1'); rateLimiter.checkRateLimit('convert_color', 'client2'); rateLimiter.reset('convert_color', 'client1'); const client1Remaining = rateLimiter.getRemainingRequests( 'convert_color', 'client1' ); const client2Remaining = rateLimiter.getRemainingRequests( 'convert_color', 'client2' ); expect(client1Remaining).toBe(1000); // Reset to max expect(client2Remaining).toBe(999); // Still has used request }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/keyurgolani/ColorMcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server