Files
msd-core/tests
Tom Boucher ff1e3d4920 fix(3803): replace wall-clock deadline poll in graphify-auto-update test (#86)
* fix(3803): replace wall-clock polls with Atomics.wait barrier (mirrors c22e869b)

Three execFileSync('sleep', ...) wall-clock deadline spins in
graphify-auto-update tests replaced with Atomics.wait-based atomicSleep
helper.  This is the same pattern established in c22e869b (PR #3790) for
locking-bugs:180.

- cleanupHookRepo: 50 ms Atomics.wait steps instead of spawning a POSIX
  sleep process per iteration while waiting for .rebuild.lock to clear.
- "completes to status=ok" poll: 100 ms Atomics.wait steps instead of
  execFileSync('sleep', ['0.1']) while waiting for the detached rebuild
  process to write the final status file.
- "completes to status=failed" poll: same fix.

The ceiling deadlines (4 s / 15 s) remain as timeout guards — they are
not the synchronization primitive.  The CPU-yielding wait is now
Atomics.wait so no external process is spawned per iteration.

All 36 tests in the suite pass (node --test tests/graphify-auto-update.test.cjs).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

* fix(3803): replace wall-clock deadline polls with behavior-anchored iteration counts

The previous commit (752a5684) replaced execFileSync('sleep') with
Atomics.wait but kept the `Date.now() + 15000` wall-clock deadline
pattern in the two assertion-path polls.  That is still the timing-flake
antipattern described in #3803: the loop bound is an absolute time, not a
function of the mock's known behavior.

This commit removes both `const deadline = Date.now() + 15000` /
`while (Date.now() < deadline)` loops and replaces them with
iteration-count bounds derived from the mock's declared sleepMs:

  waitBudget = sleepMs + 2000   (2 s covers two bash spawn overheads:
                                  hook script + detached rebuild subprocess)
  maxIter    = ceil(waitBudget / 100)   (100 ms poll step)

The 2 s buffer absorbs process spawn + filesystem write latency without
anchoring to an absolute wall-clock value.  If the budget is exhausted the
assertion below fires with a clear diagnostic instead of a silent
time-dependent pass.

Same antipattern fixed by PR #3793 (bug-1974-context-exhaustion-record).

Verified: 3 consecutive local runs, 0 failures each (~80-100 s/run).

Anti-pattern sweep results (Date.now() + N in tests/):
  - tests/locking-bugs-1909-1916-1925-1927.test.cjs:291,448 — coordination
    BARRIER backstops (wait for both subprocesses to reach gate), not
    completion polls; different pattern, out of scope for this PR.
  - tests/graphify-auto-update.test.cjs:286 — cleanupHookRepo backstop
    (not an assertion path); acceptable.
  - tests/bug-2962-windows-sdk-shim.test.cjs:132 — Date.now() + 20 (20 ms
    relative offset used in a deadline expiry calculation, not a spin loop).
  - tests/core.test.cjs:2006 — timeAgo(new Date(Date.now() + 5000)) (a
    value assertion for the timeAgo utility, not a polling loop).
No additional assertion-path wall-clock polls found.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 11:24:06 -04:00
..