brackt/app/routes/__tests__/api.upload-sport-icon.test.ts
2026-05-07 16:07:34 -07:00

104 lines
3 KiB
TypeScript

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(["<svg viewBox=\"0 0 1 1\" />"], "icon.svg", { type: "image/svg+xml" });
Object.defineProperty(file, "arrayBuffer", {
value: async () => Buffer.from("<svg viewBox=\"0 0 1 1\" />"),
});
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,
});
});
});