- 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>
47 lines
1.3 KiB
TypeScript
47 lines
1.3 KiB
TypeScript
import { describe, it, expect, beforeEach, afterAll } from "vitest";
|
|
import { cacheGet, cacheSet, cacheGetOrSet, cacheDelete } from "./cache";
|
|
import { db, pool } from "./db";
|
|
import { cacheEntries } from "@cmbot/db";
|
|
import { sql } from "drizzle-orm";
|
|
|
|
describe("cache helpers", () => {
|
|
beforeEach(async () => {
|
|
await db.delete(cacheEntries).where(sql`true`);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await pool.end();
|
|
});
|
|
|
|
it("set + get round-trip", async () => {
|
|
await cacheSet("k1", { hello: "world" }, 60);
|
|
const v = await cacheGet<{ hello: string }>("k1");
|
|
expect(v).toEqual({ hello: "world" });
|
|
});
|
|
|
|
it("getOrSet computes once, then returns cached", async () => {
|
|
let calls = 0;
|
|
const compute = async () => {
|
|
calls++;
|
|
return { stamp: 42 };
|
|
};
|
|
const a = await cacheGetOrSet("k2", 60, compute);
|
|
const b = await cacheGetOrSet("k2", 60, compute);
|
|
expect(a).toEqual({ stamp: 42 });
|
|
expect(b).toEqual({ stamp: 42 });
|
|
expect(calls).toBe(1);
|
|
});
|
|
|
|
it("expired entries are skipped on get", async () => {
|
|
await cacheSet("k3", { stale: true }, -1); // already expired
|
|
const v = await cacheGet("k3");
|
|
expect(v).toBeNull();
|
|
});
|
|
|
|
it("delete removes the entry", async () => {
|
|
await cacheSet("k4", { x: 1 }, 60);
|
|
await cacheDelete("k4");
|
|
expect(await cacheGet("k4")).toBeNull();
|
|
});
|
|
});
|