fix(nightly): de-flake matrix timeouts, exclude browser tool from fuzz (#349)

Systematic round - attack the failure classes, not one bug at a time:

- Flakiness (Coverage/Extended Matrix flipped green<->red): the generated
  format-matrix conversion tests hardcode a 30s/60s per-test timeout that the
  jobs' VITEST_TEST_TIMEOUT can't override. Heavy conversions under coverage
  instrumentation / full matrix intermittently exceed it. Raise to 120s.
- Schemathesis: html-to-image returns 503 when its headless browser isn't
  present (always, in the fuzz env) - same expected-unavailable class as the
  AI 501s. Exclude it from the fuzz (it can't be exercised without a browser).
- Baseline regen: bump timeout 60->120 min so it can finish now that the
  route sweep (#347) lets the visual specs pass instead of failing+retrying.
This commit is contained in:
SnapOtter
2026-06-25 00:14:45 +08:00
committed by GitHub
parent 078743d6b2
commit bbad953e79
6 changed files with 6 additions and 6 deletions
+1 -1
View File
@@ -338,7 +338,7 @@ jobs:
--url http://localhost:13490 \ --url http://localhost:13490 \
--checks not_a_server_error \ --checks not_a_server_error \
--include-path-regex "^/api/v1/(tools|health|info)" \ --include-path-regex "^/api/v1/(tools|health|info)" \
--exclude-path-regex "/(remove-background|upscale|blur-faces|erase-object|ocr|ocr-pdf|colorize|enhance-faces|noise-removal|smart-crop|red-eye-removal|restore-photo|passport-photo|transparency-fixer|ai-canvas-expand|transcribe-audio|auto-subtitles|background-replace|blur-background)(/[a-z-]+)?$" \ --exclude-path-regex "/(remove-background|upscale|html-to-image|blur-faces|erase-object|ocr|ocr-pdf|colorize|enhance-faces|noise-removal|smart-crop|red-eye-removal|restore-photo|passport-photo|transparency-fixer|ai-canvas-expand|transcribe-audio|auto-subtitles|background-replace|blur-background)(/[a-z-]+)?$" \
--max-examples 25 \ --max-examples 25 \
--report junit \ --report junit \
--report-dir st-report --report-dir st-report
@@ -11,7 +11,7 @@ jobs:
update-baselines: update-baselines:
name: Regenerate linux visual baselines name: Regenerate linux visual baselines
runs-on: ubuntu-latest runs-on: ubuntu-latest
timeout-minutes: 60 timeout-minutes: 120
# The e2e webServer (playwright.config.ts) creates an isolated database via # The e2e webServer (playwright.config.ts) creates an isolated database via
# tests/e2e-pg-create-db.cjs, so it needs Postgres + Redis on localhost. # tests/e2e-pg-create-db.cjs, so it needs Postgres + Redis on localhost.
# Without these services the webServer fails with "failed to create database". # Without these services the webServer fails with "failed to create database".
@@ -955,7 +955,7 @@ describe("Extended conversion targets (core formats)", () => {
if (fmt.name.toLowerCase() === target.format) continue; if (fmt.name.toLowerCase() === target.format) continue;
if (fmt.name === "AVIF" && target.format === "avif") continue; if (fmt.name === "AVIF" && target.format === "avif") continue;
const testTimeout = target.format === "avif" || target.format === "gif" ? 120_000 : 30_000; const testTimeout = target.format === "avif" || target.format === "gif" ? 120_000 : 120_000;
it(`${fmt.name} -> ${target.format}`, { timeout: testTimeout }, async () => { it(`${fmt.name} -> ${target.format}`, { timeout: testTimeout }, async () => {
const res = await callTool("convert", fmt, { format: target.format }); const res = await callTool("convert", fmt, { format: target.format });
if (!res) return; if (!res) return;
@@ -859,7 +859,7 @@ describe("SVG-to-raster", () => {
const SVG_OUTPUT_FORMATS = ["png", "jpg", "webp", "avif"] as const; const SVG_OUTPUT_FORMATS = ["png", "jpg", "webp", "avif"] as const;
for (const outFmt of SVG_OUTPUT_FORMATS) { for (const outFmt of SVG_OUTPUT_FORMATS) {
const testTimeout = outFmt === "avif" ? 120_000 : 30_000; const testTimeout = outFmt === "avif" ? 120_000 : 120_000;
it(`converts SVG to ${outFmt}`, { timeout: testTimeout }, async () => { it(`converts SVG to ${outFmt}`, { timeout: testTimeout }, async () => {
const fixturePath = fixtures.image.formats("svg"); const fixturePath = fixtures.image.formats("svg");
if (!existsSync(fixturePath)) return; if (!existsSync(fixturePath)) return;
@@ -704,7 +704,7 @@ describe("Cross-format conversion matrix", () => {
if (inputLower === outFmt) continue; if (inputLower === outFmt) continue;
if (inputLower === "jpeg" && outFmt === "jpg") continue; if (inputLower === "jpeg" && outFmt === "jpg") continue;
const testTimeout = outFmt === "avif" ? 120_000 : 60_000; const testTimeout = outFmt === "avif" ? 120_000 : 120_000;
it(`${fmt.name} -> ${outFmt}`, { timeout: testTimeout }, async () => { it(`${fmt.name} -> ${outFmt}`, { timeout: testTimeout }, async () => {
const fixturePath = join(fixtureDir.formats, fmt.file); const fixturePath = join(fixtureDir.formats, fmt.file);
if (!existsSync(fixturePath)) return; if (!existsSync(fixturePath)) return;
@@ -185,7 +185,7 @@ describe("New format support", () => {
for (const outFmt of OUTPUTS) { for (const outFmt of OUTPUTS) {
const inLower = input.name.toLowerCase(); const inLower = input.name.toLowerCase();
if (inLower === outFmt || (inLower === "jpeg" && outFmt === "jpg")) continue; if (inLower === outFmt || (inLower === "jpeg" && outFmt === "jpg")) continue;
const testTimeout = outFmt === "avif" ? 120_000 : 60_000; const testTimeout = outFmt === "avif" ? 120_000 : 120_000;
it(`converts ${input.name} to ${outFmt}`, { timeout: testTimeout }, async () => { it(`converts ${input.name} to ${outFmt}`, { timeout: testTimeout }, async () => {
const fileBuffer = readFileSync(join(fixtureDir.formats, input.file)); const fileBuffer = readFileSync(join(fixtureDir.formats, input.file));
const { body, contentType } = createMultipartPayload([ const { body, contentType } = createMultipartPayload([