Merge pull request #4566 from open-gsd/test/4514-batch3-git-adjacent
test(#4514): migrate git-adjacent workflow checks to named timeout constants
This commit is contained in:
@@ -21,7 +21,6 @@
|
||||
"tests/check-glossary-refs.test.cjs",
|
||||
"tests/check-predicate.test.cjs",
|
||||
"tests/check-tdd-review-checkpoint-e2e.test.cjs",
|
||||
"tests/ci-rebase-check.test.cjs",
|
||||
"tests/cjs-command-router-adapter.test.cjs",
|
||||
"tests/code-review-pipeline-regression.test.cjs",
|
||||
"tests/code-review.test.cjs",
|
||||
@@ -50,7 +49,6 @@
|
||||
"tests/gsd-mcp-server-bin.test.cjs",
|
||||
"tests/gsd-secret-read-guard.test.cjs",
|
||||
"tests/gsd-statusline.test.cjs",
|
||||
"tests/gsd-validate-commit-crash-policy.test.cjs",
|
||||
"tests/gsd-write-guard.test.cjs",
|
||||
"tests/health-validation.test.cjs",
|
||||
"tests/hooks-commonjs-marker.test.cjs",
|
||||
@@ -87,7 +85,6 @@
|
||||
"tests/plan-review-convergence.test.cjs",
|
||||
"tests/plugin-manifest.test.cjs",
|
||||
"tests/policy-160-route0-resume.test.cjs",
|
||||
"tests/pr-branch-planning-filter.test.cjs",
|
||||
"tests/prohibition-enforcement.test.cjs",
|
||||
"tests/prompt-injection-scan.security.test.cjs",
|
||||
"tests/qa/tdd-walk.cjs",
|
||||
@@ -95,7 +92,6 @@
|
||||
"tests/read-guard.test.cjs",
|
||||
"tests/read-injection-scanner.property.test.cjs",
|
||||
"tests/read-injection-scanner.security.test.cjs",
|
||||
"tests/reapply-verify-hunks.test.cjs",
|
||||
"tests/release-tarball-smoke.install.test.cjs",
|
||||
"tests/representative-corpus.test.cjs",
|
||||
"tests/review-lane-invocation.test.cjs",
|
||||
@@ -108,14 +104,11 @@
|
||||
"tests/security.test.cjs",
|
||||
"tests/shared-hooks-dir-resolution.test.cjs",
|
||||
"tests/shell-command-projection-dispatch.test.cjs",
|
||||
"tests/ship-notes-wedged-pr.test.cjs",
|
||||
"tests/slug-derivation-drift-guard.test.cjs",
|
||||
"tests/state-document.test.cjs",
|
||||
"tests/state-todos-render.test.cjs",
|
||||
"tests/task-command-router-resolve-content.test.cjs",
|
||||
"tests/task-content-resolution.test.cjs",
|
||||
"tests/task-content-resolver-grammar-parity.test.cjs",
|
||||
"tests/teams-status.test.cjs",
|
||||
"tests/windsurf-hooks-bridge.test.cjs",
|
||||
"tests/worktree-safety.test.cjs"
|
||||
"tests/windsurf-hooks-bridge.test.cjs"
|
||||
]
|
||||
|
||||
@@ -27,7 +27,16 @@ const NODE = process.execPath;
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
const { gitOrThrow } = require('./helpers/git-fixture.cjs');
|
||||
// #3145: class-norm timeout, not a per-suite value — see helpers/timeouts.cjs.
|
||||
const { GIT_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
const { GIT_TIMEOUT_MS, QUICK_SPAWN_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
/**
|
||||
* The real ci-rebase-check.cjs script run below (fetch+merge against a real
|
||||
* git fixture remote) is heavier than a QUICK_SPAWN_TIMEOUT_MS-class call —
|
||||
* it does real git network/merge work, not a trivial mocked/in-process
|
||||
* operation. Kept as its own pre-existing value, unchanged by this
|
||||
* migration (#4514).
|
||||
*/
|
||||
const CI_REBASE_SCRIPT_TIMEOUT_MS = 20000;
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Helper: run a small inline Node snippet that requires the run() helper
|
||||
@@ -54,7 +63,7 @@ function evalRunHelper(stmts) {
|
||||
}
|
||||
${stmts}
|
||||
`;
|
||||
const r = spawnSync(NODE, ['-e', code], { encoding: 'utf8', timeout: 10_000 });
|
||||
const r = spawnSync(NODE, ['-e', code], { encoding: 'utf8', timeout: QUICK_SPAWN_TIMEOUT_MS });
|
||||
return { status: r.status ?? 1, stdout: r.stdout ?? '', stderr: r.stderr ?? '' };
|
||||
}
|
||||
|
||||
@@ -157,7 +166,7 @@ describe('ci-rebase-check: fetch-retry loop resolves when git fetch succeeds', (
|
||||
const r = spawnSync(NODE, [SCRIPT], {
|
||||
cwd: workDir,
|
||||
encoding: 'utf8',
|
||||
timeout: 20_000,
|
||||
timeout: CI_REBASE_SCRIPT_TIMEOUT_MS,
|
||||
env: {
|
||||
...process.env,
|
||||
GITHUB_BASE_REF: 'main',
|
||||
|
||||
@@ -31,6 +31,17 @@ const path = require('node:path');
|
||||
const { createTempDir, cleanup, TEST_ENV_BASE } = require('./helpers.cjs');
|
||||
const { runHook, OUTCOME } = require('./helpers/process-seam.cjs');
|
||||
|
||||
/**
|
||||
* gsd-validate-commit.sh is invoked via bash and internally fans out to
|
||||
* nested `node -e` calls (the classifier/config-read/JSON-extraction
|
||||
* sites this file's own header docblock describes) -- conceptually the
|
||||
* same shape `HOOK_FANOUT_TIMEOUT_MS` (60000ms) describes, but this hook
|
||||
* sits at a much lighter pre-existing value. Not reclassified/raised: no
|
||||
* bench citation exists for whether this specific hook needs more.
|
||||
* Pre-existing value, unchanged by this migration (#4514).
|
||||
*/
|
||||
const VALIDATE_COMMIT_HOOK_TIMEOUT_MS = 15000;
|
||||
|
||||
const HOOK_PATH = path.join(__dirname, '..', 'hooks', 'gsd-validate-commit.sh');
|
||||
|
||||
const CONFORMING_COMMIT_PAYLOAD = JSON.stringify({
|
||||
@@ -94,7 +105,7 @@ function runValidateCommit({ payload, cwd, env } = {}) {
|
||||
cwd,
|
||||
env: { ...process.env, ...TEST_ENV_BASE, ...env },
|
||||
input: payload,
|
||||
timeoutMs: 15000,
|
||||
timeoutMs: VALIDATE_COMMIT_HOOK_TIMEOUT_MS,
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -110,6 +110,23 @@ const INSTALL_TIMEOUT_MS = 120000;
|
||||
*/
|
||||
const SEAM_DEFAULT_TIMEOUT_MS = 60000;
|
||||
|
||||
/**
|
||||
* A cheap, lightweight subprocess/hook invocation whose own work is
|
||||
* trivial -- a synchronous in-process guard hook, a fully-mocked shell
|
||||
* harness (git/gh functions stubbed out), a small `node -e` snippet, or a
|
||||
* lint script scanning a tiny temp fixture -- with no real git/network/
|
||||
* fan-out work inside. 10000ms leaves generous headroom over each call
|
||||
* site's sub-second observed worst case (#4514, batch 3 of the ad hoc
|
||||
* timeout literal migration, epic #4445 -- consolidates 5 call sites
|
||||
* across 4 files that had all independently arrived at this exact value).
|
||||
*
|
||||
* Deliberately NOT the same as `PROBE_TIMEOUT_MS` (15000ms): consolidating
|
||||
* onto that would RAISE these sites' bound with no bench citation, which
|
||||
* this migration's scope (naming, not tuning) does not permit. This norm
|
||||
* is for the even-lighter end of the spectrum PROBE_TIMEOUT_MS occupies.
|
||||
*/
|
||||
const QUICK_SPAWN_TIMEOUT_MS = 10000;
|
||||
|
||||
module.exports = {
|
||||
PROBE_TIMEOUT_MS,
|
||||
HOOK_FANOUT_TIMEOUT_MS,
|
||||
@@ -118,4 +135,5 @@ module.exports = {
|
||||
BUILD_TIMEOUT_MS,
|
||||
INSTALL_TIMEOUT_MS,
|
||||
SEAM_DEFAULT_TIMEOUT_MS,
|
||||
QUICK_SPAWN_TIMEOUT_MS,
|
||||
};
|
||||
|
||||
@@ -84,6 +84,14 @@ const CONFIG_SCHEMA_MANIFEST_PATH = path.join(
|
||||
REPO_ROOT, 'gsd-core', 'bin', 'shared', 'config-schema.manifest.json',
|
||||
);
|
||||
|
||||
/**
|
||||
* The cherry-pick recipe below runs the REAL create_pr_branch loop
|
||||
* extracted from pr-branch.md against a real git fixture (rev-list,
|
||||
* checkout, cherry-pick) -- genuine git work, not a mocked/trivial
|
||||
* operation. Pre-existing value, unchanged by this migration (#4514).
|
||||
*/
|
||||
const CHERRY_PICK_RECIPE_TIMEOUT_MS = 30000;
|
||||
|
||||
// ── Shared git-fixture primitives (L2 + L4) ────────────────────────────────
|
||||
|
||||
function git(args, cwd) {
|
||||
@@ -379,7 +387,7 @@ describe('#2971 — pr-branch.md planning.pr_strict filter (failing-first)', ()
|
||||
const filterPaths = strict ? ['.planning/'] : transientDirs.map((d) => `.planning/${d}/`);
|
||||
const script = buildRecipeScript(filterPaths);
|
||||
try {
|
||||
const stdout = execFileSync('sh', ['-c', script], { cwd: repoDir, encoding: 'utf8', timeout: 30000 });
|
||||
const stdout = execFileSync('sh', ['-c', script], { cwd: repoDir, encoding: 'utf8', timeout: CHERRY_PICK_RECIPE_TIMEOUT_MS });
|
||||
return { status: 0, stdout, stderr: '' };
|
||||
} catch (err) {
|
||||
return {
|
||||
|
||||
@@ -1099,6 +1099,12 @@ let savedHome;
|
||||
let savedUserProfile;
|
||||
let restoreConfigEnv;
|
||||
|
||||
/**
|
||||
* Runs the real verify-reapply-patches.cjs CLI against patches+config
|
||||
* fixture dirs. Pre-existing value, unchanged by this migration (#4514).
|
||||
*/
|
||||
const VERIFY_REAPPLY_TIMEOUT_MS = 60000;
|
||||
|
||||
function writeFile(absPath, content) {
|
||||
fs.mkdirSync(path.dirname(absPath), { recursive: true });
|
||||
fs.writeFileSync(absPath, content);
|
||||
@@ -1110,7 +1116,7 @@ function runVerifier() {
|
||||
'--patches-dir', patchesDir,
|
||||
'--config-dir', configDir,
|
||||
'--json',
|
||||
], { timeoutMs: 60_000 });
|
||||
], { timeoutMs: VERIFY_REAPPLY_TIMEOUT_MS });
|
||||
return {
|
||||
status: r.exitCode,
|
||||
report: r.stdout && r.stdout.length ? JSON.parse(r.stdout) : null,
|
||||
@@ -1238,6 +1244,14 @@ function resetFixture() {
|
||||
fs.mkdirSync(pristineDir);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the real verify-reapply-patches.cjs CLI against patches+config+
|
||||
* pristine fixture dirs -- a distinct describe-block scope from the other
|
||||
* runVerifier() above (different fixture, different pre-existing budget).
|
||||
* Pre-existing value, unchanged by this migration (#4514).
|
||||
*/
|
||||
const VERIFY_REAPPLY_PRISTINE_TIMEOUT_MS = 30000;
|
||||
|
||||
function runVerifier() {
|
||||
const r = runNode([
|
||||
SCRIPT,
|
||||
@@ -1245,7 +1259,7 @@ function runVerifier() {
|
||||
'--config-dir', configDir,
|
||||
'--pristine-dir', pristineDir,
|
||||
'--json',
|
||||
], { timeoutMs: 30_000 });
|
||||
], { timeoutMs: VERIFY_REAPPLY_PRISTINE_TIMEOUT_MS });
|
||||
return {
|
||||
status: r.exitCode,
|
||||
report: r.stdout && r.stdout.length ? JSON.parse(r.stdout) : null,
|
||||
|
||||
@@ -5,6 +5,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { stripFencedCode, scanFencedBlocks } = require('../gsd-core/bin/lib/markdown-sectionizer.cjs');
|
||||
const { cleanup, createTempDir, readFileNormalized } = require('./helpers.cjs');
|
||||
const { QUICK_SPAWN_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const SHIP_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'ship.md');
|
||||
|
||||
@@ -76,7 +77,7 @@ function runTrackShipping(responses) {
|
||||
const result = spawnSync('bash', ['-c', `${preamble}\n${extractTrackShippingScript()}`], {
|
||||
cwd: tmpDir,
|
||||
encoding: 'utf8',
|
||||
timeout: 10000,
|
||||
timeout: QUICK_SPAWN_TIMEOUT_MS,
|
||||
env: {
|
||||
...process.env,
|
||||
CURRENT_BRANCH: 'fix/ship-note',
|
||||
|
||||
@@ -34,6 +34,7 @@ const { generateSlugInternal } = require(path.join(ROOT, 'gsd-core', 'bin', 'lib
|
||||
const { getPhaseDirFromPhaseId } = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'phase-id.cjs'));
|
||||
const { slugify: qaSmellRatchetSlugify } = require(path.join(ROOT, 'scripts', 'qa-smell-ratchet.cjs'));
|
||||
const { createTempDir, cleanup } = require('./helpers.cjs');
|
||||
const { QUICK_SPAWN_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
const { splitLines } = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'text-lines.cjs'));
|
||||
const { MAX_REGEX_LITERAL_LEN, resetRegexScanStats, getRegexScanStats } = require(path.join(ROOT, 'scripts', 'lib', 'drift-scan.cjs'));
|
||||
|
||||
@@ -439,7 +440,7 @@ describe('CLI (main()) — the process.exitCode/stderr surface scanRepo alone do
|
||||
|
||||
const res = spawnSync(process.execPath, [path.join(tmpRoot, 'scripts', 'lint-slug-derivation-drift.cjs')], {
|
||||
encoding: 'utf8',
|
||||
timeout: 10000,
|
||||
timeout: QUICK_SPAWN_TIMEOUT_MS,
|
||||
});
|
||||
|
||||
assert.equal(res.status, 1, 'main() must set a non-zero process.exitCode when a violation is found');
|
||||
@@ -470,7 +471,7 @@ describe('CLI (main()) — the process.exitCode/stderr surface scanRepo alone do
|
||||
|
||||
const res = spawnSync(process.execPath, [path.join(tmpRoot, 'scripts', 'lint-slug-derivation-drift.cjs')], {
|
||||
encoding: 'utf8',
|
||||
timeout: 10000,
|
||||
timeout: QUICK_SPAWN_TIMEOUT_MS,
|
||||
});
|
||||
|
||||
assert.equal(res.status, 0);
|
||||
|
||||
@@ -4980,6 +4980,7 @@ const path = require('node:path');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
const { runHook: seamRunHook } = require('./helpers/process-seam.cjs');
|
||||
const { gitOrThrow } = require('./helpers/git-fixture.cjs');
|
||||
const { QUICK_SPAWN_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
|
||||
|
||||
const HOOK_PATH = path.join(__dirname, '..', 'hooks', 'gsd-worktree-path-guard.js');
|
||||
const INSTALL_SRC = path.join(__dirname, '..', 'bin', 'install.js');
|
||||
@@ -5036,15 +5037,17 @@ function makeWorktree(mainRepo, branchName) {
|
||||
* Run the hook with a given payload, returning the spawnSync result.
|
||||
*/
|
||||
function runHook(cwd, payload) {
|
||||
// 10000ms: previously UNBOUNDED (no `timeout` option passed to spawnSync).
|
||||
// gsd-worktree-path-guard.js is a synchronous, in-process path-guard hook
|
||||
// (fs/path checks against a JSON stdin payload) — no subprocess or network
|
||||
// work of its own. 10s leaves generous headroom over its sub-second
|
||||
// worst case even on a heavily contended CI runner.
|
||||
// QUICK_SPAWN_TIMEOUT_MS (10000ms): previously UNBOUNDED (no `timeout`
|
||||
// option passed to spawnSync). gsd-worktree-path-guard.js is a
|
||||
// synchronous, in-process path-guard hook (fs/path checks against a JSON
|
||||
// stdin payload) — no subprocess or network work of its own. 10s leaves
|
||||
// generous headroom over its sub-second worst case even on a heavily
|
||||
// contended CI runner. See tests/helpers/timeouts.cjs for the shared
|
||||
// norm this value was promoted to (#4514).
|
||||
const r = seamRunHook(HOOK_PATH, [], {
|
||||
cwd,
|
||||
input: JSON.stringify(payload),
|
||||
timeoutMs: 10_000,
|
||||
timeoutMs: QUICK_SPAWN_TIMEOUT_MS,
|
||||
});
|
||||
return { status: r.exitCode, stdout: r.stdout, stderr: r.stderr };
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user