UNPKG

rwsdk

Version:

Build fast, server-driven webapps on Cloudflare with SSR, RSC, and realtime

124 lines (123 loc) 4.96 kB
import { afterEach, describe, expect, it, vi } from "vitest"; vi.mock("cloudflare:workers", () => { class DurableObject { } return { DurableObject }; }); import { SyncedStateServer } from "../SyncedStateServer.mjs"; const createStub = (onInvoke) => { const makeStub = () => { const fn = Object.assign(async (value) => { await onInvoke(value); }, { dup: () => makeStub(), }); return fn; }; return makeStub(); }; describe("SyncedStateServer", () => { it("notifies subscribers when state changes", async () => { const coordinator = new SyncedStateServer({}, {}); const received = []; const stub = createStub((value) => { received.push(value); }); coordinator.subscribe("counter", stub); coordinator.setState(5, "counter"); expect(coordinator.getState("counter")).toBe(5); expect(received).toEqual([5]); }); it("removes subscriptions on unsubscribe", () => { const coordinator = new SyncedStateServer({}, {}); const received = []; const stub = createStub((value) => { received.push(value); }); coordinator.subscribe("counter", stub); coordinator.unsubscribe("counter", stub); coordinator.setState(1, "counter"); expect(coordinator.getState("counter")).toBe(1); expect(received).toEqual([]); }); it("drops failing subscribers", async () => { const coordinator = new SyncedStateServer({}, {}); const stub = createStub(async () => { throw new Error("fail"); }); coordinator.subscribe("counter", stub); coordinator.setState(3, "counter"); await Promise.resolve(); coordinator.setState(4, "counter"); expect(coordinator.getState("counter")).toBe(4); }); it("invokes registered onSet handler", () => { const coordinator = new SyncedStateServer({}, {}); const mockStub = {}; coordinator.setStub(mockStub); const calls = []; SyncedStateServer.registerSetStateHandler((key, value, stub) => { calls.push({ key, value }); expect(stub).toBe(mockStub); }); coordinator.setState(2, "counter"); expect(calls).toEqual([{ key: "counter", value: 2 }]); SyncedStateServer.registerSetStateHandler(null); }); it("invokes registered onGet handler", () => { const coordinator = new SyncedStateServer({}, {}); const mockStub = {}; coordinator.setStub(mockStub); const calls = []; SyncedStateServer.registerGetStateHandler((key, value, stub) => { calls.push({ key, value }); expect(stub).toBe(mockStub); }); coordinator.setState(4, "counter"); expect(coordinator.getState("counter")).toBe(4); expect(calls).toEqual([{ key: "counter", value: 4 }]); SyncedStateServer.registerGetStateHandler(null); }); describe("registerKeyHandler", () => { const mockStub = {}; afterEach(() => { SyncedStateServer.registerKeyHandler(async (key, stub) => key); }); it("stores and retrieves the registered handler", async () => { const handler = async (key, stub) => `transformed:${key}`; SyncedStateServer.registerKeyHandler(handler); const retrievedHandler = SyncedStateServer.getKeyHandler(); expect(retrievedHandler).toBe(handler); }); it("transforms keys using the registered handler", async () => { const handler = async (key, stub) => `user:123:${key}`; SyncedStateServer.registerKeyHandler(handler); const result = await handler("counter", mockStub); expect(result).toBe("user:123:counter"); }); it("returns null when no handler is registered", () => { SyncedStateServer.registerKeyHandler(async (key, stub) => key); const handler = SyncedStateServer.getKeyHandler(); expect(handler).not.toBeNull(); }); it("allows handler to be async", async () => { const handler = async (key, stub) => { await new Promise((resolve) => setTimeout(resolve, 10)); return `async:${key}`; }; SyncedStateServer.registerKeyHandler(handler); const result = await handler("test", mockStub); expect(result).toBe("async:test"); }); it("handler receives the correct key parameter", async () => { let receivedKey = ""; const handler = async (key, stub) => { receivedKey = key; return key; }; SyncedStateServer.registerKeyHandler(handler); await handler("myKey", mockStub); expect(receivedKey).toBe("myKey"); }); }); });