import { describe, it, expect } from "vitest"; import { clientIpFromHeaders, createTrpcContext } from "@/trpc/context"; describe("clientIpFromHeaders", () => { it("takes the first x-forwarded-for hop", () => { const headers = new Headers({ "x-forwarded-for": "203.0.113.7, 10.0.0.1, 10.0.0.2", }); expect(clientIpFromHeaders(headers)).toBe("203.0.113.7"); }); it("trims whitespace around the first hop", () => { const headers = new Headers({ "x-forwarded-for": " 203.0.113.7 , 10.0.0.1", }); expect(clientIpFromHeaders(headers)).toBe("203.0.113.7"); }); it("falls back to x-real-ip when x-forwarded-for is absent", () => { const headers = new Headers({ "x-real-ip": "198.51.100.4" }); expect(clientIpFromHeaders(headers)).toBe("198.51.100.4"); }); it("returns undefined when neither header is present", () => { expect(clientIpFromHeaders(new Headers())).toBeUndefined(); }); it("returns undefined for empty header values", () => { const headers = new Headers({ "x-forwarded-for": " ", "x-real-ip": "" }); expect(clientIpFromHeaders(headers)).toBeUndefined(); }); }); describe("createTrpcContext", () => { it("attaches the derived clientIp from the request", async () => { const req = new Request("https://example.test/api/trpc", { headers: { "x-forwarded-for": "203.0.113.7" }, }); await expect(createTrpcContext(req)).resolves.toEqual({ clientIp: "203.0.113.7", }); }); it("yields an undefined clientIp without a request", async () => { await expect(createTrpcContext()).resolves.toEqual({ clientIp: undefined, }); }); it("attaches the resolved user and mirrors userId (A11)", async () => { const req = new Request("https://example.test/api/trpc"); const ctx = await createTrpcContext(req, { resolveUser: async () => ({ id: "user-1", roles: ["admin"] }), }); expect(ctx.user).toEqual({ id: "user-1", roles: ["admin"] }); expect(ctx.userId).toBe("user-1"); }); it("treats a null resolver result as anonymous", async () => { const req = new Request("https://example.test/api/trpc"); const ctx = await createTrpcContext(req, { resolveUser: async () => null, }); expect(ctx.user).toBeUndefined(); expect(ctx.userId).toBeUndefined(); }); it("treats a throwing resolver as anonymous instead of failing", async () => { const req = new Request("https://example.test/api/trpc"); const ctx = await createTrpcContext(req, { resolveUser: async () => { throw new Error("expired session"); }, }); expect(ctx.user).toBeUndefined(); expect(ctx.clientIp).toBeUndefined(); }); it("does not invoke the resolver without a request", async () => { let called = false; await createTrpcContext(undefined, { resolveUser: async () => { called = true; return null; }, }); expect(called).toBe(false); }); });