test: expand API and GUI test coverage across all tools

Add ~500 new E2E tests and ~300 new integration tests covering:

- 24 new GUI E2E specs: navigation, responsive layout, keyboard shortcuts,
  tool UI for all 35 non-AI tools, batch/pipeline workflows, settings/RBAC,
  visual regression, accessibility, and performance budgets
- 3 new E2E-Docker specs: batch workflows, advanced pipelines, cross-format
- 1 new adversarial integration test: memory pressure, corrupted files,
  unicode filenames, extreme dimensions, pipeline/batch edge cases
- 29 expanded integration test files: HEIC/HEIF input, large files, parameter
  boundaries, batch processing, format edge cases across all tools
- Cross-format matrix expanded: 641 tests covering every tool x 18 formats
- AI bridge unit tests expanded: lifecycle, tool modules, error propagation
- Unit test gaps filled: analytics, tool-registry, web stores

Also fixes:
- vitest.config.ts: exclude e2e-docs and e2e-landing from Vitest runner
- AI E2E specs: add sidecar health check to skip gracefully when Python
  AI backend is not running instead of timing out
This commit is contained in:
SnapOtter
2026-04-29 01:39:25 +08:00
parent 4acec0846c
commit 03f82567d0
74 changed files with 15893 additions and 14 deletions
+151
View File
@@ -301,3 +301,154 @@ describe("Combined sharpening + denoise", () => {
expect(res.statusCode).toBe(200);
});
});
// ── Unsharp mask with denoise ──────────────────────────────────
describe("Unsharp mask + denoise", () => {
it("applies unsharp-mask with medium denoise", async () => {
const res = await postTool({
method: "unsharp-mask",
amount: 150,
radius: 2.0,
threshold: 5,
denoise: "medium",
});
expect(res.statusCode).toBe(200);
const result = JSON.parse(res.body);
expect(result.downloadUrl).toBeDefined();
});
});
// ── Unauthenticated request ────────────────────────────────────
describe("Authentication", () => {
it("rejects unauthenticated request", async () => {
const { body: payload, contentType } = makePayload({});
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/sharpening",
payload,
headers: { "content-type": contentType },
});
expect(res.statusCode).toBe(401);
});
});
// ── Boundary parameters for adaptive ───────────────────────────
describe("Adaptive boundary parameters", () => {
it("uses minimum sigma (0.5)", async () => {
const res = await postTool({ method: "adaptive", sigma: 0.5 });
expect(res.statusCode).toBe(200);
});
it("uses maximum sigma (10)", async () => {
const res = await postTool({ method: "adaptive", sigma: 10 });
expect(res.statusCode).toBe(200);
});
it("uses maximum m1, m2, x1, y2, y3 values", async () => {
const res = await postTool({
method: "adaptive",
sigma: 2.0,
m1: 10,
m2: 20,
x1: 10,
y2: 50,
y3: 50,
});
expect(res.statusCode).toBe(200);
});
});
// ── Boundary parameters for unsharp mask ───────────────────────
describe("Unsharp mask boundary parameters", () => {
it("uses maximum amount (1000)", async () => {
const res = await postTool({
method: "unsharp-mask",
amount: 1000,
radius: 1.0,
threshold: 0,
});
expect(res.statusCode).toBe(200);
});
it("uses minimum radius (0.1)", async () => {
const res = await postTool({
method: "unsharp-mask",
amount: 100,
radius: 0.1,
});
expect(res.statusCode).toBe(200);
});
it("uses maximum radius (5)", async () => {
const res = await postTool({
method: "unsharp-mask",
amount: 100,
radius: 5.0,
});
expect(res.statusCode).toBe(200);
});
it("uses maximum threshold (255)", async () => {
const res = await postTool({
method: "unsharp-mask",
threshold: 255,
});
expect(res.statusCode).toBe(200);
});
it("rejects amount out of range (>1000)", async () => {
const res = await postTool({ method: "unsharp-mask", amount: 1001 });
expect(res.statusCode).toBe(400);
});
});
// ── High-pass boundary parameters ──────────────────────────────
describe("High-pass boundary parameters", () => {
it("uses minimum strength (0)", async () => {
const res = await postTool({ method: "high-pass", strength: 0, kernelSize: 3 });
expect(res.statusCode).toBe(200);
});
it("uses maximum strength (100)", async () => {
const res = await postTool({ method: "high-pass", strength: 100, kernelSize: 3 });
expect(res.statusCode).toBe(200);
});
it("rejects strength out of range (>100)", async () => {
const res = await postTool({ method: "high-pass", strength: 101 });
expect(res.statusCode).toBe(400);
});
});
// ── Denoise boundary: off ──────────────────────────────────────
describe("Denoise off", () => {
it("explicitly sets denoise to off", async () => {
const res = await postTool({ denoise: "off" });
expect(res.statusCode).toBe(200);
});
});
// ── HEIC with unsharp-mask method ──────────────────────────────
describe("HEIC with different methods", () => {
it("processes HEIC with unsharp-mask", async () => {
const HEIC = readFileSync(join(FIXTURES, "test-200x150.heic"));
const res = await postTool(
{ method: "unsharp-mask", amount: 200 },
HEIC,
"photo.heic",
"image/heic",
);
expect(res.statusCode).toBe(200);
});
it("processes HEIC with high-pass", async () => {
const HEIC = readFileSync(join(FIXTURES, "test-200x150.heic"));
const res = await postTool(
{ method: "high-pass", strength: 60 },
HEIC,
"photo.heic",
"image/heic",
);
expect(res.statusCode).toBe(200);
});
});