Files
buzz-sheet/app/api/profile/route.test.ts
T
2026-10-02 17:33:14 +07:00

64 lines
3.0 KiB
TypeScript

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();
});
});