test(#4525): migrate statusline/teams batch to named timeout constants (#4677)

Batch 14 of the ad hoc timeout literal migration (epic #4445). Replaces
every bare numeric timeout/timeoutMs object-literal property in
tests/gsd-statusline.test.cjs and tests/teams-status.test.cjs with a named
constant, per eslint-rules/no-adhoc-timeout-literal.cjs. Removes these 2
files from the rule's allowlist.

Issue #4525 cautions against forcing either file onto PROBE_TIMEOUT_MS
without checking, since both invoke a "long-lived status renderer."
Checking each file's actual production handler separately found the
caution applies to one file and not the other: gsd-statusline.test.cjs's 7
sites all spawn hooks/gsd-statusline.js, which does real rendering work
(context-window, git, teams state) -- two new file-local constants
(STATUSLINE_HOOK_TIMEOUT_MS=4000, 6 sites; STATUSLINE_HOOK_GIT_SHIM_TIMEOUT_MS
=5000, 1 site for a heavier git-shim test). teams-status.test.cjs's 5 sites
spawn `gsd-tools.cjs query teams-status`, whose handler
(gsd-core/bin/lib/teams-status.cjs's cmdTeamsStatus) is a lightweight
env-truthiness check with no rendering or fan-out -- genuinely matching
PROBE_TIMEOUT_MS's class, confirmed by reading the handler directly rather
than assumed from the matching value. No new shared-helper constant is
needed this batch. No src/bin file touched, no numeric value changed
anywhere.

Co-authored-by: sim <sim@local>
Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Tom Boucher
2026-09-13 00:23:50 -04:00
committed by GitHub
parent 56b706a07a
commit 0cee0eee47
3 changed files with 47 additions and 15 deletions

View File

@@ -7,7 +7,6 @@
"tests/code-review.test.cjs",
"tests/dispatcher.test.cjs",
"tests/gate-predicate-evaluator.test.cjs",
"tests/gsd-statusline.test.cjs",
"tests/phase6-capstone-conformance.test.cjs",
"tests/plan-phase-stall-detection.test.cjs",
"tests/plan-review-convergence.test.cjs",
@@ -17,6 +16,5 @@
"tests/reviewer-manifest-body.test.cjs",
"tests/reviewer-trust-disclosure.test.cjs",
"tests/run-tests-temp-root.test.cjs",
"tests/shell-command-projection-dispatch.test.cjs",
"tests/teams-status.test.cjs"
"tests/shell-command-projection-dispatch.test.cjs"
]

View File

@@ -24,6 +24,26 @@ const {
} = require('../hooks/gsd-statusline.js');
const { cleanup, saveSessionEnv, restoreSessionEnv, clearSessionEnv } = require('./helpers.cjs');
/**
* A single hooks/gsd-statusline.js spawn, no fan-out -- the "long-lived
* status renderer" class (renders context-window percentage, git
* branch/status, active-teams state), a distinct and heavier operation
* than a trivial CLI query despite this file's own PROBE_TIMEOUT_MS-sized
* sibling class norm elsewhere in the suite.
*/
const STATUSLINE_HOOK_TIMEOUT_MS = 4000;
/**
* The same hooks/gsd-statusline.js hook as STATUSLINE_HOOK_TIMEOUT_MS, but
* this one test rigs a custom PATH (a git shim directory) and
* CLAUDE_CONFIG_DIR override -- a heavier setup than the plain invocation,
* hence the larger pre-existing bound. Coincides numerically with
* tests/helpers/timeouts.cjs's SCAN_USAGE_ERROR_TIMEOUT_MS and
* MALFORMED_INPUT_HOOK_TIMEOUT_MS (both 5000ms) but describes neither of
* those operations -- kept local.
*/
const STATUSLINE_HOOK_GIT_SHIM_TIMEOUT_MS = 5000;
// ─── parseStateMd ───────────────────────────────────────────────────────────
describe('parseStateMd', () => {
@@ -639,7 +659,7 @@ describe('context meter respects CLAUDE_CODE_AUTO_COMPACT_WINDOW (#2219)', () =>
delete env.CLAUDE_CODE_AUTO_COMPACT_WINDOW;
}
const r = runHookSeam(hookPath, [], { input: payload, env, timeoutMs: 4000 });
const r = runHookSeam(hookPath, [], { input: payload, env, timeoutMs: STATUSLINE_HOOK_TIMEOUT_MS });
const stdout = r.stdout;
// Parse normalized used% from the statusline bar output (e.g. "60%")
@@ -736,7 +756,7 @@ describe('context meter boundary: acw at/near totalCtx does not pin used at 100%
input: payload,
env,
encoding: 'utf8',
timeout: 4000,
timeout: STATUSLINE_HOOK_TIMEOUT_MS,
});
} catch (e) {
stdout = e.stdout || '';
@@ -878,7 +898,7 @@ describe('todo-resolution: resolves in_progress task from the newest matching to
input: payload,
env,
encoding: 'utf8',
timeout: 4000,
timeout: STATUSLINE_HOOK_TIMEOUT_MS,
});
} catch (e) {
stdout = e.stdout || '';
@@ -1656,7 +1676,7 @@ test('config-set statusline.show_context_tokens yes → rejected', () => {
},
},
});
const r = runHookSeam(hookPath, [], { input: payload, timeoutMs: 4000 });
const r = runHookSeam(hookPath, [], { input: payload, timeoutMs: STATUSLINE_HOOK_TIMEOUT_MS });
// eslint-disable-next-line no-control-regex -- stripping ANSI SGR sequences from captured CLI output
return r.stdout.replace(/\x1b\[[0-9;]*m/g, '');
}
@@ -2254,7 +2274,7 @@ test('config-set statusline.show_context_tokens yes → rejected', () => {
workspace: { current_dir: dir },
session_id: `test-git-${Date.now()}-${Math.random().toString(36).slice(2)}`,
});
const r = runHookSeam(hookPath, [], { input: payload, timeoutMs: 4000 });
const r = runHookSeam(hookPath, [], { input: payload, timeoutMs: STATUSLINE_HOOK_TIMEOUT_MS });
// eslint-disable-next-line no-control-regex -- stripping ANSI SGR sequences from captured CLI output
return r.stdout.replace(/\x1b\[[0-9;]*m/g, '');
}
@@ -2514,7 +2534,7 @@ describe('evaluateUpdateCache lineage guard', () => {
});
const r = runHookSeam(path.join(cold.hooksDir, 'gsd-statusline.js'), [], {
input: payload,
timeoutMs: 4000,
timeoutMs: STATUSLINE_HOOK_TIMEOUT_MS,
});
assert.equal(r.exitCode, 0, `must exit 0 on a build failure; stdout: ${r.stdout} stderr: ${r.stderr}`);
assert.equal(r.stdout, '', 'must degrade to empty output, not throw a stack trace to stdout');
@@ -3056,7 +3076,7 @@ describe('evaluateUpdateCache lineage guard', () => {
const r = runHookSeam(hookPath, [], {
input: payload,
env: { ...process.env, PATH: `${shimDir}${path.delimiter}${process.env.PATH}`, CLAUDE_CONFIG_DIR: claudeDir },
timeoutMs: 5000,
timeoutMs: STATUSLINE_HOOK_GIT_SHIM_TIMEOUT_MS,
});
assert.equal(r.outcome, OUTCOME.EXITED, `expected clean exit, got outcome=${r.outcome}`);
assert.equal(r.exitCode, 0);

View File

@@ -15,6 +15,20 @@ const path = require('node:path');
const { spawnSync } = require('node:child_process');
const { resolveTeamsStatus } = require('../gsd-core/bin/lib/teams-status.cjs');
/**
* Reuses the existing PROBE_TIMEOUT_MS constant rather than declaring a
* new one, despite this batch's own issue (#4525, epic #4445) cautioning
* against forcing a "long-lived status renderer" onto PROBE_TIMEOUT_MS
* without checking. Checked directly: gsd-core/bin/lib/teams-status.cjs's
* cmdTeamsStatus (what these 5 sites spawn via `gsd-tools.cjs query
* teams-status`) is a lightweight env-truthiness check plus one fast
* synchronous runtime resolution -- no rendering, no subprocess fan-out --
* genuinely matching PROBE_TIMEOUT_MS's class, not the heavier
* "long-lived renderer" class the issue's caution targets (that class is
* hooks/gsd-statusline.js, correctly kept off PROBE_TIMEOUT_MS in this
* same batch's sibling file, tests/gsd-statusline.test.cjs).
*/
const { PROBE_TIMEOUT_MS } = require('./helpers/timeouts.cjs');
const gsdToolsPath = path.resolve(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
@@ -120,7 +134,7 @@ describe('gsd-tools query teams-status — CLI subprocess tests', () => {
[gsdToolsPath, 'query', 'teams-status'],
{
encoding: 'utf8',
timeout: 15000,
timeout: PROBE_TIMEOUT_MS,
env: makeEnv({ runtime: 'claude', teamsFlag: '1' }),
},
);
@@ -142,7 +156,7 @@ describe('gsd-tools query teams-status — CLI subprocess tests', () => {
[gsdToolsPath, 'query', 'teams-status'],
{
encoding: 'utf8',
timeout: 15000,
timeout: PROBE_TIMEOUT_MS,
env: makeEnv({ runtime: 'claude' }),
},
);
@@ -158,7 +172,7 @@ describe('gsd-tools query teams-status — CLI subprocess tests', () => {
[gsdToolsPath, 'query', 'teams-status', '--active'],
{
encoding: 'utf8',
timeout: 15000,
timeout: PROBE_TIMEOUT_MS,
env: makeEnv({ runtime: 'claude', teamsFlag: '1' }),
},
);
@@ -173,7 +187,7 @@ describe('gsd-tools query teams-status — CLI subprocess tests', () => {
[gsdToolsPath, 'query', 'teams-status', '--active'],
{
encoding: 'utf8',
timeout: 15000,
timeout: PROBE_TIMEOUT_MS,
env: makeEnv({ runtime: 'claude' }),
},
);
@@ -186,7 +200,7 @@ describe('gsd-tools query teams-status — CLI subprocess tests', () => {
[gsdToolsPath, 'query', 'teams-status', '--active'],
{
encoding: 'utf8',
timeout: 15000,
timeout: PROBE_TIMEOUT_MS,
env: makeEnv({ runtime: 'codex', teamsFlag: '1' }),
},
);