test(pr): serve fake GitHub in-process for Node PR helpers

Use one fixture handler for the fake GitHub CLI and fixture-scoped Node
preload, avoiding repeated Node startup for synchronous fake-gh reads.
Real Git operations and process supervision keep their native boundaries.

In a 4-CPU Linux container with Node 24.21.0, all 20 importer files passed
728/728 tests in both variants. Suite wall fell 36.4%, from 1,070.10s
(17m50s) to 680.06s (11m20s); the main-drift settlement case fell from
16.213s to 7.096s under its unchanged 20-second deadline.

Production code, deadlines, test cases and assertions are unchanged.
This reduces but does not eliminate load-sensitive deadline risk; it is
not claimed as a complete flake fix. The loaded comparison was incomplete.

Validation: full Linux importer comparison, check-changed, four root test
type shards, formatting and boundary lint, Knip, source-contract checks,
and independent P2 review.
This commit is contained in:
Peter Steinberger 2026-09-29 19:04:44 -07:00
parent 25afb0c390
commit 12253cc519
No known key found for this signature in database
2 changed files with 108 additions and 9 deletions

View file

@ -0,0 +1,99 @@
/** Keep fake GitHub responses in the requesting Node helper; real Git stays native. */
export function createMergeGhFixturePrograms(body: string) {
return {
cli: String.raw`
"use strict";
const nativeFs = require("node:fs");
const { execFileSync, spawnSync } = require("node:child_process");
const { createHash } = require("node:crypto");
const { format } = require("node:util");
const hostProcess = process;
module.exports = function runGh(route, args, options = {}) {
let stdout = "", stderr = "", status = 0;
const stopped = {};
const process = {
env: options.env ?? hostProcess.env,
execArgv: hostProcess.execArgv,
kill: (pid, signal) => hostProcess.kill(pid, signal),
stdout: { write: (value) => { stdout += value; return true; } },
stderr: { write: (value) => { stderr += value; return true; } },
exit(code = 0) { status = code; throw stopped; },
};
const console = {
log: (...values) => { stdout += format(...values) + "\n"; },
error: (...values) => { stderr += format(...values) + "\n"; },
};
const fs = {
...nativeFs,
readFileSync(file, encoding) {
if (file !== 0 || options.input === undefined) return nativeFs.readFileSync(file, encoding);
const bytes = Buffer.from(options.input);
const selected = typeof encoding === "string" ? encoding : encoding?.encoding;
return selected ? bytes.toString(selected) : bytes;
},
};
try {
${body}
} catch (error) {
if (error !== stopped) { status = 1; stderr += String(error?.stack ?? error) + "\n"; }
}
return { status, stdout, stderr };
};
if (require.main === module) {
const [route, ...args] = process.argv.slice(2);
const result = module.exports(route, args);
process.stdout.write(result.stdout);
process.stderr.write(result.stderr);
process.exit(result.status);
}
`,
preload: String.raw`
"use strict";
const childProcess = require("node:child_process");
const fs = require("node:fs");
const path = require("node:path");
const { syncBuiltinESMExports } = require("node:module");
const nativeExecFileSync = childProcess.execFileSync;
function resolvesToFixture(file, options) {
const env = options.env ?? process.env;
const cwd = options.cwd ?? process.cwd();
if (file.includes("/")) return path.resolve(cwd, file) === env.FIXTURE_GH_BIN;
if (file !== "gh") return false;
for (const directory of (env.PATH ?? "").split(path.delimiter)) {
const candidate = path.resolve(cwd, directory, file);
try { fs.accessSync(candidate, fs.constants.X_OK); } catch { continue; }
return candidate === env.FIXTURE_GH_BIN;
}
return false;
}
childProcess.execFileSync = function(file, args, options = {}) {
// Other child boundaries, working directories and stream shapes remain native.
if (typeof file !== "string" || !Array.isArray(args) || typeof options !== "object" ||
options.timeout !== undefined || options.signal !== undefined ||
(options.cwd !== undefined && path.resolve(options.cwd) !== process.cwd()) ||
(options.stdio !== undefined && (!Array.isArray(options.stdio) ||
options.stdio[1] !== "pipe" || options.stdio[2] !== "pipe")) ||
!resolvesToFixture(file, options)) return nativeExecFileSync(file, args, options);
const env = options.env ?? process.env;
const stdin = options.stdio?.[0];
if (stdin !== undefined && !["pipe", "ignore", "inherit", 0].includes(stdin)) {
return nativeExecFileSync(file, args, options);
}
const result = require(env.FIXTURE_GH)("direct", args, {
env,
input: options.input ?? (stdin === "inherit" || stdin === 0 ? undefined : Buffer.alloc(0)),
});
const encode = (value) => options.encoding && options.encoding !== "buffer"
? Buffer.from(value).toString(options.encoding) : Buffer.from(value);
const stdout = encode(result.stdout), stderr = encode(result.stderr);
if (result.status !== 0) {
throw Object.assign(new Error("Command failed: " + file + "\n" + result.stderr), {
status: result.status, signal: null, stdout, stderr, output: [null, stdout, stderr],
});
}
return stdout;
};
syncBuiltinESMExports();
`,
};
}

