fix(ci): free-tests lane actually runs the make-pdf e2e gates

The 9 make-pdf/test/e2e gate tests probe make-pdf/dist/pdf,
browse/dist/browse, and the diagram-render bundle, then self-skip when
absent. The required free-tests lane never built any of them, so the
gates silently skipped on Linux for their entire life (verified: 9 of
14 skip, exit 0). make-pdf-gate.yml's justification for deleting its
Linux leg claimed the free lane covered this — it didn't.

- new build:gates script: exactly the three artifacts the gates probe
  (full bun run build compiles five binaries; ~60-90s tax on the only
  required check is not warranted)
- free-tests.yml: build:gates step + poppler-utils +
  fonts-noto-color-emoji (fonts must precede the first browse daemon
  launch — Chromium snapshots fontconfig at startup; verified live:
  a warm daemon renders tofu, a fresh one embeds NotoColorEmoji)
- make-pdf/test/e2e/ci-prereqs.test.ts: GSTACK_EXPECT_BINARIES=1 (set
  by the workflow) inverts the skip polarity in CI — dropping the
  build step or poppler fails the lane instead of re-opening the
  silent-skip hole

Pre-flight: all 9 gates green on Linux locally.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-08-29 04:36:38 +00:00
co-authored by Claude Fable 5
parent 394db326f2
commit 29d94a505d
4 changed files with 73 additions and 7 deletions
+44
View File
@@ -0,0 +1,44 @@
/**
* CI tripwire for the silent-skip class (#audit-2026-08: the 9 make-pdf e2e
* gate tests self-skipped on Linux for their entire life because the
* free-tests lane never built the binaries they probe — exit 0, no signal).
*
* Every sibling gate file guards itself with test.skipIf(!prerequisitesAvailable()),
* which is correct for LOCAL runs (a contributor without a build shouldn't
* fail) but is exactly how CI green stopped meaning "ran". This file inverts
* the polarity in CI: when GSTACK_EXPECT_BINARIES=1 (set by free-tests.yml's
* "Run free suite" step), the prerequisites are ASSERTED, so dropping the
* gate-build step or poppler from the workflow fails the required lane
* instead of quietly skipping the gates.
*
* Not set locally → the whole file self-skips, same as the gates.
*/
import { describe, expect, test } from "bun:test";
import * as fs from "node:fs";
import * as path from "node:path";
import { resolvePdftotext } from "../../src/pdftotext";
const ROOT = path.resolve(__dirname, "../../..");
const EXPECT_BINARIES = process.env.GSTACK_EXPECT_BINARIES === "1";
describe("gate prerequisites (CI tripwire)", () => {
test.skipIf(!EXPECT_BINARIES)("gate artifacts and tools exist when the lane promises them", () => {
const missing: string[] = [];
for (const rel of [
"make-pdf/dist/pdf",
"browse/dist/browse",
"lib/diagram-render/dist/diagram-render.html",
]) {
if (!fs.existsSync(path.join(ROOT, rel))) missing.push(rel);
}
try {
resolvePdftotext();
} catch (err: any) {
missing.push(`pdftotext (${err?.message ?? "unresolvable"})`);
}
// One assertion naming everything missing beats N opaque ones: the fix
// is always "restore the build:gates step / apt packages in free-tests.yml".
expect(missing).toEqual([]);
});
});