import { readFileSync } from "node:fs"; import { join } from "node:path"; import { expect, test } from "@playwright/test"; // ─── AI Tools ─────────────────────────────────────────────────────── // Tests for: remove-background, upscale, ocr, blur-faces, smart-crop, // enhance-faces, colorize, noise-removal, red-eye-removal, restore-photo, // passport-photo, erase-object // // These tools require the AI sidecar (Python bridge). Each test detects // whether the required feature bundle is installed: // - 200 = tool works, verify output // - 501 FEATURE_NOT_INSTALLED = skip gracefully (expected when bundle missing) // - Other errors = genuine failures const FIXTURES = join(process.cwd(), "tests", "fixtures"); const CONTENT = join(FIXTURES, "content"); let token: string; test.beforeAll(async ({ request }) => { const res = await request.post("/api/auth/login", { data: { username: "admin", password: "admin" }, }); const body = await res.json(); token = body.token; }); function fixture(name: string): Buffer { return readFileSync(join(FIXTURES, name)); } function contentFixture(name: string): Buffer { return readFileSync(join(CONTENT, name)); } const JPG_100x100 = fixture("test-100x100.jpg"); const BLANK_PNG = fixture("test-blank.png"); const HEIC_PORTRAIT = fixture("test-portrait.heic"); /** Minimal valid 1x1 PNG for quick feature-detection probes. */ const TINY_PNG = fixture("test-1x1.png"); /** * Determine whether a given AI tool's feature bundle is installed by * querying the features API and checking the status of the bundle that * enables the tool. */ async function isFeatureInstalled( request: import("@playwright/test").APIRequestContext, toolId: string, ): Promise { const res = await request.get("/api/v1/features", { headers: { Authorization: `Bearer ${token}` }, }); if (!res.ok()) return false; const data = await res.json(); // Map tool IDs to their bundle IDs const toolBundleMap: Record = { "remove-background": "background-removal", "passport-photo": "background-removal", "blur-faces": "face-detection", "red-eye-removal": "face-detection", "smart-crop": "face-detection", "erase-object": "object-eraser-colorize", colorize: "object-eraser-colorize", upscale: "upscale-enhance", "enhance-faces": "upscale-enhance", "noise-removal": "upscale-enhance", "restore-photo": "photo-restoration", ocr: "ocr", }; const bundleId = toolBundleMap[toolId]; if (!bundleId) return false; const bundle = data.bundles?.find((b: { id: string }) => b.id === bundleId); return bundle?.status === "installed"; } /** * Post a file to an AI tool and return the response. * Handles the 501 FEATURE_NOT_INSTALLED case gracefully. */ async function callAiTool( request: import("@playwright/test").APIRequestContext, toolId: string, imageBuffer: Buffer, settings: Record = {}, filename = "test.png", mimeType = "image/png", ): Promise<{ installed: boolean; ok: boolean; status: number; body: Record }> { const res = await request.post(`/api/v1/tools/${toolId}`, { headers: { Authorization: `Bearer ${token}` }, multipart: { file: { name: filename, mimeType, buffer: imageBuffer }, settings: JSON.stringify(settings), }, }); const status = res.status(); const body = await res.json(); if (status === 501 && body.code === "FEATURE_NOT_INSTALLED") { return { installed: false, ok: false, status, body }; } return { installed: true, ok: res.ok(), status, body }; } // ─── Remove Background ────────────────────────────────────────────── test.describe("Remove Background", () => { test("remove background returns download URL or 501", async ({ request }) => { const result = await callAiTool(request, "remove-background", JPG_100x100, {}); if (!result.installed) { expect(result.body.feature).toBe("background-removal"); expect(result.body.featureName).toBeTruthy(); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); expect(result.body.processedSize).toBeGreaterThan(0); }); test("remove background with specific model", async ({ request }) => { const result = await callAiTool(request, "remove-background", JPG_100x100, { model: "u2net", }); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("remove background on HEIC image", async ({ request }) => { const result = await callAiTool( request, "remove-background", HEIC_PORTRAIT, {}, "portrait.heic", "image/heic", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); }); // ─── Upscale ──────────────────────────────────────────────────────── test.describe("Upscale", () => { test("upscale 2x returns larger image or 501", async ({ request }) => { const result = await callAiTool(request, "upscale", JPG_100x100, { scale: 2, model: "auto", }); if (!result.installed) { expect(result.body.feature).toBe("upscale-enhance"); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); expect(result.body.processedSize).toBeGreaterThan(0); }); test("upscale 4x", async ({ request }) => { const result = await callAiTool(request, "upscale", TINY_PNG, { scale: 4, model: "auto", }); if (!result.installed) { test.skip(); return; } // 4x upscale on a tiny image may fail with processing error — accept both if (result.ok) { expect(result.body.downloadUrl).toBeTruthy(); } else { expect(result.body.error).toBeDefined(); } }); test("upscale with lanczos (CPU fallback)", async ({ request }) => { const result = await callAiTool(request, "upscale", JPG_100x100, { scale: 2, model: "lanczos", }); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("upscale with realesrgan model", async ({ request }) => { const result = await callAiTool(request, "upscale", TINY_PNG, { scale: 2, model: "realesrgan", }); if (!result.installed) { test.skip(); return; } // RealESRGAN may fail on very small images — accept both success and error if (result.ok) { expect(result.body.downloadUrl).toBeTruthy(); } else { expect(result.body.error).toBeDefined(); } }); }); // ─── OCR ──────────────────────────────────────────────────────────── test.describe("OCR", () => { test("OCR processes image or returns 501", async ({ request }) => { const result = await callAiTool(request, "ocr", JPG_100x100, {}); if (!result.installed) { expect(result.body.feature).toBe("ocr"); test.skip(); return; } expect(result.ok).toBe(true); // OCR response may have text, blocks, or engine field expect(result.body.text !== undefined || result.body.blocks !== undefined).toBe(true); }); test("OCR with tesseract engine", async ({ request }) => { const result = await callAiTool(request, "ocr", JPG_100x100, { engine: "tesseract" }); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); }); test("OCR with paddleocr engine", async ({ request }) => { const result = await callAiTool(request, "ocr", JPG_100x100, { engine: "paddleocr" }); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); }); test("OCR on Japanese text image", async ({ request }) => { const ocrJapanese = contentFixture("ocr-japanese.png"); const result = await callAiTool( request, "ocr", ocrJapanese, {}, "ocr-japanese.png", "image/png", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); }); test("OCR on chat screenshot", async ({ request }) => { const ocrChat = contentFixture("ocr-chat.jpeg"); const result = await callAiTool(request, "ocr", ocrChat, {}, "ocr-chat.jpeg", "image/jpeg"); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); // Chat screenshot should contain some readable text if (result.body.text) { expect((result.body.text as string).length).toBeGreaterThan(0); } }); }); // ─── Blur Faces ───────────────────────────────────────────────────── test.describe("Blur Faces", () => { test("blur faces processes image or returns 501", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "blur-faces", portrait, { blurRadius: 30, sensitivity: 0.5 }, "portrait.jpg", "image/jpeg", ); if (!result.installed) { expect(result.body.feature).toBe("face-detection"); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("blur faces on HEIC portrait", async ({ request }) => { const result = await callAiTool( request, "blur-faces", HEIC_PORTRAIT, { blurRadius: 30 }, "portrait.heic", "image/heic", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("blur faces on image with no faces returns warning", async ({ request }) => { const result = await callAiTool(request, "blur-faces", BLANK_PNG, { blurRadius: 30 }); if (!result.installed) { test.skip(); return; } // Should succeed but indicate no faces were found expect(result.ok).toBe(true); // facesDetected should be 0 or a warning should be present if (result.body.facesDetected !== undefined) { expect(result.body.facesDetected).toBe(0); } }); test("blur faces on multi-face image", async ({ request }) => { const multiFace = contentFixture("multi-face.webp"); const result = await callAiTool( request, "blur-faces", multiFace, { blurRadius: 20 }, "multi-face.webp", "image/webp", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); }); // ─── Smart Crop ───────────────────────────────────────────────────── test.describe("Smart Crop", () => { test("smart crop to portrait dimensions or returns 501", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "smart-crop", portrait, { width: 400, height: 400 }, "portrait.jpg", "image/jpeg", ); if (!result.installed) { expect(result.body.feature).toBe("face-detection"); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("smart crop with landscape aspect ratio", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "smart-crop", portrait, { width: 800, height: 400 }, "portrait.jpg", "image/jpeg", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); }); // ─── Enhance Faces ────────────────────────────────────────────────── test.describe("Enhance Faces", () => { test("enhance faces with auto model or returns 501", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "enhance-faces", portrait, { model: "auto" }, "portrait.jpg", "image/jpeg", ); if (!result.installed) { expect(result.body.feature).toBe("upscale-enhance"); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("enhance faces with gfpgan model", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "enhance-faces", portrait, { model: "gfpgan" }, "portrait.jpg", "image/jpeg", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("enhance faces with codeformer model", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "enhance-faces", portrait, { model: "codeformer" }, "portrait.jpg", "image/jpeg", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); }); // ─── Colorize ─────────────────────────────────────────────────────── test.describe("Colorize", () => { test("colorize B&W image or returns 501", async ({ request }) => { const bwPortrait = contentFixture("portrait-bw.jpeg"); const result = await callAiTool( request, "colorize", bwPortrait, { model: "auto" }, "portrait-bw.jpeg", "image/jpeg", ); if (!result.installed) { expect(result.body.feature).toBe("object-eraser-colorize"); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("colorize with ddcolor model", async ({ request }) => { const bwPortrait = contentFixture("portrait-bw.jpeg"); const result = await callAiTool( request, "colorize", bwPortrait, { model: "ddcolor" }, "portrait-bw.jpeg", "image/jpeg", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); }); // ─── Noise Removal ────────────────────────────────────────────────── test.describe("Noise Removal", () => { test("noise removal with quick tier or returns 501", async ({ request }) => { const result = await callAiTool(request, "noise-removal", JPG_100x100, { tier: "quick", }); if (!result.installed) { expect(result.body.feature).toBe("upscale-enhance"); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("noise removal with balanced tier", async ({ request }) => { const result = await callAiTool(request, "noise-removal", JPG_100x100, { tier: "balanced", }); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("noise removal with quality tier (SCUNet)", async ({ request }) => { const result = await callAiTool(request, "noise-removal", JPG_100x100, { tier: "quality", }); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("noise removal with maximum tier (NAFNet)", async ({ request }) => { const result = await callAiTool(request, "noise-removal", TINY_PNG, { tier: "maximum", }); if (!result.installed) { test.skip(); return; } // NAFNet may fail on very small images — accept both success and error if (result.ok) { expect(result.body.downloadUrl).toBeTruthy(); } else { expect(result.body.error).toBeDefined(); } }); }); // ─── Red-Eye Removal ──────────────────────────────────────────────── test.describe("Red-Eye Removal", () => { test("red-eye removal processes image or returns 501", async ({ request }) => { const redEye = contentFixture("red-eye.jpg"); const result = await callAiTool( request, "red-eye-removal", redEye, {}, "red-eye.jpg", "image/jpeg", ); if (!result.installed) { expect(result.body.feature).toBe("face-detection"); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("red-eye removal on image without red eyes succeeds gracefully", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "red-eye-removal", portrait, {}, "portrait.jpg", "image/jpeg", ); if (!result.installed) { test.skip(); return; } // Should succeed even if no red eyes found expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); }); // ─── Restore Photo ────────────────────────────────────────────────── test.describe("Restore Photo", () => { test("restore photo processes image or returns 501", async ({ request }) => { const bwPortrait = contentFixture("portrait-bw.jpeg"); const result = await callAiTool( request, "restore-photo", bwPortrait, {}, "old-photo.jpeg", "image/jpeg", ); if (!result.installed) { expect(result.body.feature).toBe("photo-restoration"); test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); expect(result.body.processedSize).toBeGreaterThan(0); }); }); // ─── Passport Photo ───────────────────────────────────────────────── test.describe("Passport Photo", () => { test("passport photo processes portrait or returns 501", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "passport-photo", portrait, {}, "portrait.jpg", "image/jpeg", ); if (!result.installed) { expect(result.body.feature).toBe("background-removal"); test.skip(); return; } // Passport photo may succeed or fail with "no face detected" if (result.ok) { expect(result.body.downloadUrl).toBeTruthy(); } else { // Acceptable failure if face detection didn't find a face expect(result.body.error).toBeDefined(); expect(typeof result.body.error).toBe("string"); expect(result.body.error).not.toContain("[object Object]"); } }); test("passport photo error is readable for non-face image", async ({ request }) => { const result = await callAiTool(request, "passport-photo", BLANK_PNG, {}); if (!result.installed) { test.skip(); return; } // Should fail gracefully with a readable error, not [object Object] if (!result.ok) { expect(typeof result.body.error).toBe("string"); expect(result.body.error).not.toContain("[object Object]"); if (result.body.details) { expect(typeof result.body.details).toBe("string"); } } }); test("passport photo on headshot portrait", async ({ request }) => { const headshot = contentFixture("portrait-headshot.heic"); const result = await callAiTool( request, "passport-photo", headshot, {}, "headshot.heic", "image/heic", ); if (!result.installed) { test.skip(); return; } if (result.ok) { expect(result.body.downloadUrl).toBeTruthy(); } }); }); // ─── Erase Object ─────────────────────────────────────────────────── test.describe("Erase Object", () => { test("erase object returns 501 when feature not installed", async ({ request }) => { const result = await callAiTool(request, "erase-object", JPG_100x100, {}); if (!result.installed) { expect(result.body.feature).toBe("object-eraser-colorize"); expect(result.body.code).toBe("FEATURE_NOT_INSTALLED"); // This is the expected state — erase-object needs a mask and LaMa model return; } // If installed, it may fail because no mask was provided // That's still a valid test — we're checking the tool doesn't crash expect(typeof result.body.error === "string" || result.body.downloadUrl).toBeTruthy(); }); }); // ─── Feature Bundle Status ────────────────────────────────────────── test.describe("AI Feature Bundle Status", () => { test("all AI tools return correct 501 response when uninstalled", async ({ request }) => { // Map from tool ID used for the API POST to the lookup key in isFeatureInstalled const aiTools = [ { tool: "remove-background", featureKey: "remove-background", bundle: "background-removal" }, { tool: "upscale", featureKey: "upscale", bundle: "upscale-enhance" }, { tool: "blur-faces", featureKey: "blur-faces", bundle: "face-detection" }, { tool: "erase-object", featureKey: "erase-object", bundle: "object-eraser-colorize" }, { tool: "ocr", featureKey: "ocr", bundle: "ocr" }, { tool: "colorize", featureKey: "colorize", bundle: "object-eraser-colorize" }, { tool: "enhance-faces", featureKey: "enhance-faces", bundle: "upscale-enhance" }, { tool: "noise-removal", featureKey: "noise-removal", bundle: "upscale-enhance" }, { tool: "red-eye-removal", featureKey: "red-eye-removal", bundle: "face-detection" }, { tool: "restore-photo", featureKey: "restore-photo", bundle: "photo-restoration" }, { tool: "passport-photo", featureKey: "passport-photo", bundle: "background-removal" }, ]; let testedCount = 0; for (const { tool, featureKey, bundle } of aiTools) { const installed = await isFeatureInstalled(request, featureKey); if (installed) continue; testedCount++; const res = await request.post(`/api/v1/tools/${tool}`, { headers: { Authorization: `Bearer ${token}` }, multipart: { file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, settings: JSON.stringify({}), }, }); expect(res.status(), `${tool} should return 501`).toBe(501); const body = await res.json(); expect(body.code, `${tool} missing code`).toBe("FEATURE_NOT_INSTALLED"); expect(body.feature, `${tool} wrong bundle`).toBe(bundle); expect(body.featureName, `${tool} missing featureName`).toBeTruthy(); expect( typeof body.estimatedSize === "string", `${tool} estimatedSize should be a string`, ).toBe(true); } // If all bundles are installed, skip the test gracefully if (testedCount === 0) { test.skip(); } }); }); // ─── Transparency Fixer ──────────────────────────────────────────── test.describe("Transparency Fixer", () => { test("transparency fixer returns 202 or 501", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const res = await request.post("/api/v1/tools/transparency-fixer", { headers: { Authorization: `Bearer ${token}` }, multipart: { file: { name: "portrait.jpg", mimeType: "image/jpeg", buffer: portrait }, settings: JSON.stringify({}), }, }); if (res.status() === 501) { const body = await res.json(); expect(body.code).toBe("FEATURE_NOT_INSTALLED"); expect(body.feature).toBe("background-removal"); } else { // Async tool returns 202 expect(res.status()).toBe(202); const body = await res.json(); expect(body.jobId).toBeTruthy(); expect(body.async).toBe(true); } }); test("transparency fixer with custom defringe", async ({ request }) => { const res = await request.post("/api/v1/tools/transparency-fixer", { headers: { Authorization: `Bearer ${token}` }, multipart: { file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, settings: JSON.stringify({ defringe: 50, outputFormat: "png" }), }, }); if (res.status() === 501) { const body = await res.json(); expect(body.code).toBe("FEATURE_NOT_INSTALLED"); } else { expect(res.status()).toBe(202); const body = await res.json(); expect(body.jobId).toBeTruthy(); } }); test("transparency fixer with webp output", async ({ request }) => { const res = await request.post("/api/v1/tools/transparency-fixer", { headers: { Authorization: `Bearer ${token}` }, multipart: { file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 }, settings: JSON.stringify({ outputFormat: "webp" }), }, }); if (res.status() === 501) { const body = await res.json(); expect(body.code).toBe("FEATURE_NOT_INSTALLED"); } else { expect(res.status()).toBe(202); const body = await res.json(); expect(body.jobId).toBeTruthy(); } }); test("transparency fixer rejects empty file", async ({ request }) => { const res = await request.post("/api/v1/tools/transparency-fixer", { headers: { Authorization: `Bearer ${token}` }, multipart: { settings: JSON.stringify({}), }, }); expect(res.ok()).toBe(false); const body = await res.json(); expect(body.error).toBeDefined(); }); }); // ─── Smart Crop — Additional ────────────────────────────────────── test.describe("Smart Crop — additional", () => { test("smart crop with square output", async ({ request }) => { const portrait = contentFixture("portrait-color.jpg"); const result = await callAiTool( request, "smart-crop", portrait, { width: 500, height: 500 }, "portrait.jpg", "image/jpeg", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); test("smart crop on multi-face image", async ({ request }) => { const multiFace = contentFixture("multi-face.webp"); const result = await callAiTool( request, "smart-crop", multiFace, { width: 300, height: 300 }, "multi-face.webp", "image/webp", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); }); }); // ─── Colorize — Additional ─────────────────────────────────────── test.describe("Colorize — additional", () => { test("colorize with auto model", async ({ request }) => { const bwPortrait = contentFixture("portrait-bw.jpeg"); const result = await callAiTool( request, "colorize", bwPortrait, { model: "auto" }, "bw.jpeg", "image/jpeg", ); if (!result.installed) { test.skip(); return; } expect(result.ok).toBe(true); expect(result.body.downloadUrl).toBeTruthy(); expect(result.body.processedSize).toBeGreaterThan(0); }); }); // ─── Auth Failure ────────────────────────────────────────────────── test.describe("Auth failure", () => { test("remove-background without token returns 401", async ({ request }) => { const res = await request.post("/api/v1/tools/remove-background", { multipart: { file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, settings: JSON.stringify({}), }, }); expect(res.status()).toBe(401); }); test("upscale without token returns 401", async ({ request }) => { const res = await request.post("/api/v1/tools/upscale", { multipart: { file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, settings: JSON.stringify({ scale: 2, model: "auto" }), }, }); expect(res.status()).toBe(401); }); test("ocr without token returns 401", async ({ request }) => { const res = await request.post("/api/v1/tools/ocr", { multipart: { file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, settings: JSON.stringify({}), }, }); expect(res.status()).toBe(401); }); test("transparency-fixer without token returns 401", async ({ request }) => { const res = await request.post("/api/v1/tools/transparency-fixer", { multipart: { file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, settings: JSON.stringify({}), }, }); expect(res.status()).toBe(401); }); test("blur-faces without token returns 401", async ({ request }) => { const res = await request.post("/api/v1/tools/blur-faces", { multipart: { file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, settings: JSON.stringify({ blurRadius: 30 }), }, }); expect(res.status()).toBe(401); }); test("colorize without token returns 401", async ({ request }) => { const res = await request.post("/api/v1/tools/colorize", { multipart: { file: { name: "test.png", mimeType: "image/png", buffer: TINY_PNG }, settings: JSON.stringify({}), }, }); expect(res.status()).toBe(401); }); });