import { env } from "cloudflare:workers"; import { evictDurableObject } from "cloudflare:test"; import { describe, expect, it } from "vitest"; import { M, fromSmallcaps, makeTagged, matches, mustMatch, toSmallcaps } from "../src/patterns.js"; import { encodeShapes } from "../src/store-do.js"; type TestEnv = { STORE_DO: DurableObjectNamespace; MAP_STORE_DO: DurableObjectNamespace; }; const testEnv = env as unknown as TestEnv; const SECRET = "session=s3kr1t"; /** the message an RPC call fails with, or "" if it succeeds */ async function errorOf(call: Promise): Promise { try { await call; return ""; } catch (err) { return String(err); } } describe("patterns", () => { it("M.string() matches strings only; a literal matches only itself", () => { expect(matches("x", M.string())).toBe(true); expect(matches(1, M.string())).toBe(false); expect(matches(undefined, M.any())).toBe(true); expect(matches("cookie", "cookie")).toBe(true); expect(matches("authorization", "cookie")).toBe(false); }); it("matchers are endo's tagged records", () => { const s = M.string() as unknown as Record; expect(s[Symbol.for("passStyle")]).toBe("tagged"); expect(s[Symbol.toStringTag]).toBe("match:string"); expect(Object.keys(s)).toEqual(["payload"]); expect(s.payload).toEqual([]); expect((M.any() as unknown as Record).payload).toBeUndefined(); expect(Object.isFrozen(s)).toBe(true); }); it("smallcaps: endo's encoding, round-tripped", () => { expect(toSmallcaps(M.string())).toEqual({ "#tag": "match:string", payload: [] }); expect(toSmallcaps(M.any())).toEqual({ "#tag": "match:any", payload: "#undefined" }); expect(toSmallcaps("#cookie")).toBe("!#cookie"); expect(toSmallcaps([NaN, -0, Infinity])).toEqual(["#NaN", 0, "#Infinity"]); const back = fromSmallcaps(toSmallcaps(M.string())) as Parameters[1]; expect(matches("x", back)).toBe(true); expect(matches(1, back)).toBe(false); expect(fromSmallcaps("!#cookie")).toBe("#cookie"); expect(() => matches("x", makeTagged("match:nope", []))).toThrow("not a pattern"); }); it("mustMatch names the shape but not the specimen", () => { expect(() => mustMatch(SECRET, "cookie", "jar: key")).toThrow('jar: key: must match "cookie"'); expect(() => mustMatch(SECRET, "cookie", "jar: key")).not.toThrow(SECRET); }); }); describe("MapStore over RPC", () => { const fresh = () => testEnv.STORE_DO.get(testEnv.STORE_DO.newUniqueId()); it("rotates: the admin's set is what the read-only view sees next", async () => { const store = await fresh().cookies(); const rd = await store.readOnly(); await store.init("cookie", "session=old"); expect(await rd.get("cookie")).toBe("session=old"); await store.set("cookie", "session=new"); expect(await rd.entries()).toEqual([["cookie", "session=new"]]); }); it("the read-only view has no write methods", async () => { const store = await fresh().cookies(); const rd = (await store.readOnly()) as unknown as { set(k: string, v: string): Promise }; expect(await errorOf(rd.set("cookie", SECRET))).not.toBe(""); expect(await store.has("cookie")).toBe(false); }); it("the key shape admits only `cookie`; errors carry no values", async () => { const store = await fresh().cookies(); const err = await errorOf(store.init("authorization", SECRET)); expect(err).toContain('cookie: key: must match "cookie"'); expect(err).not.toContain(SECRET); expect(await errorOf(store.init("cookie", 42 as unknown as string))).toContain("cookie: value: must match M.string()"); }); it("init refuses a present key; set and get refuse a missing one", async () => { const store = await fresh().cookies(); expect(await errorOf(store.set("cookie", SECRET))).toContain("cookie: key not found"); expect(await errorOf(store.get("cookie"))).toContain("cookie: key not found"); await store.init("cookie", SECRET); const err = await errorOf(store.init("cookie", SECRET)); expect(err).toContain("cookie: key already registered"); expect(err).not.toContain(SECRET); }); it("survives eviction: entries live in DO storage", async () => { const stub = fresh(); { // disposed before eviction: a live store stub holds the DO open using store = await stub.cookies(); await store.init("cookie", "session=durable"); } await evictDurableObject(stub); using again = await stub.cookies(); expect(await again.get("cookie")).toBe("session=durable"); expect(await again.getSize()).toBe(1); await again.delete("cookie"); expect(await again.keys()).toEqual([]); }); }); describe("MapStoreDo", () => { const COOKIE_SHAPES = encodeShapes({ keyShape: "cookie", valueShape: M.string() }); const fresh = () => testEnv.MAP_STORE_DO.get(testEnv.MAP_STORE_DO.newUniqueId()); it("keeps the shapes its creator chose, across eviction", async () => { const stub = fresh(); await stub.configure("cookie", COOKIE_SHAPES); await evictDurableObject(stub); { using store = await stub.store(); expect(await errorOf(store.init("authorization", SECRET))).toContain('cookie: key: must match "cookie"'); expect(await errorOf(store.init("cookie", 42))).toContain("cookie: value: must match M.string()"); await store.init("cookie", "session=a"); } using rd = await stub.readOnly(); expect(await rd.entries()).toEqual([["cookie", "session=a"]]); }); it("configures once: the same shapes again is a no-op, others are refused", async () => { const stub = fresh(); await stub.configure("cookie", COOKIE_SHAPES); await stub.configure("cookie", encodeShapes({ keyShape: "cookie", valueShape: M.string() })); expect(await errorOf(stub.configure("cookie", encodeShapes({ keyShape: M.string() })))).toContain( "already configured", ); expect(await errorOf(fresh().store())).toContain("not configured"); }); });