UNPKG

rwsdk

Version:

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

110 lines (109 loc) 4.63 kB
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("cloudflare:workers", () => { class DurableObject { } return { DurableObject, env: {} }; }); vi.mock("capnweb", () => ({ RpcTarget: class RpcTarget { }, newWorkersRpcResponse: vi.fn(), })); vi.mock("../../runtime/entries/router", () => ({ route: vi.fn((path, handler) => ({ path, handler })), })); vi.mock("../../runtime/requestInfo/worker", () => ({ runWithRequestInfo: (_requestInfo, fn) => fn(), })); import { newWorkersRpcResponse } from "capnweb"; import { syncedStateRoutes, SyncedStateServer } from "../worker.mjs"; describe("SyncedStateProxy", () => { let mockCoordinator; beforeEach(() => { mockCoordinator = new SyncedStateServer({}, {}); }); const mockStub = {}; afterEach(() => { vi.mocked(newWorkersRpcResponse).mockReset(); SyncedStateServer.registerKeyHandler(async (key, stub) => key); }); it("transforms keys before calling coordinator methods when handler is registered", async () => { const handler = async (key, stub) => `transformed:${key}`; SyncedStateServer.registerKeyHandler(handler); const transformedKey = await handler("counter", mockStub); expect(transformedKey).toBe("transformed:counter"); mockCoordinator.setState(5, transformedKey); const value = mockCoordinator.getState(transformedKey); expect(value).toBe(5); }); it("does not transform keys when no handler is registered", () => { SyncedStateServer.registerKeyHandler(async (key, stub) => key); const handler = SyncedStateServer.getKeyHandler(); expect(handler).not.toBeNull(); }); it("passes through original key when handler returns it unchanged", async () => { const handler = async (key, stub) => key; SyncedStateServer.registerKeyHandler(handler); const result = await handler("counter", mockStub); expect(result).toBe("counter"); }); it("handler can scope keys per user", async () => { const handler = async (key, stub) => { const userId = "user123"; return `user:${userId}:${key}`; }; SyncedStateServer.registerKeyHandler(handler); const result = await handler("settings", mockStub); expect(result).toBe("user:user123:settings"); }); it("allows errors from handler to propagate", async () => { const handler = async (_key, stub) => { throw new Error("Handler error"); }; SyncedStateServer.registerKeyHandler(handler); await expect(handler("test", mockStub)).rejects.toThrow("Handler error"); }); it("uses the subscribed duplicated RPC client when unsubscribing (#1207)", async () => { const coordinator = { _setStub: vi.fn(), subscribe: vi.fn().mockResolvedValue(undefined), unsubscribe: vi.fn().mockResolvedValue(undefined), }; const namespace = { idFromName: vi.fn(() => "synced-state-id"), get: vi.fn(() => coordinator), }; let proxy; vi.mocked(newWorkersRpcResponse).mockImplementation(async (_request, api) => { proxy = api; return new Response(null, { status: 204 }); }); SyncedStateServer.registerKeyHandler(async (key) => key); const [baseRoute] = syncedStateRoutes(() => namespace); await baseRoute.handler({ request: new Request("https://example.com/__synced-state"), params: {}, }); const duplicatedClient = Object.assign(vi.fn(), { dup: vi.fn(() => duplicatedClient), }); const originalClient = Object.assign(vi.fn(), { dup: vi.fn(() => duplicatedClient), }); await proxy.subscribe("notifications", originalClient); await proxy.unsubscribe("notifications", originalClient); const subscribeClient = coordinator.subscribe.mock.calls[0][1]; const unsubscribeClient = coordinator.unsubscribe.mock.calls[0][1]; expect(subscribeClient).not.toBe(originalClient); expect(unsubscribeClient).toBe(subscribeClient); }); it("handles async operations in handler", async () => { const handler = async (key, stub) => { await new Promise((resolve) => setTimeout(resolve, 5)); return `async:${key}`; }; SyncedStateServer.registerKeyHandler(handler); const result = await handler("data", mockStub); expect(result).toBe("async:data"); }); });