import { beforeEach, describe, expect, it, vi } from "vitest"; const fetchWithTimeoutMock = vi.fn(); const resolveFetchMock = vi.fn(); vi.mock("../../../src/infra/fetch.js", () => ({ resolveFetch: (...args: unknown[]) => resolveFetchMock(...args), })); vi.mock("../../../src/infra/secure-random.js", () => ({ generateSecureUuid: () => "test-id", })); vi.mock("../../../src/utils/fetch-timeout.js", () => ({ fetchWithTimeout: (...args: unknown[]) => fetchWithTimeoutMock(...args), })); import { signalRpcRequest } from "./client.js"; function rpcResponse(body: unknown, status = 200): Response { if (typeof body === "string") { return new Response(body, { status }); } return new Response(JSON.stringify(body), { status }); } describe("signalRpcRequest", () => { beforeEach(() => { vi.clearAllMocks(); resolveFetchMock.mockReturnValue(vi.fn()); }); it("returns parsed RPC result", async () => { fetchWithTimeoutMock.mockResolvedValueOnce( rpcResponse({ jsonrpc: "2.0", result: { version: "0.13.22" }, id: "test-id" }), ); const result = await signalRpcRequest<{ version: string }>("version", undefined, { baseUrl: "http://127.0.0.1:8080", }); expect(result).toEqual({ version: "0.13.22" }); }); it("throws a wrapped error when RPC response JSON is malformed", async () => { fetchWithTimeoutMock.mockResolvedValueOnce(rpcResponse("not-json", 502)); await expect( signalRpcRequest("version", undefined, { baseUrl: "http://127.0.0.1:8080", }), ).rejects.toMatchObject({ message: "Signal RPC returned malformed JSON (status 502)", cause: expect.any(SyntaxError), }); }); it("throws when RPC response envelope has neither result nor error", async () => { fetchWithTimeoutMock.mockResolvedValueOnce(rpcResponse({ jsonrpc: "2.0", id: "test-id" })); await expect( signalRpcRequest("version", undefined, { baseUrl: "http://127.0.0.1:8080", }), ).rejects.toThrow("Signal RPC returned invalid response envelope (status 200)"); }); });