mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix(fuzz): bound settings-fuzz inputs, cap split tiles, budget the slow codecs, steady flakes (#696)
Follow-up to #695, greening the last nightly jobs it exposed. split gains a 400-tile product cap (a 100x100 split was a 10,000-file ZIP and ~20s of work). The settings-fuzz bounds its image inputs to 640px and gives the tools whose cost is output-driven (border, gif-tools, split) or codec-driven (heic/webp-to-avif) honest per-case budgets, since #649's settle-job wiring made every case wait for the real encode. The delete-team serial spec waits with toHaveCount(0) so the success toast can't trip strict mode, and type-to-search allows a route announcer's programmatic reading focus so it works on WebKit. Confirmed on a nightly dispatch: Extended Matrix (all 4 shards), Serial Bucket, Cross-Browser, and Coverage all green; Docker Container E2E's failures were GitHub runner reclamation (exit 137, tests passing throughout), which cleared on the #695 dispatch and is unaffected by this change.
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { PYTHON_SIDECAR_TOOLS, TOOLS } from "@snapotter/shared";
|
||||
import fc from "fast-check";
|
||||
import sharp from "sharp";
|
||||
import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
||||
import type { z } from "zod";
|
||||
import { ZodFastCheck } from "zod-fast-check";
|
||||
@@ -39,6 +40,44 @@ const FUZZ_CONFIG = parseFuzzConfig(FUZZ ? process.env : {});
|
||||
const REQUIRE_AI_FEATURES = process.env.REQUIRE_AI_FEATURES === "1";
|
||||
const FIXTURE_INDEX = buildGeneratedFixtureIndex(generatedFixtureDirectories());
|
||||
|
||||
// This lane checks that valid settings never crash a tool, not how fast a codec
|
||||
// is on a big image. Several image tools scale their work with the input
|
||||
// (AVIF/JXL encodes, gif upscales, per-tile splits), so a multi-megapixel
|
||||
// fixture makes cases legitimately run many seconds and time out without ever
|
||||
// crashing. Bound image inputs to a small canvas; the same settings paths run
|
||||
// in a fraction of the time. The format matrix still covers full-size inputs on
|
||||
// its own path. Formats sharp cannot re-encode (heic, jxl, raw) are left as-is
|
||||
// and covered by their per-tool fuzz budgets instead.
|
||||
const FUZZ_MAX_IMAGE_DIMENSION = 640;
|
||||
|
||||
async function boundFuzzImageInputs(
|
||||
inputs: Awaited<ReturnType<typeof buildGeneratedProcessInputs>>,
|
||||
modality?: string,
|
||||
): Promise<typeof inputs> {
|
||||
if (modality !== "image") return inputs;
|
||||
return Promise.all(
|
||||
inputs.map(async (input) => {
|
||||
try {
|
||||
const image = sharp(input.buffer, { animated: true });
|
||||
const meta = await image.metadata();
|
||||
const longest = Math.max(meta.width ?? 0, meta.height ?? 0);
|
||||
if (longest <= FUZZ_MAX_IMAGE_DIMENSION) return input;
|
||||
const buffer = await image
|
||||
.resize({
|
||||
width: FUZZ_MAX_IMAGE_DIMENSION,
|
||||
height: FUZZ_MAX_IMAGE_DIMENSION,
|
||||
fit: "inside",
|
||||
withoutEnlargement: true,
|
||||
})
|
||||
.toBuffer();
|
||||
return { ...input, buffer };
|
||||
} catch {
|
||||
return input;
|
||||
}
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
describe.skipIf(!FUZZ)("settings fuzz (property-based)", () => {
|
||||
let testApp: TestApp;
|
||||
|
||||
@@ -81,7 +120,8 @@ describe.skipIf(!FUZZ)("settings fuzz (property-based)", () => {
|
||||
if (fixtures.length === 0) {
|
||||
return context.skip(`${toolId}: no compatible generated fixture`);
|
||||
}
|
||||
const inputs = await buildGeneratedProcessInputs(fixtures, config, tool.modality);
|
||||
const rawInputs = await buildGeneratedProcessInputs(fixtures, config, tool.modality);
|
||||
const inputs = await boundFuzzImageInputs(rawInputs, tool.modality);
|
||||
const accounting = new GeneratedCaseAccounting(toolId, {
|
||||
expectedAttempts: FUZZ_CONFIG.runs + 1,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user