UNPKG

alepha

Version:

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

137 lines (110 loc) 3.82 kB
import { $pipeline, Alepha } from "alepha"; import { describe, test } from "vitest"; import { $throttle } from "../primitives/$throttle.ts"; import { DateTimeProvider } from "../providers/DateTimeProvider.ts"; // ----------------------------------------------------------------------------------------------------------------- // $throttle — core behavior // ----------------------------------------------------------------------------------------------------------------- describe("$throttle", () => { test("calls within rate pass through instantly", async ({ expect }) => { const alepha = Alepha.create(); class TestService { fn = $pipeline({ use: [$throttle({ rate: 5, per: [1, "seconds"] })], handler: async (x: number) => x * 2, }); } const svc = alepha.inject(TestService); // 5 calls within rate limit for (let i = 0; i < 5; i++) { expect(await svc.fn(i)).toBe(i * 2); } }); test("handler arguments and return value are preserved", async ({ expect, }) => { const alepha = Alepha.create(); class TestService { fn = $pipeline({ use: [$throttle({ rate: 10, per: [1, "seconds"] })], handler: async (a: number, b: number) => a + b, }); } const svc = alepha.inject(TestService); expect(await svc.fn(3, 4)).toBe(7); }); test("handler errors propagate", async ({ expect }) => { const alepha = Alepha.create(); class TestService { fn = $pipeline({ use: [$throttle({ rate: 10, per: [1, "seconds"] })], handler: async () => { throw new Error("Failed"); }, }); } const svc = alepha.inject(TestService); await expect(svc.fn()).rejects.toThrowError("Failed"); }); test("excess calls are delayed, not rejected", async ({ expect }) => { const alepha = Alepha.create(); const dt = alepha.inject(DateTimeProvider); let callCount = 0; class TestService { fn = $pipeline({ use: [$throttle({ rate: 2, per: [100, "milliseconds"] })], handler: async () => { callCount++; return "ok"; }, }); } const svc = alepha.inject(TestService); // Exhaust the 2 tokens await svc.fn(); await svc.fn(); expect(callCount).toBe(2); // Third call should be delayed (wait for token refill), not rejected const promise = svc.fn(); // It will eventually resolve expect(await promise).toBe("ok"); expect(callCount).toBe(3); }); test("tokens refill over time", async ({ expect }) => { const alepha = Alepha.create(); let callCount = 0; class TestService { fn = $pipeline({ use: [$throttle({ rate: 2, per: [50, "milliseconds"] })], handler: async () => ++callCount, }); } const svc = alepha.inject(TestService); // Exhaust tokens await svc.fn(); await svc.fn(); // Wait for refill await new Promise((r) => setTimeout(r, 60)); // Should have tokens again const result = await svc.fn(); expect(result).toBe(3); }); }); // ----------------------------------------------------------------------------------------------------------------- // $throttle — metadata // ----------------------------------------------------------------------------------------------------------------- describe("$throttle metadata", () => { test("has middleware metadata", ({ expect }) => { const alepha = Alepha.create(); class TestService { fn = $pipeline({ use: [$throttle({ rate: 10, per: [1, "seconds"] })], handler: async () => "ok", }); } const svc = alepha.inject(TestService); const meta = svc.fn.middlewares; expect(meta).toHaveLength(1); expect(meta[0].name).toBe("$throttle"); }); });