import { beforeEach, describe, expect, it, vi } from "vitest"; vi.mock("~/lib/auth.server", () => ({ auth: { api: { getSession: vi.fn(), }, }, })); vi.mock("~/models/user", () => ({ isUserAdmin: vi.fn(), })); vi.mock("~/lib/cloudinary.server", () => ({ uploadSportIcon: vi.fn(), })); vi.mock("~/lib/logger", () => ({ logger: { error: vi.fn(), info: vi.fn(), warn: vi.fn() }, })); import { action } from "../api.upload-sport-icon"; import { auth } from "~/lib/auth.server"; import { uploadSportIcon } from "~/lib/cloudinary.server"; import { isUserAdmin } from "~/models/user"; function makeRequest(file?: File) { const formData = new FormData(); if (file) formData.append("file", file); return { headers: new Headers(), formData: async () => formData, } as Request; } beforeEach(() => { vi.clearAllMocks(); vi.mocked(auth.api.getSession).mockResolvedValue({ user: { id: "admin-1" } } as never); vi.mocked(isUserAdmin).mockResolvedValue(true as never); vi.mocked(uploadSportIcon).mockResolvedValue({ secure_url: "https://res.cloudinary.com/demo/image/upload/v1/sports-icons/nfl.svg", } as never); }); describe("upload sport icon action", () => { it("requires a signed-in user", async () => { vi.mocked(auth.api.getSession).mockResolvedValue(null as never); const response = await action({ request: makeRequest(), params: {}, context: {} } as never); expect(response.status).toBe(401); }); it("requires an admin user", async () => { vi.mocked(isUserAdmin).mockResolvedValue(false as never); const response = await action({ request: makeRequest(), params: {}, context: {} } as never); expect(response.status).toBe(403); }); it("rejects non-SVG files", async () => { const response = await action({ request: makeRequest(new File(["hello"], "icon.png", { type: "image/png" })), params: {}, context: {}, } as never); expect(response.status).toBe(400); expect(uploadSportIcon).not.toHaveBeenCalled(); }); it("rejects SVG files over 1 MB", async () => { const response = await action({ request: makeRequest(new File([new Uint8Array(1024 * 1024 + 1)], "icon.svg", { type: "image/svg+xml" })), params: {}, context: {}, } as never); expect(response.status).toBe(400); expect(uploadSportIcon).not.toHaveBeenCalled(); }); it("uploads valid SVG files to Cloudinary", async () => { const file = new File([""], "icon.svg", { type: "image/svg+xml" }); Object.defineProperty(file, "arrayBuffer", { value: async () => Buffer.from(""), }); const response = await action({ request: makeRequest(file), params: {}, context: {}, } as never); expect(response.status).toBe(200); await expect(response.json()).resolves.toEqual({ iconUrl: "https://res.cloudinary.com/demo/image/upload/v1/sports-icons/nfl.svg", }); expect(uploadSportIcon).toHaveBeenCalledWith({ buffer: expect.any(Buffer), slug: null, }); }); });