64 lines
3.0 KiB
TypeScript
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();
|
|
});
|
|
});
|