Files
SnapOtter/tests/integration/generated/format-matrix-3.test.ts
T
SnapOtterandGitHub f1ec3beaf7 test: settle 202 jobs instead of returning without asserting (#652)
A 202 means the sync window expired while the job was still running. Tests treated it as a terminal pass: `if (isAsyncFallback(res)) return;` checked the envelope and returned, asserting nothing about the outcome and leaving the job running into the next test, which is the leak cancelAcceptedJobAndWait exists to prevent.

Because the window only expires under load, coverage tracked runner load. On CI 44 tests took this path and verified nothing; the same tests on a dev machine asserted in full (one measured 6.4s locally against 31s on CI).

settleAsyncFallback waits for a terminal state and asserts the job finished, and that a failure carries a message rather than being a crash. A clean failure stays valid, since the exotic-format fixtures are meant to be rejected. All 82 call sites moved over.

per-fork-env no longer floors SYNC_WAIT_MS, so forcing it to 0 drives every request through its 202 path. 570 tests were validated that way and matched their normal-window results exactly.

The 29-34s band dropped from 44 tests (23.4% of test time) to 6 (3.0%). Total test time rose 5.8% and CI wall went 12.8 to 13.1 min: the forks were doing real work during that wait, so this buys determinism, not speed. Per-shard totals unchanged at 9903 tests, 9435 passed, 468 skipped.
2026-07-27 12:16:48 +08:00

269 lines
8.9 KiB
TypeScript

/**
* Cross-format matrix, part 3 of 4 (see format-matrix.shared.ts).
*
* Holds the multipage-TIFF, exotic-format resilience, enhancement-analysis and
* strip-metadata-inspection blocks. Split out of format-matrix.test.ts because
* vitest shards by file and runs a file's tests serially in one fork.
*/
import { existsSync, readFileSync } from "node:fs";
import { join } from "node:path";
import { apiToolPath } from "@snapotter/shared";
import { describe, expect, it } from "vitest";
import { fixtureDir, fixtures } from "../../fixtures/index.js";
import { settleAsyncFallback } from "../settle-job.js";
import { createMultipartPayload } from "../test-server.js";
import {
adminToken,
app,
buildPayload,
FORMAT_SAMPLES,
setupMatrixApp,
TOOLS,
} from "./format-matrix.shared.js";
setupMatrixApp();
// ---------------------------------------------------------------------------
// Edge-case matrix: multipage TIFF
// ---------------------------------------------------------------------------
describe("Multipage TIFF handling", () => {
const multipagePath = fixtures.image.multipageTiff;
for (const tool of TOOLS) {
it(`${tool.label} handles multipage TIFF`, async () => {
if (!existsSync(multipagePath)) return;
const buffer = readFileSync(multipagePath);
const { body: payload, contentType } = createMultipartPayload([
{
name: "file",
filename: "multipage.tiff",
contentType: "image/tiff",
content: buffer,
},
...(tool.responseType !== "info"
? [{ name: "settings", content: JSON.stringify(tool.settings) }]
: []),
]);
const res = await app.inject({
method: "POST",
url: apiToolPath(tool.id),
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body: payload,
});
// Multipage TIFF should either succeed or return a clean error
expect([200, 202, 400, 422]).toContain(res.statusCode);
if (res.statusCode === 200) {
const body = JSON.parse(res.body);
if (tool.responseType === "info") {
expect(body.width).toBeGreaterThan(0);
expect(body.height).toBeGreaterThan(0);
// Multipage TIFFs should report pages > 1
if (body.pages !== undefined) {
expect(body.pages).toBeGreaterThanOrEqual(1);
}
} else if (tool.responseType === "base64") {
expect(Array.isArray(body.results)).toBe(true);
} else if (tool.responseType === "palette") {
expect(Array.isArray(body.colors)).toBe(true);
expect(body.count).toBeGreaterThan(0);
} else if (tool.responseType === "pdf") {
expect(body.downloadUrl).toBeDefined();
expect(body.processedSize).toBeGreaterThan(0);
expect(body.pages).toBeGreaterThanOrEqual(1);
} else {
expect(body.downloadUrl).toBeDefined();
expect(body.processedSize).toBeGreaterThan(0);
}
} else {
const body = JSON.parse(res.body);
expect(body.error).toBeDefined();
}
});
}
});
// ---------------------------------------------------------------------------
// Error resilience: exotic formats must return clean errors, never crash
// ---------------------------------------------------------------------------
describe("Exotic format error resilience", () => {
const EXOTIC_FORMATS = FORMAT_SAMPLES.filter((f) => f.needsCliDecoder);
// Tools that actually process the image (not just read metadata)
const PROCESSING_TOOLS = TOOLS.filter(
(t) =>
t.responseType === "download" || t.responseType === "palette" || t.responseType === "pdf",
);
for (const fmt of EXOTIC_FORMATS) {
for (const tool of PROCESSING_TOOLS) {
it(`${fmt.name} + ${tool.label}: returns JSON error (no crash)`, {
timeout: 120_000,
}, async () => {
const fixturePath = join(fixtureDir.formats, fmt.file);
if (!existsSync(fixturePath)) return;
const buffer = readFileSync(fixturePath);
const { body: payload, contentType } = buildPayload(fmt, tool, buffer);
const res = await app.inject({
method: "POST",
url: apiToolPath(tool.id),
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body: payload,
});
// Must not crash (500) — either succeed or return a clean error
if (await settleAsyncFallback(res)) return;
expect(res.statusCode).not.toBe(500);
expect([200, 202, 400, 422]).toContain(res.statusCode);
// Response must always be valid JSON
const body = JSON.parse(res.body);
if (res.statusCode >= 400) {
expect(body.error).toBeDefined();
expect(typeof body.error).toBe("string");
expect(body.error.length).toBeGreaterThan(0);
}
});
}
}
});
// ---------------------------------------------------------------------------
// Image enhancement analysis: dedicated /analyze endpoint
// ---------------------------------------------------------------------------
describe("Image enhancement analysis across formats", () => {
// Core formats that Sharp can read natively
const ANALYZABLE_FORMATS = FORMAT_SAMPLES.filter(
(f) => !f.needsCliDecoder && !f.needsHeifDecoder && !f.mayFailValidation,
);
for (const fmt of ANALYZABLE_FORMATS) {
it(`analyzes ${fmt.name} and returns correction recommendations`, async () => {
const fixturePath = join(fixtureDir.formats, fmt.file);
if (!existsSync(fixturePath)) return;
const buffer = readFileSync(fixturePath);
const { body: payload, contentType } = createMultipartPayload([
{
name: "file",
filename: fmt.file,
contentType: fmt.mime,
content: buffer,
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/image/image-enhancement/analyze",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body: payload,
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
// Analysis should return corrections object
expect(body.corrections).toBeDefined();
expect(typeof body.corrections).toBe("object");
});
}
// Exotic formats: should not crash, return clean error or succeed
const EXOTIC_FORMATS = FORMAT_SAMPLES.filter((f) => f.needsCliDecoder);
for (const fmt of EXOTIC_FORMATS) {
it(`${fmt.name} analyze: returns clean response (no crash)`, async () => {
const fixturePath = join(fixtureDir.formats, fmt.file);
if (!existsSync(fixturePath)) return;
const buffer = readFileSync(fixturePath);
const { body: payload, contentType } = createMultipartPayload([
{
name: "file",
filename: fmt.file,
contentType: fmt.mime,
content: buffer,
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/image/image-enhancement/analyze",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body: payload,
});
expect(res.statusCode).not.toBe(500);
expect([200, 202, 400, 422]).toContain(res.statusCode);
const body = JSON.parse(res.body);
if (res.statusCode >= 400) {
expect(body.error).toBeDefined();
expect(typeof body.error).toBe("string");
}
});
}
});
// ---------------------------------------------------------------------------
// Strip-metadata inspect: dedicated /inspect endpoint
// ---------------------------------------------------------------------------
describe("Strip-metadata inspection across formats", () => {
// Core formats that Sharp can read natively
const INSPECTABLE_FORMATS = FORMAT_SAMPLES.filter(
(f) => !f.needsCliDecoder && !f.needsHeifDecoder && !f.mayFailValidation,
);
for (const fmt of INSPECTABLE_FORMATS) {
it(`inspects ${fmt.name} metadata`, async () => {
const fixturePath = join(fixtureDir.formats, fmt.file);
if (!existsSync(fixturePath)) return;
const buffer = readFileSync(fixturePath);
const { body: payload, contentType } = createMultipartPayload([
{
name: "file",
filename: fmt.file,
contentType: fmt.mime,
content: buffer,
},
]);
const res = await app.inject({
method: "POST",
url: "/api/v1/tools/image/strip-metadata/inspect",
headers: {
authorization: `Bearer ${adminToken}`,
"content-type": contentType,
},
body: payload,
});
expect(res.statusCode).toBe(200);
const body = JSON.parse(res.body);
expect(body.filename).toBeDefined();
expect(body.fileSize).toBeGreaterThan(0);
});
}
});