67 lines
2.7 KiB
TypeScript
67 lines
2.7 KiB
TypeScript
import { describe, expect, test } from "bun:test";
|
|
import { cachedPermissionStore, memoryPermissionStore } from "../src/store.ts";
|
|
import { runStoreConformance } from "./store-conformance.ts";
|
|
|
|
// A cache must not change observable behaviour: writes invalidate internally.
|
|
runStoreConformance("cached(memory)", async () => cachedPermissionStore(memoryPermissionStore()));
|
|
|
|
describe("cachedPermissionStore", () => {
|
|
test("serves a repeat read from cache", async () => {
|
|
const inner = memoryPermissionStore();
|
|
let reads = 0;
|
|
const counting = {
|
|
...inner,
|
|
assignmentsFor: (id: string, scope?: { tenantId?: string }) => {
|
|
reads++;
|
|
return inner.assignmentsFor(id, scope);
|
|
},
|
|
};
|
|
const store = cachedPermissionStore(counting, { ttlMs: 60_000 });
|
|
await store.assignmentsFor("u1");
|
|
await store.assignmentsFor("u1");
|
|
expect(reads).toBe(1);
|
|
});
|
|
|
|
test("a write invalidates that subject", async () => {
|
|
const store = cachedPermissionStore(memoryPermissionStore(), { ttlMs: 60_000 });
|
|
await store.assignmentsFor("u1");
|
|
await store.assignRole("u1", "editor");
|
|
expect((await store.assignmentsFor("u1")).roles).toEqual(["editor"]);
|
|
});
|
|
|
|
test("invalidate() drops a cached subject", async () => {
|
|
const inner = memoryPermissionStore();
|
|
const store = cachedPermissionStore(inner, { ttlMs: 60_000 });
|
|
await store.assignmentsFor("u1");
|
|
await inner.assignRole("u1", "editor"); // behind the cache's back
|
|
expect((await store.assignmentsFor("u1")).roles).toEqual([]);
|
|
store.invalidate("u1");
|
|
expect((await store.assignmentsFor("u1")).roles).toEqual(["editor"]);
|
|
});
|
|
|
|
test("entries expire after ttlMs", async () => {
|
|
const inner = memoryPermissionStore();
|
|
const store = cachedPermissionStore(inner, { ttlMs: 1 });
|
|
await store.assignmentsFor("u1");
|
|
await inner.assignRole("u1", "editor");
|
|
await Bun.sleep(5);
|
|
expect((await store.assignmentsFor("u1")).roles).toEqual(["editor"]);
|
|
});
|
|
|
|
test("cache is bounded by max", async () => {
|
|
const store = cachedPermissionStore(memoryPermissionStore(), { ttlMs: 60_000, max: 2 });
|
|
await store.assignmentsFor("a");
|
|
await store.assignmentsFor("b");
|
|
await store.assignmentsFor("c");
|
|
expect(store.size()).toBeLessThanOrEqual(2);
|
|
});
|
|
|
|
test("scoped and global reads cache separately", async () => {
|
|
const inner = memoryPermissionStore();
|
|
const store = cachedPermissionStore(inner, { ttlMs: 60_000 });
|
|
await inner.assignRole("u1", "editor", { tenantId: "t1" });
|
|
expect((await store.assignmentsFor("u1")).roles).toEqual([]);
|
|
expect((await store.assignmentsFor("u1", { tenantId: "t1" })).roles).toEqual(["editor"]);
|
|
});
|
|
});
|