import { beforeEach, describe, expect, mock, test } from "bun:test"; import { Hono } from "hono"; import { createTestDb } from "../helpers/db.ts"; // Mock storage service before importing routes const mockUploadImage = mock(() => Promise.resolve()); mock.module("../../src/server/services/storage.service", () => ({ uploadImage: mockUploadImage, deleteImage: mock(() => Promise.resolve()), getImageUrl: mock(() => Promise.resolve("https://s3.example.com/test.png")), withImageUrl: mock((r: any) => Promise.resolve({ ...r, imageUrl: null })), withImageUrls: mock((rs: any[]) => Promise.resolve(rs.map((r) => ({ ...r, imageUrl: null }))), ), })); // Also mock image service for from-url test const mockFetchImageFromUrl = mock(() => Promise.resolve({ filename: "test.png", sourceUrl: "https://example.com/img.png", }), ); mock.module("../../src/server/services/image.service", () => ({ fetchImageFromUrl: mockFetchImageFromUrl, })); const { imageRoutes } = await import("../../src/server/routes/images.ts"); let app: Hono; beforeEach(async () => { const { db, userId } = await createTestDb(); app = new Hono(); app.use("*", async (c, next) => { c.set("db", db); c.set("userId", userId); await next(); }); app.route("/api/images", imageRoutes); mockUploadImage.mockClear(); mockFetchImageFromUrl.mockClear(); }); describe("POST /api/images/from-url", () => { test("returns 400 for missing URL", async () => { const res = await app.request("/api/images/from-url", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({}), }); expect(res.status).toBe(400); }); test("returns 400 for invalid URL", async () => { const res = await app.request("/api/images/from-url", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ url: "not-a-url" }), }); expect(res.status).toBe(400); }); }); describe("POST /api/images", () => { test("returns 400 when no image file provided", async () => { const formData = new FormData(); const res = await app.request("/api/images", { method: "POST", body: formData, }); expect(res.status).toBe(400); const json = await res.json(); expect(json.error).toBe("No image file provided"); }); test("returns 400 for invalid file type", async () => { const formData = new FormData(); formData.append( "image", new Blob(["test"], { type: "text/plain" }), "test.txt", ); const res = await app.request("/api/images", { method: "POST", body: formData, }); expect(res.status).toBe(400); const json = await res.json(); expect(json.error).toContain("Invalid file type"); }); test("uploads valid image and calls storage service", async () => { const formData = new FormData(); const pngBlob = new Blob([new Uint8Array([0x89, 0x50, 0x4e, 0x47])], { type: "image/png", }); formData.append("image", pngBlob, "test.png"); const res = await app.request("/api/images", { method: "POST", body: formData, }); expect(res.status).toBe(201); const json = await res.json(); expect(json.filename).toMatch(/^\d+-[\w-]+\.png$/); expect(mockUploadImage).toHaveBeenCalledTimes(1); }); });