Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions scripts/guard-push.mjs
Original file line numberDiff line numberDiff line change
Expand Up@@ -414,13 +414,18 @@ export function defaultRunsFetch(branch, exec = execFileSync) {
export function inFlightCiGuard(
branches,
_ranges = [],
{ prViewer = defaultPrView, runFetcher = defaultRunsFetch } = {},
// `ghAvailable` is injectable for the same reason `prViewer`/`runFetcher` are:
// without it this "unit" test still spawned the real `gh` binary just to ask
// whether it exists, so the test's runtime was hostage to an external process.
// On a loaded machine `gh --version` was measured at 97 s, blowing vitest's 30 s
// limit and failing a test that injects every other dependency.
{ prViewer = defaultPrView, runFetcher = defaultRunsFetch, ghAvailable = ghIsAvailable } = {},
) {
void _ranges;
if (process.env.SKIP_IN_FLIGHT_CI_GUARD === "1") {
return { name: "in-flight-ci", ok: true, skipped: "SKIP_IN_FLIGHT_CI_GUARD=1" };
}
if (!ghIsAvailable()) {
if (!ghAvailable()) {
return { name: "in-flight-ci", ok: true, note: "gh not available — in-flight CI check skipped (fail-open)" };
}

Expand Down
35 changes: 35 additions & 0 deletions tests/guard-push.test.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -464,6 +464,7 @@ describe("in-flight CI push guard (#HSSHRG)", () => {
const result = inFlightCiGuard(["claude/my-fix"], [], {
prViewer: () => ({ state: "OPEN", number: 77 }),
runFetcher: () => runs,
ghAvailable: () => true,
});
expect(result.ok).toBe(false);
expect(result.message).toContain("PR #77 on claude/my-fix has required CI run(s) currently IN-FLIGHT");
Expand All@@ -472,13 +473,47 @@ describe("in-flight CI push guard (#HSSHRG)", () => {
expect(result.message).toContain("#HSSHRG");
});

it("inFlightCiGuard fails open when gh is unavailable, without consulting the PR", () => {
let prViewerCalls = 0;
const result = inFlightCiGuard(["claude/my-fix"], [], {
prViewer: () => {
prViewerCalls += 1;
return { state: "OPEN", number: 77 };
},
runFetcher: () => [{ databaseId: 555, name: "CI", status: "in_progress" }],
ghAvailable: () => false,
});
expect(result.ok).toBe(true);
expect(result.note).toContain("gh not available");
expect(prViewerCalls).toBe(0);
});

it("inFlightCiGuard never spawns a process when every dependency is injected (#HSSHRG)", () => {
// Regression guard: the availability probe used to call the real `gh` binary
// even here. `gh --version` was measured at 97 s on a loaded machine, which
// timed this suite out at vitest's 30 s limit — a unit test must not be
// hostage to an external process it never asked for.
let spawned = 0;
const result = inFlightCiGuard(["claude/my-fix"], [], {
prViewer: () => ({ state: "OPEN", number: 77 }),
runFetcher: () => [{ databaseId: 555, name: "CI", status: "in_progress" }],
ghAvailable: () => {
spawned += 1;
return true;
},
});
expect(spawned).toBe(1);
expect(result.ok).toBe(false);
});

it("inFlightCiGuard skips when SKIP_IN_FLIGHT_CI_GUARD=1 is set", () => {
const previous = process.env.SKIP_IN_FLIGHT_CI_GUARD;
process.env.SKIP_IN_FLIGHT_CI_GUARD = "1";
try {
const result = inFlightCiGuard(["claude/my-fix"], [], {
prViewer: () => ({ state: "OPEN", number: 77 }),
runFetcher: () => [{ databaseId: 555, name: "CI", status: "in_progress" }],
ghAvailable: () => true,
});
expect(result.ok).toBe(true);
expect(result.skipped).toBe("SKIP_IN_FLIGHT_CI_GUARD=1");
Expand Down
Loading