fix(tests): the post-compact hook test takes its child timeout from the shared timing helper

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-10-15 15:43:36 +02:00
commit c9248bbb52
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
4 changed files with 40 additions and 5 deletions

View file

@ -7,6 +7,7 @@
import { execFile } from 'node:child_process';
import { childEnv } from './child-env.mjs';
import { HOOK_CHILD_TIMEOUT_MS } from './timing.mjs';
// How long a hook may run before the helper kills it. The tests check a
// hook's answer, not its speed (SC #14 times itself, tests/helpers/timing.mjs),
@ -14,7 +15,8 @@ import { childEnv } from './child-env.mjs';
// (post-edit-check) at load 95-177 in four wave sessions while it was 8/8
// green alone (order 10, 2026-10-09). hooks/hooks.json sets no timeout for
// these hooks, so 5 s was stricter than anything the hook meets in use.
export const HOOK_TIMEOUT_MS = 30_000;
// The number lives in tests/helpers/timing.mjs (decision 44), so there is one.
export const HOOK_TIMEOUT_MS = HOOK_CHILD_TIMEOUT_MS;
/**
* Run a hook script by spawning `node <scriptPath>` and piping `input` to stdin.

View file

@ -35,3 +35,13 @@ export async function fastestWithin(limitMs, measure, tries = 3) {
// (2000 -> 15000 ms, 6000 -> 45000 ms) that the floor guard may report as
// loosened-threshold: plan step 1 of round 3 asks for it.
export const CONTROL_RUN = Object.freeze({ timeoutMs: 15_000, hangMs: 45_000, slowStartMs: 2_500 });
// How long a hook test may let its hook child run before it is killed
// (tests/helpers/hook-helper.mjs reads it as HOOK_TIMEOUT_MS, and
// tests/hooks/post-compact-flush.test.mjs uses it directly). A fixed 5 s limit
// killed hook children under full-suite load (decision 44, the same class as
// review 703d87e1). The limit is raised on purpose and is not a loosened
// assertion: the tests check the hook's answer, not its speed. This is the
// raise (5000 -> 30000 ms) that the floor guard may report as
// loosened-threshold: plan step 1 of the converging-review-loops plan asks for it.
export const HOOK_CHILD_TIMEOUT_MS = 30_000;

View file

@ -13,6 +13,7 @@ import { join, dirname } from 'node:path';
import { fileURLToPath } from 'node:url';
import { execFile } from 'node:child_process';
import { childEnv } from '../helpers/child-env.mjs';
import { HOOK_CHILD_TIMEOUT_MS } from '../helpers/timing.mjs';
const HERE = dirname(fileURLToPath(import.meta.url));
const ROOT = join(HERE, '..', '..');
@ -23,10 +24,10 @@ function runHookIn(cwd, input = {}) {
const child = execFile(
'node',
[HOOK],
{ timeout: 5000, cwd, env: childEnv() },
{ timeout: HOOK_CHILD_TIMEOUT_MS, cwd, env: childEnv() },
(err, stdout, stderr) => {
resolve({
code: child.exitCode ?? 0,
code: child.exitCode ?? 1,
stdout: stdout || '',
stderr: stderr || '',
});

View file

@ -12,9 +12,10 @@
import { test } from 'node:test';
import { strict as assert } from 'node:assert';
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
import { tmpdir } from 'node:os';
import { join } from 'node:path';
import { dirname, join } from 'node:path';
import { fileURLToPath } from 'node:url';
import { fastestWithin } from '../helpers/timing.mjs';
import * as timing from '../helpers/timing.mjs';
import { HOOK_TIMEOUT_MS, runHookWithEnv } from '../helpers/hook-helper.mjs';
@ -81,3 +82,24 @@ test('red proof control run (703d87e1): the timeout leaves headroom over a slow
assert.ok(c.hangMs >= c.timeoutMs + 30000, `the hang outlasts the timeout by 30 s: ${seen}`);
assert.ok(c.hangMs < 2 ** 31 - 1, `the hang fits a real timer (below 2^31-1 ms): ${seen}`);
});
// The post-compact hook test (tests/hooks/post-compact-flush.test.mjs) ran its
// hook child with a fixed 5 s limit and was killed under full-suite load
// (decision 44, the same class as 703d87e1). Its limit now comes from
// tests/helpers/timing.mjs (HOOK_CHILD_TIMEOUT_MS), shared with the hook
// helper. The fallback 5000 is the record of the limit before decision 44,
// taken only when the export is absent.
test('post-compact-flush (decision 44): its child timeout comes from tests/helpers/timing.mjs', () => {
const hookTest = readFileSync(
join(dirname(fileURLToPath(import.meta.url)), '..', 'hooks', 'post-compact-flush.test.mjs'),
'utf8',
);
assert.ok(hookTest.includes("'../helpers/timing.mjs'"), 'the hook test imports from ../helpers/timing.mjs');
assert.ok(hookTest.includes('timeout: HOOK_CHILD_TIMEOUT_MS'), 'the hook test uses timeout: HOOK_CHILD_TIMEOUT_MS');
assert.doesNotMatch(hookTest, /\b5_?000\b/, 'the hook test holds no 5000 literal');
const limit = timing.HOOK_CHILD_TIMEOUT_MS ?? 5000;
assert.ok(limit > 5000, `the shared child timeout is above the old 5 s: ${limit}`);
assert.equal(HOOK_TIMEOUT_MS, timing.HOOK_CHILD_TIMEOUT_MS, 'the hook helper and the hook test share one number');
assert.ok(hookTest.includes('child.exitCode ?? 1'), 'a killed child reads as non-zero');
assert.ok(!hookTest.includes('?? 0'), 'no killed child reads as exit 0');
});