161 lines
5.2 KiB
JavaScript
161 lines
5.2 KiB
JavaScript
const { describe, test, expect, beforeEach } = require("@jest/globals");
|
|
|
|
// Mock config
|
|
jest.mock("../../config", () => ({
|
|
app: {
|
|
delayMinutes: 5,
|
|
},
|
|
}));
|
|
|
|
// Mock logger
|
|
jest.mock("../../lib/logger", () => ({
|
|
rateLimitPause: jest.fn(),
|
|
}));
|
|
|
|
const rateLimiter = require("../../lib/rateLimiter");
|
|
|
|
describe("RateLimiter", () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
rateLimiter.reset();
|
|
});
|
|
|
|
describe("getRandomDelay", () => {
|
|
test("should return delay within expected range", () => {
|
|
const delay = rateLimiter.getRandomDelay();
|
|
|
|
// Base delay is 5 minutes = 300,000ms
|
|
// With 20% variance and jitter, expect roughly 240,000 to 390,000ms
|
|
expect(delay).toBeGreaterThan(200000);
|
|
expect(delay).toBeLessThan(400000);
|
|
});
|
|
|
|
test("should return different delays each time (due to randomization)", () => {
|
|
const delay1 = rateLimiter.getRandomDelay();
|
|
const delay2 = rateLimiter.getRandomDelay();
|
|
const delay3 = rateLimiter.getRandomDelay();
|
|
|
|
// Extremely unlikely to be identical due to randomization
|
|
expect(delay1).not.toBe(delay2);
|
|
expect(delay2).not.toBe(delay3);
|
|
});
|
|
});
|
|
|
|
describe("shouldPause", () => {
|
|
test("should not pause initially", () => {
|
|
expect(rateLimiter.shouldPause()).toBe(false);
|
|
});
|
|
|
|
test("should pause after sending many emails in short time", () => {
|
|
// Simulate sending 20 emails in 1 hour (exceeds 15/hour limit)
|
|
rateLimiter.sentCount = 20;
|
|
rateLimiter.startTime = Date.now() - 60 * 60 * 1000; // 1 hour ago
|
|
|
|
expect(rateLimiter.shouldPause()).toBe(true);
|
|
});
|
|
|
|
test("should pause every 50 emails", () => {
|
|
rateLimiter.sentCount = 50;
|
|
expect(rateLimiter.shouldPause()).toBe(true);
|
|
|
|
rateLimiter.sentCount = 100;
|
|
expect(rateLimiter.shouldPause()).toBe(true);
|
|
});
|
|
|
|
test("should not pause if under rate limit", () => {
|
|
// Simulate sending 10 emails in 1 hour (under 15/hour limit)
|
|
rateLimiter.sentCount = 10;
|
|
rateLimiter.startTime = Date.now() - 60 * 60 * 1000; // 1 hour ago
|
|
|
|
expect(rateLimiter.shouldPause()).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe("getPauseDuration", () => {
|
|
test("should return pause duration around 30 minutes", () => {
|
|
const duration = rateLimiter.getPauseDuration();
|
|
|
|
// Base pause is 30 minutes = 1,800,000ms
|
|
// With randomization, expect 30-40 minutes
|
|
expect(duration).toBeGreaterThan(30 * 60 * 1000);
|
|
expect(duration).toBeLessThan(40 * 60 * 1000);
|
|
});
|
|
});
|
|
|
|
describe("getNextSendDelay", () => {
|
|
test("should increment sent count", async () => {
|
|
const initialCount = rateLimiter.sentCount;
|
|
await rateLimiter.getNextSendDelay();
|
|
expect(rateLimiter.sentCount).toBe(initialCount + 1);
|
|
});
|
|
|
|
test("should return pause duration when should pause", async () => {
|
|
// Force a pause condition
|
|
rateLimiter.sentCount = 49; // Next increment will trigger pause at 50
|
|
|
|
const delay = await rateLimiter.getNextSendDelay();
|
|
|
|
// Should be a long pause, not normal delay
|
|
expect(delay).toBeGreaterThan(30 * 60 * 1000); // More than 30 minutes
|
|
});
|
|
|
|
test("should return normal delay when not pausing", async () => {
|
|
rateLimiter.sentCount = 5; // Low count, won't trigger pause
|
|
|
|
const delay = await rateLimiter.getNextSendDelay();
|
|
|
|
// Should be normal delay (around 5 minutes with variance)
|
|
expect(delay).toBeGreaterThan(200000);
|
|
expect(delay).toBeLessThan(400000);
|
|
});
|
|
});
|
|
|
|
describe("formatDelay", () => {
|
|
test("should format milliseconds to readable time", () => {
|
|
expect(rateLimiter.formatDelay(60000)).toBe("1m 0s");
|
|
expect(rateLimiter.formatDelay(90000)).toBe("1m 30s");
|
|
expect(rateLimiter.formatDelay(125000)).toBe("2m 5s");
|
|
expect(rateLimiter.formatDelay(3661000)).toBe("61m 1s");
|
|
});
|
|
});
|
|
|
|
describe("getStats", () => {
|
|
test("should return correct statistics", () => {
|
|
rateLimiter.sentCount = 10;
|
|
rateLimiter.startTime = Date.now() - 60 * 60 * 1000; // 1 hour ago
|
|
|
|
const stats = rateLimiter.getStats();
|
|
|
|
expect(stats.sentCount).toBe(10);
|
|
expect(stats.runtime).toBe(60); // 60 minutes
|
|
expect(stats.averageRate).toBe("10.0"); // 10 emails per hour
|
|
expect(stats.nextDelay).toMatch(/^\d+m \d+s$/); // Format like "5m 23s"
|
|
});
|
|
|
|
test("should handle zero runtime gracefully", () => {
|
|
rateLimiter.sentCount = 5;
|
|
rateLimiter.startTime = Date.now(); // Just started
|
|
|
|
const stats = rateLimiter.getStats();
|
|
|
|
expect(stats.sentCount).toBe(5);
|
|
expect(stats.runtime).toBe(0);
|
|
// Average rate should be very high for instant runtime
|
|
expect(parseFloat(stats.averageRate)).toBeGreaterThan(1000);
|
|
});
|
|
});
|
|
|
|
describe("reset", () => {
|
|
test("should reset all counters", () => {
|
|
rateLimiter.sentCount = 10;
|
|
rateLimiter.lastSentTime = Date.now() - 60000;
|
|
|
|
rateLimiter.reset();
|
|
|
|
expect(rateLimiter.sentCount).toBe(0);
|
|
expect(rateLimiter.lastSentTime).toBeNull();
|
|
expect(rateLimiter.startTime).toBeCloseTo(Date.now(), -1); // Within 10ms
|
|
});
|
|
});
|
|
});
|