chore(lint): refactor magic-sleep tests to async waits + ratchet rules to error (#735)
Replace raw setTimeout/Atomics.wait synchronization sleeps in 4 test files with a shared async delay()/waitFor() poll-for-condition helper in tests/helpers.cjs, then flip local/no-magic-sleep-in-tests and no-restricted-syntax from warn to error so the debt can't regrow. - tests/helpers.cjs: add delay(ms) + waitFor(predicate, opts), exported - bug-1974: setTimeout backoff -> await delay() - config.test: drop Atomics.wait sleep(); async retry via await delay() - graphify: waitForBuildStatus/cleanupHookRepo async via await delay() - locking-bugs: 3 Atomics.wait poll loops -> await waitFor() - eslint.config.mjs: ratchet both rules warn -> error Refs #733 Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ const { test, describe, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
const { runGsdTools, createTempProject, cleanup, delay } = require('./helpers.cjs');
|
||||
|
||||
// ─── helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
@@ -25,11 +25,7 @@ function writeConfig(tmpDir, obj) {
|
||||
fs.writeFileSync(configPath, JSON.stringify(obj, null, 2), 'utf-8');
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
||||
}
|
||||
|
||||
function runConfigEnsureSectionWithRetry(tmpDir, attempts = 4) {
|
||||
async function runConfigEnsureSectionWithRetry(tmpDir, attempts = 4) {
|
||||
let last;
|
||||
for (let i = 0; i < attempts; i += 1) {
|
||||
last = runGsdTools('config-ensure-section', tmpDir);
|
||||
@@ -38,7 +34,7 @@ function runConfigEnsureSectionWithRetry(tmpDir, attempts = 4) {
|
||||
const detail = `${last.error || ''}\n${last.output || ''}`;
|
||||
const transient = /(EPERM|EBUSY|EACCES|ENOTEMPTY|resource busy|used by another process|permission denied)/i.test(detail);
|
||||
if (!transient || i === attempts - 1) return last;
|
||||
sleep(150 * (i + 1));
|
||||
await delay(150 * (i + 1));
|
||||
}
|
||||
return last;
|
||||
}
|
||||
@@ -81,13 +77,13 @@ describe('config-ensure-section command', () => {
|
||||
assert.ok('search_gitignored' in config, 'search_gitignored should exist');
|
||||
});
|
||||
|
||||
test('is idempotent — returns already_exists on second call', () => {
|
||||
const first = runConfigEnsureSectionWithRetry(tmpDir);
|
||||
test('is idempotent — returns already_exists on second call', async () => {
|
||||
const first = await runConfigEnsureSectionWithRetry(tmpDir);
|
||||
assert.ok(first.success, `First call failed: ${first.error}`);
|
||||
const firstOutput = JSON.parse(first.output);
|
||||
assert.strictEqual(firstOutput.created, true);
|
||||
|
||||
const second = runConfigEnsureSectionWithRetry(tmpDir);
|
||||
const second = await runConfigEnsureSectionWithRetry(tmpDir);
|
||||
assert.ok(second.success, `Second call failed: ${second.error}`);
|
||||
const secondOutput = JSON.parse(second.output);
|
||||
assert.strictEqual(secondOutput.created, false);
|
||||
|
||||
Reference in New Issue
Block a user