- 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>
41 lines
1.3 KiB
TypeScript
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);
|
|
});
|
|
});
|