test(catalog): stop the yield guard from dying on production's build budget (#13906)

tests/unit/9147-catalog-eventloop-yield.test.ts exists to prove the /v1/models
builder yields to the event loop while assembling a catalog-scale dataset. It
asserts res.status === 200 first, and only then the two checks that carry the
invariant: the max event-loop gap and the traversal to the last seeded model.

The case did not set CATALOG_BUILD_TIMEOUT_MS, so it inherited production's 8s
cold-path budget. When the seeded build overruns that on a loaded runner,
getUnifiedModelsResponse answers 503 catalog_build_timeout and the status check
fails BEFORE either real assertion runs — the guard goes silently dead exactly
when the machine is under the load that would make a pin most visible. CI hit
it at 8350ms, right at the bound.

Measured on the release tip, pristine file, 3 runs at load ~21: 2 pass with max
gaps of 247ms and 180ms, 1 fails with 503 !== 200 — roughly a 1-in-3 flake, and
the flake has nothing to do with yielding.

Pinning a 120s budget lets the invariant be evaluated. The 800ms gap bound is
untouched, and with the budget pinned the builder measures 261ms (idle) to
790ms (loaded) against it — still failing a true pin, which is seconds.
Build-latency budgeting is a separate concern from this case.

Refs #12732
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-09-17 10:46:17 -03:00
committed by GitHub
parent 209112df36
commit d032431a5f

View File

@@ -7,6 +7,16 @@ import path from "node:path";
const TEST_DATA_DIR = fs.mkdtempSync(path.join(os.tmpdir(), "omniroute-9147-"));
process.env.DATA_DIR = TEST_DATA_DIR;
process.env.API_KEY_SECRET = process.env.API_KEY_SECRET || "catalog-9147-test-secret";
// This case measures whether the builder YIELDS, not how fast it finishes. The
// production cold-path budget (CATALOG_BUILD_TIMEOUT_MS, 8s) is not the subject:
// when the seeded catalog-scale build overruns it, getUnifiedModelsResponse
// answers 503 `catalog_build_timeout` and the two assertions that actually guard
// the invariant — the max event-loop gap and the traversal to the last seeded
// model — are never reached, because the status check precedes them. That is how
// this guard went silently dead on loaded runners (CI observed 8350ms, right at
// the bound). Pin a budget far above any healthy build so the yield invariant is
// evaluated; build-latency budgeting is a separate concern from this test.
process.env.CATALOG_BUILD_TIMEOUT_MS = "120000";
const core = await import("../../src/lib/db/core.ts");
const apiKeysDb = await import("../../src/lib/db/apiKeys.ts");