View file

@ -6,6 +6,7 @@ import { resolveVitestNodeArgs } from "../../scripts/lib/vitest-process-env.mts"
import { requireNodeTool } from "../helpers/node-toolchain.js";
import { useAutoCleanupTempDirTracker } from "../helpers/temp-dir.js";
import { createMergeGitFixtureFactory } from "./pr-merge-fixture-git.test-support.js";
import { createMergeGhFixturePrograms } from "./pr-merge-gh-process.test-support.js";
import {
createPriorCiFixtureState,
priorCiSecurityFixtureSource,
@ -291,16 +292,11 @@ export function createMergeOutcomeFixtureHarness() {
};
const state = (): typeof initial => JSON.parse(readFileSync(statePath, "utf8"));
save(initial);
const gh = template.program(
"gh.mjs",
const ghPrograms = createMergeGhFixturePrograms(
`
import fs from "node:fs";
import { createHash } from "node:crypto";
import { execFileSync, spawnSync } from "node:child_process";
const [route,...args]=process.argv.slice(2);
const file=process.env.FIXTURE_STATE;
const s=JSON.parse(fs.readFileSync(file,"utf8"));
const git=(args,input)=>execFileSync("git",["-c","commit.gpgsign=false","-c","core.hooksPath=/dev/null",...args],{cwd:process.env.FIXTURE_REPO,input,encoding:"utf8"}).trim();
const git=(args,input)=>execFileSync("git",["-c","commit.gpgsign=false","-c","core.hooksPath=/dev/null",...args],{cwd:process.env.FIXTURE_REPO,env:process.env,input,encoding:"utf8"}).trim();
const save=()=>fs.writeFileSync(file,JSON.stringify(s));
const out=(value)=>{
const body=typeof value==="string"?value:JSON.stringify(value);
@ -409,7 +405,7 @@ else if(args[0]==="api"&&args.some(arg=>new RegExp("^repos/[^/]+/[^/]+$").test(a
if(!args.includes("--hostname")) fail("missing repository hostname");
if(s.repoAuthorityUnavailable) fail("repository metadata unavailable");
if(s.restDispatchChange) {
const retained=spawnSync("git",["show","refs/openclaw/pr-merge-outcomes/123:outcome.json"],{cwd:process.env.FIXTURE_REPO,encoding:"utf8"});
const retained=spawnSync("git",["show","refs/openclaw/pr-merge-outcomes/123:outcome.json"],{cwd:process.env.FIXTURE_REPO,env:process.env,encoding:"utf8"});
if(retained.status===0) {
const intent=JSON.parse(retained.stdout);
if(intent.phase==="intent"&&intent.accepted===false) {
@ -463,7 +459,7 @@ else if(args[0]==="api"&&args.includes("repos/fixture/repo/git/ref/heads/main"))
if(s.restObservation?.main&&!s.restObservation.afterPolicyRead&&s.restMainReads===0&&s.observationReads>0) applyRestObservation();
s.restMainReads++;
if(s.restMainAdvance&&s.pr.state==="OPEN") {
const retained=spawnSync("git",["show","refs/openclaw/pr-merge-outcomes/123:outcome.json"],{cwd:process.env.FIXTURE_REPO,encoding:"utf8"});
const retained=spawnSync("git",["show","refs/openclaw/pr-merge-outcomes/123:outcome.json"],{cwd:process.env.FIXTURE_REPO,env:process.env,encoding:"utf8"});
if(retained.status===0) {
const intent=JSON.parse(retained.stdout);
if(intent.phase==="intent"&&intent.accepted===false) {
@ -693,6 +689,8 @@ else if(args[0]==="pr"&&args[1]==="view") {
save();
`,
);
const gh = template.program("gh.cjs", ghPrograms.cli);
const ghPreload = template.program("gh-preload.cjs", ghPrograms.preload);
const shell = template.program(
"invoke.sh",
`#!/usr/bin/env bash
@ -787,12 +785,14 @@ fi
TMPDIR: root,
// Reuse compiled owner modules across native children, never mutable fixture state.
NODE_COMPILE_CACHE: template.compileCache,
NODE_OPTIONS: `--require ${JSON.stringify(ghPreload)}`,
FIXTURE_STATE: statePath,
FIXTURE_ROOT: root,
FIXTURE_REPO: repo,
FIXTURE_REMOTE: remote,
FIXTURE_SCRIPTS: scripts,
FIXTURE_GH: gh,
FIXTURE_GH_BIN: join(bin, "gh"),
FIXTURE_NODE: nodeExecutable,
OPENCLAW_PR_MERGE_METHOD: "squash",
OPENCLAW_PR_STRICT_DRIFT: "",