UNPKG

alepha

Version:

Easy-to-use modern TypeScript framework for building many kind of applications.

180 lines (146 loc) 5.53 kB
import { Alepha } from "alepha"; import { AlephaCache } from "alepha/cache"; import { $action, AlephaServer, type ServerRequest } from "alepha/server"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { AlephaServerRateLimit, rateLimitOptions, ServerRateLimitProvider, } from "../index.ts"; describe("ServerRateLimitProvider", () => { let alepha: Alepha; let provider: ServerRateLimitProvider; beforeEach(async () => { alepha = Alepha.create().with(AlephaCache); provider = alepha.inject(ServerRateLimitProvider); await alepha.start(); }); afterEach(async () => { await alepha.stop(); }); const createMockRequest = (ip: string = "127.0.0.1"): ServerRequest => ({ ip, headers: {}, method: "GET", url: "/test", path: "/test", query: {}, params: {}, body: undefined, }) as any; it("should allow requests within limit", async () => { const req = createMockRequest(); const result = await provider.checkLimit(req, { max: 5, windowMs: 60000 }); expect(result.allowed).toBe(true); expect(result.limit).toBe(5); expect(result.remaining).toBe(4); }); it("should block requests exceeding limit", async () => { const req = createMockRequest(); const options = { max: 2, windowMs: 60000 }; // First request should be allowed const result1 = await provider.checkLimit(req, options); expect(result1.allowed).toBe(true); expect(result1.remaining).toBe(1); // Second request should be allowed const result2 = await provider.checkLimit(req, options); expect(result2.allowed).toBe(true); expect(result2.remaining).toBe(0); // Third request should be blocked const result3 = await provider.checkLimit(req, options); expect(result3.allowed).toBe(false); expect(result3.remaining).toBe(0); expect(result3.retryAfter).toBeGreaterThan(0); }); it("should handle different IPs separately", async () => { const req1 = createMockRequest("192.168.1.1"); const req2 = createMockRequest("192.168.1.2"); const options = { max: 1, windowMs: 60000 }; // Both requests should be allowed as they come from different IPs const result1 = await provider.checkLimit(req1, options); const result2 = await provider.checkLimit(req2, options); expect(result1.allowed).toBe(true); expect(result2.allowed).toBe(true); }); it("should use req.ip for rate limiting (trust proxy handled by ServerRequestParser)", async () => { const options = { max: 1, windowMs: 60000 }; // Rate limit uses req.ip which is resolved by ServerRequestParser // Trust proxy configuration is at server level via TRUST_PROXY env var const req1 = createMockRequest("192.168.1.100"); const result1 = await provider.checkLimit(req1, options); expect(result1.allowed).toBe(true); // Same IP should be blocked const req2 = createMockRequest("192.168.1.100"); const result2 = await provider.checkLimit(req2, options); expect(result2.allowed).toBe(false); }); it("should handle truly concurrent requests atomically", async () => { const options = { max: 3, windowMs: 60000 }; // Fire 5 requests concurrently from the same IP const requests = Array.from({ length: 5 }, () => createMockRequest("10.0.0.1"), ); const results = await Promise.all( requests.map((req) => provider.checkLimit(req, options)), ); // Exactly 3 should be allowed, 2 should be blocked const allowed = results.filter((r) => r.allowed).length; const blocked = results.filter((r) => !r.allowed).length; expect(allowed).toBe(3); expect(blocked).toBe(2); }); it("should return correct resetTime within the fixed window", async () => { const windowMs = 60000; const options = { max: 10, windowMs }; const req = createMockRequest(); const result = await provider.checkLimit(req, options); // resetTime should be at the end of the current window const now = Date.now(); const windowStart = Math.floor(now / windowMs) * windowMs; const expectedResetTime = windowStart + windowMs; expect(result.resetTime).toBe(expectedResetTime); }); }); describe("ServerRateLimitProvider Module Integration", () => { let alepha: Alepha; let provider: ServerRateLimitProvider; class TestApp { test = $action({ handler: () => "success", }); } beforeEach(async () => { alepha = Alepha.create() .with(AlephaCache) .with(AlephaServer) .with(AlephaServerRateLimit) .with(TestApp); provider = alepha.inject(ServerRateLimitProvider); await alepha.start(); }); afterEach(async () => { await alepha.stop(); }); it("should integrate with Alepha framework successfully", async () => { expect(provider).toBeDefined(); expect(provider).toBeInstanceOf(ServerRateLimitProvider); expect(typeof provider.checkLimit).toBe("function"); }); it("should work with real action requests", async () => { // Configure rate limit for testing alepha.store.mut(rateLimitOptions, () => ({ max: 10, windowMs: 60000, })); const app = alepha.inject(TestApp); // First request should succeed const result = await app.test.run({}); expect(result).toBe("success"); // Multiple requests should still work within limit for (let i = 0; i < 5; i++) { const result = await app.test.run({}); expect(result).toBe("success"); } }); });