mirror of
https://github.com/snapotter-hq/SnapOtter.git
synced 2026-08-03 07:46:42 +02:00
fix: repair docker validation QA tooling, dispatcher crash-accounting, and image-enhancement RAW hang (#391)
Found and fixed during a full local Docker build validation (amd64/arm64, all four fleet targets, AI bundle installs, QA harness) and the follow-up bug sweep requested afterward. None of the affected scripts run in CI, so these had been silently broken indefinitely. - docker/feature-manifest.json: pythonVersion was a flat "3.11", but the amd64 base (Ubuntu 24.04) ships Python 3.12 while arm64 (Debian bookworm) ships 3.11. Changed to a per-arch object matching the file's existing convention. - tests/qa/api-sweep.mts and verify-ai.mts: bare "@snapotter/shared" import can't resolve since tests/ is not a pnpm workspace member, making both silently unrunnable via their own documented command on any fresh checkout. Switched to a relative import. - tests/qa/generate-ledger.mts: wrote to docs/qa/ without creating the directory first; docs/ is gitignored except COMMUNITY_GUIDE.md, so a fresh checkout threw ENOENT. - Seven QA Playwright spec files (input-preview, settings, settings-extended, multifile, output-preview, pipeline-ui, smoke) had ~115 fixture() calls using directory names that don't exist. Resolved every call programmatically against the real fixture tree. - packages/ai/src/bridge.ts: AI dispatcher restart (happens on every bundle install) was falsely counted as a crash, risking permanent dispatcher disable after enough legitimate restarts within the crash window. Added a shuttingDown flag checked at all three recordCrash() call sites. - packages/image-engine/src/operations/auto-enhance.ts: image-enhancement hung 40+ seconds on large RAW photos (confirmed on a real 20.2MP file) in Sharp's .clahe() step, whose cost scales with total pixel count regardless of tile size. Added a 16-megapixel cap above which CLAHE is skipped; verified against the real file (40+s -> 2.0s) with no regression to other RAW formats or normal-sized images. Fixing this surfaced a second, smaller bug where the saturation step's CLAHE compensation boost was keyed off the raw toggle instead of whether CLAHE actually ran. - Two QA-harness robustness gaps closed per "fix everything, even the small bugs": the passport-photo/erase-object input-preview tests now skip cleanly with a clear reason on a container without their AI bundle installed, and docker-compose.qa.yml's hardcoded project/container name (the actual root cause of a mid-validation container swap between two concurrent sessions) is now parameterized via QA_PROJECT_NAME. Full validation report is local-only per repo convention.
This commit is contained in:
@@ -329,6 +329,89 @@ describe("applyCorrections pipeline (CLAHE + normalise + gamma)", () => {
|
||||
|
||||
expect(Buffer.compare(lowBuf, highBuf)).not.toBe(0);
|
||||
});
|
||||
|
||||
it("skips CLAHE above MAX_CLAHE_PIXELS (regression: 20MP RAW photo hung 40+s on this step alone)", async () => {
|
||||
const corrections = {
|
||||
brightness: 20,
|
||||
contrast: 20,
|
||||
temperature: 0,
|
||||
saturation: 0,
|
||||
sharpness: 0,
|
||||
denoise: 0,
|
||||
};
|
||||
// A real 5504x3672 (20.2MP) photo triggered this; reuse those dimensions
|
||||
// as the reported imageSize so the pixel-count gate is exercised without
|
||||
// needing to decode an actual 20MP buffer in a unit test.
|
||||
const overCap = applyCorrections(
|
||||
sharp(PNG_200x150),
|
||||
corrections,
|
||||
"auto",
|
||||
50,
|
||||
{},
|
||||
{
|
||||
width: 5504,
|
||||
height: 3672,
|
||||
},
|
||||
);
|
||||
const overCapBuf = await overCap.toBuffer();
|
||||
|
||||
const claheDisabled = applyCorrections(
|
||||
sharp(PNG_200x150),
|
||||
corrections,
|
||||
"auto",
|
||||
50,
|
||||
{ contrast: false },
|
||||
{ width: 5504, height: 3672 },
|
||||
);
|
||||
const claheDisabledBuf = await claheDisabled.toBuffer();
|
||||
|
||||
// Skipping CLAHE via the size cap must produce byte-identical output to
|
||||
// skipping it via the explicit toggle -- proof the cap actually took effect.
|
||||
expect(Buffer.compare(overCapBuf, claheDisabledBuf)).toBe(0);
|
||||
});
|
||||
|
||||
it("still applies CLAHE at or below MAX_CLAHE_PIXELS", async () => {
|
||||
const corrections = {
|
||||
brightness: 20,
|
||||
contrast: 20,
|
||||
temperature: 0,
|
||||
saturation: 0,
|
||||
sharpness: 0,
|
||||
denoise: 0,
|
||||
};
|
||||
// Use the real buffer's actual 200x150 dimensions (30,000 px, well under
|
||||
// the 16M cap) -- CLAHE's tile size is derived from imageSize, and Sharp
|
||||
// rejects a tile window larger than the real underlying image, so a fake
|
||||
// imageSize far bigger than the actual small test buffer isn't valid here
|
||||
// (that's exactly what the "above the cap" test above uses instead,
|
||||
// where CLAHE never actually runs so the mismatch never surfaces).
|
||||
const underCap = applyCorrections(
|
||||
sharp(PNG_200x150),
|
||||
corrections,
|
||||
"auto",
|
||||
50,
|
||||
{},
|
||||
{
|
||||
width: 200,
|
||||
height: 150,
|
||||
},
|
||||
);
|
||||
const underCapBuf = await underCap.toBuffer();
|
||||
|
||||
const claheDisabled = applyCorrections(
|
||||
sharp(PNG_200x150),
|
||||
corrections,
|
||||
"auto",
|
||||
50,
|
||||
{ contrast: false },
|
||||
{ width: 200, height: 150 },
|
||||
);
|
||||
const claheDisabledBuf = await claheDisabled.toBuffer();
|
||||
|
||||
// Under the cap, CLAHE should still run -- output must differ from the
|
||||
// contrast-disabled baseline.
|
||||
expect(Buffer.compare(underCapBuf, claheDisabledBuf)).not.toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe("auto-enhance edge cases", () => {
|
||||
|
||||
Reference in New Issue
Block a user