import { mkdtemp, readdir, rm, stat } from "node:fs/promises"; import { tmpdir } from "node:os"; import path from "node:path"; import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { MAX_UPLOAD_BYTES, saveUploadedImage } from "@/lib/uploads"; const PNG_BYTES = Buffer.from( "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==", "base64", ); const originalCwd = process.cwd(); let rootDir: string; let uploadsDir: string; beforeEach(async () => { rootDir = await mkdtemp(path.join(tmpdir(), "blog-uploads-")); uploadsDir = path.join(rootDir, "uploads"); process.chdir(rootDir); }); afterEach(async () => { process.chdir(originalCwd); await rm(rootDir, { recursive: true, force: true }); }); describe("saveUploadedImage", () => { it("saves a valid image under a random name with the right extension", async () => { const file = new File([PNG_BYTES], "user chosen name!!.png", { type: "image/png" }); const result = await saveUploadedImage(file); expect(result.ok).toBe(true); if (!result.ok) return; expect(result.filename).toMatch(/^[0-9a-f-]{36}\.png$/); const written = await stat(path.join(uploadsDir, result.filename)); expect(written.size).toBe(PNG_BYTES.length); // The client-supplied filename never reaches the filesystem. expect(await readdir(uploadsDir)).toEqual([result.filename]); }); it("maps jpeg MIME to a .jpg extension", async () => { const file = new File([PNG_BYTES], "x", { type: "image/jpeg" }); const result = await saveUploadedImage(file); expect(result.ok && result.filename.endsWith(".jpg")).toBe(true); }); it("rejects non-image MIME types", async () => { const file = new File([""], "evil.svg", { type: "image/svg+xml" }); const result = await saveUploadedImage(file); expect(result).toMatchObject({ ok: false, status: 400 }); await expect(readdir(uploadsDir)).rejects.toMatchObject({ code: "ENOENT" }); }); it("rejects files over the size limit", async () => { const big = new File([Buffer.alloc(MAX_UPLOAD_BYTES + 1)], "big.png", { type: "image/png", }); const result = await saveUploadedImage(big); expect(result).toMatchObject({ ok: false, status: 413 }); }); it("rejects empty files", async () => { const empty = new File([], "empty.png", { type: "image/png" }); const result = await saveUploadedImage(empty); expect(result).toMatchObject({ ok: false, status: 400 }); }); });