import { afterEach, describe, expect, test } from "bun:test"; import type { Context } from "@wrnexus/core"; import { v } from "@wrnexus/validation"; import { serviceClient } from "../src/client.ts"; import { defineService, procedure } from "../src/contract.ts"; import { RPC_ERROR_CODES } from "../src/errors.ts"; import { httpTransport } from "../src/http.ts"; import { implement } from "../src/server.ts"; import { inProcessTransport } from "../src/transport.ts"; import { handleRpcRequest } from "../../dev-server/src/rpc-dispatch.ts"; const originalRpcSecret = process.env.WRNEXUS_RPC_SECRET; const originalAppName = process.env.WRNEXUS_APP_NAME; afterEach(() => { if (originalRpcSecret === undefined) delete process.env.WRNEXUS_RPC_SECRET; else process.env.WRNEXUS_RPC_SECRET = originalRpcSecret; if (originalAppName === undefined) delete process.env.WRNEXUS_APP_NAME; else process.env.WRNEXUS_APP_NAME = originalAppName; }); const billing = defineService({ name: "billing", procedures: { createInvoice: procedure .input(v.object({ amountCents: v.number() })) .output<{ invoiceId: string; forSubject: string }>() .permission("invoice:create") .build(), }, }); function wire(allowed: boolean) { const service = implement( billing, { createInvoice: async ({ amountCents }, ctx) => ({ invoiceId: `inv_${amountCents}`, forSubject: ctx.subject?.subjectId ?? "anon", }), }, { selfApp: "billing", checkPermission: async () => allowed }, ); return inProcessTransport({ "billing/createInvoice": (payload, identity) => service.invoke("createInvoice", payload, identity), }); } function httpWire(allowed: boolean) { const service = implement( billing, { createInvoice: async ({ amountCents }, ctx) => ({ invoiceId: `inv_${amountCents}`, forSubject: ctx.subject?.subjectId ?? "anon", }), }, { selfApp: "billing", checkPermission: async () => allowed }, ); return httpTransport({ resolveOrigin: () => "http://billing.internal", fetch: (async (input: RequestInfo | URL, init?: RequestInit) => { const request = new Request(input, init); return (await handleRpcRequest( request, new URL(request.url), new Map([["billing", service]]), ))!; }) as typeof fetch, }); } describe("RPC integration", () => { test("propagates identity and validates the callee permission", async () => { process.env.WRNEXUS_RPC_SECRET = "test-rpc-secret-at-least-32-chars-long"; process.env.WRNEXUS_APP_NAME = "web"; const client = serviceClient(billing, { app: "billing", as: { user: { id: "u1" }, tenant: { id: "acme" }, locals: {} } as unknown as Context, transport: wire(true), }); expect(await client.createInvoice({ amountCents: 250 })).toEqual({ invoiceId: "inv_250", forSubject: "u1", }); }); test("denies when the callee permission check refuses", async () => { process.env.WRNEXUS_RPC_SECRET = "test-rpc-secret-at-least-32-chars-long"; process.env.WRNEXUS_APP_NAME = "web"; const client = serviceClient(billing, { app: "billing", as: { user: { id: "u1" }, locals: {} } as unknown as Context, transport: wire(false), }); await expect(client.createInvoice({ amountCents: 1 })).rejects.toMatchObject({ code: RPC_ERROR_CODES.denied, }); }); test("uses the real HTTP transport and private dispatcher end to end", async () => { process.env.WRNEXUS_RPC_SECRET = "test-rpc-secret-at-least-32-chars-long"; process.env.WRNEXUS_APP_NAME = "web"; const client = serviceClient(billing, { app: "billing", as: { user: { id: "u1" }, locals: {} } as unknown as Context, transport: httpWire(true), }); expect(await client.createInvoice({ amountCents: 7 })).toEqual({ invoiceId: "inv_7", forSubject: "u1", }); await expect(client.createInvoice({ amountCents: "bad" } as never)).rejects.toMatchObject({ code: RPC_ERROR_CODES.invalid, }); }); test("the default httpTransport reaches the callee over a real socket via the internal origin", async () => { const service = implement( billing, { createInvoice: async ({ amountCents }, ctx) => ({ invoiceId: `inv_${amountCents}`, forSubject: ctx.subject?.subjectId ?? "anon", }), }, { selfApp: "billing", checkPermission: async () => true }, ); const server = Bun.serve({ port: 0, hostname: "127.0.0.1", async fetch(req) { const url = new URL(req.url); const res = await handleRpcRequest(req, url, new Map([["billing", service]])); return res ?? new Response("Not found", { status: 404 }); }, }); const originalWorkspace = process.env.WRNEXUS_WORKSPACE_ORIGINS; const originalInternal = process.env.WRNEXUS_INTERNAL_ORIGINS; try { // The workspace (public) origin deliberately points somewhere that // cannot serve the RPC — the gateway 404s the RPC prefix on any // request that arrives at a public origin. Only the internal-origin // map points at the real server. If httpTransport() ever falls back // to the public origin by default again, this call fails. process.env.WRNEXUS_WORKSPACE_ORIGINS = JSON.stringify({ billing: "http://127.0.0.1:1", // unroutable — nothing listens here }); process.env.WRNEXUS_INTERNAL_ORIGINS = JSON.stringify({ billing: `http://127.0.0.1:${server.port}`, }); const client = serviceClient(billing, { app: "billing", transport: httpTransport(), // no resolveOrigin override — uses the real default }); expect(await client.createInvoice({ amountCents: 42 })).toEqual({ invoiceId: "inv_42", forSubject: "anon", }); } finally { server.stop(true); if (originalWorkspace === undefined) delete process.env.WRNEXUS_WORKSPACE_ORIGINS; else process.env.WRNEXUS_WORKSPACE_ORIGINS = originalWorkspace; if (originalInternal === undefined) delete process.env.WRNEXUS_INTERNAL_ORIGINS; else process.env.WRNEXUS_INTERNAL_ORIGINS = originalInternal; } }); });