cm_whatsapp_bot_v1/apps/web/src/lib/rate-limit.test.ts
yiekheng 17f9ee179f feat(db,web): pg_trgm + indexes + Postgres-backed cache and rate-limit
- Add cacheEntries and rateLimitBuckets tables to schema
- Generate migration 0002_left_jimmy_woo.sql with pg_trgm extension and all indexes
- Implement cache.ts (get/set/delete/getOrSet/sweep) backed by Postgres
- Implement rate-limit.ts (sliding-window UPSERT) backed by Postgres
- Implement search.ts (trigramMatch / trigramRank helpers)
- Add vitest 2.1.9 + vitest.config.ts; 7 unit tests pass (4 cache + 3 rate-limit)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 23:03:10 +08:00

41 lines
1.3 KiB
TypeScript

import { describe, it, expect, beforeEach, afterAll } from "vitest";
import { checkRateLimit } from "./rate-limit";
import { db, pool } from "./db";
import { rateLimitBuckets } from "@cmbot/db";
import { sql } from "drizzle-orm";
describe("checkRateLimit", () => {
beforeEach(async () => {
await db.delete(rateLimitBuckets).where(sql`true`);
});
afterAll(async () => {
await pool.end();
});
it("allows requests under the limit", async () => {
for (let i = 0; i < 5; i++) {
const r = await checkRateLimit("test:1", { max: 5, windowSec: 10 });
expect(r.limited).toBe(false);
}
});
it("blocks the (max+1)th request within the window", async () => {
for (let i = 0; i < 3; i++) {
await checkRateLimit("test:2", { max: 3, windowSec: 10 });
}
const r = await checkRateLimit("test:2", { max: 3, windowSec: 10 });
expect(r.limited).toBe(true);
expect(r.count).toBeGreaterThan(3);
});
it("isolates buckets by key", async () => {
await checkRateLimit("a", { max: 1, windowSec: 10 });
await checkRateLimit("a", { max: 1, windowSec: 10 });
const aLimited = await checkRateLimit("a", { max: 1, windowSec: 10 });
const bFresh = await checkRateLimit("b", { max: 1, windowSec: 10 });
expect(aLimited.limited).toBe(true);
expect(bFresh.limited).toBe(false);
});
});