import { beforeEach, describe, expect, it, vi } from "vitest"; import sharp from "sharp"; import { HttpError } from "@/lib/security/http"; const requireCommissionUser = vi.fn(); const returning = vi.fn(); const where = vi.fn(() => ({ returning })); const set = vi.fn(() => ({ where })); const write = vi.fn(); vi.mock("@/lib/commission/server", () => ({ requireCommissionUser })); vi.mock("@/db", () => ({ getDb: () => ({ update: () => ({ set }) }) })); vi.mock("@/lib/media/storage", () => ({ getMediaStorage: async () => ({ write }), publicMediaUrl: (key: string) => `https://cdn.test/${key}` })); vi.mock("@/lib/security/rate-limit", () => ({ limitRequest: async () => undefined })); const { POST } = await import("./route"); function profileRequest(name: string, image?: File, origin = "https://guide.sudloh.com") { const body = new FormData(); body.set("name", name); if (image) body.set("image", image); return new Request("https://guide.sudloh.com/api/profile", { method: "POST", headers: { Origin: origin }, body }); } describe("profile update", () => { beforeEach(() => { process.env.BETTER_AUTH_URL = "https://guide.sudloh.com"; vi.clearAllMocks(); requireCommissionUser.mockResolvedValue({ id: "user-1", image: null }); returning.mockResolvedValue([{ name: "New Name", image: null }]); }); it("updates the signed-in user's display name", async () => { const response = await POST(profileRequest(" New Name ")); expect(response.status).toBe(200); expect(set).toHaveBeenCalledWith({ name: "New Name" }); expect(await response.json()).toEqual({ name: "New Name", image: null }); }); it("rejects invalid names, image types, and cross-origin submissions", async () => { expect((await POST(profileRequest("x"))).status).toBe(400); expect((await POST(profileRequest("New Name", new File(["x"], "avatar.svg", { type: "image/svg+xml" })))).status).toBe(415); expect((await POST(profileRequest("New Name", undefined, "https://elsewhere.test"))).status).toBe(403); expect(set).not.toHaveBeenCalled(); expect(write).not.toHaveBeenCalled(); }); it("validates and stores an uploaded image", async () => { const bytes = await sharp({ create: { width: 8, height: 8, channels: 3, background: "red" } }).png().toBuffer(); const response = await POST(profileRequest("New Name", new File([bytes], "avatar.png", { type: "image/png" }))); expect(response.status).toBe(200); expect(write).toHaveBeenCalledWith(expect.stringMatching(/^profiles\/.+\.webp$/), expect.any(Uint8Array), { type: "image/webp", acl: "public-read" }); expect(set).toHaveBeenCalledWith(expect.objectContaining({ name: "New Name", image: expect.stringMatching(/^https:\/\/cdn\.test\/profiles\/.+\.webp$/) })); }); it("requires an authenticated user", async () => { requireCommissionUser.mockRejectedValueOnce(new HttpError(401, "unauthorized")); expect((await POST(profileRequest("New Name"))).status).toBe(401); expect(set).not.toHaveBeenCalled(); }); });