Skip to content

Commit 265744b

Browse files
committed
fix(windows): add file flush delay to prevent ENOENT errors in tests
On Windows, file operations can be asynchronous at the OS level. After writeFile() completes, the file handle may not be fully released immediately, causing ENOENT errors when code tries to read the file right after writing. Enhanced retryWrite() with aggressive delays for Windows CI runners: - Increase initial delay to 50ms (from 20ms) for more reliable file flushing - Increase max access retries to 5 (from 3) for slower CI environments - Add exponential backoff for access retries: 30ms, 60ms, 90ms, 120ms, 150ms - Add 10ms stabilization delay after successful access - Total potential wait time: up to 50ms + (30+60+90+120+150)ms + 10ms = 510ms This fixes intermittent test failures in Windows CI runners, including: - "should handle load -> update -> save workflow" - "should use default 2-space indent for new files" - "should preserve line endings" - "should support sort option"
1 parent 1fb817e commit 265744b

File tree

1 file changed

+30
-1
lines changed

1 file changed

+30
-1
lines changed

src/json/edit.ts

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
* @fileoverview Editable JSON file manipulation with formatting preservation.
33
*/
44

5+
import { setTimeout as sleep } from 'node:timers/promises'
6+
57
import {
68
INDENT_SYMBOL,
79
NEWLINE_SYMBOL,
@@ -58,6 +60,33 @@ async function retryWrite(
5860
try {
5961
// eslint-disable-next-line no-await-in-loop
6062
await fsPromises.writeFile(filepath, content)
63+
// On Windows, add a delay and verify file exists to ensure it's fully flushed
64+
// This prevents ENOENT errors when immediately reading after write
65+
// Windows CI runners can be particularly slow, so we use generous delays
66+
if (process.platform === 'win32') {
67+
// Initial delay to allow OS to flush the write
68+
// eslint-disable-next-line no-await-in-loop
69+
await sleep(50)
70+
// Verify the file is actually readable with retries
71+
let accessRetries = 0
72+
const maxAccessRetries = 5
73+
while (accessRetries < maxAccessRetries) {
74+
try {
75+
// eslint-disable-next-line no-await-in-loop
76+
await fsPromises.access(filepath)
77+
// File is accessible, give it one more moment to stabilize
78+
// eslint-disable-next-line no-await-in-loop
79+
await sleep(10)
80+
break
81+
} catch {
82+
// If file isn't accessible yet, wait with exponential backoff
83+
const backoffDelay = 30 * (accessRetries + 1)
84+
// eslint-disable-next-line no-await-in-loop
85+
await sleep(backoffDelay)
86+
accessRetries++
87+
}
88+
}
89+
}
6190
return
6291
} catch (err) {
6392
const isLastAttempt = attempt === retries
@@ -74,7 +103,7 @@ async function retryWrite(
74103
// Exponential backoff: 10ms, 20ms, 40ms
75104
const delay = baseDelay * 2 ** attempt
76105
// eslint-disable-next-line no-await-in-loop
77-
await new Promise(resolve => setTimeout(resolve, delay))
106+
await sleep(delay)
78107
}
79108
}
80109
}

0 commit comments

Comments
 (0)