UNPKG

trpc-bun-adapter

Version:

TRPC adapter for bun js runtime

466 lines (416 loc) 12.9 kB
import { afterAll, beforeAll, describe, expect, test } from "bun:test"; import { initTRPC } from "@trpc/server"; import { observable } from "@trpc/server/observable"; import type { Server } from "bun"; import { type CreateBunContextOptions, createBunServeHandler } from "./index"; function wait(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } describe("e2e", () => { let server: Server; const t = initTRPC .context<{ name: string; params: CreateBunContextOptions["info"]["connectionParams"]; }>() .create(); const router = t.router({ hello: t.procedure.query(({ ctx }) => `Hello ${ctx.name}!`), exception: t.procedure.query(() => { throw new Error("MyError"); }), digits: t.procedure.subscription(() => observable<number>((subscriber) => { setTimeout(() => { subscriber.next(0); subscriber.next(1); subscriber.next(2); subscriber.error(new Error("MyError")); }, 10); }), ), names: t.procedure.subscription(async function* () { await wait(10); yield "gollum"; yield "gandalf"; yield "frodo"; throw new Error("MyError"); }), params: t.procedure.query(({ ctx }) => ctx.params), }); beforeAll(async () => { server = Bun.serve( createBunServeHandler( { router, endpoint: "/trpc", createContext({ req, info }) { return { name: req.headers.get("x-name") ?? "World", params: info.connectionParams, }; }, }, { port: 13123, fetch(request, server): Response | Promise<Response> { return new Response("Falling back to fetch"); }, }, ), ); }); afterAll(() => server.stop(true)); test("http call procedure", async () => { const response = await fetch("http://localhost:13123/trpc/hello"); expect(response.ok).toBe(true); const result = await response.json(); expect(result).toEqual({ result: { data: "Hello World!" } }); }); test("http call procedure +ctx", async () => { const response = await fetch("http://localhost:13123/trpc/hello", { headers: { "x-name": "John", }, }); expect(response.ok).toBe(true); const result = await response.json(); expect(result).toEqual({ result: { data: "Hello John!" } }); }); test("http call exception", async () => { const response = await fetch("http://localhost:13123/trpc/exception"); expect(response.ok).toBe(false); const result = await response.json(); expect(result).toEqual({ error: { code: -32603, message: "MyError", data: { code: "INTERNAL_SERVER_ERROR", httpStatus: 500, path: "exception", stack: expect.any(String), }, }, }); }); test("websocket call procedure", async () => { const ws = new WebSocket("ws://localhost:13123/trpc"); const id = Math.random(); ws.onopen = () => { ws.send( JSON.stringify({ id, method: "query", params: { path: "hello", }, }), ); }; await new Promise((resolve, reject) => { ws.onmessage = (event) => { const data = JSON.parse(event.data); try { expect(data).toEqual({ id, result: { type: "data", data: "Hello World!", }, }); } finally { resolve(true); } }; }); ws.close(); }); test("ws error", async () => { const ws = new WebSocket("ws://localhost:13123/trpc"); const id = Math.random(); ws.onopen = () => { ws.send( JSON.stringify({ id, method: "query", params: { path: "unknown", }, }), ); }; await new Promise((resolve, reject) => { ws.onmessage = (event) => { const data = JSON.parse(event.data); try { expect(data).toEqual({ id, error: { code: -32004, message: `No "query"-procedure on path "unknown"`, data: { code: "NOT_FOUND", httpStatus: 404, path: "unknown", stack: expect.any(String), }, }, }); } finally { resolve(true); } }; }); ws.close(); }); test("ws exception", async () => { const ws = new WebSocket("ws://localhost:13123/trpc"); const id = Math.random(); ws.onopen = () => { ws.send( JSON.stringify({ id, method: "query", params: { path: "exception", }, }), ); }; await new Promise((resolve, reject) => { ws.onmessage = (event) => { const data = JSON.parse(event.data); try { expect(data).toEqual({ id, error: { code: -32603, message: "MyError", data: { code: "INTERNAL_SERVER_ERROR", httpStatus: 500, path: "exception", stack: expect.any(String), }, }, }); } finally { resolve(true); } }; }); ws.close(); }); test("websocket call subscription", async () => { const ws = new WebSocket("ws://localhost:13123/trpc"); const messages: unknown[] = []; const id = Math.random(); ws.onopen = () => { ws.send( JSON.stringify({ id, method: "subscription", params: { path: "digits", }, }), ); }; await new Promise<void>((resolve) => { ws.onmessage = (event) => { const data = JSON.parse(event.data); messages.push(data); if (data?.result?.type === "stopped") { resolve(); } }; }); ws.send( JSON.stringify({ id, method: "subscription.stop", }), ); ws.close(); expect(messages).toEqual([ { id, result: { type: "started", }, }, { id, result: { type: "data", data: 0, }, }, { id, result: { type: "data", data: 1, }, }, { id, result: { type: "data", data: 2, }, }, { id, error: { code: -32603, message: "MyError", data: { code: "INTERNAL_SERVER_ERROR", httpStatus: 500, path: "digits", stack: expect.any(String), }, }, }, { id, result: { type: "stopped", }, }, ]); }); test("websocket call subscription (generator function)", async () => { const ws = new WebSocket("ws://localhost:13123/trpc"); const messages: unknown[] = []; const id = Math.random(); ws.onopen = () => { ws.send( JSON.stringify({ id, method: "subscription", params: { path: "names", }, }), ); }; await new Promise<void>((resolve) => { ws.onmessage = (event) => { const data = JSON.parse(event.data); messages.push(data); if (data?.result?.type === "stopped") { resolve(); } }; }); ws.send( JSON.stringify({ id, method: "subscription.stop", }), ); ws.close(); expect(messages).toEqual([ { id, result: { type: "started", }, }, { id, result: { type: "data", data: "gollum", }, }, { id, result: { type: "data", data: "gandalf", }, }, { id, result: { type: "data", data: "frodo", }, }, { id, error: { code: -32603, message: "MyError", data: { code: "INTERNAL_SERVER_ERROR", httpStatus: 500, path: "names", stack: expect.any(String), }, }, }, { id, result: { type: "stopped", }, }, ]); }); test("fall through to fetch", async () => { const response = await fetch("http://localhost:13123/other"); expect(response.ok).toBe(true); const result = await response.text(); expect(result).toEqual("Falling back to fetch"); }); test("websocket connection params", async () => { const ws = new WebSocket( "ws://localhost:13123/trpc?connectionParams=1", ); ws.onopen = () => { ws.send( JSON.stringify({ id: 1, method: "connectionParams", data: { foo: "bar", }, }), ); ws.send( JSON.stringify({ id: 2, method: "query", params: { path: "params", }, }), ); }; const messages: unknown[] = []; await new Promise<void>((resolve) => { ws.onmessage = (event) => { const data = JSON.parse(event.data); messages.push(data); resolve(); }; }); ws.close(); expect(messages).toEqual([ { id: 2, result: { type: "data", data: { foo: "bar", }, }, }, ]); }); });