test: expand E2E-Docker coverage to all 47 tools with auth-failure tests

This commit is contained in:
SnapOtter
2026-05-01 02:51:42 +08:00
parent 710b580cee
commit e3c5279b92
11 changed files with 368 additions and 4 deletions
+34
View File
@@ -398,3 +398,37 @@ test.describe("Image Enhancement", () => {
expect(body.processedSize).not.toBe(body.originalSize);
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("adjust-colors without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/adjust-colors", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({ brightness: 20 }),
},
});
expect(res.status()).toBe(401);
});
test("strip-metadata without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/strip-metadata", {
multipart: {
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_WITH_EXIF },
settings: JSON.stringify({}),
},
});
expect(res.status()).toBe(401);
});
test("edit-metadata without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/edit-metadata", {
multipart: {
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
settings: JSON.stringify({ artist: "Test" }),
},
});
expect(res.status()).toBe(401);
});
});
+34
View File
@@ -754,3 +754,37 @@ test.describe("AI Feature Bundle Status", () => {
}
});
});
// ─── 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);
});
});
+11 -4
View File
@@ -1,6 +1,6 @@
import { mkdirSync } from "node:fs";
import path from "node:path";
import { expect, test as setup } from "@playwright/test";
import { test as setup } from "@playwright/test";
const authFile = path.join(__dirname, "..", "..", ".playwright", ".auth", "analytics-user.json");
@@ -21,11 +21,18 @@ setup("authenticate", async ({ page }) => {
await acceptBtn.waitFor({ state: "visible", timeout: 15_000 });
await acceptBtn.click();
// Consent page does window.location.href = "/" (full reload)
await page.waitForURL("/", { timeout: 30_000 });
await page.waitForURL((url) => !url.pathname.includes("analytics-consent"), {
timeout: 30_000,
});
}
// At this point we should be on the home page
await expect(page).toHaveURL("/");
// At this point we should be on the home page (or at least past the consent page)
await page.waitForURL(
(url) => !url.pathname.includes("login") && !url.pathname.includes("analytics-consent"),
{
timeout: 30_000,
},
);
mkdirSync(path.dirname(authFile), { recursive: true });
await page.context().storageState({ path: authFile });
});
+36
View File
@@ -581,3 +581,39 @@ test.describe("Convert format matrix", () => {
}
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("gif-tools without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/gif-tools", {
multipart: {
file: { name: "animated.gif", mimeType: "image/gif", buffer: ANIMATED_GIF },
settings: JSON.stringify({}),
},
});
expect(res.status()).toBe(401);
});
test("image-to-base64 without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/image-to-base64", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({}),
},
});
expect(res.status()).toBe(401);
});
test("image-to-pdf without token returns 401", async ({ request }) => {
const { body, contentType } = buildMultipart(
[{ name: "file", filename: "test.jpg", contentType: "image/jpeg", buffer: JPG_100x100 }],
[{ name: "settings", value: JSON.stringify({ pageSize: "A4" }) }],
);
const res = await request.post("/api/v1/tools/image-to-pdf", {
headers: { "Content-Type": contentType },
data: body,
});
expect(res.status()).toBe(401);
});
});
+54
View File
@@ -589,3 +589,57 @@ test.describe("Content-Aware Resize", () => {
}
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("collage without token returns 401", async ({ request }) => {
const { body, contentType } = buildMultipart(
[
{ name: "file", filename: "a.png", contentType: "image/png", buffer: PNG_200x150 },
{ name: "file", filename: "b.jpg", contentType: "image/jpeg", buffer: JPG_100x100 },
],
[
{
name: "settings",
value: JSON.stringify({ templateId: "2-h-equal", width: 400 }),
},
],
);
const res = await request.post("/api/v1/tools/collage", {
headers: { "Content-Type": contentType },
data: body,
});
expect(res.status()).toBe(401);
});
test("stitch without token returns 401", async ({ request }) => {
const { body, contentType } = buildMultipart(
[
{ name: "file", filename: "a.png", contentType: "image/png", buffer: PNG_200x150 },
{ name: "file", filename: "b.jpg", contentType: "image/jpeg", buffer: JPG_100x100 },
],
[{ name: "settings", value: JSON.stringify({ direction: "horizontal" }) }],
);
const res = await request.post("/api/v1/tools/stitch", {
headers: { "Content-Type": contentType },
data: body,
});
expect(res.status()).toBe(401);
});
test("text-overlay without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/text-overlay", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({
text: "Test",
fontSize: 24,
color: "#FFFFFF",
position: "center",
}),
},
});
expect(res.status()).toBe(401);
});
});
+34
View File
@@ -618,3 +618,37 @@ test.describe("Sharpening", () => {
expect(body.downloadUrl).toBeTruthy();
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("resize without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/resize", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({ width: 100 }),
},
});
expect(res.status()).toBe(401);
});
test("crop without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/crop", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({ left: 0, top: 0, width: 50, height: 50 }),
},
});
expect(res.status()).toBe(401);
});
test("convert without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/convert", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({ format: "jpg" }),
},
});
expect(res.status()).toBe(401);
});
});
@@ -717,3 +717,37 @@ test.describe("PDF to Image — additional formats", () => {
expect(body.downloadUrl || body.pages || body.jobId).toBeTruthy();
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("svg-to-raster without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/svg-to-raster", {
multipart: {
file: { name: "test.svg", mimeType: "image/svg+xml", buffer: SVG_100x100 },
settings: JSON.stringify({ format: "png", width: 200 }),
},
});
expect(res.status()).toBe(401);
});
test("vectorize without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/vectorize", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({}),
},
});
expect(res.status()).toBe(401);
});
test("pdf-to-image without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/pdf-to-image", {
multipart: {
file: { name: "test.pdf", mimeType: "application/pdf", buffer: PDF_3PAGE },
settings: JSON.stringify({ format: "png", dpi: 150, pages: "1" }),
},
});
expect(res.status()).toBe(401);
});
});
+24
View File
@@ -543,3 +543,27 @@ test.describe("Border — extended", () => {
expect(body.processedSize).toBeGreaterThan(0);
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("split without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/split", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({ columns: 2, rows: 2 }),
},
});
expect(res.status()).toBe(401);
});
test("border without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/border", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({ size: 10, color: "#ff0000" }),
},
});
expect(res.status()).toBe(401);
});
});
@@ -734,3 +734,41 @@ test.describe("Content-Aware Resize — extended", () => {
}
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("optimize-for-web without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/optimize-for-web", {
multipart: {
file: { name: "test.jpg", mimeType: "image/jpeg", buffer: JPG_100x100 },
settings: JSON.stringify({ maxWidth: 800, quality: 75 }),
},
});
expect(res.status()).toBe(401);
});
test("favicon without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/favicon", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({}),
},
});
expect(res.status()).toBe(401);
});
test("replace-color without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/replace-color", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({
targetColor: "#ffffff",
replacementColor: "#000000",
tolerance: 30,
}),
},
});
expect(res.status()).toBe(401);
});
});
+29
View File
@@ -758,3 +758,32 @@ test.describe("QR Read", () => {
}
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("info without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/info", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
},
});
expect(res.status()).toBe(401);
});
test("qr-generate without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/qr-generate", {
data: { text: "https://snapotter.app", size: 512 },
});
expect(res.status()).toBe(401);
});
test("color-palette without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/color-palette", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
},
});
expect(res.status()).toBe(401);
});
});
@@ -538,3 +538,43 @@ test.describe("Compose — extended", () => {
expect(json.downloadUrl).toBeTruthy();
});
});
// ─── Auth Failure ──────────────────────────────────────────────────
test.describe("Auth failure", () => {
test("watermark-text without token returns 401", async ({ request }) => {
const res = await request.post("/api/v1/tools/watermark-text", {
multipart: {
file: { name: "test.png", mimeType: "image/png", buffer: PNG_200x150 },
settings: JSON.stringify({
text: "TEST",
fontSize: 24,
color: "#ff0000",
opacity: 50,
position: "center",
}),
},
});
expect(res.status()).toBe(401);
});
test("compose without token returns 401", async ({ request }) => {
const { body, contentType } = buildMultipart(
[
{ name: "file", filename: "base.png", contentType: "image/png", buffer: PNG_200x150 },
{
name: "overlay",
filename: "overlay.webp",
contentType: "image/webp",
buffer: WEBP_50x50,
},
],
[{ name: "settings", value: JSON.stringify({ x: 0, y: 0, opacity: 100 }) }],
);
const res = await request.post("/api/v1/tools/compose", {
headers: { "Content-Type": contentType },
data: body,
});
expect(res.status()).toBe(401);
});
});